ETH Price: $1,976.18 (+0.38%)
 

Overview

ETH Balance

0 ETH

Eth Value

$0.00

More Info

Private Name Tags

Multichain Info

No addresses found
Transaction Hash
Method
Block
From
To
Extend Loan244936582026-02-19 21:43:592 days ago1771537439IN
0x20a3d851...87a9029Db
0 ETH0.000003880.05151553
Extend Loan244936502026-02-19 21:42:232 days ago1771537343IN
0x20a3d851...87a9029Db
0 ETH0.000004620.05002726
Finish Redemptio...244833012026-02-18 11:04:353 days ago1771412675IN
0x20a3d851...87a9029Db
0 ETH0.000013860.05132554
Start Redemption244832972026-02-18 11:03:473 days ago1771412627IN
0x20a3d851...87a9029Db
0 ETH0.000013450.05517138
Start Redemption244616272026-02-15 10:32:356 days ago1771151555IN
0x20a3d851...87a9029Db
0 ETH0.000014810.08086776
Borrow Against R...244224992026-02-09 23:29:1112 days ago1770679751IN
0x20a3d851...87a9029Db
0 ETH0.000019720.06103549
Start Redemption243858302026-02-04 20:20:5917 days ago1770236459IN
0x20a3d851...87a9029Db
0 ETH0.000078070.42610494
Borrow Against R...243810382026-02-04 4:15:4718 days ago1770178547IN
0x20a3d851...87a9029Db
0 ETH0.000034940.10933198
Start Redemption243810342026-02-04 4:14:5918 days ago1770178499IN
0x20a3d851...87a9029Db
0 ETH0.000020.10918726
Start Redemption243720302026-02-02 22:03:1119 days ago1770069791IN
0x20a3d851...87a9029Db
0 ETH0.000436712.18022366
Borrow Against R...243718482026-02-02 21:26:1119 days ago1770067571IN
0x20a3d851...87a9029Db
0 ETH0.000051590.16139962
Borrow Against R...243718452026-02-02 21:25:3519 days ago1770067535IN
0x20a3d851...87a9029Db
0 ETH0.000049150.15341798
Start Redemption243718372026-02-02 21:23:5919 days ago1770067439IN
0x20a3d851...87a9029Db
0 ETH0.000032990.18008544
Start Redemption243611172026-02-01 9:28:1120 days ago1769938091IN
0x20a3d851...87a9029Db
0 ETH0.000013860.07567554
Borrow Against R...243610952026-02-01 9:23:3520 days ago1769937815IN
0x20a3d851...87a9029Db
0 ETH0.000026320.08191287
Start Redemption243610782026-02-01 9:20:1120 days ago1769937611IN
0x20a3d851...87a9029Db
0 ETH0.000014710.08032017
Borrow Against R...243549762026-01-31 12:52:2321 days ago1769863943IN
0x20a3d851...87a9029Db
0 ETH0.000036070.10694534
Start Redemption243549732026-01-31 12:51:4721 days ago1769863907IN
0x20a3d851...87a9029Db
0 ETH0.000041250.22516373
Finish Redemptio...243531612026-01-31 6:48:2322 days ago1769842103IN
0x20a3d851...87a9029Db
0 ETH0.000022270.08532174
Finish Redemptio...243504012026-01-30 21:34:1122 days ago1769808851IN
0x20a3d851...87a9029Db
0 ETH0.000160590.5795173
Start Redemption243481052026-01-30 13:52:4722 days ago1769781167IN
0x20a3d851...87a9029Db
0 ETH0.000060250.24712477
Start Redemption243322962026-01-28 8:58:1124 days ago1769590691IN
0x20a3d851...87a9029Db
0 ETH0.000026670.1093823
Start Redemption243201752026-01-26 16:23:5926 days ago1769444639IN
0x20a3d851...87a9029Db
0 ETH0.000517222.28136864
Extend Loan242902802026-01-22 12:19:5930 days ago1769084399IN
0x20a3d851...87a9029Db
0 ETH0.000054360.72141713
Extend Loan242902752026-01-22 12:18:4730 days ago1769084327IN
0x20a3d851...87a9029Db
0 ETH0.000057070.75737222
View all transactions

View more zero value Internal Transactions in Advanced View mode

Advanced mode:
Loading...
Loading
Loading...
Loading
Cross-Chain Transactions

Block Transaction Difficulty Gas Used Reward
View All Blocks Produced

Validator Index Block Amount
View All Withdrawals

Transaction Hash Block Value Eth2 PubKey Valid
View All Deposits
Loading...
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
DepositRedemptionVault

Compiler Version
v0.8.30+commit.73712a01

Optimization Enabled:
Yes with 10000 runs

Other Settings:
prague EvmVersion
File 1 of 35 : DepositRedemptionVault.sol
// SPDX-License-Identifier: AGPL-3.0
/// forge-lint: disable-start(asm-keccak256, mixed-case-variable)
pragma solidity >=0.8.15;

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

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

// Bophades
import {TRSRYv1} from "src/modules/TRSRY/TRSRY.v1.sol";
import {DEPOSv1} from "src/modules/DEPOS/DEPOS.v1.sol";
import {PolicyEnabler} from "src/policies/utils/PolicyEnabler.sol";
import {Kernel, Policy, Keycode, Permissions, toKeycode} from "src/Kernel.sol";
import {ROLESv1} from "src/modules/ROLES/ROLES.v1.sol";

/// @title  DepositRedemptionVault
/// @notice A contract that manages the redemption of receipt tokens with facility coordination and borrowing
contract DepositRedemptionVault is Policy, IDepositRedemptionVault, PolicyEnabler, ReentrancyGuard {
    using TransferHelper for ERC20;
    using FullMath for uint256;
    using EnumerableSet for EnumerableSet.AddressSet;

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

    /// @notice The number representing 100%
    uint16 public constant ONE_HUNDRED_PERCENT = 100e2;

    /// @notice The number of months in a year
    uint8 internal constant _MONTHS_IN_YEAR = 12;

    /// @notice Constant for one month
    uint48 internal constant _ONE_MONTH = 30 days;

    /// @notice Used to denote no position ID
    uint256 internal constant _NO_POSITION = type(uint256).max;

    // ========== CONFIGURABLE PARAMETERS ========== //

    /// @notice Per-asset-facility max borrow percentage (in 100e2, e.g. 8500 = 85%)
    mapping(bytes32 => uint16) internal _assetFacilityMaxBorrowPercentages;

    /// @notice Per-asset-facility interest rate (annual, in 100e2, e.g. 500 = 5%)
    mapping(bytes32 => uint16) internal _assetFacilityAnnualInterestRates;

    /// @notice Keeper reward percentage (in 100e2, e.g. 500 = 5%)
    uint16 internal _claimDefaultRewardPercentage;

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

    /// @notice The address of the token manager
    IDepositManager public immutable DEPOSIT_MANAGER;

    /// @notice The TRSRY module.
    TRSRYv1 public TRSRY;

    /// @notice The DEPOS module.
    DEPOSv1 public DEPOS;

    /// @notice The number of redemptions per user
    mapping(address => uint16) internal _userRedemptionCount;

    /// @notice The redemption for each user and redemption ID
    /// @dev    Use `_getUserRedemptionKey()` to calculate the key for the mapping.
    ///         A complex key is used to save gas compared to a nested mapping.
    mapping(bytes32 => UserRedemption) internal _userRedemptions;

    /// @notice Registered facilities
    EnumerableSet.AddressSet internal _authorizedFacilities;

    /// @notice Loan for each redemption
    /// @dev    Use `_getUserRedemptionKey()` to calculate the key for the mapping.
    ///         A complex key is used to save gas compared to a nested mapping.
    mapping(bytes32 => Loan) internal _redemptionLoan;

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

    constructor(address kernel_, address depositManager_) Policy(Kernel(kernel_)) {
        // Validate that the DepositManager implements IDepositManager
        if (!IERC165(depositManager_).supportsInterface(type(IDepositManager).interfaceId)) {
            revert RedemptionVault_InvalidDepositManager(depositManager_);
        }

        DEPOSIT_MANAGER = IDepositManager(depositManager_);
    }

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

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

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

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

    // ========== FACILITY MANAGEMENT ========== //

    /// @inheritdoc IDepositRedemptionVault
    function authorizeFacility(address facility_) external onlyAdminRole {
        if (facility_ == address(0)) revert RedemptionVault_InvalidFacility(facility_);
        if (_authorizedFacilities.contains(facility_))
            revert RedemptionVault_FacilityExists(facility_);

        // Validate that the facility implements IDepositFacility (even if it doesn't have the function)
        {
            (bool success, bytes memory data) = facility_.staticcall(
                abi.encodeWithSelector(
                    IERC165.supportsInterface.selector,
                    type(IDepositFacility).interfaceId
                )
            );
            if (!success || abi.decode(data, (bool)) == false)
                revert RedemptionVault_InvalidFacility(facility_);
        }

        _authorizedFacilities.add(facility_);

        emit FacilityAuthorized(facility_);
    }

    /// @inheritdoc IDepositRedemptionVault
    function deauthorizeFacility(address facility_) external onlyEmergencyOrAdminRole {
        if (!_authorizedFacilities.contains(facility_))
            revert RedemptionVault_FacilityNotRegistered(facility_);

        _authorizedFacilities.remove(facility_);

        emit FacilityDeauthorized(facility_);
    }

    /// @inheritdoc IDepositRedemptionVault
    function isAuthorizedFacility(address facility_) external view returns (bool) {
        return _authorizedFacilities.contains(facility_);
    }

    /// @inheritdoc IDepositRedemptionVault
    function getAuthorizedFacilities() external view returns (address[] memory) {
        return _authorizedFacilities.values();
    }

    // ========== ASSETS ========== //

    /// @notice Pull the receipt tokens from the caller
    function _pullReceiptToken(
        IERC20 depositToken_,
        uint8 depositPeriod_,
        address facility_,
        uint256 amount_
    ) internal {
        // Transfer the receipt tokens from the caller to this contract
        IReceiptTokenManager rtm = DEPOSIT_MANAGER.getReceiptTokenManager();
        rtm.transferFrom(
            msg.sender,
            address(this),
            DEPOSIT_MANAGER.getReceiptTokenId(depositToken_, depositPeriod_, facility_),
            amount_
        );
    }

    // ========== USER REDEMPTIONS ========== //

    function _getUserRedemptionKey(
        address user_,
        uint16 redemptionId_
    ) internal pure returns (bytes32) {
        return keccak256(abi.encode(user_, redemptionId_));
    }

    /// @notice Generate a key for the asset-facility parameter mappings
    /// @param asset_ The asset address
    /// @param facility_ The facility address
    /// @return The key for the mapping
    function _getAssetFacilityKey(
        address asset_,
        address facility_
    ) internal pure returns (bytes32) {
        return keccak256(abi.encode(asset_, facility_));
    }

    /// @inheritdoc IDepositRedemptionVault
    function getUserRedemptionCount(address user_) external view returns (uint16 count) {
        return _userRedemptionCount[user_];
    }

    /// @inheritdoc IDepositRedemptionVault
    function getUserRedemption(
        address user_,
        uint16 redemptionId_
    ) external view returns (UserRedemption memory redemption) {
        redemption = _userRedemptions[_getUserRedemptionKey(user_, redemptionId_)];
        if (redemption.depositToken == address(0))
            revert RedemptionVault_InvalidRedemptionId(user_, redemptionId_);

        return redemption;
    }

    /// @inheritdoc IDepositRedemptionVault
    /// @dev        Notes:
    ///             - This function is gas-intensive for users with many redemptions.
    ///             - The index of an element in the returned array is the redemption ID.
    ///             - Redemptions with an amount of 0 (fully redeemed) are included in the array.
    function getUserRedemptions(address user_) external view returns (UserRedemption[] memory) {
        uint16 count = _userRedemptionCount[user_];
        UserRedemption[] memory redemptions = new UserRedemption[](count);

        for (uint16 i = 0; i < count; i++) {
            redemptions[i] = _userRedemptions[_getUserRedemptionKey(user_, i)];
        }

        return redemptions;
    }

    // ========== REDEMPTION FLOW ========== //

    function _onlyValidRedemptionId(address user_, uint16 redemptionId_) internal view {
        // If the deposit token is the zero address, the redemption is invalid
        if (
            _userRedemptions[_getUserRedemptionKey(user_, redemptionId_)].depositToken == address(0)
        ) revert RedemptionVault_InvalidRedemptionId(user_, redemptionId_);
    }

    modifier onlyValidRedemptionId(address user_, uint16 redemptionId_) {
        _onlyValidRedemptionId(user_, redemptionId_);
        _;
    }

    function _validateFacility(address facility_) internal view {
        if (!_authorizedFacilities.contains(facility_))
            revert RedemptionVault_FacilityNotRegistered(facility_);
    }

    modifier onlyValidFacility(address facility_) {
        _validateFacility(facility_);
        _;
    }

    /// @inheritdoc IDepositRedemptionVault
    /// @dev        This function expects receipt tokens to be unwrapped (i.e. native ERC6909 tokens)
    ///
    ///             This function reverts if:
    ///             - The contract is disabled
    ///             - The amount is 0
    ///             - The provided facility is not authorized
    function startRedemption(
        IERC20 depositToken_,
        uint8 depositPeriod_,
        uint256 amount_,
        address facility_
    ) external nonReentrant onlyEnabled onlyValidFacility(facility_) returns (uint16 redemptionId) {
        // Validate that the amount is not 0
        if (amount_ == 0) revert RedemptionVault_ZeroAmount();

        // Create a User Redemption
        redemptionId = _userRedemptionCount[msg.sender]++;
        _userRedemptions[_getUserRedemptionKey(msg.sender, redemptionId)] = UserRedemption({
            depositToken: address(depositToken_),
            depositPeriod: depositPeriod_,
            redeemableAt: uint48(block.timestamp) + uint48(depositPeriod_) * _ONE_MONTH,
            amount: amount_,
            facility: facility_,
            positionId: _NO_POSITION
        });

        // Mark the funds as committed
        IDepositFacility(facility_).handleCommit(depositToken_, depositPeriod_, amount_);

        // Pull the receipt tokens from the caller
        _pullReceiptToken(depositToken_, depositPeriod_, facility_, amount_);

        // Emit events
        emit RedemptionStarted(
            msg.sender,
            redemptionId,
            address(depositToken_),
            depositPeriod_,
            amount_,
            facility_
        );

        return redemptionId;
    }

    /// @inheritdoc IDepositRedemptionVault
    /// @dev        This function expects receipt tokens to be unwrapped (i.e. native ERC6909 tokens)
    ///
    ///             This function reverts if:
    ///             - The contract is disabled
    ///             - The amount is 0
    ///             - The caller is not the owner of the position
    ///             - The amount is greater than the remainingDeposit of the position
    ///             - The facility that created the position is not authorized
    function startRedemption(
        uint256 positionId_,
        uint256 amount_
    ) external nonReentrant onlyEnabled returns (uint16 redemptionId) {
        // Validate that the amount is not 0
        if (amount_ == 0) revert RedemptionVault_ZeroAmount();

        // Get the position details from DEPOS module
        IDepositPositionManager.Position memory position = DEPOS.getPosition(positionId_);

        // Validate that the caller owns the position
        if (position.owner != msg.sender)
            revert IDepositPositionManager.DEPOS_NotOwner(positionId_);

        // Validate that the amount is not greater than the remaining deposit
        if (amount_ > position.remainingDeposit)
            revert IDepositPositionManager.DEPOS_InvalidParams("amount");

        // Extract position data
        IERC20 depositToken = IERC20(position.asset);
        uint8 depositPeriod = position.periodMonths;
        address facility = position.operator; // The facility is the operator of the position

        // Validate that the facility is authorized
        _validateFacility(facility);

        // Create a User Redemption
        redemptionId = _userRedemptionCount[msg.sender]++;
        _userRedemptions[_getUserRedemptionKey(msg.sender, redemptionId)] = UserRedemption({
            depositToken: address(depositToken),
            depositPeriod: depositPeriod,
            redeemableAt: position.expiry, // Use conversion expiry instead of calculated time
            amount: amount_,
            facility: facility,
            positionId: positionId_ // Store the position ID for later use
        });

        // Mark the funds as committed
        IDepositFacility(facility).handleCommit(depositToken, depositPeriod, amount_);

        // Immediately update position's remainingDeposit to prevent split/transfer issues
        // This change will be reverted if cancelRedemption is called
        IDepositFacility(facility).handlePositionRedemption(positionId_, amount_);

        // Pull the receipt tokens from the caller
        _pullReceiptToken(depositToken, depositPeriod, facility, amount_);

        // Emit events
        emit RedemptionStarted(
            msg.sender,
            redemptionId,
            address(depositToken),
            depositPeriod,
            amount_,
            facility
        );

        return redemptionId;
    }

    /// @inheritdoc IDepositRedemptionVault
    /// @dev        This function reverts if:
    ///             - The contract is disabled
    ///             - The caller is not the owner of the redemption ID
    ///             - The facility in the redemption record is not authorized
    ///             - The amount is 0
    ///             - The amount is greater than the redemption amount
    ///             - There is an unpaid loan
    function cancelRedemption(
        uint16 redemptionId_,
        uint256 amount_
    ) external nonReentrant onlyEnabled onlyValidRedemptionId(msg.sender, redemptionId_) {
        // Get the redemption
        bytes32 redemptionKey = _getUserRedemptionKey(msg.sender, redemptionId_);
        UserRedemption storage redemption = _userRedemptions[redemptionKey];

        // Check that the facility is authorized
        _validateFacility(redemption.facility);

        // Check that the amount is not 0
        if (amount_ == 0) revert RedemptionVault_ZeroAmount();

        // Check that the amount is not greater than the redemption
        if (amount_ > redemption.amount)
            revert RedemptionVault_InvalidAmount(msg.sender, redemptionId_, amount_);

        // Check that there isn't an unpaid loan
        if (_redemptionLoan[redemptionKey].principal > 0)
            revert RedemptionVault_UnpaidLoan(msg.sender, redemptionId_);

        // Update the redemption
        redemption.amount -= amount_;

        // Reduce the committed funds
        IDepositFacility(redemption.facility).handleCommitCancel(
            IERC20(redemption.depositToken),
            redemption.depositPeriod,
            amount_
        );

        // Handle position-based redemption
        if (redemption.positionId != _NO_POSITION) {
            // Only increase position.remainingDeposit if the position owner is the same
            // The redemption ID has been validated against the caller earlier, so we know that msg.sender is the creator of the original redemption
            if (DEPOS.getPosition(redemption.positionId).owner == msg.sender) {
                IDepositFacility(redemption.facility).handlePositionCancelRedemption(
                    redemption.positionId,
                    amount_
                );
            }
            // If the position ownership has changed, the original (and redemption) owner will receive the receipt tokens,
            // but the position will not be modified
        }

        // Transfer the quantity of receipt tokens to the caller
        // Redemptions are only accessible to the owner, so msg.sender is safe here
        IReceiptTokenManager rtm = DEPOSIT_MANAGER.getReceiptTokenManager();
        rtm.transfer(
            msg.sender,
            DEPOSIT_MANAGER.getReceiptTokenId(
                IERC20(redemption.depositToken),
                redemption.depositPeriod,
                redemption.facility
            ),
            amount_
        );

        // Emit the cancelled event
        emit RedemptionCancelled(
            msg.sender,
            redemptionId_,
            redemption.depositToken,
            redemption.depositPeriod,
            amount_,
            redemption.amount
        );
    }

    /// @inheritdoc IDepositRedemptionVault
    /// @dev        This function reverts if:
    ///             - The contract is disabled
    ///             - The caller is not the owner of the redemption ID
    ///             - The facility in the redemption record is not authorized
    ///             - The redemption amount is 0
    ///             - It is too early for redemption
    ///             - There is an unpaid loan
    function finishRedemption(
        uint16 redemptionId_
    )
        external
        nonReentrant
        onlyEnabled
        onlyValidRedemptionId(msg.sender, redemptionId_)
        returns (uint256 actualAmount)
    {
        // Get the redemption
        bytes32 redemptionKey = _getUserRedemptionKey(msg.sender, redemptionId_);
        UserRedemption storage redemption = _userRedemptions[redemptionKey];

        // Validate that the facility is authorized
        _validateFacility(redemption.facility);

        // Check that the redemption is not already redeemed
        if (redemption.amount == 0)
            revert RedemptionVault_AlreadyRedeemed(msg.sender, redemptionId_);

        // Check that the redemption is redeemable
        if (block.timestamp < redemption.redeemableAt)
            revert RedemptionVault_TooEarly(msg.sender, redemptionId_, redemption.redeemableAt);

        // Check that there isn't an unpaid loan
        if (_redemptionLoan[redemptionKey].principal > 0)
            revert RedemptionVault_UnpaidLoan(msg.sender, redemptionId_);

        // Update the redemption
        uint256 redemptionAmount = redemption.amount;
        redemption.amount = 0;

        // Handle the withdrawal
        // Redemptions are only accessible to the owner, so msg.sender is safe here
        uint256 receiptTokenId = DEPOSIT_MANAGER.getReceiptTokenId(
            IERC20(redemption.depositToken),
            redemption.depositPeriod,
            redemption.facility
        );
        IReceiptTokenManager rtm = DEPOSIT_MANAGER.getReceiptTokenManager();
        rtm.approve(address(DEPOSIT_MANAGER), receiptTokenId, redemptionAmount);
        // Withdraw the deposit tokens from the facility to the caller
        // The value returned can also be zero
        actualAmount = IDepositFacility(redemption.facility).handleCommitWithdraw(
            IERC20(redemption.depositToken),
            redemption.depositPeriod,
            redemptionAmount,
            msg.sender
        );
        // Reset approval, in case not all was used
        rtm.approve(address(DEPOSIT_MANAGER), receiptTokenId, 0);

        // Emit the redeemed event
        emit RedemptionFinished(
            msg.sender,
            redemptionId_,
            redemption.depositToken,
            redemption.depositPeriod,
            redemptionAmount
        );

        return actualAmount;
    }

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

    function _calculateInterest(
        uint256 principal_,
        uint256 interestRate_,
        uint256 depositPeriod_
    ) internal pure returns (uint256) {
        // Rounded up, in favour of the protocol
        return
            principal_.mulDivUp(
                interestRate_ * depositPeriod_,
                uint256(_MONTHS_IN_YEAR) * uint256(ONE_HUNDRED_PERCENT)
            );
    }

    function _previewBorrowAgainstRedemption(
        address user_,
        uint16 redemptionId_
    ) internal view returns (uint256, uint256, uint48) {
        // Get the redemption
        bytes32 redemptionKey = _getUserRedemptionKey(user_, redemptionId_);
        UserRedemption memory redemption = _userRedemptions[redemptionKey];

        // Validate that the facility is still authorized
        _validateFacility(redemption.facility);

        // Determine the amount to borrow
        // This deliberately does not revert. That will be handled in the borrowAgainstRedemption() function
        bytes32 assetFacilityKey = _getAssetFacilityKey(
            redemption.depositToken,
            redemption.facility
        );
        uint256 principal = redemption.amount.mulDiv(
            _assetFacilityMaxBorrowPercentages[assetFacilityKey],
            ONE_HUNDRED_PERCENT
        );

        // Interest: annualized, prorated for period
        uint256 interest = _calculateInterest(
            principal,
            _assetFacilityAnnualInterestRates[assetFacilityKey],
            redemption.depositPeriod
        );

        // Due date: now + deposit period
        uint48 dueDate = uint48(block.timestamp) + uint48(redemption.depositPeriod) * _ONE_MONTH;

        return (principal, interest, dueDate);
    }

    /// @inheritdoc IDepositRedemptionVault
    /// @dev        Notes:
    ///             - The calculated amount may differ from the actual amount borrowed (using `borrowAgainstRedemption()`) by a few wei, due to rounding behaviour in ERC4626 vaults.
    function previewBorrowAgainstRedemption(
        address user_,
        uint16 redemptionId_
    ) external view returns (uint256, uint256, uint48) {
        return _previewBorrowAgainstRedemption(user_, redemptionId_);
    }

    /// @inheritdoc IDepositRedemptionVault
    /// @dev        Borrows the maximum possible amount against an existing redemption.
    ///             The loan will be for a fixed-term. The interest is calculated on the
    ///             basis of that term, and the full amount will be payable in order to
    ///             close the loan.
    ///
    ///             This function will revert if:
    ///             - The contract is not enabled
    ///             - The redemption ID is invalid
    ///             - The facility is not authorized
    ///             - The amount is 0
    ///             - The interest rate is not set
    function borrowAgainstRedemption(
        uint16 redemptionId_
    )
        external
        nonReentrant
        onlyEnabled
        onlyValidRedemptionId(msg.sender, redemptionId_)
        returns (uint256)
    {
        // Get the redemption
        bytes32 redemptionKey = _getUserRedemptionKey(msg.sender, redemptionId_);
        UserRedemption storage redemption = _userRedemptions[redemptionKey];

        // Validate that the redemption is not already borrowed against
        if (_redemptionLoan[redemptionKey].dueDate != 0)
            revert RedemptionVault_LoanIncorrectState(msg.sender, redemptionId_);

        // Ensure a non-zero interest rate is configured
        if (
            _assetFacilityAnnualInterestRates[
                _getAssetFacilityKey(redemption.depositToken, redemption.facility)
            ] == 0
        ) revert RedemptionVault_InterestRateNotSet(redemption.depositToken, redemption.facility);

        // This will also validate the facility
        (uint256 principal, uint256 interest, uint48 dueDate) = _previewBorrowAgainstRedemption(
            msg.sender,
            redemptionId_
        );

        if (principal == 0) revert RedemptionVault_ZeroAmount();

        // Create loan
        // Use the calculated amount, independent of any off-by-one rounding errors
        Loan memory newLoan = Loan({
            initialPrincipal: principal,
            principal: principal,
            interest: interest,
            dueDate: dueDate,
            isDefaulted: false
        });
        _redemptionLoan[redemptionKey] = newLoan;

        // Delegate to the facility for borrowing
        uint256 principalActual = IDepositFacility(redemption.facility).handleBorrow(
            IERC20(redemption.depositToken),
            redemption.depositPeriod,
            principal,
            msg.sender
        );

        // Validate that the actual loan amount is not 0
        // This can happen when calculating the withdrawal amount from a vault
        if (principalActual == 0) revert RedemptionVault_ZeroAmount();

        // Emit event
        emit LoanCreated(msg.sender, redemptionId_, principalActual, redemption.facility);

        return principalActual;
    }

    /// @inheritdoc IDepositRedemptionVault
    /// @dev        This function will repay the outstanding loan amount.
    ///             Interest is paid back first, followed by principal.
    ///             To prevent irrecoverable overpayments, the maximum slippage is used to validate that a repayment is within bounds of the remaining loan principal.
    ///
    ///             This function will revert if:
    ///             - The contract is not enabled
    ///             - The redemption ID is invalid
    ///             - The redemption has no loan
    ///             - The amount is 0
    ///             - The loan is expired, defaulted or fully repaid
    function repayLoan(
        uint16 redemptionId_,
        uint256 amount_,
        uint256 maxSlippage_
    ) external nonReentrant onlyEnabled onlyValidRedemptionId(msg.sender, redemptionId_) {
        // Validate that the amount is not 0
        if (amount_ == 0) revert RedemptionVault_ZeroAmount();

        // Get the redemption
        bytes32 redemptionKey = _getUserRedemptionKey(msg.sender, redemptionId_);
        UserRedemption memory redemption = _userRedemptions[redemptionKey];

        // Check that the facility is still authorized
        _validateFacility(redemption.facility);

        // Get the loan
        Loan storage loan = _redemptionLoan[redemptionKey];

        // Validate that the redemption has a loan
        if (loan.dueDate == 0) revert RedemptionVault_InvalidLoan(msg.sender, redemptionId_);

        // Validate that the loan is not:
        // - expired
        // - defaulted
        // - fully repaid
        if (block.timestamp >= loan.dueDate || loan.isDefaulted || loan.principal == 0)
            revert RedemptionVault_LoanIncorrectState(msg.sender, redemptionId_);

        // Pull in the deposit tokens from the caller
        // This takes place before any state changes to avoid ERC777 re-entrancy
        ERC20(redemption.depositToken).safeTransferFrom(msg.sender, address(this), amount_);

        // Determine the repayment amounts
        // Note that the principal repayment may be different to the calculated amount, due to rounding errors in ERC4626 vaults. The value is updated once the actual value is known.
        uint256 interestToRepay;
        uint256 principalToRepay;
        // Pay interest first, then principal
        if (amount_ <= loan.interest) {
            interestToRepay = amount_;
        } else {
            interestToRepay = loan.interest;
            principalToRepay = amount_ - loan.interest;
        }

        // Handle interest
        if (interestToRepay > 0) {
            // Transfer interest to the TRSRY
            ERC20(redemption.depositToken).safeTransfer(address(TRSRY), interestToRepay);
        }

        // Handle principal
        uint256 principalRepaidActual;
        if (principalToRepay > 0) {
            ERC20(redemption.depositToken).safeApprove(address(DEPOSIT_MANAGER), principalToRepay);

            // Delegate to the facility for repayment of principal
            principalRepaidActual = IDepositFacility(redemption.facility).handleLoanRepay(
                IERC20(redemption.depositToken),
                redemption.depositPeriod,
                principalToRepay,
                loan.principal, // Repayment is capped at the outstanding principal of the loan
                address(this)
            );

            // Validate that the slippage is not too large if this is the final repayment
            if (
                principalToRepay >= loan.principal &&
                principalRepaidActual > loan.principal + maxSlippage_
            ) {
                revert RedemptionVault_MaxSlippageExceeded(
                    msg.sender,
                    redemptionId_,
                    principalRepaidActual,
                    loan.principal + maxSlippage_
                );
            }

            // The DepositFacility may not use all of the approval, so reset it to 0
            ERC20(redemption.depositToken).safeApprove(address(DEPOSIT_MANAGER), 0);
        }

        // Update loan state
        if (interestToRepay > 0) {
            loan.interest -= interestToRepay > loan.interest ? loan.interest : interestToRepay;
        }
        if (principalRepaidActual > 0) {
            loan.principal -= principalRepaidActual > loan.principal
                ? loan.principal
                : principalRepaidActual;
        }

        // Receipt tokens are not returned here.
        // They are only returned through cancelRedemption() or finishRedemption().

        emit LoanRepaid(msg.sender, redemptionId_, principalRepaidActual, interestToRepay);
    }

    function _previewExtendLoan(
        address asset_,
        address facility_,
        uint256 principal_,
        uint48 dueDate_,
        uint8 extensionMonths_
    ) internal view returns (uint48, uint256) {
        // Validate the facility
        _validateFacility(facility_);

        // Validate interest rate
        uint16 interestRate = _assetFacilityAnnualInterestRates[
            _getAssetFacilityKey(asset_, facility_)
        ];
        if (interestRate == 0) revert RedemptionVault_InterestRateNotSet(asset_, facility_);

        uint256 interestPayable = _calculateInterest(principal_, interestRate, extensionMonths_);

        uint48 newDueDate = dueDate_ + uint48(extensionMonths_) * _ONE_MONTH;

        return (newDueDate, interestPayable);
    }

    /// @inheritdoc IDepositRedemptionVault
    /// @dev        This function will revert if:
    ///             - The redemption ID is invalid
    ///             - The loan is invalid
    ///             - The loan is expired, defaulted or fully repaid
    ///             - The months is 0
    function previewExtendLoan(
        address user_,
        uint16 redemptionId_,
        uint8 months_
    ) external view onlyValidRedemptionId(user_, redemptionId_) returns (uint48, uint256) {
        // Validate that the months is not 0
        if (months_ == 0) revert RedemptionVault_ZeroAmount();

        // Get the redemption
        bytes32 redemptionKey = _getUserRedemptionKey(user_, redemptionId_);
        UserRedemption memory redemption = _userRedemptions[redemptionKey];

        // Get the loan
        Loan memory loan = _redemptionLoan[redemptionKey];

        // Validate that the redemption has a loan
        if (loan.dueDate == 0) revert RedemptionVault_InvalidLoan(user_, redemptionId_);

        // Validate that the loan is not:
        // - expired
        // - defaulted
        // - fully repaid
        if (block.timestamp >= loan.dueDate || loan.isDefaulted || loan.principal == 0)
            revert RedemptionVault_LoanIncorrectState(user_, redemptionId_);

        // Preview the new due date and interest payable
        (uint48 newDueDate, uint256 interestPayable) = _previewExtendLoan(
            redemption.depositToken,
            redemption.facility,
            loan.principal,
            loan.dueDate,
            months_
        );

        return (newDueDate, interestPayable);
    }

    /// @inheritdoc IDepositRedemptionVault
    /// @dev        This function will revert if:
    ///             - The contract is not enabled
    ///             - The redemption ID is invalid
    ///             - The loan is invalid
    ///             - The loan is expired, defaulted or fully repaid
    ///             - The months is 0
    function extendLoan(
        uint16 redemptionId_,
        uint8 months_
    ) external nonReentrant onlyEnabled onlyValidRedemptionId(msg.sender, redemptionId_) {
        // Validate that the months is not 0
        if (months_ == 0) revert RedemptionVault_ZeroAmount();

        // Get the redemption
        bytes32 redemptionKey = _getUserRedemptionKey(msg.sender, redemptionId_);
        UserRedemption memory redemption = _userRedemptions[redemptionKey];

        // Check that the facility is still authorized
        _validateFacility(redemption.facility);

        // Get the loan
        Loan storage loan = _redemptionLoan[redemptionKey];

        // Validate that the redemption has a loan
        if (loan.dueDate == 0) revert RedemptionVault_InvalidLoan(msg.sender, redemptionId_);

        // Validate that the loan is not:
        // - expired
        // - defaulted
        // - fully repaid
        if (block.timestamp >= loan.dueDate || loan.isDefaulted || loan.principal == 0)
            revert RedemptionVault_LoanIncorrectState(msg.sender, redemptionId_);

        (uint48 newDueDate, uint256 interestPayable) = _previewExtendLoan(
            redemption.depositToken,
            redemption.facility,
            loan.principal,
            loan.dueDate,
            months_
        );

        // Transfer the interest from the caller to the TRSRY
        // This takes place before any state changes to avoid ERC777 re-entrancy
        ERC20(redemption.depositToken).safeTransferFrom(
            msg.sender,
            address(TRSRY),
            interestPayable
        );

        // Update due date by the number of months
        loan.dueDate = newDueDate;

        // No need to update the interest payable, as it is collected immediately

        emit LoanExtended(msg.sender, redemptionId_, loan.dueDate);
    }

    /// @inheritdoc IDepositRedemptionVault
    /// @dev        This function will revert if:
    ///             - The contract is not enabled
    ///             - The redemption ID is invalid
    ///             - The loan is invalid
    ///             - The loan is not expired
    ///             - The loan is already defaulted
    function claimDefaultedLoan(
        address user_,
        uint16 redemptionId_
    ) external nonReentrant onlyEnabled onlyValidRedemptionId(user_, redemptionId_) {
        // Get the redemption and loan
        UserRedemption storage redemption;
        Loan storage loan;
        {
            bytes32 redemptionKey = _getUserRedemptionKey(user_, redemptionId_);
            redemption = _userRedemptions[redemptionKey];
            loan = _redemptionLoan[redemptionKey];
        }

        // Validate that the facility is still authorized
        _validateFacility(redemption.facility);

        // Validate that the redemption has a loan
        if (loan.dueDate == 0) revert RedemptionVault_InvalidLoan(user_, redemptionId_);

        // Validate that the loan is:
        // - expired
        // - not defaulted
        // - not fully repaid
        if (block.timestamp < loan.dueDate || loan.isDefaulted || loan.principal == 0)
            revert RedemptionVault_LoanIncorrectState(user_, redemptionId_);

        // Determine how much collateral to confiscate
        // Any principal that has been paid off will be retained by the borrower
        // The remainder, including the buffer, will be confiscated
        // e.g. the borrower has a redemption amount of 100, the borrower has borrowed 80, and paid off 20,
        // the borrower has an outstanding principal of 60.
        // The borrower will retain a redemption amount of 20 (due to the payment).
        // The protocol will burn the custodied receipt tokens for the unpaid principal: 80 - 20 = 60.
        // The remainder (20) will be sent to the treasury.
        uint256 previousPrincipal = loan.principal;
        uint256 previousInterest = loan.interest;
        uint256 retainedCollateral = redemption.amount - loan.initialPrincipal; // Buffer amount

        // Mark loan as defaulted
        loan.isDefaulted = true;
        loan.principal = 0;
        loan.interest = 0;

        uint256 receiptTokenId = DEPOSIT_MANAGER.getReceiptTokenId(
            IERC20(redemption.depositToken),
            redemption.depositPeriod,
            redemption.facility
        );
        uint256 totalToConsume = retainedCollateral + previousPrincipal;

        // Handle transfers
        uint256 retainedCollateralActual;
        {
            IReceiptTokenManager rtm = DEPOSIT_MANAGER.getReceiptTokenManager();
            rtm.approve(address(DEPOSIT_MANAGER), receiptTokenId, totalToConsume);
            // Burn the receipt tokens for the principal
            if (previousPrincipal > 0) {
                IDepositFacility(redemption.facility).handleLoanDefault(
                    IERC20(redemption.depositToken),
                    redemption.depositPeriod,
                    previousPrincipal,
                    address(this)
                );
            }
            // Withdraw deposit for retained collateral
            if (retainedCollateral > 0) {
                // Caution: can be zero
                retainedCollateralActual = IDepositFacility(redemption.facility)
                    .handleCommitWithdraw(
                        IERC20(redemption.depositToken),
                        redemption.depositPeriod,
                        retainedCollateral,
                        address(this)
                    );
            }
            // Reset the approval, in case not all was used
            rtm.approve(address(DEPOSIT_MANAGER), receiptTokenId, 0);
        }

        // Reduce redemption amount by the burned and retained collateral
        // Use the calculated amount (retainedCollateral + previousPrincipal) to adjust redemption.
        // This leaves redemption.amount equal to (initialPrincipal - previousPrincipal), i.e.,
        // any principal already repaid remains redeemable by the borrower. Using calculated amounts
        // avoids inconsistencies from ERC4626 rounding in actual transfers.
        redemption.amount -= retainedCollateral + previousPrincipal;

        // Distribute residual value (keeper reward + treasury)
        // Keeper reward is a percentage of the retained collateral, and can be zero
        uint256 keeperReward = retainedCollateralActual.mulDiv(
            _claimDefaultRewardPercentage,
            ONE_HUNDRED_PERCENT
        );
        // Treasury amount is the remainder of the retained collateral after the keeper reward has been deducted, and can be zero
        uint256 treasuryAmount = retainedCollateralActual - keeperReward;

        if (keeperReward > 0) {
            ERC20(redemption.depositToken).safeTransfer(msg.sender, keeperReward);
        }

        if (treasuryAmount > 0) {
            ERC20(redemption.depositToken).safeTransfer(address(TRSRY), treasuryAmount);
        }

        emit LoanDefaulted(
            user_,
            redemptionId_,
            previousPrincipal,
            previousInterest,
            retainedCollateral + previousPrincipal // Calculated amount
        );
        emit RedemptionCancelled(
            user_,
            redemptionId_,
            address(redemption.depositToken),
            redemption.depositPeriod,
            retainedCollateral + previousPrincipal, // Calculated amount
            redemption.amount
        );
    }

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

    /// @inheritdoc IDepositRedemptionVault
    function getRedemptionLoan(
        address user_,
        uint16 redemptionId_
    ) external view returns (Loan memory) {
        return _redemptionLoan[_getUserRedemptionKey(user_, redemptionId_)];
    }

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

    /// @inheritdoc IDepositRedemptionVault
    /// @dev    Notes:
    ///         - When setting the max borrow percentage, keep in mind the annual interest rate and claim default reward percentage, as the three configuration values can create incentives for borrowers to not repay their loans (e.g. claim default on their own loan)
    ///         - This function allows setting the value even if the asset or facility are not registered
    ///
    ///         This function reverts if:
    ///         - The contract is not enabled
    ///         - The caller does not have the admin or manager role
    ///         - asset_ is the zero address
    ///         - facility_ is the zero address
    ///         - percent_ is out of range
    function setMaxBorrowPercentage(
        IERC20 asset_,
        address facility_,
        uint16 percent_
    ) external onlyEnabled onlyManagerOrAdminRole {
        if (address(asset_) == address(0)) revert RedemptionVault_ZeroAddress();
        if (address(facility_) == address(0)) revert RedemptionVault_ZeroAddress();
        if (percent_ > ONE_HUNDRED_PERCENT) revert RedemptionVault_OutOfBounds(percent_);

        _assetFacilityMaxBorrowPercentages[
            _getAssetFacilityKey(address(asset_), facility_)
        ] = percent_;

        emit MaxBorrowPercentageSet(address(asset_), facility_, percent_);
    }

    /// @inheritdoc IDepositRedemptionVault
    function getMaxBorrowPercentage(
        IERC20 asset_,
        address facility_
    ) external view returns (uint16) {
        return _assetFacilityMaxBorrowPercentages[_getAssetFacilityKey(address(asset_), facility_)];
    }

    /// @inheritdoc IDepositRedemptionVault
    /// @dev    Notes:
    ///         - When setting the annual interest rate, keep in mind the max borrow percentage and claim default reward percentage, as the three configuration values can create incentives for borrowers to not repay their loans (e.g. claim default on their own loan)
    ///         - This function allows setting the value even if the asset or facility are not registered
    ///
    ///         This function reverts if:
    ///         - The contract is not enabled
    ///         - The caller does not have the admin or manager role
    ///         - asset_ is the zero address
    ///         - facility_ is the zero address
    ///         - percent_ is out of range
    function setAnnualInterestRate(
        IERC20 asset_,
        address facility_,
        uint16 rate_
    ) external onlyEnabled onlyManagerOrAdminRole {
        if (address(asset_) == address(0)) revert RedemptionVault_ZeroAddress();
        if (address(facility_) == address(0)) revert RedemptionVault_ZeroAddress();
        if (rate_ > ONE_HUNDRED_PERCENT) revert RedemptionVault_OutOfBounds(rate_);

        _assetFacilityAnnualInterestRates[_getAssetFacilityKey(address(asset_), facility_)] = rate_;

        emit AnnualInterestRateSet(address(asset_), facility_, rate_);
    }

    /// @inheritdoc IDepositRedemptionVault
    function getAnnualInterestRate(
        IERC20 asset_,
        address facility_
    ) external view returns (uint16) {
        return _assetFacilityAnnualInterestRates[_getAssetFacilityKey(address(asset_), facility_)];
    }

    /// @inheritdoc IDepositRedemptionVault
    /// @dev    Notes:
    ///         - When setting the claim default reward percentage, keep in mind the annual interest rate and max borrow percentage, as the three configuration values can create incentives for borrowers to not repay their loans (e.g. claim default on their own loan)
    function setClaimDefaultRewardPercentage(
        uint16 percent_
    ) external onlyEnabled onlyManagerOrAdminRole {
        if (percent_ > ONE_HUNDRED_PERCENT) revert RedemptionVault_OutOfBounds(percent_);

        _claimDefaultRewardPercentage = percent_;

        emit ClaimDefaultRewardPercentageSet(percent_);
    }

    /// @inheritdoc IDepositRedemptionVault
    function getClaimDefaultRewardPercentage() external view returns (uint16) {
        return _claimDefaultRewardPercentage;
    }

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

    function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
        return
            interfaceId == type(IERC165).interfaceId ||
            interfaceId == type(IDepositRedemptionVault).interfaceId ||
            super.supportsInterface(interfaceId);
    }
}
/// forge-lint: disable-end(asm-keccak256, mixed-case-variable)

// 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);
}

// SPDX-License-Identifier: MIT
pragma solidity >=0.8.0;

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

/// @title  IDepositRedemptionVault
/// @notice Interface for a contract that can manage the redemption of receipt tokens for their deposit
interface IDepositRedemptionVault {
    // ========== EVENTS ========== //

    event RedemptionStarted(
        address indexed user,
        uint16 indexed redemptionId,
        address indexed depositToken,
        uint8 depositPeriod,
        uint256 amount,
        address facility
    );

    event RedemptionFinished(
        address indexed user,
        uint16 indexed redemptionId,
        address indexed depositToken,
        uint8 depositPeriod,
        uint256 amount
    );

    event RedemptionCancelled(
        address indexed user,
        uint16 indexed redemptionId,
        address indexed depositToken,
        uint8 depositPeriod,
        uint256 amount,
        uint256 remainingAmount
    );

    // Borrowing Events
    event LoanCreated(
        address indexed user,
        uint16 indexed redemptionId,
        uint256 amount,
        address facility
    );

    event LoanRepaid(
        address indexed user,
        uint16 indexed redemptionId,
        uint256 principal,
        uint256 interest
    );

    event LoanExtended(address indexed user, uint16 indexed redemptionId, uint256 newDueDate);

    event LoanDefaulted(
        address indexed user,
        uint16 indexed redemptionId,
        uint256 principal,
        uint256 interest,
        uint256 remainingCollateral
    );

    event FacilityAuthorized(address indexed facility);
    event FacilityDeauthorized(address indexed facility);

    event AnnualInterestRateSet(address indexed asset, address indexed facility, uint16 rate);
    event MaxBorrowPercentageSet(address indexed asset, address indexed facility, uint16 percent);
    event ClaimDefaultRewardPercentageSet(uint16 percent);

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

    error RedemptionVault_InvalidDepositManager(address depositManager);

    error RedemptionVault_ZeroAmount();

    error RedemptionVault_InvalidRedemptionId(address user, uint16 redemptionId);

    error RedemptionVault_InvalidAmount(address user, uint16 redemptionId, uint256 amount);

    error RedemptionVault_TooEarly(address user, uint16 redemptionId, uint48 redeemableAt);

    error RedemptionVault_AlreadyRedeemed(address user, uint16 redemptionId);

    error RedemptionVault_ZeroAddress();
    error RedemptionVault_OutOfBounds(uint16 rate);

    error RedemptionVault_UnpaidLoan(address user, uint16 redemptionId);

    // Facility Authorization
    error RedemptionVault_InvalidFacility(address facility);
    error RedemptionVault_FacilityExists(address facility);
    error RedemptionVault_FacilityNotRegistered(address facility);

    // Borrowing Errors
    error RedemptionVault_InterestRateNotSet(address asset, address facility);
    error RedemptionVault_LoanAmountExceeded(address user, uint16 redemptionId, uint256 amount);
    error RedemptionVault_LoanIncorrectState(address user, uint16 redemptionId);
    error RedemptionVault_InvalidLoan(address user, uint16 redemptionId);
    error RedemptionVault_MaxSlippageExceeded(
        address user,
        uint16 redemptionId,
        uint256 actualAmount,
        uint256 maxAmount
    );

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

    /// @notice Data structure for a redemption of a receipt token
    ///
    /// @param  depositToken    The address of the deposit token
    /// @param  depositPeriod   The period of the deposit in months
    /// @param  redeemableAt    The timestamp at which the redemption can be finished
    /// @param  amount          The amount of deposit tokens to redeem
    /// @param  facility        The facility that handles this redemption
    /// @param  positionId      The position ID for position-based redemptions (type(uint256).max without a position)
    struct UserRedemption {
        address depositToken;
        uint8 depositPeriod;
        uint48 redeemableAt;
        uint256 amount;
        address facility;
        uint256 positionId;
    }

    /// @notice Data structure for a loan against a redemption
    ///
    /// @param  initialPrincipal    The initial principal amount borrowed
    /// @param  principal           The principal owed
    /// @param  interest            The interest owed
    /// @param  dueDate             The timestamp when the loan is due
    /// @param  isDefaulted         Whether the loan has defaulted
    struct Loan {
        uint256 initialPrincipal;
        uint256 principal;
        uint256 interest;
        uint48 dueDate;
        bool isDefaulted;
    }

    // ========== FACILITY MANAGEMENT ========== //

    /// @notice Authorize a facility
    ///
    /// @param facility_    The address of the facility to authorize
    function authorizeFacility(address facility_) external;

    /// @notice Deauthorize a facility
    ///
    /// @param facility_    The address of the facility to deauthorize
    function deauthorizeFacility(address facility_) external;

    /// @notice Check if a facility is authorized
    ///
    /// @param facility_        The address of the facility to check
    /// @return isAuthorized    True if the facility is authorized
    function isAuthorizedFacility(address facility_) external view returns (bool isAuthorized);

    /// @notice Get all authorized facilities
    ///
    /// @return facilities  Array of authorized facility addresses
    function getAuthorizedFacilities() external view returns (address[] memory facilities);

    // ========== REDEMPTION FLOW ========== //

    /// @notice Gets the details of a user's redemption
    ///
    /// @param  user_            The address of the user
    /// @param  redemptionId_    The ID of the redemption
    /// @return redemption       The details of the redemption
    function getUserRedemption(
        address user_,
        uint16 redemptionId_
    ) external view returns (UserRedemption memory redemption);

    /// @notice Gets the number of redemptions a user has started
    ///
    /// @param  user_ The address of the user
    /// @return count The number of redemptions
    function getUserRedemptionCount(address user_) external view returns (uint16 count);

    /// @notice Gets all redemptions for a user
    ///
    /// @param  user_ The address of the user
    /// @return redemptions The array of redemptions
    function getUserRedemptions(address user_) external view returns (UserRedemption[] memory);

    /// @notice Starts a redemption of a quantity of deposit tokens
    ///
    /// @param  depositToken_   The address of the deposit token
    /// @param  depositPeriod_  The period of the deposit in months
    /// @param  amount_         The amount of deposit tokens to redeem
    /// @param  facility_       The facility to handle this redemption
    /// @return redemptionId    The ID of the user redemption
    function startRedemption(
        IERC20 depositToken_,
        uint8 depositPeriod_,
        uint256 amount_,
        address facility_
    ) external returns (uint16 redemptionId);

    /// @notice Starts a redemption based on a position ID, using the position's conversion expiry
    ///
    /// @param  positionId_     The ID of the position to redeem from
    /// @param  amount_         The amount of deposit tokens to redeem
    /// @return redemptionId    The ID of the user redemption
    function startRedemption(
        uint256 positionId_,
        uint256 amount_
    ) external returns (uint16 redemptionId);

    /// @notice Cancels a redemption of a quantity of deposit tokens
    ///
    /// @param  redemptionId_ The ID of the user redemption
    /// @param  amount_       The amount of deposit tokens to cancel
    function cancelRedemption(uint16 redemptionId_, uint256 amount_) external;

    /// @notice Finishes a redemption of a quantity of deposit tokens
    /// @dev    This function does not take an amount as an argument, because the amount is determined by the redemption
    ///
    /// @param  redemptionId_   The ID of the user redemption
    /// @return actualAmount    The quantity of deposit tokens transferred to the caller
    function finishRedemption(uint16 redemptionId_) external returns (uint256 actualAmount);

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

    /// @notice Borrow the maximum amount against an active redemption
    ///
    /// @param redemptionId_    The ID of the redemption to borrow against
    /// @return actualAmount    The quantity of underlying assets transferred to the recipient
    function borrowAgainstRedemption(uint16 redemptionId_) external returns (uint256 actualAmount);

    /// @notice Preview the maximum amount that can be borrowed against an active redemption
    ///
    /// @param user_            The address of the user
    /// @param redemptionId_    The ID of the redemption to borrow against
    /// @return principal       The principal amount that can be borrowed
    /// @return interest        The interest amount that will be charged
    /// @return dueDate         The due date of the loan
    function previewBorrowAgainstRedemption(
        address user_,
        uint16 redemptionId_
    ) external view returns (uint256 principal, uint256 interest, uint48 dueDate);

    /// @notice Repay a loan
    ///
    /// @param redemptionId_    The ID of the redemption
    /// @param amount_          The amount to repay
    /// @param maxSlippage_     The maximum slippage allowed for the repayment
    function repayLoan(uint16 redemptionId_, uint256 amount_, uint256 maxSlippage_) external;

    /// @notice Preview the interest payable for extending a loan
    ///
    /// @param user_            The address of the user
    /// @param redemptionId_    The ID of the redemption
    /// @param months_          The number of months to extend the loan
    /// @return newDueDate      The new due date
    /// @return interestPayable The interest payable upon extension
    function previewExtendLoan(
        address user_,
        uint16 redemptionId_,
        uint8 months_
    ) external view returns (uint48 newDueDate, uint256 interestPayable);

    /// @notice Extend a loan's due date
    ///
    /// @param redemptionId_    The ID of the redemption
    /// @param months_          The number of months to extend the loan
    function extendLoan(uint16 redemptionId_, uint8 months_) external;

    /// @notice Claim a defaulted loan and collect the reward
    ///
    /// @param user_            The address of the user
    /// @param redemptionId_    The ID of the redemption
    function claimDefaultedLoan(address user_, uint16 redemptionId_) external;

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

    /// @notice Get all loans for a redemption
    ///
    /// @param user_            The address of the user
    /// @param redemptionId_    The ID of the redemption
    /// @return loan            The loan
    function getRedemptionLoan(
        address user_,
        uint16 redemptionId_
    ) external view returns (Loan memory loan);

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

    /// @notice Set the maximum borrow percentage for an asset-facility combination
    ///
    /// @param asset_    The address of the asset
    /// @param facility_ The address of the facility
    /// @param percent_  The maximum borrow percentage
    function setMaxBorrowPercentage(IERC20 asset_, address facility_, uint16 percent_) external;

    /// @notice Get the maximum borrow percentage for an asset-facility combination
    ///
    /// @param asset_    The address of the asset
    /// @param facility_ The address of the facility
    /// @return percent  The maximum borrow percentage (100e2 == 100%)
    function getMaxBorrowPercentage(
        IERC20 asset_,
        address facility_
    ) external view returns (uint16 percent);

    /// @notice Set the annual interest rate for an asset-facility combination
    ///
    /// @param asset_    The address of the asset
    /// @param facility_ The address of the facility
    /// @param rate_     The annual interest rate (100e2 == 100%)
    function setAnnualInterestRate(IERC20 asset_, address facility_, uint16 rate_) external;

    /// @notice Get the annual interest rate for an asset-facility combination
    ///
    /// @param asset_    The address of the asset
    /// @param facility_ The address of the facility
    /// @return rate     The annual interest rate, in terms of 100e2
    function getAnnualInterestRate(
        IERC20 asset_,
        address facility_
    ) external view returns (uint16 rate);

    /// @notice Set the reward percentage when a claiming a defaulted loan
    ///
    /// @param percent_  The claim default reward percentage
    function setClaimDefaultRewardPercentage(uint16 percent_) external;

    /// @notice Get the claim default reward percentage
    ///
    /// @return percent The claim default reward percentage, in terms of 100e2
    function getClaimDefaultRewardPercentage() external view returns (uint16 percent);
}

// 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);
}

// 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_       The operator address
    /// @param  operatorName_   The operator name for token metadata
    /// @return tokenId         The created token ID
    function createToken(
        IERC20 asset_,
        uint8 depositPeriod_,
        address operator_,
        string memory operatorName_
    ) external returns (uint256 tokenId);

    /// @notice Mints tokens to a recipient
    /// @dev    Gated to the owner (creator) of the token
    ///
    /// @param  to_         The recipient
    /// @param  tokenId_    The token ID
    /// @param  amount_     The amount to mint
    /// @param  shouldWrap_ Whether to wrap as ERC20
    function mint(address to_, uint256 tokenId_, uint256 amount_, bool shouldWrap_) external;

    /// @notice Burns tokens from a holder
    /// @dev    Gated to the owner (creator) of the token
    ///
    /// @param  from_       The holder
    /// @param  tokenId_    The token ID
    /// @param  amount_     The amount to burn
    /// @param  isWrapped_  Whether the tokens are wrapped
    function burn(address from_, uint256 tokenId_, uint256 amount_, bool isWrapped_) external;

    /// @notice Generates a receipt token ID
    ///
    /// @param  owner_          The owner address
    /// @param  asset_          The asset
    /// @param  depositPeriod_  The deposit period
    /// @param  operator_       The operator
    /// @return tokenId         The generated token ID
    function getReceiptTokenId(
        address owner_,
        IERC20 asset_,
        uint8 depositPeriod_,
        address operator_
    ) external pure returns (uint256 tokenId);

    /// @notice Returns the name of a receipt token
    ///
    /// @param  tokenId_    The ID of the receipt token
    /// @return name        The name of the receipt token
    function getTokenName(uint256 tokenId_) external view returns (string memory name);

    /// @notice Returns the symbol of a receipt token
    ///
    /// @param  tokenId_    The ID of the receipt token
    /// @return symbol      The symbol of the receipt token
    function getTokenSymbol(uint256 tokenId_) external view returns (string memory symbol);

    /// @notice Returns the decimals of a receipt token
    ///
    /// @param  tokenId_    The ID of the receipt token
    /// @return decimals    The decimals of the receipt token
    function getTokenDecimals(uint256 tokenId_) external view returns (uint8 decimals);

    /// @notice Gets the owner of a token
    ///
    /// @param  tokenId_    The token ID
    /// @return owner       The token owner
    function getTokenOwner(uint256 tokenId_) external view returns (address owner);

    /// @notice Gets the asset of a token
    ///
    /// @param  tokenId_    The token ID
    /// @return asset       The underlying asset
    function getTokenAsset(uint256 tokenId_) external view returns (IERC20 asset);

    /// @notice Gets the deposit period of a token
    ///
    /// @param  tokenId_        The token ID
    /// @return depositPeriod   The deposit period
    function getTokenDepositPeriod(uint256 tokenId_) external view returns (uint8 depositPeriod);

    /// @notice Gets the operator of a token
    ///
    /// @param  tokenId_    The token ID
    /// @return operator    The operator address
    function getTokenOperator(uint256 tokenId_) external view returns (address operator);
}

// SPDX-License-Identifier: MIT
pragma solidity >=0.8.0;

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

/// @title  IDepositFacility
/// @notice Interface for deposit facilities to coordinate with generic operators (e.g., redemption vaults)
interface IDepositFacility {
    // ========== EVENTS ========== //

    event OperatorAuthorized(address indexed operator);

    event OperatorDeauthorized(address indexed operator);

    event AssetCommitted(address indexed asset, address indexed operator, uint256 amount);

    event AssetCommitCancelled(address indexed asset, address indexed operator, uint256 amount);

    event AssetCommitWithdrawn(address indexed asset, address indexed operator, uint256 amount);

    event Reclaimed(
        address indexed user,
        address indexed depositToken,
        uint8 depositPeriod,
        uint256 reclaimedAmount,
        uint256 forfeitedAmount
    );

    event AssetPeriodReclaimRateSet(address indexed asset, uint8 depositPeriod, uint16 reclaimRate);

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

    error DepositFacility_ZeroAmount();

    error DepositFacility_InvalidAddress(address operator);

    error DepositFacility_UnauthorizedOperator(address operator);

    error DepositFacility_InsufficientDeposits(uint256 requested, uint256 available);

    error DepositFacility_InsufficientCommitment(
        address operator,
        uint256 requested,
        uint256 available
    );

    error DepositFacility_InvalidReclaimRate(uint16 reclaimRate, uint16 maxReclaimRate);

    // ========== OPERATOR AUTHORIZATION ========== //

    /// @notice Authorize an operator (e.g., a redemption vault) to handle actions through this facility
    ///
    /// @param  operator_   The address of the operator to authorize
    function authorizeOperator(address operator_) external;

    /// @notice Deauthorize an operator
    ///
    /// @param  operator_   The address of the operator to deauthorize
    function deauthorizeOperator(address operator_) external;

    /// @notice Check if an operator is authorized
    ///
    /// @param  operator_       The address of the operator to check
    /// @return isAuthorized    True if the operator is authorized
    function isAuthorizedOperator(address operator_) external view returns (bool isAuthorized);

    /// @notice Get the list of operators authorized to handle actions through this facility
    ///
    /// @return operators   The list of operators
    function getOperators() external view returns (address[] memory operators);

    // ========== REDEMPTION HANDLING ========== //

    /// @notice Allows an operator to commit funds. This will ensure that enough funds are available to honour the commitments.
    ///
    /// @param depositToken_    The deposit token committed
    /// @param depositPeriod_   The deposit period in months
    /// @param amount_          The amount to commit
    function handleCommit(IERC20 depositToken_, uint8 depositPeriod_, uint256 amount_) external;

    /// @notice Allows an operator to cancel committed funds.
    ///
    /// @param depositToken_    The deposit token committed
    /// @param depositPeriod_   The deposit period in months
    /// @param amount_          The amount to reduce the commitment by
    function handleCommitCancel(
        IERC20 depositToken_,
        uint8 depositPeriod_,
        uint256 amount_
    ) external;

    /// @notice Allows an operator to withdraw committed funds. This will withdraw deposit tokens to the recipient.
    ///
    /// @param depositToken_    The deposit token to withdraw
    /// @param depositPeriod_   The deposit period in months
    /// @param amount_          The amount to withdraw
    /// @param recipient_       The address to receive the deposit tokens
    /// @return actualAmount    The amount of tokens transferred
    function handleCommitWithdraw(
        IERC20 depositToken_,
        uint8 depositPeriod_,
        uint256 amount_,
        address recipient_
    ) external returns (uint256 actualAmount);

    /// @notice Allows an operator to handle position-based redemptions by updating the position's remainingDeposit
    ///
    /// @param positionId_  The position ID to update
    /// @param amount_      The amount being redeemed from the position
    function handlePositionRedemption(uint256 positionId_, uint256 amount_) external;

    /// @notice Allows an operator to handle the cancellation of position-based redemptions by updating the position's remainingDeposit
    ///
    /// @param positionId_  The position ID to update
    /// @param amount_      The redemption amount to be cancelled
    function handlePositionCancelRedemption(uint256 positionId_, uint256 amount_) external;

    // ========== BORROW HANDLING ========== //

    /// @notice Allows an operator to borrow against this facility's committed funds.
    ///
    /// @param depositToken_    The deposit token to borrow against
    /// @param depositPeriod_   The deposit period in months
    /// @param amount_          The amount to borrow
    /// @param recipient_       The address to receive the borrowed tokens
    /// @return actualAmount    The amount of tokens borrowed
    function handleBorrow(
        IERC20 depositToken_,
        uint8 depositPeriod_,
        uint256 amount_,
        address recipient_
    ) external returns (uint256 actualAmount);

    /// @notice Allows an operator to repay borrowed funds
    ///
    /// @param depositToken_    The deposit token being repaid
    /// @param depositPeriod_   The deposit period in months
    /// @param amount_          The amount of principal being repaid
    /// @param maxAmount_       The maximum amount of principal that can be repaid
    /// @param payer_           The address making the repayment
    /// @return actualAmount    The amount of tokens repaid
    function handleLoanRepay(
        IERC20 depositToken_,
        uint8 depositPeriod_,
        uint256 amount_,
        uint256 maxAmount_,
        address payer_
    ) external returns (uint256 actualAmount);

    /// @notice Allows an operator to default on a loan
    ///
    /// @param depositToken_ The deposit token being defaulted
    /// @param depositPeriod_ The deposit period in months
    /// @param amount_ The amount being defaulted
    /// @param payer_ The address making the default
    function handleLoanDefault(
        IERC20 depositToken_,
        uint8 depositPeriod_,
        uint256 amount_,
        address payer_
    ) external;

    // ========== RECLAIM ========== //

    /// @notice Preview the amount of deposit token that would be reclaimed
    /// @dev    The implementing contract is expected to handle the following:
    ///         - Returning the total amount of deposit tokens that would be reclaimed
    ///
    /// @param  depositToken_   The address of the deposit token
    /// @param  depositPeriod_  The period of the deposit in months
    /// @param  amount_         The amount of deposit tokens to reclaim
    /// @return reclaimed       The amount of deposit token returned to the caller
    function previewReclaim(
        IERC20 depositToken_,
        uint8 depositPeriod_,
        uint256 amount_
    ) external view returns (uint256 reclaimed);

    /// @notice Reclaims deposit tokens, after applying a discount
    ///         Deposit tokens can be reclaimed at any time.
    ///         The caller is not required to have a position in the facility.
    /// @dev    The implementing contract is expected to handle the following:
    ///         - Burning the receipt tokens
    ///         - Transferring the deposit token to `recipient_`
    ///         - Emitting an event
    ///
    /// @param  depositToken_   The address of the deposit token
    /// @param  depositPeriod_  The period of the deposit in months
    /// @param  amount_         The amount of deposit tokens to reclaim
    /// @return reclaimed       The amount of deposit token returned to the caller
    function reclaim(
        IERC20 depositToken_,
        uint8 depositPeriod_,
        uint256 amount_
    ) external returns (uint256 reclaimed);

    /// @notice Sets the reclaim rate for an asset period
    ///
    /// @param asset_         The address of the underlying asset
    /// @param depositPeriod_ The deposit period, in months
    /// @param reclaimRate_   The reclaim rate to set (in basis points, where 100e2 = 100%)
    function setAssetPeriodReclaimRate(
        IERC20 asset_,
        uint8 depositPeriod_,
        uint16 reclaimRate_
    ) external;

    /// @notice Returns the reclaim rate for an asset period
    ///
    /// @param asset_         The address of the underlying asset
    /// @param depositPeriod_ The deposit period, in months
    /// @return reclaimRate   The reclaim rate for the asset period
    function getAssetPeriodReclaimRate(
        IERC20 asset_,
        uint8 depositPeriod_
    ) external view returns (uint16 reclaimRate);

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

    /// @notice Splits the specified amount of the position into a new position
    ///
    /// @param  positionId_     The ID of the position to split
    /// @param  amount_         The amount to split from the position
    /// @param  to_             The address to receive the new position
    /// @param  wrap_           Whether to wrap the new position
    /// @return newPositionId   The ID of the newly created position
    function split(
        uint256 positionId_,
        uint256 amount_,
        address to_,
        bool wrap_
    ) external returns (uint256 newPositionId);

    // ========== BALANCE QUERIES ========== //

    /// @notice Get the available deposit balance for a specific token.
    ///         This excludes any committed funds.
    ///
    /// @param depositToken_ The deposit token to query
    /// @return balance     The available deposit balance
    function getAvailableDeposits(IERC20 depositToken_) external view returns (uint256 balance);

    /// @notice Get the committed deposits for a specific token.
    ///         The committed deposits value represents the amount of the deposit token
    ///         that this contract ensures will be available for all operators to use.
    ///
    /// @param depositToken_    The deposit token to query
    /// @return committed       The total committed deposits
    function getCommittedDeposits(IERC20 depositToken_) external view returns (uint256 committed);

    /// @notice Get the committed deposits for a specific token and operator.
    ///         The committed deposits value represents the amount of the deposit token
    ///         that this contract ensures will be available for the specific operator to use.
    ///
    /// @param depositToken_    The deposit token to query
    /// @param operator_        The operator
    /// @return committed       The committed deposits for the operator
    function getCommittedDeposits(
        IERC20 depositToken_,
        address operator_
    ) external view returns (uint256 committed);
}

// 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);
}

File 8 of 35 : IERC165.sol
// 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";

// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity >=0.8.0;

/// @notice Modern and gas efficient ERC20 + EIP-2612 implementation.
/// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/tokens/ERC20.sol)
/// @author Modified from Uniswap (https://github.com/Uniswap/uniswap-v2-core/blob/master/contracts/UniswapV2ERC20.sol)
/// @dev Do not manually set balances without updating totalSupply, as the sum of all user balances must not exceed it.
abstract contract ERC20 {
    /*//////////////////////////////////////////////////////////////
                                 EVENTS
    //////////////////////////////////////////////////////////////*/

    event Transfer(address indexed from, address indexed to, uint256 amount);

    event Approval(address indexed owner, address indexed spender, uint256 amount);

    /*//////////////////////////////////////////////////////////////
                            METADATA STORAGE
    //////////////////////////////////////////////////////////////*/

    string public name;

    string public symbol;

    uint8 public immutable decimals;

    /*//////////////////////////////////////////////////////////////
                              ERC20 STORAGE
    //////////////////////////////////////////////////////////////*/

    uint256 public totalSupply;

    mapping(address => uint256) public balanceOf;

    mapping(address => mapping(address => uint256)) public allowance;

    /*//////////////////////////////////////////////////////////////
                            EIP-2612 STORAGE
    //////////////////////////////////////////////////////////////*/

    uint256 internal immutable INITIAL_CHAIN_ID;

    bytes32 internal immutable INITIAL_DOMAIN_SEPARATOR;

    mapping(address => uint256) public nonces;

    /*//////////////////////////////////////////////////////////////
                               CONSTRUCTOR
    //////////////////////////////////////////////////////////////*/

    constructor(
        string memory _name,
        string memory _symbol,
        uint8 _decimals
    ) {
        name = _name;
        symbol = _symbol;
        decimals = _decimals;

        INITIAL_CHAIN_ID = block.chainid;
        INITIAL_DOMAIN_SEPARATOR = computeDomainSeparator();
    }

    /*//////////////////////////////////////////////////////////////
                               ERC20 LOGIC
    //////////////////////////////////////////////////////////////*/

    function approve(address spender, uint256 amount) public virtual returns (bool) {
        allowance[msg.sender][spender] = amount;

        emit Approval(msg.sender, spender, amount);

        return true;
    }

    function transfer(address to, uint256 amount) public virtual returns (bool) {
        balanceOf[msg.sender] -= amount;

        // Cannot overflow because the sum of all user
        // balances can't exceed the max uint256 value.
        unchecked {
            balanceOf[to] += amount;
        }

        emit Transfer(msg.sender, to, amount);

        return true;
    }

    function transferFrom(
        address from,
        address to,
        uint256 amount
    ) public virtual returns (bool) {
        uint256 allowed = allowance[from][msg.sender]; // Saves gas for limited approvals.

        if (allowed != type(uint256).max) allowance[from][msg.sender] = allowed - amount;

        balanceOf[from] -= amount;

        // Cannot overflow because the sum of all user
        // balances can't exceed the max uint256 value.
        unchecked {
            balanceOf[to] += amount;
        }

        emit Transfer(from, to, amount);

        return true;
    }

    /*//////////////////////////////////////////////////////////////
                             EIP-2612 LOGIC
    //////////////////////////////////////////////////////////////*/

    function permit(
        address owner,
        address spender,
        uint256 value,
        uint256 deadline,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) public virtual {
        require(deadline >= block.timestamp, "PERMIT_DEADLINE_EXPIRED");

        // Unchecked because the only math done is incrementing
        // the owner's nonce which cannot realistically overflow.
        unchecked {
            address recoveredAddress = ecrecover(
                keccak256(
                    abi.encodePacked(
                        "\x19\x01",
                        DOMAIN_SEPARATOR(),
                        keccak256(
                            abi.encode(
                                keccak256(
                                    "Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)"
                                ),
                                owner,
                                spender,
                                value,
                                nonces[owner]++,
                                deadline
                            )
                        )
                    )
                ),
                v,
                r,
                s
            );

            require(recoveredAddress != address(0) && recoveredAddress == owner, "INVALID_SIGNER");

            allowance[recoveredAddress][spender] = value;
        }

        emit Approval(owner, spender, value);
    }

    function DOMAIN_SEPARATOR() public view virtual returns (bytes32) {
        return block.chainid == INITIAL_CHAIN_ID ? INITIAL_DOMAIN_SEPARATOR : computeDomainSeparator();
    }

    function computeDomainSeparator() internal view virtual returns (bytes32) {
        return
            keccak256(
                abi.encode(
                    keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"),
                    keccak256(bytes(name)),
                    keccak256("1"),
                    block.chainid,
                    address(this)
                )
            );
    }

    /*//////////////////////////////////////////////////////////////
                        INTERNAL MINT/BURN LOGIC
    //////////////////////////////////////////////////////////////*/

    function _mint(address to, uint256 amount) internal virtual {
        totalSupply += amount;

        // Cannot overflow because the sum of all user
        // balances can't exceed the max uint256 value.
        unchecked {
            balanceOf[to] += amount;
        }

        emit Transfer(address(0), to, amount);
    }

    function _burn(address from, uint256 amount) internal virtual {
        balanceOf[from] -= amount;

        // Cannot underflow because a user's balance
        // will never be larger than the total supply.
        unchecked {
            totalSupply -= amount;
        }

        emit Transfer(from, address(0), amount);
    }
}

File 10 of 35 : ReentrancyGuard.sol
// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity >=0.8.0;

/// @notice Gas optimized reentrancy protection for smart contracts.
/// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/ReentrancyGuard.sol)
/// @author Modified from OpenZeppelin (https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/security/ReentrancyGuard.sol)
abstract contract ReentrancyGuard {
    uint256 private locked = 1;

    modifier nonReentrant() virtual {
        require(locked == 1, "REENTRANCY");

        locked = 2;

        _;

        locked = 1;
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.3.0) (utils/structs/EnumerableSet.sol)
// This file was procedurally generated from scripts/generate/templates/EnumerableSet.js.

pragma solidity ^0.8.20;

import {Arrays} from "../Arrays.sol";

/**
 * @dev Library for managing
 * https://en.wikipedia.org/wiki/Set_(abstract_data_type)[sets] of primitive
 * types.
 *
 * Sets have the following properties:
 *
 * - Elements are added, removed, and checked for existence in constant time
 * (O(1)).
 * - Elements are enumerated in O(n). No guarantees are made on the ordering.
 * - Set can be cleared (all elements removed) in O(n).
 *
 * ```solidity
 * contract Example {
 *     // Add the library methods
 *     using EnumerableSet for EnumerableSet.AddressSet;
 *
 *     // Declare a set state variable
 *     EnumerableSet.AddressSet private mySet;
 * }
 * ```
 *
 * As of v3.3.0, sets of type `bytes32` (`Bytes32Set`), `address` (`AddressSet`)
 * and `uint256` (`UintSet`) are supported.
 *
 * [WARNING]
 * ====
 * Trying to delete such a structure from storage will likely result in data corruption, rendering the structure
 * unusable.
 * See https://github.com/ethereum/solidity/pull/11843[ethereum/solidity#11843] for more info.
 *
 * In order to clean an EnumerableSet, you can either remove all elements one by one or create a fresh instance using an
 * array of EnumerableSet.
 * ====
 */
library EnumerableSet {
    // To implement this library for multiple types with as little code
    // repetition as possible, we write it in terms of a generic Set type with
    // bytes32 values.
    // The Set implementation uses private functions, and user-facing
    // implementations (such as AddressSet) are just wrappers around the
    // underlying Set.
    // This means that we can only create new EnumerableSets for types that fit
    // in bytes32.

    struct Set {
        // Storage of set values
        bytes32[] _values;
        // Position is the index of the value in the `values` array plus 1.
        // Position 0 is used to mean a value is not in the set.
        mapping(bytes32 value => uint256) _positions;
    }

    /**
     * @dev Add a value to a set. O(1).
     *
     * Returns true if the value was added to the set, that is if it was not
     * already present.
     */
    function _add(Set storage set, bytes32 value) private returns (bool) {
        if (!_contains(set, value)) {
            set._values.push(value);
            // The value is stored at length-1, but we add 1 to all indexes
            // and use 0 as a sentinel value
            set._positions[value] = set._values.length;
            return true;
        } else {
            return false;
        }
    }

    /**
     * @dev Removes a value from a set. O(1).
     *
     * Returns true if the value was removed from the set, that is if it was
     * present.
     */
    function _remove(Set storage set, bytes32 value) private returns (bool) {
        // We cache the value's position to prevent multiple reads from the same storage slot
        uint256 position = set._positions[value];

        if (position != 0) {
            // Equivalent to contains(set, value)
            // To delete an element from the _values array in O(1), we swap the element to delete with the last one in
            // the array, and then remove the last element (sometimes called as 'swap and pop').
            // This modifies the order of the array, as noted in {at}.

            uint256 valueIndex = position - 1;
            uint256 lastIndex = set._values.length - 1;

            if (valueIndex != lastIndex) {
                bytes32 lastValue = set._values[lastIndex];

                // Move the lastValue to the index where the value to delete is
                set._values[valueIndex] = lastValue;
                // Update the tracked position of the lastValue (that was just moved)
                set._positions[lastValue] = position;
            }

            // Delete the slot where the moved value was stored
            set._values.pop();

            // Delete the tracked position for the deleted slot
            delete set._positions[value];

            return true;
        } else {
            return false;
        }
    }

    /**
     * @dev Removes all the values from a set. O(n).
     *
     * WARNING: Developers should keep in mind that this function has an unbounded cost and using it may render the
     * function uncallable if the set grows to the point where clearing it consumes too much gas to fit in a block.
     */
    function _clear(Set storage set) private {
        uint256 len = _length(set);
        for (uint256 i = 0; i < len; ++i) {
            delete set._positions[set._values[i]];
        }
        Arrays.unsafeSetLength(set._values, 0);
    }

    /**
     * @dev Returns true if the value is in the set. O(1).
     */
    function _contains(Set storage set, bytes32 value) private view returns (bool) {
        return set._positions[value] != 0;
    }

    /**
     * @dev Returns the number of values on the set. O(1).
     */
    function _length(Set storage set) private view returns (uint256) {
        return set._values.length;
    }

    /**
     * @dev Returns the value stored at position `index` in the set. O(1).
     *
     * Note that there are no guarantees on the ordering of values inside the
     * array, and it may change when more values are added or removed.
     *
     * Requirements:
     *
     * - `index` must be strictly less than {length}.
     */
    function _at(Set storage set, uint256 index) private view returns (bytes32) {
        return set._values[index];
    }

    /**
     * @dev Return the entire set in an array
     *
     * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
     * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
     * this function has an unbounded cost, and using it as part of a state-changing function may render the function
     * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
     */
    function _values(Set storage set) private view returns (bytes32[] memory) {
        return set._values;
    }

    // Bytes32Set

    struct Bytes32Set {
        Set _inner;
    }

    /**
     * @dev Add a value to a set. O(1).
     *
     * Returns true if the value was added to the set, that is if it was not
     * already present.
     */
    function add(Bytes32Set storage set, bytes32 value) internal returns (bool) {
        return _add(set._inner, value);
    }

    /**
     * @dev Removes a value from a set. O(1).
     *
     * Returns true if the value was removed from the set, that is if it was
     * present.
     */
    function remove(Bytes32Set storage set, bytes32 value) internal returns (bool) {
        return _remove(set._inner, value);
    }

    /**
     * @dev Removes all the values from a set. O(n).
     *
     * WARNING: Developers should keep in mind that this function has an unbounded cost and using it may render the
     * function uncallable if the set grows to the point where clearing it consumes too much gas to fit in a block.
     */
    function clear(Bytes32Set storage set) internal {
        _clear(set._inner);
    }

    /**
     * @dev Returns true if the value is in the set. O(1).
     */
    function contains(Bytes32Set storage set, bytes32 value) internal view returns (bool) {
        return _contains(set._inner, value);
    }

    /**
     * @dev Returns the number of values in the set. O(1).
     */
    function length(Bytes32Set storage set) internal view returns (uint256) {
        return _length(set._inner);
    }

    /**
     * @dev Returns the value stored at position `index` in the set. O(1).
     *
     * Note that there are no guarantees on the ordering of values inside the
     * array, and it may change when more values are added or removed.
     *
     * Requirements:
     *
     * - `index` must be strictly less than {length}.
     */
    function at(Bytes32Set storage set, uint256 index) internal view returns (bytes32) {
        return _at(set._inner, index);
    }

    /**
     * @dev Return the entire set in an array
     *
     * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
     * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
     * this function has an unbounded cost, and using it as part of a state-changing function may render the function
     * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
     */
    function values(Bytes32Set storage set) internal view returns (bytes32[] memory) {
        bytes32[] memory store = _values(set._inner);
        bytes32[] memory result;

        assembly ("memory-safe") {
            result := store
        }

        return result;
    }

    // AddressSet

    struct AddressSet {
        Set _inner;
    }

    /**
     * @dev Add a value to a set. O(1).
     *
     * Returns true if the value was added to the set, that is if it was not
     * already present.
     */
    function add(AddressSet storage set, address value) internal returns (bool) {
        return _add(set._inner, bytes32(uint256(uint160(value))));
    }

    /**
     * @dev Removes a value from a set. O(1).
     *
     * Returns true if the value was removed from the set, that is if it was
     * present.
     */
    function remove(AddressSet storage set, address value) internal returns (bool) {
        return _remove(set._inner, bytes32(uint256(uint160(value))));
    }

    /**
     * @dev Removes all the values from a set. O(n).
     *
     * WARNING: Developers should keep in mind that this function has an unbounded cost and using it may render the
     * function uncallable if the set grows to the point where clearing it consumes too much gas to fit in a block.
     */
    function clear(AddressSet storage set) internal {
        _clear(set._inner);
    }

    /**
     * @dev Returns true if the value is in the set. O(1).
     */
    function contains(AddressSet storage set, address value) internal view returns (bool) {
        return _contains(set._inner, bytes32(uint256(uint160(value))));
    }

    /**
     * @dev Returns the number of values in the set. O(1).
     */
    function length(AddressSet storage set) internal view returns (uint256) {
        return _length(set._inner);
    }

    /**
     * @dev Returns the value stored at position `index` in the set. O(1).
     *
     * Note that there are no guarantees on the ordering of values inside the
     * array, and it may change when more values are added or removed.
     *
     * Requirements:
     *
     * - `index` must be strictly less than {length}.
     */
    function at(AddressSet storage set, uint256 index) internal view returns (address) {
        return address(uint160(uint256(_at(set._inner, index))));
    }

    /**
     * @dev Return the entire set in an array
     *
     * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
     * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
     * this function has an unbounded cost, and using it as part of a state-changing function may render the function
     * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
     */
    function values(AddressSet storage set) internal view returns (address[] memory) {
        bytes32[] memory store = _values(set._inner);
        address[] memory result;

        assembly ("memory-safe") {
            result := store
        }

        return result;
    }

    // UintSet

    struct UintSet {
        Set _inner;
    }

    /**
     * @dev Add a value to a set. O(1).
     *
     * Returns true if the value was added to the set, that is if it was not
     * already present.
     */
    function add(UintSet storage set, uint256 value) internal returns (bool) {
        return _add(set._inner, bytes32(value));
    }

    /**
     * @dev Removes a value from a set. O(1).
     *
     * Returns true if the value was removed from the set, that is if it was
     * present.
     */
    function remove(UintSet storage set, uint256 value) internal returns (bool) {
        return _remove(set._inner, bytes32(value));
    }

    /**
     * @dev Removes all the values from a set. O(n).
     *
     * WARNING: Developers should keep in mind that this function has an unbounded cost and using it may render the
     * function uncallable if the set grows to the point where clearing it consumes too much gas to fit in a block.
     */
    function clear(UintSet storage set) internal {
        _clear(set._inner);
    }

    /**
     * @dev Returns true if the value is in the set. O(1).
     */
    function contains(UintSet storage set, uint256 value) internal view returns (bool) {
        return _contains(set._inner, bytes32(value));
    }

    /**
     * @dev Returns the number of values in the set. O(1).
     */
    function length(UintSet storage set) internal view returns (uint256) {
        return _length(set._inner);
    }

    /**
     * @dev Returns the value stored at position `index` in the set. O(1).
     *
     * Note that there are no guarantees on the ordering of values inside the
     * array, and it may change when more values are added or removed.
     *
     * Requirements:
     *
     * - `index` must be strictly less than {length}.
     */
    function at(UintSet storage set, uint256 index) internal view returns (uint256) {
        return uint256(_at(set._inner, index));
    }

    /**
     * @dev Return the entire set in an array
     *
     * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
     * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
     * this function has an unbounded cost, and using it as part of a state-changing function may render the function
     * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
     */
    function values(UintSet storage set) internal view returns (uint256[] memory) {
        bytes32[] memory store = _values(set._inner);
        uint256[] memory result;

        assembly ("memory-safe") {
            result := store
        }

        return result;
    }
}

// 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(a, b, not(0))
                prod0 := mul(a, b)
                prod1 := sub(sub(mm, prod0), lt(mm, prod0))
            }

            // Handle non-overflow cases, 256 by 256 division
            if (prod1 == 0) {
                require(denominator > 0);
                assembly {
                    result := div(prod0, denominator)
                }
                return result;
            }

            // Make sure the result is less than 2**256.
            // Also prevents denominator == 0
            require(denominator > prod1);

            ///////////////////////////////////////////////
            // 512 by 256 division.
            ///////////////////////////////////////////////

            // Make division exact by subtracting the remainder from [prod1 prod0]
            // Compute remainder using mulmod
            uint256 remainder;
            assembly {
                remainder := mulmod(a, b, denominator)
            }
            // Subtract 256 bit number from 512 bit number
            assembly {
                prod1 := sub(prod1, gt(remainder, prod0))
                prod0 := sub(prod0, remainder)
            }

            // Factor powers of two out of denominator
            // Compute largest power of two divisor of denominator.
            // Always >= 1.
            uint256 twos = (type(uint256).max - denominator + 1) & denominator;
            // Divide denominator by power of two
            assembly {
                denominator := div(denominator, twos)
            }

            // Divide [prod1 prod0] by the factors of two
            assembly {
                prod0 := div(prod0, twos)
            }
            // Shift in bits from prod1 into prod0. For this we need
            // to flip `twos` such that it is 2**256 / twos.
            // If twos is zero, then it becomes one
            assembly {
                twos := add(div(sub(0, twos), twos), 1)
            }
            prod0 |= prod1 * twos;

            // Invert denominator mod 2**256
            // Now that denominator is an odd number, it has an inverse
            // modulo 2**256 such that denominator * inv = 1 mod 2**256.
            // Compute the inverse by starting with a seed that is correct
            // correct for four bits. That is, denominator * inv = 1 mod 2**4
            uint256 inv = (3 * denominator) ^ 2;
            // Now use Newton-Raphson iteration to improve the precision.
            // Thanks to Hensel's lifting lemma, this also works in modular
            // arithmetic, doubling the correct bits in each step.
            inv *= 2 - denominator * inv; // inverse mod 2**8
            inv *= 2 - denominator * inv; // inverse mod 2**16
            inv *= 2 - denominator * inv; // inverse mod 2**32
            inv *= 2 - denominator * inv; // inverse mod 2**64
            inv *= 2 - denominator * inv; // inverse mod 2**128
            inv *= 2 - denominator * inv; // inverse mod 2**256

            // Because the division is now exact we can divide by multiplying
            // with the modular inverse of denominator. This will give us the
            // correct result modulo 2**256. Since the precoditions guarantee
            // that the outcome is less than 2**256, this is the final result.
            // We don't need to compute the high bits of the result and prod1
            // is no longer required.
            result = prod0 * inv;
            return result;
        }
    }

    /// @notice Calculates ceil(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
    function mulDivUp(
        uint256 a,
        uint256 b,
        uint256 denominator
    ) internal pure returns (uint256 result) {
        result = mulDiv(a, b, denominator);
        unchecked {
            if (mulmod(a, b, denominator) > 0) {
                require(result < type(uint256).max);
                result++;
            }
        }
    }
}

// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity >=0.8.0;

import {ERC20} from "solmate/tokens/ERC20.sol";

/// @notice Safe ERC20 and ETH transfer library that safely handles missing return values.
/// @author Modified from Uniswap & old Solmate (https://github.com/Uniswap/uniswap-v3-periphery/blob/main/contracts/libraries/TransferHelper.sol)
library TransferHelper {
    function safeTransferFrom(ERC20 token, address from, address to, uint256 amount) internal {
        (bool success, bytes memory data) = address(token).call(
            abi.encodeWithSelector(ERC20.transferFrom.selector, from, to, amount)
        );

        require(success && (data.length == 0 || abi.decode(data, (bool))), "TRANSFER_FROM_FAILED");
    }

    function safeTransfer(ERC20 token, address to, uint256 amount) internal {
        (bool success, bytes memory data) = address(token).call(
            abi.encodeWithSelector(ERC20.transfer.selector, to, amount)
        );

        require(success && (data.length == 0 || abi.decode(data, (bool))), "TRANSFER_FAILED");
    }

    function safeApprove(ERC20 token, address to, uint256 amount) internal {
        (bool success, bytes memory data) = address(token).call(
            abi.encodeWithSelector(ERC20.approve.selector, to, amount)
        );

        require(success && (data.length == 0 || abi.decode(data, (bool))), "APPROVE_FAILED");
    }
}

// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity >=0.8.15;

import {ERC20} from "solmate/tokens/ERC20.sol";
import "src/Kernel.sol";

/// @notice Treasury holds all other assets under the control of the protocol.
abstract contract TRSRYv1 is Module {
    // =========  EVENTS ========= //

    event IncreaseWithdrawApproval(
        address indexed withdrawer_,
        ERC20 indexed token_,
        uint256 newAmount_
    );
    event DecreaseWithdrawApproval(
        address indexed withdrawer_,
        ERC20 indexed token_,
        uint256 newAmount_
    );
    event Withdrawal(
        address indexed policy_,
        address indexed withdrawer_,
        ERC20 indexed token_,
        uint256 amount_
    );
    event IncreaseDebtorApproval(address indexed debtor_, ERC20 indexed token_, uint256 newAmount_);
    event DecreaseDebtorApproval(address indexed debtor_, ERC20 indexed token_, uint256 newAmount_);
    event DebtIncurred(ERC20 indexed token_, address indexed policy_, uint256 amount_);
    event DebtRepaid(ERC20 indexed token_, address indexed policy_, uint256 amount_);
    event DebtSet(ERC20 indexed token_, address indexed policy_, uint256 amount_);

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

    error TRSRY_NoDebtOutstanding();
    error TRSRY_NotActive();

    // =========  STATE ========= //

    /// @notice Status of the treasury. If false, no withdrawals or debt can be incurred.
    bool public active;

    /// @notice Mapping of who is approved for withdrawal.
    /// @dev    withdrawer -> token -> amount. Infinite approval is max(uint256).
    mapping(address => mapping(ERC20 => uint256)) public withdrawApproval;

    /// @notice Mapping of who is approved to incur debt.
    /// @dev    debtor -> token -> amount. Infinite approval is max(uint256).
    mapping(address => mapping(ERC20 => uint256)) public debtApproval;

    /// @notice Total debt for token across all withdrawals.
    mapping(ERC20 => uint256) public totalDebt;

    /// @notice Debt for particular token and debtor address
    mapping(ERC20 => mapping(address => uint256)) public reserveDebt;

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

    modifier onlyWhileActive() {
        if (!active) revert TRSRY_NotActive();
        _;
    }

    /// @notice Increase approval for specific withdrawer addresses
    function increaseWithdrawApproval(
        address withdrawer_,
        ERC20 token_,
        uint256 amount_
    ) external virtual;

    /// @notice Decrease approval for specific withdrawer addresses
    function decreaseWithdrawApproval(
        address withdrawer_,
        ERC20 token_,
        uint256 amount_
    ) external virtual;

    /// @notice Allow withdrawal of reserve funds from pre-approved addresses.
    function withdrawReserves(address to_, ERC20 token_, uint256 amount_) external virtual;

    /// @notice Increase approval for someone to accrue debt in order to withdraw reserves.
    /// @dev    Debt will generally be taken by contracts to allocate treasury funds in yield sources.
    function increaseDebtorApproval(
        address debtor_,
        ERC20 token_,
        uint256 amount_
    ) external virtual;

    /// @notice Decrease approval for someone to withdraw reserves as debt.
    function decreaseDebtorApproval(
        address debtor_,
        ERC20 token_,
        uint256 amount_
    ) external virtual;

    /// @notice Pre-approved policies can get a loan to perform operations with treasury assets.
    function incurDebt(ERC20 token_, uint256 amount_) external virtual;

    /// @notice Repay a debtor debt.
    /// @dev    Only confirmed to safely handle standard and non-standard ERC20s.
    /// @dev    Can have unforeseen consequences with ERC777. Be careful with ERC777 as reserve.
    function repayDebt(address debtor_, ERC20 token_, uint256 amount_) external virtual;

    /// @notice An escape hatch for setting debt in special cases, like swapping reserves to another token.
    function setDebt(address debtor_, ERC20 token_, uint256 amount_) external virtual;

    /// @notice Get total balance of assets inside the treasury + any debt taken out against those assets.
    function getReserveBalance(ERC20 token_) external view virtual returns (uint256);

    /// @notice Emergency shutdown of withdrawals.
    function deactivate() external virtual;

    /// @notice Re-activate withdrawals after shutdown.
    function activate() external virtual;
}

File 15 of 35 : DEPOS.v1.sol
// SPDX-License-Identifier: AGPL-3.0-or-later
pragma solidity >=0.8.15;

// Interfaces
import {IDepositPositionManager} from "src/modules/DEPOS/IDepositPositionManager.sol";

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

// Bophades
import {Module} from "src/Kernel.sol";

/// @title  DEPOSv1
/// @notice This defines the interface for the DEPOS module.
///         The objective of this module is to track the terms of a deposit position.
abstract contract DEPOSv1 is Module, ERC721, IDepositPositionManager {
    // ========== CONSTANTS ========== //

    /// @notice The value used for the conversion price if conversion is not supported
    uint256 public constant NON_CONVERSION_PRICE = type(uint256).max;

    /// @notice The value used for the conversion expiry if conversion is not supported
    uint48 public constant NON_CONVERSION_EXPIRY = type(uint48).max;

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

    /// @notice The number of positions created
    uint256 internal _positionCount;

    /// @notice Mapping of position records to an ID
    /// @dev    IDs are assigned sequentially starting from 0
    ///         Mapping entries should not be deleted, but can be overwritten
    mapping(uint256 => Position) internal _positions;

    /// @notice Mapping of user addresses to their position IDs
    mapping(address => EnumerableSet.UintSet) internal _userPositions;
}

// SPDX-License-Identifier: MIT
pragma solidity >=0.8.15;

import {IEnabler} from "src/periphery/interfaces/IEnabler.sol";
import {PolicyAdmin} from "./PolicyAdmin.sol";

/// @title  PolicyEnabler
/// @notice This contract is designed to be inherited by contracts that need to be enabled or disabled. It replaces the inconsistent usage of `active` and `locallyActive` state variables across the codebase.
/// @dev    A contract that inherits from this contract should use the `onlyEnabled` and `onlyDisabled` modifiers to gate access to certain functions.
///
///         Inheriting contracts must do the following:
///         - In `configureDependencies()`, assign the module address to the `ROLES` state variable, e.g. `ROLES = ROLESv1(getModuleAddress(toKeycode("ROLES")));`
///
///         The following are optional:
///         - Override the `_enable()` and `_disable()` functions if custom logic and/or parameters are needed for the enable/disable functions.
///           - For example, `enable()` could be called with initialisation data that is decoded, validated and assigned in `_enable()`.
abstract contract PolicyEnabler is IEnabler, PolicyAdmin {
    // ===== STATE VARIABLES ===== //

    /// @notice Whether the policy functionality is enabled
    bool public isEnabled;

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

    function _onlyEnabled() internal view {
        if (!isEnabled) revert NotEnabled();
    }

    /// @notice Modifier that reverts if the policy is not enabled
    modifier onlyEnabled() {
        _onlyEnabled();
        _;
    }

    function _onlyDisabled() internal view {
        if (isEnabled) revert NotDisabled();
    }

    /// @notice Modifier that reverts if the policy is enabled
    modifier onlyDisabled() {
        _onlyDisabled();
        _;
    }

    // ===== ENABLEABLE FUNCTIONS ===== //

    /// @notice Enable the contract
    /// @dev    This function performs the following steps:
    ///         1. Validates that the caller has the admin role
    ///         2. Validates that the contract is disabled
    ///         3. Calls the implementation-specific `_enable()` function
    ///         4. Changes the state of the contract to enabled
    ///         5. Emits the `Enabled` event
    ///
    /// @param  enableData_ The data to pass to the implementation-specific `_enable()` function
    function enable(bytes calldata enableData_) public onlyAdminRole onlyDisabled {
        // Call the implementation-specific enable function
        _enable(enableData_);

        // Change the state
        isEnabled = true;

        // Emit the enabled event
        emit Enabled();
    }

    /// @notice Implementation-specific enable function
    /// @dev    This function is called by the `enable()` function
    ///
    ///         The implementing contract can override this function and perform the following:
    ///         1. Validate any parameters (if needed) or revert
    ///         2. Validate state (if needed) or revert
    ///         3. Perform any necessary actions, apart from modifying the `isEnabled` state variable
    ///
    /// @param  enableData_ Custom data that can be used by the implementation. The format of this data is
    ///         left to the discretion of the implementation.
    function _enable(bytes calldata enableData_) internal virtual {}

    /// @notice Disable the contract
    /// @dev    This function performs the following steps:
    ///         1. Validates that the caller has the admin or emergency role
    ///         2. Validates that the contract is enabled
    ///         3. Calls the implementation-specific `_disable()` function
    ///         4. Changes the state of the contract to disabled
    ///         5. Emits the `Disabled` event
    ///
    /// @param  disableData_ The data to pass to the implementation-specific `_disable()` function
    function disable(bytes calldata disableData_) public onlyEmergencyOrAdminRole onlyEnabled {
        // Call the implementation-specific disable function
        _disable(disableData_);

        // Change the state
        isEnabled = false;

        // Emit the disabled event
        emit Disabled();
    }

    /// @notice Implementation-specific disable function
    /// @dev    This function is called by the `disable()` function.
    ///
    ///         The implementing contract can override this function and perform the following:
    ///         1. Validate any parameters (if needed) or revert
    ///         2. Validate state (if needed) or revert
    ///         3. Perform any necessary actions, apart from modifying the `isEnabled` state variable
    ///
    /// @param  disableData_ Custom data that can be used by the implementation. The format of this data is
    ///         left to the discretion of the implementation.
    function _disable(bytes calldata disableData_) internal virtual {}

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

    function supportsInterface(bytes4 interfaceId) public view virtual returns (bool) {
        return
            interfaceId == bytes4(0x01ffc9a7) || // ERC-165
            interfaceId == type(IEnabler).interfaceId;
    }
}

// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity >=0.8.15;

//     ███████    █████       █████ █████ ██████   ██████ ███████████  █████  █████  █████████
//   ███░░░░░███ ░░███       ░░███ ░░███ ░░██████ ██████ ░░███░░░░░███░░███  ░░███  ███░░░░░███
//  ███     ░░███ ░███        ░░███ ███   ░███░█████░███  ░███    ░███ ░███   ░███ ░███    ░░░
// ░███      ░███ ░███         ░░█████    ░███░░███ ░███  ░██████████  ░███   ░███ ░░█████████
// ░███      ░███ ░███          ░░███     ░███ ░░░  ░███  ░███░░░░░░   ░███   ░███  ░░░░░░░░███
// ░░███     ███  ░███      █    ░███     ░███      ░███  ░███         ░███   ░███  ███    ░███
//  ░░░███████░   ███████████    █████    █████     █████ █████        ░░████████  ░░█████████
//    ░░░░░░░    ░░░░░░░░░░░    ░░░░░    ░░░░░     ░░░░░ ░░░░░          ░░░░░░░░    ░░░░░░░░░

//============================================================================================//
//                                        GLOBAL TYPES                                        //
//============================================================================================//

/// @notice Actions to trigger state changes in the kernel. Passed by the executor
enum Actions {
    InstallModule,
    UpgradeModule,
    ActivatePolicy,
    DeactivatePolicy,
    ChangeExecutor,
    MigrateKernel
}

/// @notice Used by executor to select an action and a target contract for a kernel action
struct Instruction {
    Actions action;
    address target;
}

/// @notice Used to define which module functions a policy needs access to
struct Permissions {
    Keycode keycode;
    bytes4 funcSelector;
}

type Keycode is bytes5;

//============================================================================================//
//                                       UTIL FUNCTIONS                                       //
//============================================================================================//

error TargetNotAContract(address target_);
error InvalidKeycode(Keycode keycode_);

// solhint-disable-next-line func-visibility
function toKeycode(bytes5 keycode_) pure returns (Keycode) {
    return Keycode.wrap(keycode_);
}

// solhint-disable-next-line func-visibility
function fromKeycode(Keycode keycode_) pure returns (bytes5) {
    return Keycode.unwrap(keycode_);
}

// solhint-disable-next-line func-visibility
function ensureContract(address target_) view {
    if (target_.code.length == 0) revert TargetNotAContract(target_);
}

// solhint-disable-next-line func-visibility
function ensureValidKeycode(Keycode keycode_) pure {
    bytes5 unwrapped = Keycode.unwrap(keycode_);
    for (uint256 i = 0; i < 5; ) {
        bytes1 char = unwrapped[i];
        if (char < 0x41 || char > 0x5A) revert InvalidKeycode(keycode_); // A-Z only
        unchecked {
            i++;
        }
    }
}

//============================================================================================//
//                                        COMPONENTS                                          //
//============================================================================================//

/// @notice Generic adapter interface for kernel access in modules and policies.
abstract contract KernelAdapter {
    error KernelAdapter_OnlyKernel(address caller_);

    Kernel public kernel;

    constructor(Kernel kernel_) {
        kernel = kernel_;
    }

    /// @notice Modifier to restrict functions to be called only by kernel.
    modifier onlyKernel() {
        if (msg.sender != address(kernel)) revert KernelAdapter_OnlyKernel(msg.sender);
        _;
    }

    /// @notice Function used by kernel when migrating to a new kernel.
    function changeKernel(Kernel newKernel_) external onlyKernel {
        kernel = newKernel_;
    }
}

/// @notice Base level extension of the kernel. Modules act as independent state components to be
///         interacted with and mutated through policies.
/// @dev    Modules are installed and uninstalled via the executor.
abstract contract Module is KernelAdapter {
    error Module_PolicyNotPermitted(address policy_);

    constructor(Kernel kernel_) KernelAdapter(kernel_) {}

    /// @notice Modifier to restrict which policies have access to module functions.
    modifier permissioned() {
        if (
            msg.sender == address(kernel) ||
            !kernel.modulePermissions(KEYCODE(), Policy(msg.sender), msg.sig)
        ) revert Module_PolicyNotPermitted(msg.sender);
        _;
    }

    /// @notice 5 byte identifier for a module.
    function KEYCODE() public pure virtual returns (Keycode) {}

    /// @notice Returns which semantic version of a module is being implemented.
    /// @return major - Major version upgrade indicates breaking change to the interface.
    /// @return minor - Minor version change retains backward-compatible interface.
    function VERSION() external pure virtual returns (uint8 major, uint8 minor) {}

    /// @notice Initialization function for the module
    /// @dev    This function is called when the module is installed or upgraded by the kernel.
    /// @dev    MUST BE GATED BY onlyKernel. Used to encompass any initialization or upgrade logic.
    function INIT() external virtual onlyKernel {}
}

/// @notice Policies are application logic and external interface for the kernel and installed modules.
/// @dev    Policies are activated and deactivated in the kernel by the executor.
/// @dev    Module dependencies and function permissions must be defined in appropriate functions.
abstract contract Policy is KernelAdapter {
    error Policy_ModuleDoesNotExist(Keycode keycode_);
    error Policy_WrongModuleVersion(bytes expected_);

    constructor(Kernel kernel_) KernelAdapter(kernel_) {}

    /// @notice Easily accessible indicator for if a policy is activated or not.
    function isActive() external view returns (bool) {
        return kernel.isPolicyActive(this);
    }

    /// @notice Function to grab module address from a given keycode.
    function getModuleAddress(Keycode keycode_) internal view returns (address) {
        address moduleForKeycode = address(kernel.getModuleForKeycode(keycode_));
        if (moduleForKeycode == address(0)) revert Policy_ModuleDoesNotExist(keycode_);
        return moduleForKeycode;
    }

    /// @notice Define module dependencies for this policy.
    /// @return dependencies - Keycode array of module dependencies.
    function configureDependencies() external virtual returns (Keycode[] memory dependencies) {}

    /// @notice Function called by kernel to set module function permissions.
    /// @return requests - Array of keycodes and function selectors for requested permissions.
    function requestPermissions() external view virtual returns (Permissions[] memory requests) {}
}

/// @notice Main contract that acts as a central component registry for the protocol.
/// @dev    The kernel manages modules and policies. The kernel is mutated via predefined Actions,
/// @dev    which are input from any address assigned as the executor. The executor can be changed as needed.
contract Kernel {
    // =========  EVENTS ========= //

    event PermissionsUpdated(
        Keycode indexed keycode_,
        Policy indexed policy_,
        bytes4 funcSelector_,
        bool granted_
    );
    event ActionExecuted(Actions indexed action_, address indexed target_);

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

    error Kernel_OnlyExecutor(address caller_);
    error Kernel_ModuleAlreadyInstalled(Keycode module_);
    error Kernel_InvalidModuleUpgrade(Keycode module_);
    error Kernel_PolicyAlreadyActivated(address policy_);
    error Kernel_PolicyNotActivated(address policy_);

    // =========  PRIVILEGED ADDRESSES ========= //

    /// @notice Address that is able to initiate Actions in the kernel. Can be assigned to a multisig or governance contract.
    address public executor;

    // =========  MODULE MANAGEMENT ========= //

    /// @notice Array of all modules currently installed.
    Keycode[] public allKeycodes;

    /// @notice Mapping of module address to keycode.
    mapping(Keycode => Module) public getModuleForKeycode;

    /// @notice Mapping of keycode to module address.
    mapping(Module => Keycode) public getKeycodeForModule;

    /// @notice Mapping of a keycode to all of its policy dependents. Used to efficiently reconfigure policy dependencies.
    mapping(Keycode => Policy[]) public moduleDependents;

    /// @notice Helper for module dependent arrays. Prevents the need to loop through array.
    mapping(Keycode => mapping(Policy => uint256)) public getDependentIndex;

    /// @notice Module <> Policy Permissions.
    /// @dev    Keycode -> Policy -> Function Selector -> bool for permission
    mapping(Keycode => mapping(Policy => mapping(bytes4 => bool))) public modulePermissions;

    // =========  POLICY MANAGEMENT ========= //

    /// @notice List of all active policies
    Policy[] public activePolicies;

    /// @notice Helper to get active policy quickly. Prevents need to loop through array.
    mapping(Policy => uint256) public getPolicyIndex;

    //============================================================================================//
    //                                       CORE FUNCTIONS                                       //
    //============================================================================================//

    constructor() {
        executor = msg.sender;
    }

    /// @notice Modifier to check if caller is the executor.
    modifier onlyExecutor() {
        if (msg.sender != executor) revert Kernel_OnlyExecutor(msg.sender);
        _;
    }

    function isPolicyActive(Policy policy_) public view returns (bool) {
        return activePolicies.length > 0 && activePolicies[getPolicyIndex[policy_]] == policy_;
    }

    /// @notice Main kernel function. Initiates state changes to kernel depending on Action passed in.
    function executeAction(Actions action_, address target_) external onlyExecutor {
        if (action_ == Actions.InstallModule) {
            ensureContract(target_);
            ensureValidKeycode(Module(target_).KEYCODE());
            _installModule(Module(target_));
        } else if (action_ == Actions.UpgradeModule) {
            ensureContract(target_);
            ensureValidKeycode(Module(target_).KEYCODE());
            _upgradeModule(Module(target_));
        } else if (action_ == Actions.ActivatePolicy) {
            ensureContract(target_);
            _activatePolicy(Policy(target_));
        } else if (action_ == Actions.DeactivatePolicy) {
            ensureContract(target_);
            _deactivatePolicy(Policy(target_));
        } else if (action_ == Actions.ChangeExecutor) {
            executor = target_;
        } else if (action_ == Actions.MigrateKernel) {
            ensureContract(target_);
            _migrateKernel(Kernel(target_));
        }

        emit ActionExecuted(action_, target_);
    }

    function _installModule(Module newModule_) internal {
        Keycode keycode = newModule_.KEYCODE();

        if (address(getModuleForKeycode[keycode]) != address(0))
            revert Kernel_ModuleAlreadyInstalled(keycode);

        getModuleForKeycode[keycode] = newModule_;
        getKeycodeForModule[newModule_] = keycode;
        allKeycodes.push(keycode);

        newModule_.INIT();
    }

    function _upgradeModule(Module newModule_) internal {
        Keycode keycode = newModule_.KEYCODE();
        Module oldModule = getModuleForKeycode[keycode];

        if (address(oldModule) == address(0) || oldModule == newModule_)
            revert Kernel_InvalidModuleUpgrade(keycode);

        getKeycodeForModule[oldModule] = Keycode.wrap(bytes5(0));
        getKeycodeForModule[newModule_] = keycode;
        getModuleForKeycode[keycode] = newModule_;

        newModule_.INIT();

        _reconfigurePolicies(keycode);
    }

    function _activatePolicy(Policy policy_) internal {
        if (isPolicyActive(policy_)) revert Kernel_PolicyAlreadyActivated(address(policy_));

        // Add policy to list of active policies
        activePolicies.push(policy_);
        getPolicyIndex[policy_] = activePolicies.length - 1;

        // Record module dependencies
        Keycode[] memory dependencies = policy_.configureDependencies();
        uint256 depLength = dependencies.length;

        for (uint256 i; i < depLength; ) {
            Keycode keycode = dependencies[i];

            moduleDependents[keycode].push(policy_);
            getDependentIndex[keycode][policy_] = moduleDependents[keycode].length - 1;

            unchecked {
                ++i;
            }
        }

        // Grant permissions for policy to access restricted module functions
        Permissions[] memory requests = policy_.requestPermissions();
        _setPolicyPermissions(policy_, requests, true);
    }

    function _deactivatePolicy(Policy policy_) internal {
        if (!isPolicyActive(policy_)) revert Kernel_PolicyNotActivated(address(policy_));

        // Revoke permissions
        Permissions[] memory requests = policy_.requestPermissions();
        _setPolicyPermissions(policy_, requests, false);

        // Remove policy from all policy data structures
        uint256 idx = getPolicyIndex[policy_];
        Policy lastPolicy = activePolicies[activePolicies.length - 1];

        activePolicies[idx] = lastPolicy;
        activePolicies.pop();
        getPolicyIndex[lastPolicy] = idx;
        delete getPolicyIndex[policy_];

        // Remove policy from module dependents
        _pruneFromDependents(policy_);
    }

    /// @notice All functionality will move to the new kernel. WARNING: ACTION WILL BRICK THIS KERNEL.
    /// @dev    New kernel must add in all of the modules and policies via executeAction.
    /// @dev    NOTE: Data does not get cleared from this kernel.
    function _migrateKernel(Kernel newKernel_) internal {
        uint256 keycodeLen = allKeycodes.length;
        for (uint256 i; i < keycodeLen; ) {
            Module module = Module(getModuleForKeycode[allKeycodes[i]]);
            module.changeKernel(newKernel_);
            unchecked {
                ++i;
            }
        }

        uint256 policiesLen = activePolicies.length;
        for (uint256 j; j < policiesLen; ) {
            Policy policy = activePolicies[j];

            // Deactivate before changing kernel
            policy.changeKernel(newKernel_);
            unchecked {
                ++j;
            }
        }
    }

    function _reconfigurePolicies(Keycode keycode_) internal {
        Policy[] memory dependents = moduleDependents[keycode_];
        uint256 depLength = dependents.length;

        for (uint256 i; i < depLength; ) {
            dependents[i].configureDependencies();

            unchecked {
                ++i;
            }
        }
    }

    function _setPolicyPermissions(
        Policy policy_,
        Permissions[] memory requests_,
        bool grant_
    ) internal {
        uint256 reqLength = requests_.length;
        for (uint256 i = 0; i < reqLength; ) {
            Permissions memory request = requests_[i];
            modulePermissions[request.keycode][policy_][request.funcSelector] = grant_;

            emit PermissionsUpdated(request.keycode, policy_, request.funcSelector, grant_);

            unchecked {
                ++i;
            }
        }
    }

    function _pruneFromDependents(Policy policy_) internal {
        Keycode[] memory dependencies = policy_.configureDependencies();
        uint256 depcLength = dependencies.length;

        for (uint256 i; i < depcLength; ) {
            Keycode keycode = dependencies[i];
            Policy[] storage dependents = moduleDependents[keycode];

            uint256 origIndex = getDependentIndex[keycode][policy_];
            Policy lastPolicy = dependents[dependents.length - 1];

            // Swap with last and pop
            dependents[origIndex] = lastPolicy;
            dependents.pop();

            // Record new index and delete deactivated policy index
            getDependentIndex[keycode][lastPolicy] = origIndex;
            delete getDependentIndex[keycode][policy_];

            unchecked {
                ++i;
            }
        }
    }
}

// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity >=0.8.15;

import "src/Kernel.sol";

abstract contract ROLESv1 is Module {
    // =========  EVENTS ========= //

    event RoleGranted(bytes32 indexed role_, address indexed addr_);
    event RoleRevoked(bytes32 indexed role_, address indexed addr_);

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

    error ROLES_InvalidRole(bytes32 role_);
    error ROLES_RequireRole(bytes32 role_);
    error ROLES_AddressAlreadyHasRole(address addr_, bytes32 role_);
    error ROLES_AddressDoesNotHaveRole(address addr_, bytes32 role_);
    error ROLES_RoleDoesNotExist(bytes32 role_);

    // =========  STATE ========= //

    /// @notice Mapping for if an address has a policy-defined role.
    mapping(address => mapping(bytes32 => bool)) public hasRole;

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

    /// @notice Function to grant policy-defined roles to some address. Can only be called by admin.
    function saveRole(bytes32 role_, address addr_) external virtual;

    /// @notice Function to revoke policy-defined roles from some address. Can only be called by admin.
    function removeRole(bytes32 role_, address addr_) external virtual;

    /// @notice "Modifier" to restrict policy function access to certain addresses with a role.
    /// @dev    Roles are defined in the policy and granted by the ROLES admin.
    function requireRole(bytes32 role_, address caller_) external virtual;

    /// @notice Function that checks if role is valid (all lower case)
    function ensureValidRole(bytes32 role_) external pure virtual;
}

// SPDX-License-Identifier: MIT
pragma solidity >=0.8.0;

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

// Imported from forge-std

/// @dev Interface of the ERC4626 "Tokenized Vault Standard", as defined in
/// https://eips.ethereum.org/EIPS/eip-4626
interface IERC4626 is IERC20 {
    event Deposit(address indexed sender, address indexed owner, uint256 assets, uint256 shares);

    event Withdraw(
        address indexed sender,
        address indexed receiver,
        address indexed owner,
        uint256 assets,
        uint256 shares
    );

    /// @notice Returns the address of the underlying token used for the Vault for accounting, depositing, and withdrawing.
    /// @dev
    /// - MUST be an ERC-20 token contract.
    /// - MUST NOT revert.
    function asset() external view returns (address assetTokenAddress);

    /// @notice Returns the total amount of the underlying asset that is “managed” by Vault.
    /// @dev
    /// - SHOULD include any compounding that occurs from yield.
    /// - MUST be inclusive of any fees that are charged against assets in the Vault.
    /// - MUST NOT revert.
    function totalAssets() external view returns (uint256 totalManagedAssets);

    /// @notice Returns the amount of shares that the Vault would exchange for the amount of assets provided, in an ideal
    /// scenario where all the conditions are met.
    /// @dev
    /// - MUST NOT be inclusive of any fees that are charged against assets in the Vault.
    /// - MUST NOT show any variations depending on the caller.
    /// - MUST NOT reflect slippage or other on-chain conditions, when performing the actual exchange.
    /// - MUST NOT revert.
    ///
    /// NOTE: This calculation MAY NOT reflect the “per-user” price-per-share, and instead should reflect the
    /// “average-user’s” price-per-share, meaning what the average user should expect to see when exchanging to and
    /// from.
    function convertToShares(uint256 assets) external view returns (uint256 shares);

    /// @notice Returns the amount of assets that the Vault would exchange for the amount of shares provided, in an ideal
    /// scenario where all the conditions are met.
    /// @dev
    /// - MUST NOT be inclusive of any fees that are charged against assets in the Vault.
    /// - MUST NOT show any variations depending on the caller.
    /// - MUST NOT reflect slippage or other on-chain conditions, when performing the actual exchange.
    /// - MUST NOT revert.
    ///
    /// NOTE: This calculation MAY NOT reflect the “per-user” price-per-share, and instead should reflect the
    /// “average-user’s” price-per-share, meaning what the average user should expect to see when exchanging to and
    /// from.
    function convertToAssets(uint256 shares) external view returns (uint256 assets);

    /// @notice Returns the maximum amount of the underlying asset that can be deposited into the Vault for the receiver,
    /// through a deposit call.
    /// @dev
    /// - MUST return a limited value if receiver is subject to some deposit limit.
    /// - MUST return 2 ** 256 - 1 if there is no limit on the maximum amount of assets that may be deposited.
    /// - MUST NOT revert.
    function maxDeposit(address receiver) external view returns (uint256 maxAssets);

    /// @notice Allows an on-chain or off-chain user to simulate the effects of their deposit at the current block, given
    /// current on-chain conditions.
    /// @dev
    /// - MUST return as close to and no more than the exact amount of Vault shares that would be minted in a deposit
    ///   call in the same transaction. I.e. deposit should return the same or more shares as previewDeposit if called
    ///   in the same transaction.
    /// - MUST NOT account for deposit limits like those returned from maxDeposit and should always act as though the
    ///   deposit would be accepted, regardless if the user has enough tokens approved, etc.
    /// - MUST be inclusive of deposit fees. Integrators should be aware of the existence of deposit fees.
    /// - MUST NOT revert.
    ///
    /// NOTE: any unfavorable discrepancy between convertToShares and previewDeposit SHOULD be considered slippage in
    /// share price or some other type of condition, meaning the depositor will lose assets by depositing.
    function previewDeposit(uint256 assets) external view returns (uint256 shares);

    /// @notice Mints shares Vault shares to receiver by depositing exactly amount of underlying tokens.
    /// @dev
    /// - MUST emit the Deposit event.
    /// - MAY support an additional flow in which the underlying tokens are owned by the Vault contract before the
    ///   deposit execution, and are accounted for during deposit.
    /// - MUST revert if all of assets cannot be deposited (due to deposit limit being reached, slippage, the user not
    ///   approving enough underlying tokens to the Vault contract, etc).
    ///
    /// NOTE: most implementations will require pre-approval of the Vault with the Vault’s underlying asset token.
    function deposit(uint256 assets, address receiver) external returns (uint256 shares);

    /// @notice Returns the maximum amount of the Vault shares that can be minted for the receiver, through a mint call.
    /// @dev
    /// - MUST return a limited value if receiver is subject to some mint limit.
    /// - MUST return 2 ** 256 - 1 if there is no limit on the maximum amount of shares that may be minted.
    /// - MUST NOT revert.
    function maxMint(address receiver) external view returns (uint256 maxShares);

    /// @notice Allows an on-chain or off-chain user to simulate the effects of their mint at the current block, given
    /// current on-chain conditions.
    /// @dev
    /// - MUST return as close to and no fewer than the exact amount of assets that would be deposited in a mint call
    ///   in the same transaction. I.e. mint should return the same or fewer assets as previewMint if called in the
    ///   same transaction.
    /// - MUST NOT account for mint limits like those returned from maxMint and should always act as though the mint
    ///   would be accepted, regardless if the user has enough tokens approved, etc.
    /// - MUST be inclusive of deposit fees. Integrators should be aware of the existence of deposit fees.
    /// - MUST NOT revert.
    ///
    /// NOTE: any unfavorable discrepancy between convertToAssets and previewMint SHOULD be considered slippage in
    /// share price or some other type of condition, meaning the depositor will lose assets by minting.
    function previewMint(uint256 shares) external view returns (uint256 assets);

    /// @notice Mints exactly shares Vault shares to receiver by depositing amount of underlying tokens.
    /// @dev
    /// - MUST emit the Deposit event.
    /// - MAY support an additional flow in which the underlying tokens are owned by the Vault contract before the mint
    ///   execution, and are accounted for during mint.
    /// - MUST revert if all of shares cannot be minted (due to deposit limit being reached, slippage, the user not
    ///   approving enough underlying tokens to the Vault contract, etc).
    ///
    /// NOTE: most implementations will require pre-approval of the Vault with the Vault’s underlying asset token.
    function mint(uint256 shares, address receiver) external returns (uint256 assets);

    /// @notice Returns the maximum amount of the underlying asset that can be withdrawn from the owner balance in the
    /// Vault, through a withdraw call.
    /// @dev
    /// - MUST return a limited value if owner is subject to some withdrawal limit or timelock.
    /// - MUST NOT revert.
    function maxWithdraw(address owner) external view returns (uint256 maxAssets);

    /// @notice Allows an on-chain or off-chain user to simulate the effects of their withdrawal at the current block,
    /// given current on-chain conditions.
    /// @dev
    /// - MUST return as close to and no fewer than the exact amount of Vault shares that would be burned in a withdraw
    ///   call in the same transaction. I.e. withdraw should return the same or fewer shares as previewWithdraw if
    ///   called
    ///   in the same transaction.
    /// - MUST NOT account for withdrawal limits like those returned from maxWithdraw and should always act as though
    ///   the withdrawal would be accepted, regardless if the user has enough shares, etc.
    /// - MUST be inclusive of withdrawal fees. Integrators should be aware of the existence of withdrawal fees.
    /// - MUST NOT revert.
    ///
    /// NOTE: any unfavorable discrepancy between convertToShares and previewWithdraw SHOULD be considered slippage in
    /// share price or some other type of condition, meaning the depositor will lose assets by depositing.
    function previewWithdraw(uint256 assets) external view returns (uint256 shares);

    /// @notice Burns shares from owner and sends exactly assets of underlying tokens to receiver.
    /// @dev
    /// - MUST emit the Withdraw event.
    /// - MAY support an additional flow in which the underlying tokens are owned by the Vault contract before the
    ///   withdraw execution, and are accounted for during withdraw.
    /// - MUST revert if all of assets cannot be withdrawn (due to withdrawal limit being reached, slippage, the owner
    ///   not having enough shares, etc).
    ///
    /// Note that some implementations will require pre-requesting to the Vault before a withdrawal may be performed.
    /// Those methods should be performed separately.
    function withdraw(
        uint256 assets,
        address receiver,
        address owner
    ) external returns (uint256 shares);

    /// @notice Returns the maximum amount of Vault shares that can be redeemed from the owner balance in the Vault,
    /// through a redeem call.
    /// @dev
    /// - MUST return a limited value if owner is subject to some withdrawal limit or timelock.
    /// - MUST return balanceOf(owner) if owner is not subject to any withdrawal limit or timelock.
    /// - MUST NOT revert.
    function maxRedeem(address owner) external view returns (uint256 maxShares);

    /// @notice Allows an on-chain or off-chain user to simulate the effects of their redeemption at the current block,
    /// given current on-chain conditions.
    /// @dev
    /// - MUST return as close to and no more than the exact amount of assets that would be withdrawn in a redeem call
    ///   in the same transaction. I.e. redeem should return the same or more assets as previewRedeem if called in the
    ///   same transaction.
    /// - MUST NOT account for redemption limits like those returned from maxRedeem and should always act as though the
    ///   redemption would be accepted, regardless if the user has enough shares, etc.
    /// - MUST be inclusive of withdrawal fees. Integrators should be aware of the existence of withdrawal fees.
    /// - MUST NOT revert.
    ///
    /// NOTE: any unfavorable discrepancy between convertToAssets and previewRedeem SHOULD be considered slippage in
    /// share price or some other type of condition, meaning the depositor will lose assets by redeeming.
    function previewRedeem(uint256 shares) external view returns (uint256 assets);

    /// @notice Burns exactly shares from owner and sends assets of underlying tokens to receiver.
    /// @dev
    /// - MUST emit the Withdraw event.
    /// - MAY support an additional flow in which the underlying tokens are owned by the Vault contract before the
    ///   redeem execution, and are accounted for during redeem.
    /// - MUST revert if all of shares cannot be redeemed (due to withdrawal limit being reached, slippage, the owner
    ///   not having enough shares, etc).
    ///
    /// NOTE: some implementations will require pre-requesting to the Vault before a withdrawal may be performed.
    /// Those methods should be performed separately.
    function redeem(
        uint256 shares,
        address receiver,
        address owner
    ) external returns (uint256 assets);
}

// SPDX-License-Identifier: MIT
pragma solidity >=0.8.15;

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

/// @title  IAssetManager
/// @notice This interface defines the functions for custodying assets.
///         A depositor can deposit assets into a vault, and withdraw them later.
///         An operator is the contract that acts on behalf of the depositor. Only operators can interact with the contract. The deposits facilitated by an operator are siloed from other operators.
interface IAssetManager {
    // ========== ERRORS ========== //

    error AssetManager_NotConfigured();

    error AssetManager_InvalidAsset();

    error AssetManager_AssetAlreadyConfigured();

    error AssetManager_VaultAssetMismatch();

    error AssetManager_ZeroAmount();

    error AssetManager_DepositCapExceeded(
        address asset,
        uint256 existingDepositAmount,
        uint256 depositCap
    );

    error AssetManager_MinimumDepositNotMet(
        address asset,
        uint256 depositAmount,
        uint256 minimumDeposit
    );

    error AssetManager_MinimumDepositExceedsDepositCap(
        address asset,
        uint256 minimumDeposit,
        uint256 depositCap
    );

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

    event AssetConfigured(address indexed asset, address indexed vault);

    /// @notice Emitted when an asset's deposit cap is updated
    /// @param asset      The ERC20 asset
    /// @param depositCap The new deposit cap amount (in asset units)
    event AssetDepositCapSet(address indexed asset, uint256 depositCap);

    /// @notice Emitted when an asset's minimum single-deposit amount is updated
    /// @param asset           The ERC20 asset
    /// @param minimumDeposit  The new minimum deposit amount (in asset units)
    event AssetMinimumDepositSet(address indexed asset, uint256 minimumDeposit);

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

    event AssetWithdrawn(
        address indexed asset,
        address indexed withdrawer,
        address indexed operator,
        uint256 amount,
        uint256 shares
    );

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

    /// @notice Configuration for an asset
    ///
    /// @param isConfigured   Whether the asset is configured
    /// @param depositCap     The maximum amount of assets that can be deposited. Set to 0 to disable deposits.
    /// @param minimumDeposit The minimum amount of assets that can be deposited in a single transaction (set to 0 to disable the check)
    /// @param vault          The ERC4626 vault that the asset is deposited into
    struct AssetConfiguration {
        bool isConfigured;
        uint256 depositCap;
        uint256 minimumDeposit;
        address vault;
    }

    // ========== ASSET FUNCTIONS ========== //

    /// @notice Get the number of assets deposited for an asset and operator
    ///
    /// @param  asset_          The asset to get the deposited shares for
    /// @param  operator_       The operator to get the deposited shares for
    /// @return shares          The number of shares deposited
    /// @return sharesInAssets  The number of shares deposited (in terms of assets)
    function getOperatorAssets(
        IERC20 asset_,
        address operator_
    ) external view returns (uint256 shares, uint256 sharesInAssets);

    /// @notice Get the configuration for an asset
    ///
    /// @param  asset_          The asset to get the configuration for
    /// @return configuration   The configuration for the asset
    function getAssetConfiguration(
        IERC20 asset_
    ) external view returns (AssetConfiguration memory configuration);

    /// @notice Get the assets that are configured
    ///
    /// @return assets  The assets that are configured
    function getConfiguredAssets() external view returns (IERC20[] memory assets);
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.3.0) (interfaces/draft-IERC6909.sol)

pragma solidity ^0.8.20;

import {IERC165} from "../utils/introspection/IERC165.sol";

/**
 * @dev Required interface of an ERC-6909 compliant contract, as defined in the
 * https://eips.ethereum.org/EIPS/eip-6909[ERC].
 */
interface IERC6909 is IERC165 {
    /**
     * @dev Emitted when the allowance of a `spender` for an `owner` is set for a token of type `id`.
     * The new allowance is `amount`.
     */
    event Approval(address indexed owner, address indexed spender, uint256 indexed id, uint256 amount);

    /**
     * @dev Emitted when `owner` grants or revokes operator status for a `spender`.
     */
    event OperatorSet(address indexed owner, address indexed spender, bool approved);

    /**
     * @dev Emitted when `amount` tokens of type `id` are moved from `sender` to `receiver` initiated by `caller`.
     */
    event Transfer(
        address caller,
        address indexed sender,
        address indexed receiver,
        uint256 indexed id,
        uint256 amount
    );

    /**
     * @dev Returns the amount of tokens of type `id` owned by `owner`.
     */
    function balanceOf(address owner, uint256 id) external view returns (uint256);

    /**
     * @dev Returns the amount of tokens of type `id` that `spender` is allowed to spend on behalf of `owner`.
     *
     * NOTE: Does not include operator allowances.
     */
    function allowance(address owner, address spender, uint256 id) external view returns (uint256);

    /**
     * @dev Returns true if `spender` is set as an operator for `owner`.
     */
    function isOperator(address owner, address spender) external view returns (bool);

    /**
     * @dev Sets an approval to `spender` for `amount` of tokens of type `id` from the caller's tokens. An `amount` of
     * `type(uint256).max` signifies an unlimited approval.
     *
     * Must return true.
     */
    function approve(address spender, uint256 id, uint256 amount) external returns (bool);

    /**
     * @dev Grants or revokes unlimited transfer permission of any token id to `spender` for the caller's tokens.
     *
     * Must return true.
     */
    function setOperator(address spender, bool approved) external returns (bool);

    /**
     * @dev Transfers `amount` of token type `id` from the caller's account to `receiver`.
     *
     * Must return true.
     */
    function transfer(address receiver, uint256 id, uint256 amount) external returns (bool);

    /**
     * @dev Transfers `amount` of token type `id` from `sender` to `receiver`.
     *
     * Must return true.
     */
    function transferFrom(address sender, address receiver, uint256 id, uint256 amount) external returns (bool);
}

/**
 * @dev Optional extension of {IERC6909} that adds metadata functions.
 */
interface IERC6909Metadata is IERC6909 {
    /**
     * @dev Returns the name of the token of type `id`.
     */
    function name(uint256 id) external view returns (string memory);

    /**
     * @dev Returns the ticker symbol of the token of type `id`.
     */
    function symbol(uint256 id) external view returns (string memory);

    /**
     * @dev Returns the number of decimals for the token of type `id`.
     */
    function decimals(uint256 id) external view returns (uint8);
}

/**
 * @dev Optional extension of {IERC6909} that adds content URI functions.
 */
interface IERC6909ContentURI is IERC6909 {
    /**
     * @dev Returns URI for the contract.
     */
    function contractURI() external view returns (string memory);

    /**
     * @dev Returns the URI for the token of type `id`.
     */
    function tokenURI(uint256 id) external view returns (string memory);
}

/**
 * @dev Optional extension of {IERC6909} that adds a token supply function.
 */
interface IERC6909TokenSupply is IERC6909 {
    /**
     * @dev Returns the total supply of the token of type `id`.
     */
    function totalSupply(uint256 id) external view returns (uint256);
}

// SPDX-License-Identifier: MIT
pragma solidity >=0.8.0;

/// @title IERC6909Wrappable
/// @notice Declares interface for an ERC6909 implementation that allows for wrapping and unwrapping ERC6909 tokens to and from ERC20 tokens
interface IERC6909Wrappable {
    // ========== EVENTS ========== //

    event Wrapped(
        uint256 indexed tokenId,
        address indexed wrappedToken,
        address indexed account,
        uint256 amount
    );

    event Unwrapped(
        uint256 indexed tokenId,
        address indexed wrappedToken,
        address indexed account,
        uint256 amount
    );

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

    error ERC6909Wrappable_TokenIdAlreadyExists(uint256 tokenId);
    error ERC6909Wrappable_InvalidTokenId(uint256 tokenId);
    error ERC6909Wrappable_InvalidERC20Implementation(address erc20Implementation);
    error ERC6909Wrappable_ZeroAmount();

    // ========== WRAP/UNWRAP FUNCTIONS ========== //

    /// @notice Wraps an ERC6909 token to an ERC20 token
    ///
    /// @param tokenId_      The ID of the ERC6909 token
    /// @param amount_       The amount of tokens to wrap
    /// @return wrappedToken The address of the wrapped ERC20 token
    function wrap(uint256 tokenId_, uint256 amount_) external returns (address wrappedToken);

    /// @notice Unwraps an ERC20 token to an ERC6909 token
    ///
    /// @param tokenId_      The ID of the ERC6909 token
    /// @param amount_       The amount of tokens to unwrap
    function unwrap(uint256 tokenId_, uint256 amount_) external;

    /// @notice Returns the address of the wrapped ERC20 token for a given token ID
    ///
    /// @param  tokenId_        The ID of the ERC6909 token
    /// @return wrappedToken    The address of the wrapped ERC20 token (or zero address)
    function getWrappedToken(uint256 tokenId_) external view returns (address wrappedToken);

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

    /// @notice Returns whether a token ID is valid
    ///
    /// @param  tokenId_        The ID of the ERC6909 token
    /// @return isValid         Whether the token ID is valid
    function isValidTokenId(uint256 tokenId_) external view returns (bool isValid);

    /// @notice Returns the token IDs and wrapped token addresses of all tokens
    ///
    /// @return tokenIds        The IDs of all tokens
    /// @return wrappedTokens   The wrapped token addresses of all tokens
    function getWrappableTokens()
        external
        view
        returns (uint256[] memory tokenIds, address[] memory wrappedTokens);
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (utils/introspection/IERC165.sol)

pragma solidity ^0.8.20;

/**
 * @dev Interface of the ERC-165 standard, as defined in the
 * https://eips.ethereum.org/EIPS/eip-165[ERC].
 *
 * Implementers can declare support of contract interfaces, which can then be
 * queried by others ({ERC165Checker}).
 *
 * For an implementation, see {ERC165}.
 */
interface IERC165 {
    /**
     * @dev Returns true if this contract implements the interface defined by
     * `interfaceId`. See the corresponding
     * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[ERC section]
     * to learn more about how these ids are created.
     *
     * This function call must use less than 30 000 gas.
     */
    function supportsInterface(bytes4 interfaceId) external view returns (bool);
}

File 24 of 35 : Arrays.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.3.0) (utils/Arrays.sol)
// This file was procedurally generated from scripts/generate/templates/Arrays.js.

pragma solidity ^0.8.20;

import {Comparators} from "./Comparators.sol";
import {SlotDerivation} from "./SlotDerivation.sol";
import {StorageSlot} from "./StorageSlot.sol";
import {Math} from "./math/Math.sol";

/**
 * @dev Collection of functions related to array types.
 */
library Arrays {
    using SlotDerivation for bytes32;
    using StorageSlot for bytes32;

    /**
     * @dev Sort an array of uint256 (in memory) following the provided comparator function.
     *
     * This function does the sorting "in place", meaning that it overrides the input. The object is returned for
     * convenience, but that returned value can be discarded safely if the caller has a memory pointer to the array.
     *
     * NOTE: this function's cost is `O(n · log(n))` in average and `O(n²)` in the worst case, with n the length of the
     * array. Using it in view functions that are executed through `eth_call` is safe, but one should be very careful
     * when executing this as part of a transaction. If the array being sorted is too large, the sort operation may
     * consume more gas than is available in a block, leading to potential DoS.
     *
     * IMPORTANT: Consider memory side-effects when using custom comparator functions that access memory in an unsafe way.
     */
    function sort(
        uint256[] memory array,
        function(uint256, uint256) pure returns (bool) comp
    ) internal pure returns (uint256[] memory) {
        _quickSort(_begin(array), _end(array), comp);
        return array;
    }

    /**
     * @dev Variant of {sort} that sorts an array of uint256 in increasing order.
     */
    function sort(uint256[] memory array) internal pure returns (uint256[] memory) {
        sort(array, Comparators.lt);
        return array;
    }

    /**
     * @dev Sort an array of address (in memory) following the provided comparator function.
     *
     * This function does the sorting "in place", meaning that it overrides the input. The object is returned for
     * convenience, but that returned value can be discarded safely if the caller has a memory pointer to the array.
     *
     * NOTE: this function's cost is `O(n · log(n))` in average and `O(n²)` in the worst case, with n the length of the
     * array. Using it in view functions that are executed through `eth_call` is safe, but one should be very careful
     * when executing this as part of a transaction. If the array being sorted is too large, the sort operation may
     * consume more gas than is available in a block, leading to potential DoS.
     *
     * IMPORTANT: Consider memory side-effects when using custom comparator functions that access memory in an unsafe way.
     */
    function sort(
        address[] memory array,
        function(address, address) pure returns (bool) comp
    ) internal pure returns (address[] memory) {
        sort(_castToUint256Array(array), _castToUint256Comp(comp));
        return array;
    }

    /**
     * @dev Variant of {sort} that sorts an array of address in increasing order.
     */
    function sort(address[] memory array) internal pure returns (address[] memory) {
        sort(_castToUint256Array(array), Comparators.lt);
        return array;
    }

    /**
     * @dev Sort an array of bytes32 (in memory) following the provided comparator function.
     *
     * This function does the sorting "in place", meaning that it overrides the input. The object is returned for
     * convenience, but that returned value can be discarded safely if the caller has a memory pointer to the array.
     *
     * NOTE: this function's cost is `O(n · log(n))` in average and `O(n²)` in the worst case, with n the length of the
     * array. Using it in view functions that are executed through `eth_call` is safe, but one should be very careful
     * when executing this as part of a transaction. If the array being sorted is too large, the sort operation may
     * consume more gas than is available in a block, leading to potential DoS.
     *
     * IMPORTANT: Consider memory side-effects when using custom comparator functions that access memory in an unsafe way.
     */
    function sort(
        bytes32[] memory array,
        function(bytes32, bytes32) pure returns (bool) comp
    ) internal pure returns (bytes32[] memory) {
        sort(_castToUint256Array(array), _castToUint256Comp(comp));
        return array;
    }

    /**
     * @dev Variant of {sort} that sorts an array of bytes32 in increasing order.
     */
    function sort(bytes32[] memory array) internal pure returns (bytes32[] memory) {
        sort(_castToUint256Array(array), Comparators.lt);
        return array;
    }

    /**
     * @dev Performs a quick sort of a segment of memory. The segment sorted starts at `begin` (inclusive), and stops
     * at end (exclusive). Sorting follows the `comp` comparator.
     *
     * Invariant: `begin <= end`. This is the case when initially called by {sort} and is preserved in subcalls.
     *
     * IMPORTANT: Memory locations between `begin` and `end` are not validated/zeroed. This function should
     * be used only if the limits are within a memory array.
     */
    function _quickSort(uint256 begin, uint256 end, function(uint256, uint256) pure returns (bool) comp) private pure {
        unchecked {
            if (end - begin < 0x40) return;

            // Use first element as pivot
            uint256 pivot = _mload(begin);
            // Position where the pivot should be at the end of the loop
            uint256 pos = begin;

            for (uint256 it = begin + 0x20; it < end; it += 0x20) {
                if (comp(_mload(it), pivot)) {
                    // If the value stored at the iterator's position comes before the pivot, we increment the
                    // position of the pivot and move the value there.
                    pos += 0x20;
                    _swap(pos, it);
                }
            }

            _swap(begin, pos); // Swap pivot into place
            _quickSort(begin, pos, comp); // Sort the left side of the pivot
            _quickSort(pos + 0x20, end, comp); // Sort the right side of the pivot
        }
    }

    /**
     * @dev Pointer to the memory location of the first element of `array`.
     */
    function _begin(uint256[] memory array) private pure returns (uint256 ptr) {
        assembly ("memory-safe") {
            ptr := add(array, 0x20)
        }
    }

    /**
     * @dev Pointer to the memory location of the first memory word (32bytes) after `array`. This is the memory word
     * that comes just after the last element of the array.
     */
    function _end(uint256[] memory array) private pure returns (uint256 ptr) {
        unchecked {
            return _begin(array) + array.length * 0x20;
        }
    }

    /**
     * @dev Load memory word (as a uint256) at location `ptr`.
     */
    function _mload(uint256 ptr) private pure returns (uint256 value) {
        assembly {
            value := mload(ptr)
        }
    }

    /**
     * @dev Swaps the elements memory location `ptr1` and `ptr2`.
     */
    function _swap(uint256 ptr1, uint256 ptr2) private pure {
        assembly {
            let value1 := mload(ptr1)
            let value2 := mload(ptr2)
            mstore(ptr1, value2)
            mstore(ptr2, value1)
        }
    }

    /// @dev Helper: low level cast address memory array to uint256 memory array
    function _castToUint256Array(address[] memory input) private pure returns (uint256[] memory output) {
        assembly {
            output := input
        }
    }

    /// @dev Helper: low level cast bytes32 memory array to uint256 memory array
    function _castToUint256Array(bytes32[] memory input) private pure returns (uint256[] memory output) {
        assembly {
            output := input
        }
    }

    /// @dev Helper: low level cast address comp function to uint256 comp function
    function _castToUint256Comp(
        function(address, address) pure returns (bool) input
    ) private pure returns (function(uint256, uint256) pure returns (bool) output) {
        assembly {
            output := input
        }
    }

    /// @dev Helper: low level cast bytes32 comp function to uint256 comp function
    function _castToUint256Comp(
        function(bytes32, bytes32) pure returns (bool) input
    ) private pure returns (function(uint256, uint256) pure returns (bool) output) {
        assembly {
            output := input
        }
    }

    /**
     * @dev Searches a sorted `array` and returns the first index that contains
     * a value greater or equal to `element`. If no such index exists (i.e. all
     * values in the array are strictly less than `element`), the array length is
     * returned. Time complexity O(log n).
     *
     * NOTE: The `array` is expected to be sorted in ascending order, and to
     * contain no repeated elements.
     *
     * IMPORTANT: Deprecated. This implementation behaves as {lowerBound} but lacks
     * support for repeated elements in the array. The {lowerBound} function should
     * be used instead.
     */
    function findUpperBound(uint256[] storage array, uint256 element) internal view returns (uint256) {
        uint256 low = 0;
        uint256 high = array.length;

        if (high == 0) {
            return 0;
        }

        while (low < high) {
            uint256 mid = Math.average(low, high);

            // Note that mid will always be strictly less than high (i.e. it will be a valid array index)
            // because Math.average rounds towards zero (it does integer division with truncation).
            if (unsafeAccess(array, mid).value > element) {
                high = mid;
            } else {
                low = mid + 1;
            }
        }

        // At this point `low` is the exclusive upper bound. We will return the inclusive upper bound.
        if (low > 0 && unsafeAccess(array, low - 1).value == element) {
            return low - 1;
        } else {
            return low;
        }
    }

    /**
     * @dev Searches an `array` sorted in ascending order and returns the first
     * index that contains a value greater or equal than `element`. If no such index
     * exists (i.e. all values in the array are strictly less than `element`), the array
     * length is returned. Time complexity O(log n).
     *
     * See C++'s https://en.cppreference.com/w/cpp/algorithm/lower_bound[lower_bound].
     */
    function lowerBound(uint256[] storage array, uint256 element) internal view returns (uint256) {
        uint256 low = 0;
        uint256 high = array.length;

        if (high == 0) {
            return 0;
        }

        while (low < high) {
            uint256 mid = Math.average(low, high);

            // Note that mid will always be strictly less than high (i.e. it will be a valid array index)
            // because Math.average rounds towards zero (it does integer division with truncation).
            if (unsafeAccess(array, mid).value < element) {
                // this cannot overflow because mid < high
                unchecked {
                    low = mid + 1;
                }
            } else {
                high = mid;
            }
        }

        return low;
    }

    /**
     * @dev Searches an `array` sorted in ascending order and returns the first
     * index that contains a value strictly greater than `element`. If no such index
     * exists (i.e. all values in the array are strictly less than `element`), the array
     * length is returned. Time complexity O(log n).
     *
     * See C++'s https://en.cppreference.com/w/cpp/algorithm/upper_bound[upper_bound].
     */
    function upperBound(uint256[] storage array, uint256 element) internal view returns (uint256) {
        uint256 low = 0;
        uint256 high = array.length;

        if (high == 0) {
            return 0;
        }

        while (low < high) {
            uint256 mid = Math.average(low, high);

            // Note that mid will always be strictly less than high (i.e. it will be a valid array index)
            // because Math.average rounds towards zero (it does integer division with truncation).
            if (unsafeAccess(array, mid).value > element) {
                high = mid;
            } else {
                // this cannot overflow because mid < high
                unchecked {
                    low = mid + 1;
                }
            }
        }

        return low;
    }

    /**
     * @dev Same as {lowerBound}, but with an array in memory.
     */
    function lowerBoundMemory(uint256[] memory array, uint256 element) internal pure returns (uint256) {
        uint256 low = 0;
        uint256 high = array.length;

        if (high == 0) {
            return 0;
        }

        while (low < high) {
            uint256 mid = Math.average(low, high);

            // Note that mid will always be strictly less than high (i.e. it will be a valid array index)
            // because Math.average rounds towards zero (it does integer division with truncation).
            if (unsafeMemoryAccess(array, mid) < element) {
                // this cannot overflow because mid < high
                unchecked {
                    low = mid + 1;
                }
            } else {
                high = mid;
            }
        }

        return low;
    }

    /**
     * @dev Same as {upperBound}, but with an array in memory.
     */
    function upperBoundMemory(uint256[] memory array, uint256 element) internal pure returns (uint256) {
        uint256 low = 0;
        uint256 high = array.length;

        if (high == 0) {
            return 0;
        }

        while (low < high) {
            uint256 mid = Math.average(low, high);

            // Note that mid will always be strictly less than high (i.e. it will be a valid array index)
            // because Math.average rounds towards zero (it does integer division with truncation).
            if (unsafeMemoryAccess(array, mid) > element) {
                high = mid;
            } else {
                // this cannot overflow because mid < high
                unchecked {
                    low = mid + 1;
                }
            }
        }

        return low;
    }

    /**
     * @dev Access an array in an "unsafe" way. Skips solidity "index-out-of-range" check.
     *
     * WARNING: Only use if you are certain `pos` is lower than the array length.
     */
    function unsafeAccess(address[] storage arr, uint256 pos) internal pure returns (StorageSlot.AddressSlot storage) {
        bytes32 slot;
        assembly ("memory-safe") {
            slot := arr.slot
        }
        return slot.deriveArray().offset(pos).getAddressSlot();
    }

    /**
     * @dev Access an array in an "unsafe" way. Skips solidity "index-out-of-range" check.
     *
     * WARNING: Only use if you are certain `pos` is lower than the array length.
     */
    function unsafeAccess(bytes32[] storage arr, uint256 pos) internal pure returns (StorageSlot.Bytes32Slot storage) {
        bytes32 slot;
        assembly ("memory-safe") {
            slot := arr.slot
        }
        return slot.deriveArray().offset(pos).getBytes32Slot();
    }

    /**
     * @dev Access an array in an "unsafe" way. Skips solidity "index-out-of-range" check.
     *
     * WARNING: Only use if you are certain `pos` is lower than the array length.
     */
    function unsafeAccess(uint256[] storage arr, uint256 pos) internal pure returns (StorageSlot.Uint256Slot storage) {
        bytes32 slot;
        assembly ("memory-safe") {
            slot := arr.slot
        }
        return slot.deriveArray().offset(pos).getUint256Slot();
    }

    /**
     * @dev Access an array in an "unsafe" way. Skips solidity "index-out-of-range" check.
     *
     * WARNING: Only use if you are certain `pos` is lower than the array length.
     */
    function unsafeMemoryAccess(address[] memory arr, uint256 pos) internal pure returns (address res) {
        assembly {
            res := mload(add(add(arr, 0x20), mul(pos, 0x20)))
        }
    }

    /**
     * @dev Access an array in an "unsafe" way. Skips solidity "index-out-of-range" check.
     *
     * WARNING: Only use if you are certain `pos` is lower than the array length.
     */
    function unsafeMemoryAccess(bytes32[] memory arr, uint256 pos) internal pure returns (bytes32 res) {
        assembly {
            res := mload(add(add(arr, 0x20), mul(pos, 0x20)))
        }
    }

    /**
     * @dev Access an array in an "unsafe" way. Skips solidity "index-out-of-range" check.
     *
     * WARNING: Only use if you are certain `pos` is lower than the array length.
     */
    function unsafeMemoryAccess(uint256[] memory arr, uint256 pos) internal pure returns (uint256 res) {
        assembly {
            res := mload(add(add(arr, 0x20), mul(pos, 0x20)))
        }
    }

    /**
     * @dev Helper to set the length of a dynamic array. Directly writing to `.length` is forbidden.
     *
     * WARNING: this does not clear elements if length is reduced, of initialize elements if length is increased.
     */
    function unsafeSetLength(address[] storage array, uint256 len) internal {
        assembly ("memory-safe") {
            sstore(array.slot, len)
        }
    }

    /**
     * @dev Helper to set the length of a dynamic array. Directly writing to `.length` is forbidden.
     *
     * WARNING: this does not clear elements if length is reduced, of initialize elements if length is increased.
     */
    function unsafeSetLength(bytes32[] storage array, uint256 len) internal {
        assembly ("memory-safe") {
            sstore(array.slot, len)
        }
    }

    /**
     * @dev Helper to set the length of a dynamic array. Directly writing to `.length` is forbidden.
     *
     * WARNING: this does not clear elements if length is reduced, of initialize elements if length is increased.
     */
    function unsafeSetLength(uint256[] storage array, uint256 len) internal {
        assembly ("memory-safe") {
            sstore(array.slot, len)
        }
    }
}

// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity >=0.8.0;

/// @notice Modern, minimalist, and gas efficient ERC-721 implementation.
/// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/tokens/ERC721.sol)
abstract contract ERC721 {
    /*//////////////////////////////////////////////////////////////
                                 EVENTS
    //////////////////////////////////////////////////////////////*/

    event Transfer(address indexed from, address indexed to, uint256 indexed id);

    event Approval(address indexed owner, address indexed spender, uint256 indexed id);

    event ApprovalForAll(address indexed owner, address indexed operator, bool approved);

    /*//////////////////////////////////////////////////////////////
                         METADATA STORAGE/LOGIC
    //////////////////////////////////////////////////////////////*/

    string public name;

    string public symbol;

    function tokenURI(uint256 id) public view virtual returns (string memory);

    /*//////////////////////////////////////////////////////////////
                      ERC721 BALANCE/OWNER STORAGE
    //////////////////////////////////////////////////////////////*/

    mapping(uint256 => address) internal _ownerOf;

    mapping(address => uint256) internal _balanceOf;

    function ownerOf(uint256 id) public view virtual returns (address owner) {
        require((owner = _ownerOf[id]) != address(0), "NOT_MINTED");
    }

    function balanceOf(address owner) public view virtual returns (uint256) {
        require(owner != address(0), "ZERO_ADDRESS");

        return _balanceOf[owner];
    }

    /*//////////////////////////////////////////////////////////////
                         ERC721 APPROVAL STORAGE
    //////////////////////////////////////////////////////////////*/

    mapping(uint256 => address) public getApproved;

    mapping(address => mapping(address => bool)) public isApprovedForAll;

    /*//////////////////////////////////////////////////////////////
                               CONSTRUCTOR
    //////////////////////////////////////////////////////////////*/

    constructor(string memory _name, string memory _symbol) {
        name = _name;
        symbol = _symbol;
    }

    /*//////////////////////////////////////////////////////////////
                              ERC721 LOGIC
    //////////////////////////////////////////////////////////////*/

    function approve(address spender, uint256 id) public virtual {
        address owner = _ownerOf[id];

        require(msg.sender == owner || isApprovedForAll[owner][msg.sender], "NOT_AUTHORIZED");

        getApproved[id] = spender;

        emit Approval(owner, spender, id);
    }

    function setApprovalForAll(address operator, bool approved) public virtual {
        isApprovedForAll[msg.sender][operator] = approved;

        emit ApprovalForAll(msg.sender, operator, approved);
    }

    function transferFrom(
        address from,
        address to,
        uint256 id
    ) public virtual {
        require(from == _ownerOf[id], "WRONG_FROM");

        require(to != address(0), "INVALID_RECIPIENT");

        require(
            msg.sender == from || isApprovedForAll[from][msg.sender] || msg.sender == getApproved[id],
            "NOT_AUTHORIZED"
        );

        // Underflow of the sender's balance is impossible because we check for
        // ownership above and the recipient's balance can't realistically overflow.
        unchecked {
            _balanceOf[from]--;

            _balanceOf[to]++;
        }

        _ownerOf[id] = to;

        delete getApproved[id];

        emit Transfer(from, to, id);
    }

    function safeTransferFrom(
        address from,
        address to,
        uint256 id
    ) public virtual {
        transferFrom(from, to, id);

        require(
            to.code.length == 0 ||
                ERC721TokenReceiver(to).onERC721Received(msg.sender, from, id, "") ==
                ERC721TokenReceiver.onERC721Received.selector,
            "UNSAFE_RECIPIENT"
        );
    }

    function safeTransferFrom(
        address from,
        address to,
        uint256 id,
        bytes calldata data
    ) public virtual {
        transferFrom(from, to, id);

        require(
            to.code.length == 0 ||
                ERC721TokenReceiver(to).onERC721Received(msg.sender, from, id, data) ==
                ERC721TokenReceiver.onERC721Received.selector,
            "UNSAFE_RECIPIENT"
        );
    }

    /*//////////////////////////////////////////////////////////////
                              ERC165 LOGIC
    //////////////////////////////////////////////////////////////*/

    function supportsInterface(bytes4 interfaceId) public view virtual returns (bool) {
        return
            interfaceId == 0x01ffc9a7 || // ERC165 Interface ID for ERC165
            interfaceId == 0x80ac58cd || // ERC165 Interface ID for ERC721
            interfaceId == 0x5b5e139f; // ERC165 Interface ID for ERC721Metadata
    }

    /*//////////////////////////////////////////////////////////////
                        INTERNAL MINT/BURN LOGIC
    //////////////////////////////////////////////////////////////*/

    function _mint(address to, uint256 id) internal virtual {
        require(to != address(0), "INVALID_RECIPIENT");

        require(_ownerOf[id] == address(0), "ALREADY_MINTED");

        // Counter overflow is incredibly unrealistic.
        unchecked {
            _balanceOf[to]++;
        }

        _ownerOf[id] = to;

        emit Transfer(address(0), to, id);
    }

    function _burn(uint256 id) internal virtual {
        address owner = _ownerOf[id];

        require(owner != address(0), "NOT_MINTED");

        // Ownership check above ensures no underflow.
        unchecked {
            _balanceOf[owner]--;
        }

        delete _ownerOf[id];

        delete getApproved[id];

        emit Transfer(owner, address(0), id);
    }

    /*//////////////////////////////////////////////////////////////
                        INTERNAL SAFE MINT LOGIC
    //////////////////////////////////////////////////////////////*/

    function _safeMint(address to, uint256 id) internal virtual {
        _mint(to, id);

        require(
            to.code.length == 0 ||
                ERC721TokenReceiver(to).onERC721Received(msg.sender, address(0), id, "") ==
                ERC721TokenReceiver.onERC721Received.selector,
            "UNSAFE_RECIPIENT"
        );
    }

    function _safeMint(
        address to,
        uint256 id,
        bytes memory data
    ) internal virtual {
        _mint(to, id);

        require(
            to.code.length == 0 ||
                ERC721TokenReceiver(to).onERC721Received(msg.sender, address(0), id, data) ==
                ERC721TokenReceiver.onERC721Received.selector,
            "UNSAFE_RECIPIENT"
        );
    }
}

/// @notice A generic interface for a contract which properly accepts ERC721 tokens.
/// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/tokens/ERC721.sol)
abstract contract ERC721TokenReceiver {
    function onERC721Received(
        address,
        address,
        uint256,
        bytes calldata
    ) external virtual returns (bytes4) {
        return ERC721TokenReceiver.onERC721Received.selector;
    }
}

// SPDX-License-Identifier: MIT
pragma solidity >=0.8.15;

/// @title IEnabler
/// @notice Interface for contracts that can be enabled and disabled
/// @dev    This is designed for usage by periphery contracts that cannot inherit from `PolicyEnabler`. Authorization is deliberately left open to the implementing contract.
interface IEnabler {
    // ============ EVENTS ============ //

    /// @notice Emitted when the contract is enabled
    event Enabled();

    /// @notice Emitted when the contract is disabled
    event Disabled();

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

    /// @notice Thrown when the contract is not enabled
    error NotEnabled();

    /// @notice Thrown when the contract is not disabled
    error NotDisabled();

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

    /// @notice         Returns true if the contract is enabled
    /// @return enabled True if the contract is enabled, false otherwise
    function isEnabled() external view returns (bool enabled);

    /// @notice             Enables the contract
    /// @dev                Implementing contracts should implement permissioning logic
    ///
    /// @param enableData_  Optional data to pass to a custom enable function
    function enable(bytes calldata enableData_) external;

    /// @notice             Disables the contract
    /// @dev                Implementing contracts should implement permissioning logic
    ///
    /// @param disableData_ Optional data to pass to a custom disable function
    function disable(bytes calldata disableData_) external;
}

// SPDX-License-Identifier: MIT
pragma solidity >=0.8.15;

import {ROLESv1} from "src/modules/ROLES/ROLES.v1.sol";
import {RolesConsumer} from "src/modules/ROLES/OlympusRoles.sol";
import {ADMIN_ROLE, EMERGENCY_ROLE, MANAGER_ROLE} from "./RoleDefinitions.sol";

abstract contract PolicyAdmin is RolesConsumer {
    error NotAuthorised();

    /// @notice Modifier that reverts if the caller does not have the emergency or admin role
    modifier onlyEmergencyOrAdminRole() {
        if (!_isEmergency(msg.sender) && !_isAdmin(msg.sender)) revert NotAuthorised();
        _;
    }

    /// @notice Modifier that reverts if the caller does not have the manager or admin role
    modifier onlyManagerOrAdminRole() {
        if (!_isManager(msg.sender) && !_isAdmin(msg.sender)) revert NotAuthorised();
        _;
    }

    /// @notice Modifier that reverts if the caller does not have the admin role
    modifier onlyAdminRole() {
        if (!_isAdmin(msg.sender)) revert ROLESv1.ROLES_RequireRole(ADMIN_ROLE);
        _;
    }

    /// @notice Modifier that reverts if the caller does not have the emergency role
    modifier onlyEmergencyRole() {
        if (!_isEmergency(msg.sender)) revert ROLESv1.ROLES_RequireRole(EMERGENCY_ROLE);
        _;
    }

    /// @notice Modifier that reverts if the caller does not have the manager role
    modifier onlyManagerRole() {
        if (!_isManager(msg.sender)) revert ROLESv1.ROLES_RequireRole(MANAGER_ROLE);
        _;
    }

    /// @notice Check if an account has the admin role
    ///
    /// @param  account_ The account to check
    /// @return true if the account has the admin role, false otherwise
    function _isAdmin(address account_) internal view returns (bool) {
        return ROLES.hasRole(account_, ADMIN_ROLE);
    }

    /// @notice Check if an account has the emergency role
    ///
    /// @param  account_ The account to check
    /// @return true if the account has the emergency role, false otherwise
    function _isEmergency(address account_) internal view returns (bool) {
        return ROLES.hasRole(account_, EMERGENCY_ROLE);
    }

    /// @notice Check if an account has the manager role
    ///
    /// @param  account_ The account to check
    /// @return true if the account has the manager role, false otherwise
    function _isManager(address account_) internal view returns (bool) {
        return ROLES.hasRole(account_, MANAGER_ROLE);
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (utils/Comparators.sol)

pragma solidity ^0.8.20;

/**
 * @dev Provides a set of functions to compare values.
 *
 * _Available since v5.1._
 */
library Comparators {
    function lt(uint256 a, uint256 b) internal pure returns (bool) {
        return a < b;
    }

    function gt(uint256 a, uint256 b) internal pure returns (bool) {
        return a > b;
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.3.0) (utils/SlotDerivation.sol)
// This file was procedurally generated from scripts/generate/templates/SlotDerivation.js.

pragma solidity ^0.8.20;

/**
 * @dev Library for computing storage (and transient storage) locations from namespaces and deriving slots
 * corresponding to standard patterns. The derivation method for array and mapping matches the storage layout used by
 * the solidity language / compiler.
 *
 * See https://docs.soliditylang.org/en/v0.8.20/internals/layout_in_storage.html#mappings-and-dynamic-arrays[Solidity docs for mappings and dynamic arrays.].
 *
 * Example usage:
 * ```solidity
 * contract Example {
 *     // Add the library methods
 *     using StorageSlot for bytes32;
 *     using SlotDerivation for bytes32;
 *
 *     // Declare a namespace
 *     string private constant _NAMESPACE = "<namespace>"; // eg. OpenZeppelin.Slot
 *
 *     function setValueInNamespace(uint256 key, address newValue) internal {
 *         _NAMESPACE.erc7201Slot().deriveMapping(key).getAddressSlot().value = newValue;
 *     }
 *
 *     function getValueInNamespace(uint256 key) internal view returns (address) {
 *         return _NAMESPACE.erc7201Slot().deriveMapping(key).getAddressSlot().value;
 *     }
 * }
 * ```
 *
 * TIP: Consider using this library along with {StorageSlot}.
 *
 * NOTE: This library provides a way to manipulate storage locations in a non-standard way. Tooling for checking
 * upgrade safety will ignore the slots accessed through this library.
 *
 * _Available since v5.1._
 */
library SlotDerivation {
    /**
     * @dev Derive an ERC-7201 slot from a string (namespace).
     */
    function erc7201Slot(string memory namespace) internal pure returns (bytes32 slot) {
        assembly ("memory-safe") {
            mstore(0x00, sub(keccak256(add(namespace, 0x20), mload(namespace)), 1))
            slot := and(keccak256(0x00, 0x20), not(0xff))
        }
    }

    /**
     * @dev Add an offset to a slot to get the n-th element of a structure or an array.
     */
    function offset(bytes32 slot, uint256 pos) internal pure returns (bytes32 result) {
        unchecked {
            return bytes32(uint256(slot) + pos);
        }
    }

    /**
     * @dev Derive the location of the first element in an array from the slot where the length is stored.
     */
    function deriveArray(bytes32 slot) internal pure returns (bytes32 result) {
        assembly ("memory-safe") {
            mstore(0x00, slot)
            result := keccak256(0x00, 0x20)
        }
    }

    /**
     * @dev Derive the location of a mapping element from the key.
     */
    function deriveMapping(bytes32 slot, address key) internal pure returns (bytes32 result) {
        assembly ("memory-safe") {
            mstore(0x00, and(key, shr(96, not(0))))
            mstore(0x20, slot)
            result := keccak256(0x00, 0x40)
        }
    }

    /**
     * @dev Derive the location of a mapping element from the key.
     */
    function deriveMapping(bytes32 slot, bool key) internal pure returns (bytes32 result) {
        assembly ("memory-safe") {
            mstore(0x00, iszero(iszero(key)))
            mstore(0x20, slot)
            result := keccak256(0x00, 0x40)
        }
    }

    /**
     * @dev Derive the location of a mapping element from the key.
     */
    function deriveMapping(bytes32 slot, bytes32 key) internal pure returns (bytes32 result) {
        assembly ("memory-safe") {
            mstore(0x00, key)
            mstore(0x20, slot)
            result := keccak256(0x00, 0x40)
        }
    }

    /**
     * @dev Derive the location of a mapping element from the key.
     */
    function deriveMapping(bytes32 slot, uint256 key) internal pure returns (bytes32 result) {
        assembly ("memory-safe") {
            mstore(0x00, key)
            mstore(0x20, slot)
            result := keccak256(0x00, 0x40)
        }
    }

    /**
     * @dev Derive the location of a mapping element from the key.
     */
    function deriveMapping(bytes32 slot, int256 key) internal pure returns (bytes32 result) {
        assembly ("memory-safe") {
            mstore(0x00, key)
            mstore(0x20, slot)
            result := keccak256(0x00, 0x40)
        }
    }

    /**
     * @dev Derive the location of a mapping element from the key.
     */
    function deriveMapping(bytes32 slot, string memory key) internal pure returns (bytes32 result) {
        assembly ("memory-safe") {
            let length := mload(key)
            let begin := add(key, 0x20)
            let end := add(begin, length)
            let cache := mload(end)
            mstore(end, slot)
            result := keccak256(begin, add(length, 0x20))
            mstore(end, cache)
        }
    }

    /**
     * @dev Derive the location of a mapping element from the key.
     */
    function deriveMapping(bytes32 slot, bytes memory key) internal pure returns (bytes32 result) {
        assembly ("memory-safe") {
            let length := mload(key)
            let begin := add(key, 0x20)
            let end := add(begin, length)
            let cache := mload(end)
            mstore(end, slot)
            result := keccak256(begin, add(length, 0x20))
            mstore(end, cache)
        }
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (utils/StorageSlot.sol)
// This file was procedurally generated from scripts/generate/templates/StorageSlot.js.

pragma solidity ^0.8.20;

/**
 * @dev Library for reading and writing primitive types to specific storage slots.
 *
 * Storage slots are often used to avoid storage conflict when dealing with upgradeable contracts.
 * This library helps with reading and writing to such slots without the need for inline assembly.
 *
 * The functions in this library return Slot structs that contain a `value` member that can be used to read or write.
 *
 * Example usage to set ERC-1967 implementation slot:
 * ```solidity
 * contract ERC1967 {
 *     // Define the slot. Alternatively, use the SlotDerivation library to derive the slot.
 *     bytes32 internal constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;
 *
 *     function _getImplementation() internal view returns (address) {
 *         return StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value;
 *     }
 *
 *     function _setImplementation(address newImplementation) internal {
 *         require(newImplementation.code.length > 0);
 *         StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value = newImplementation;
 *     }
 * }
 * ```
 *
 * TIP: Consider using this library along with {SlotDerivation}.
 */
library StorageSlot {
    struct AddressSlot {
        address value;
    }

    struct BooleanSlot {
        bool value;
    }

    struct Bytes32Slot {
        bytes32 value;
    }

    struct Uint256Slot {
        uint256 value;
    }

    struct Int256Slot {
        int256 value;
    }

    struct StringSlot {
        string value;
    }

    struct BytesSlot {
        bytes value;
    }

    /**
     * @dev Returns an `AddressSlot` with member `value` located at `slot`.
     */
    function getAddressSlot(bytes32 slot) internal pure returns (AddressSlot storage r) {
        assembly ("memory-safe") {
            r.slot := slot
        }
    }

    /**
     * @dev Returns a `BooleanSlot` with member `value` located at `slot`.
     */
    function getBooleanSlot(bytes32 slot) internal pure returns (BooleanSlot storage r) {
        assembly ("memory-safe") {
            r.slot := slot
        }
    }

    /**
     * @dev Returns a `Bytes32Slot` with member `value` located at `slot`.
     */
    function getBytes32Slot(bytes32 slot) internal pure returns (Bytes32Slot storage r) {
        assembly ("memory-safe") {
            r.slot := slot
        }
    }

    /**
     * @dev Returns a `Uint256Slot` with member `value` located at `slot`.
     */
    function getUint256Slot(bytes32 slot) internal pure returns (Uint256Slot storage r) {
        assembly ("memory-safe") {
            r.slot := slot
        }
    }

    /**
     * @dev Returns a `Int256Slot` with member `value` located at `slot`.
     */
    function getInt256Slot(bytes32 slot) internal pure returns (Int256Slot storage r) {
        assembly ("memory-safe") {
            r.slot := slot
        }
    }

    /**
     * @dev Returns a `StringSlot` with member `value` located at `slot`.
     */
    function getStringSlot(bytes32 slot) internal pure returns (StringSlot storage r) {
        assembly ("memory-safe") {
            r.slot := slot
        }
    }

    /**
     * @dev Returns an `StringSlot` representation of the string storage pointer `store`.
     */
    function getStringSlot(string storage store) internal pure returns (StringSlot storage r) {
        assembly ("memory-safe") {
            r.slot := store.slot
        }
    }

    /**
     * @dev Returns a `BytesSlot` with member `value` located at `slot`.
     */
    function getBytesSlot(bytes32 slot) internal pure returns (BytesSlot storage r) {
        assembly ("memory-safe") {
            r.slot := slot
        }
    }

    /**
     * @dev Returns an `BytesSlot` representation of the bytes storage pointer `store`.
     */
    function getBytesSlot(bytes storage store) internal pure returns (BytesSlot storage r) {
        assembly ("memory-safe") {
            r.slot := store.slot
        }
    }
}

File 31 of 35 : Math.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.3.0) (utils/math/Math.sol)

pragma solidity ^0.8.20;

import {Panic} from "../Panic.sol";
import {SafeCast} from "./SafeCast.sol";

/**
 * @dev Standard math utilities missing in the Solidity language.
 */
library Math {
    enum Rounding {
        Floor, // Toward negative infinity
        Ceil, // Toward positive infinity
        Trunc, // Toward zero
        Expand // Away from zero
    }

    /**
     * @dev Return the 512-bit addition of two uint256.
     *
     * The result is stored in two 256 variables such that sum = high * 2²⁵⁶ + low.
     */
    function add512(uint256 a, uint256 b) internal pure returns (uint256 high, uint256 low) {
        assembly ("memory-safe") {
            low := add(a, b)
            high := lt(low, a)
        }
    }

    /**
     * @dev Return the 512-bit multiplication of two uint256.
     *
     * The result is stored in two 256 variables such that product = high * 2²⁵⁶ + low.
     */
    function mul512(uint256 a, uint256 b) internal pure returns (uint256 high, uint256 low) {
        // 512-bit multiply [high low] = x * y. Compute the product mod 2²⁵⁶ and mod 2²⁵⁶ - 1, then use
        // the Chinese Remainder Theorem to reconstruct the 512 bit result. The result is stored in two 256
        // variables such that product = high * 2²⁵⁶ + low.
        assembly ("memory-safe") {
            let mm := mulmod(a, b, not(0))
            low := mul(a, b)
            high := sub(sub(mm, low), lt(mm, low))
        }
    }

    /**
     * @dev Returns the addition of two unsigned integers, with a success flag (no overflow).
     */
    function tryAdd(uint256 a, uint256 b) internal pure returns (bool success, uint256 result) {
        unchecked {
            uint256 c = a + b;
            success = c >= a;
            result = c * SafeCast.toUint(success);
        }
    }

    /**
     * @dev Returns the subtraction of two unsigned integers, with a success flag (no overflow).
     */
    function trySub(uint256 a, uint256 b) internal pure returns (bool success, uint256 result) {
        unchecked {
            uint256 c = a - b;
            success = c <= a;
            result = c * SafeCast.toUint(success);
        }
    }

    /**
     * @dev Returns the multiplication of two unsigned integers, with a success flag (no overflow).
     */
    function tryMul(uint256 a, uint256 b) internal pure returns (bool success, uint256 result) {
        unchecked {
            uint256 c = a * b;
            assembly ("memory-safe") {
                // Only true when the multiplication doesn't overflow
                // (c / a == b) || (a == 0)
                success := or(eq(div(c, a), b), iszero(a))
            }
            // equivalent to: success ? c : 0
            result = c * SafeCast.toUint(success);
        }
    }

    /**
     * @dev Returns the division of two unsigned integers, with a success flag (no division by zero).
     */
    function tryDiv(uint256 a, uint256 b) internal pure returns (bool success, uint256 result) {
        unchecked {
            success = b > 0;
            assembly ("memory-safe") {
                // The `DIV` opcode returns zero when the denominator is 0.
                result := div(a, b)
            }
        }
    }

    /**
     * @dev Returns the remainder of dividing two unsigned integers, with a success flag (no division by zero).
     */
    function tryMod(uint256 a, uint256 b) internal pure returns (bool success, uint256 result) {
        unchecked {
            success = b > 0;
            assembly ("memory-safe") {
                // The `MOD` opcode returns zero when the denominator is 0.
                result := mod(a, b)
            }
        }
    }

    /**
     * @dev Unsigned saturating addition, bounds to `2²⁵⁶ - 1` instead of overflowing.
     */
    function saturatingAdd(uint256 a, uint256 b) internal pure returns (uint256) {
        (bool success, uint256 result) = tryAdd(a, b);
        return ternary(success, result, type(uint256).max);
    }

    /**
     * @dev Unsigned saturating subtraction, bounds to zero instead of overflowing.
     */
    function saturatingSub(uint256 a, uint256 b) internal pure returns (uint256) {
        (, uint256 result) = trySub(a, b);
        return result;
    }

    /**
     * @dev Unsigned saturating multiplication, bounds to `2²⁵⁶ - 1` instead of overflowing.
     */
    function saturatingMul(uint256 a, uint256 b) internal pure returns (uint256) {
        (bool success, uint256 result) = tryMul(a, b);
        return ternary(success, result, type(uint256).max);
    }

    /**
     * @dev Branchless ternary evaluation for `a ? b : c`. Gas costs are constant.
     *
     * IMPORTANT: This function may reduce bytecode size and consume less gas when used standalone.
     * However, the compiler may optimize Solidity ternary operations (i.e. `a ? b : c`) to only compute
     * one branch when needed, making this function more expensive.
     */
    function ternary(bool condition, uint256 a, uint256 b) internal pure returns (uint256) {
        unchecked {
            // branchless ternary works because:
            // b ^ (a ^ b) == a
            // b ^ 0 == b
            return b ^ ((a ^ b) * SafeCast.toUint(condition));
        }
    }

    /**
     * @dev Returns the largest of two numbers.
     */
    function max(uint256 a, uint256 b) internal pure returns (uint256) {
        return ternary(a > b, a, b);
    }

    /**
     * @dev Returns the smallest of two numbers.
     */
    function min(uint256 a, uint256 b) internal pure returns (uint256) {
        return ternary(a < b, a, b);
    }

    /**
     * @dev Returns the average of two numbers. The result is rounded towards
     * zero.
     */
    function average(uint256 a, uint256 b) internal pure returns (uint256) {
        // (a + b) / 2 can overflow.
        return (a & b) + (a ^ b) / 2;
    }

    /**
     * @dev Returns the ceiling of the division of two numbers.
     *
     * This differs from standard division with `/` in that it rounds towards infinity instead
     * of rounding towards zero.
     */
    function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {
        if (b == 0) {
            // Guarantee the same behavior as in a regular Solidity division.
            Panic.panic(Panic.DIVISION_BY_ZERO);
        }

        // The following calculation ensures accurate ceiling division without overflow.
        // Since a is non-zero, (a - 1) / b will not overflow.
        // The largest possible result occurs when (a - 1) / b is type(uint256).max,
        // but the largest value we can obtain is type(uint256).max - 1, which happens
        // when a = type(uint256).max and b = 1.
        unchecked {
            return SafeCast.toUint(a > 0) * ((a - 1) / b + 1);
        }
    }

    /**
     * @dev Calculates floor(x * y / denominator) with full precision. Throws if result overflows a uint256 or
     * denominator == 0.
     *
     * Original credit to Remco Bloemen under MIT license (https://xn--2-umb.com/21/muldiv) with further edits by
     * Uniswap Labs also under MIT license.
     */
    function mulDiv(uint256 x, uint256 y, uint256 denominator) internal pure returns (uint256 result) {
        unchecked {
            (uint256 high, uint256 low) = mul512(x, y);

            // Handle non-overflow cases, 256 by 256 division.
            if (high == 0) {
                // Solidity will revert if denominator == 0, unlike the div opcode on its own.
                // The surrounding unchecked block does not change this fact.
                // See https://docs.soliditylang.org/en/latest/control-structures.html#checked-or-unchecked-arithmetic.
                return low / denominator;
            }

            // Make sure the result is less than 2²⁵⁶. Also prevents denominator == 0.
            if (denominator <= high) {
                Panic.panic(ternary(denominator == 0, Panic.DIVISION_BY_ZERO, Panic.UNDER_OVERFLOW));
            }

            ///////////////////////////////////////////////
            // 512 by 256 division.
            ///////////////////////////////////////////////

            // Make division exact by subtracting the remainder from [high low].
            uint256 remainder;
            assembly ("memory-safe") {
                // Compute remainder using mulmod.
                remainder := mulmod(x, y, denominator)

                // Subtract 256 bit number from 512 bit number.
                high := sub(high, gt(remainder, low))
                low := sub(low, remainder)
            }

            // Factor powers of two out of denominator and compute largest power of two divisor of denominator.
            // Always >= 1. See https://cs.stackexchange.com/q/138556/92363.

            uint256 twos = denominator & (0 - denominator);
            assembly ("memory-safe") {
                // Divide denominator by twos.
                denominator := div(denominator, twos)

                // Divide [high low] by twos.
                low := div(low, twos)

                // Flip twos such that it is 2²⁵⁶ / twos. If twos is zero, then it becomes one.
                twos := add(div(sub(0, twos), twos), 1)
            }

            // Shift in bits from high into low.
            low |= high * twos;

            // Invert denominator mod 2²⁵⁶. Now that denominator is an odd number, it has an inverse modulo 2²⁵⁶ such
            // that denominator * inv ≡ 1 mod 2²⁵⁶. Compute the inverse by starting with a seed that is correct for
            // four bits. That is, denominator * inv ≡ 1 mod 2⁴.
            uint256 inverse = (3 * denominator) ^ 2;

            // Use the Newton-Raphson iteration to improve the precision. Thanks to Hensel's lifting lemma, this also
            // works in modular arithmetic, doubling the correct bits in each step.
            inverse *= 2 - denominator * inverse; // inverse mod 2⁸
            inverse *= 2 - denominator * inverse; // inverse mod 2¹⁶
            inverse *= 2 - denominator * inverse; // inverse mod 2³²
            inverse *= 2 - denominator * inverse; // inverse mod 2⁶⁴
            inverse *= 2 - denominator * inverse; // inverse mod 2¹²⁸
            inverse *= 2 - denominator * inverse; // inverse mod 2²⁵⁶

            // Because the division is now exact we can divide by multiplying with the modular inverse of denominator.
            // This will give us the correct result modulo 2²⁵⁶. Since the preconditions guarantee that the outcome is
            // less than 2²⁵⁶, this is the final result. We don't need to compute the high bits of the result and high
            // is no longer required.
            result = low * inverse;
            return result;
        }
    }

    /**
     * @dev Calculates x * y / denominator with full precision, following the selected rounding direction.
     */
    function mulDiv(uint256 x, uint256 y, uint256 denominator, Rounding rounding) internal pure returns (uint256) {
        return mulDiv(x, y, denominator) + SafeCast.toUint(unsignedRoundsUp(rounding) && mulmod(x, y, denominator) > 0);
    }

    /**
     * @dev Calculates floor(x * y >> n) with full precision. Throws if result overflows a uint256.
     */
    function mulShr(uint256 x, uint256 y, uint8 n) internal pure returns (uint256 result) {
        unchecked {
            (uint256 high, uint256 low) = mul512(x, y);
            if (high >= 1 << n) {
                Panic.panic(Panic.UNDER_OVERFLOW);
            }
            return (high << (256 - n)) | (low >> n);
        }
    }

    /**
     * @dev Calculates x * y >> n with full precision, following the selected rounding direction.
     */
    function mulShr(uint256 x, uint256 y, uint8 n, Rounding rounding) internal pure returns (uint256) {
        return mulShr(x, y, n) + SafeCast.toUint(unsignedRoundsUp(rounding) && mulmod(x, y, 1 << n) > 0);
    }

    /**
     * @dev Calculate the modular multiplicative inverse of a number in Z/nZ.
     *
     * If n is a prime, then Z/nZ is a field. In that case all elements are inversible, except 0.
     * If n is not a prime, then Z/nZ is not a field, and some elements might not be inversible.
     *
     * If the input value is not inversible, 0 is returned.
     *
     * NOTE: If you know for sure that n is (big) a prime, it may be cheaper to use Fermat's little theorem and get the
     * inverse using `Math.modExp(a, n - 2, n)`. See {invModPrime}.
     */
    function invMod(uint256 a, uint256 n) internal pure returns (uint256) {
        unchecked {
            if (n == 0) return 0;

            // The inverse modulo is calculated using the Extended Euclidean Algorithm (iterative version)
            // Used to compute integers x and y such that: ax + ny = gcd(a, n).
            // When the gcd is 1, then the inverse of a modulo n exists and it's x.
            // ax + ny = 1
            // ax = 1 + (-y)n
            // ax ≡ 1 (mod n) # x is the inverse of a modulo n

            // If the remainder is 0 the gcd is n right away.
            uint256 remainder = a % n;
            uint256 gcd = n;

            // Therefore the initial coefficients are:
            // ax + ny = gcd(a, n) = n
            // 0a + 1n = n
            int256 x = 0;
            int256 y = 1;

            while (remainder != 0) {
                uint256 quotient = gcd / remainder;

                (gcd, remainder) = (
                    // The old remainder is the next gcd to try.
                    remainder,
                    // Compute the next remainder.
                    // Can't overflow given that (a % gcd) * (gcd // (a % gcd)) <= gcd
                    // where gcd is at most n (capped to type(uint256).max)
                    gcd - remainder * quotient
                );

                (x, y) = (
                    // Increment the coefficient of a.
                    y,
                    // Decrement the coefficient of n.
                    // Can overflow, but the result is casted to uint256 so that the
                    // next value of y is "wrapped around" to a value between 0 and n - 1.
                    x - y * int256(quotient)
                );
            }

            if (gcd != 1) return 0; // No inverse exists.
            return ternary(x < 0, n - uint256(-x), uint256(x)); // Wrap the result if it's negative.
        }
    }

    /**
     * @dev Variant of {invMod}. More efficient, but only works if `p` is known to be a prime greater than `2`.
     *
     * From https://en.wikipedia.org/wiki/Fermat%27s_little_theorem[Fermat's little theorem], we know that if p is
     * prime, then `a**(p-1) ≡ 1 mod p`. As a consequence, we have `a * a**(p-2) ≡ 1 mod p`, which means that
     * `a**(p-2)` is the modular multiplicative inverse of a in Fp.
     *
     * NOTE: this function does NOT check that `p` is a prime greater than `2`.
     */
    function invModPrime(uint256 a, uint256 p) internal view returns (uint256) {
        unchecked {
            return Math.modExp(a, p - 2, p);
        }
    }

    /**
     * @dev Returns the modular exponentiation of the specified base, exponent and modulus (b ** e % m)
     *
     * Requirements:
     * - modulus can't be zero
     * - underlying staticcall to precompile must succeed
     *
     * IMPORTANT: The result is only valid if the underlying call succeeds. When using this function, make
     * sure the chain you're using it on supports the precompiled contract for modular exponentiation
     * at address 0x05 as specified in https://eips.ethereum.org/EIPS/eip-198[EIP-198]. Otherwise,
     * the underlying function will succeed given the lack of a revert, but the result may be incorrectly
     * interpreted as 0.
     */
    function modExp(uint256 b, uint256 e, uint256 m) internal view returns (uint256) {
        (bool success, uint256 result) = tryModExp(b, e, m);
        if (!success) {
            Panic.panic(Panic.DIVISION_BY_ZERO);
        }
        return result;
    }

    /**
     * @dev Returns the modular exponentiation of the specified base, exponent and modulus (b ** e % m).
     * It includes a success flag indicating if the operation succeeded. Operation will be marked as failed if trying
     * to operate modulo 0 or if the underlying precompile reverted.
     *
     * IMPORTANT: The result is only valid if the success flag is true. When using this function, make sure the chain
     * you're using it on supports the precompiled contract for modular exponentiation at address 0x05 as specified in
     * https://eips.ethereum.org/EIPS/eip-198[EIP-198]. Otherwise, the underlying function will succeed given the lack
     * of a revert, but the result may be incorrectly interpreted as 0.
     */
    function tryModExp(uint256 b, uint256 e, uint256 m) internal view returns (bool success, uint256 result) {
        if (m == 0) return (false, 0);
        assembly ("memory-safe") {
            let ptr := mload(0x40)
            // | Offset    | Content    | Content (Hex)                                                      |
            // |-----------|------------|--------------------------------------------------------------------|
            // | 0x00:0x1f | size of b  | 0x0000000000000000000000000000000000000000000000000000000000000020 |
            // | 0x20:0x3f | size of e  | 0x0000000000000000000000000000000000000000000000000000000000000020 |
            // | 0x40:0x5f | size of m  | 0x0000000000000000000000000000000000000000000000000000000000000020 |
            // | 0x60:0x7f | value of b | 0x<.............................................................b> |
            // | 0x80:0x9f | value of e | 0x<.............................................................e> |
            // | 0xa0:0xbf | value of m | 0x<.............................................................m> |
            mstore(ptr, 0x20)
            mstore(add(ptr, 0x20), 0x20)
            mstore(add(ptr, 0x40), 0x20)
            mstore(add(ptr, 0x60), b)
            mstore(add(ptr, 0x80), e)
            mstore(add(ptr, 0xa0), m)

            // Given the result < m, it's guaranteed to fit in 32 bytes,
            // so we can use the memory scratch space located at offset 0.
            success := staticcall(gas(), 0x05, ptr, 0xc0, 0x00, 0x20)
            result := mload(0x00)
        }
    }

    /**
     * @dev Variant of {modExp} that supports inputs of arbitrary length.
     */
    function modExp(bytes memory b, bytes memory e, bytes memory m) internal view returns (bytes memory) {
        (bool success, bytes memory result) = tryModExp(b, e, m);
        if (!success) {
            Panic.panic(Panic.DIVISION_BY_ZERO);
        }
        return result;
    }

    /**
     * @dev Variant of {tryModExp} that supports inputs of arbitrary length.
     */
    function tryModExp(
        bytes memory b,
        bytes memory e,
        bytes memory m
    ) internal view returns (bool success, bytes memory result) {
        if (_zeroBytes(m)) return (false, new bytes(0));

        uint256 mLen = m.length;

        // Encode call args in result and move the free memory pointer
        result = abi.encodePacked(b.length, e.length, mLen, b, e, m);

        assembly ("memory-safe") {
            let dataPtr := add(result, 0x20)
            // Write result on top of args to avoid allocating extra memory.
            success := staticcall(gas(), 0x05, dataPtr, mload(result), dataPtr, mLen)
            // Overwrite the length.
            // result.length > returndatasize() is guaranteed because returndatasize() == m.length
            mstore(result, mLen)
            // Set the memory pointer after the returned data.
            mstore(0x40, add(dataPtr, mLen))
        }
    }

    /**
     * @dev Returns whether the provided byte array is zero.
     */
    function _zeroBytes(bytes memory byteArray) private pure returns (bool) {
        for (uint256 i = 0; i < byteArray.length; ++i) {
            if (byteArray[i] != 0) {
                return false;
            }
        }
        return true;
    }

    /**
     * @dev Returns the square root of a number. If the number is not a perfect square, the value is rounded
     * towards zero.
     *
     * This method is based on Newton's method for computing square roots; the algorithm is restricted to only
     * using integer operations.
     */
    function sqrt(uint256 a) internal pure returns (uint256) {
        unchecked {
            // Take care of easy edge cases when a == 0 or a == 1
            if (a <= 1) {
                return a;
            }

            // In this function, we use Newton's method to get a root of `f(x) := x² - a`. It involves building a
            // sequence x_n that converges toward sqrt(a). For each iteration x_n, we also define the error between
            // the current value as `ε_n = | x_n - sqrt(a) |`.
            //
            // For our first estimation, we consider `e` the smallest power of 2 which is bigger than the square root
            // of the target. (i.e. `2**(e-1) ≤ sqrt(a) < 2**e`). We know that `e ≤ 128` because `(2¹²⁸)² = 2²⁵⁶` is
            // bigger than any uint256.
            //
            // By noticing that
            // `2**(e-1) ≤ sqrt(a) < 2**e → (2**(e-1))² ≤ a < (2**e)² → 2**(2*e-2) ≤ a < 2**(2*e)`
            // we can deduce that `e - 1` is `log2(a) / 2`. We can thus compute `x_n = 2**(e-1)` using a method similar
            // to the msb function.
            uint256 aa = a;
            uint256 xn = 1;

            if (aa >= (1 << 128)) {
                aa >>= 128;
                xn <<= 64;
            }
            if (aa >= (1 << 64)) {
                aa >>= 64;
                xn <<= 32;
            }
            if (aa >= (1 << 32)) {
                aa >>= 32;
                xn <<= 16;
            }
            if (aa >= (1 << 16)) {
                aa >>= 16;
                xn <<= 8;
            }
            if (aa >= (1 << 8)) {
                aa >>= 8;
                xn <<= 4;
            }
            if (aa >= (1 << 4)) {
                aa >>= 4;
                xn <<= 2;
            }
            if (aa >= (1 << 2)) {
                xn <<= 1;
            }

            // We now have x_n such that `x_n = 2**(e-1) ≤ sqrt(a) < 2**e = 2 * x_n`. This implies ε_n ≤ 2**(e-1).
            //
            // We can refine our estimation by noticing that the middle of that interval minimizes the error.
            // If we move x_n to equal 2**(e-1) + 2**(e-2), then we reduce the error to ε_n ≤ 2**(e-2).
            // This is going to be our x_0 (and ε_0)
            xn = (3 * xn) >> 1; // ε_0 := | x_0 - sqrt(a) | ≤ 2**(e-2)

            // From here, Newton's method give us:
            // x_{n+1} = (x_n + a / x_n) / 2
            //
            // One should note that:
            // x_{n+1}² - a = ((x_n + a / x_n) / 2)² - a
            //              = ((x_n² + a) / (2 * x_n))² - a
            //              = (x_n⁴ + 2 * a * x_n² + a²) / (4 * x_n²) - a
            //              = (x_n⁴ + 2 * a * x_n² + a² - 4 * a * x_n²) / (4 * x_n²)
            //              = (x_n⁴ - 2 * a * x_n² + a²) / (4 * x_n²)
            //              = (x_n² - a)² / (2 * x_n)²
            //              = ((x_n² - a) / (2 * x_n))²
            //              ≥ 0
            // Which proves that for all n ≥ 1, sqrt(a) ≤ x_n
            //
            // This gives us the proof of quadratic convergence of the sequence:
            // ε_{n+1} = | x_{n+1} - sqrt(a) |
            //         = | (x_n + a / x_n) / 2 - sqrt(a) |
            //         = | (x_n² + a - 2*x_n*sqrt(a)) / (2 * x_n) |
            //         = | (x_n - sqrt(a))² / (2 * x_n) |
            //         = | ε_n² / (2 * x_n) |
            //         = ε_n² / | (2 * x_n) |
            //
            // For the first iteration, we have a special case where x_0 is known:
            // ε_1 = ε_0² / | (2 * x_0) |
            //     ≤ (2**(e-2))² / (2 * (2**(e-1) + 2**(e-2)))
            //     ≤ 2**(2*e-4) / (3 * 2**(e-1))
            //     ≤ 2**(e-3) / 3
            //     ≤ 2**(e-3-log2(3))
            //     ≤ 2**(e-4.5)
            //
            // For the following iterations, we use the fact that, 2**(e-1) ≤ sqrt(a) ≤ x_n:
            // ε_{n+1} = ε_n² / | (2 * x_n) |
            //         ≤ (2**(e-k))² / (2 * 2**(e-1))
            //         ≤ 2**(2*e-2*k) / 2**e
            //         ≤ 2**(e-2*k)
            xn = (xn + a / xn) >> 1; // ε_1 := | x_1 - sqrt(a) | ≤ 2**(e-4.5)  -- special case, see above
            xn = (xn + a / xn) >> 1; // ε_2 := | x_2 - sqrt(a) | ≤ 2**(e-9)    -- general case with k = 4.5
            xn = (xn + a / xn) >> 1; // ε_3 := | x_3 - sqrt(a) | ≤ 2**(e-18)   -- general case with k = 9
            xn = (xn + a / xn) >> 1; // ε_4 := | x_4 - sqrt(a) | ≤ 2**(e-36)   -- general case with k = 18
            xn = (xn + a / xn) >> 1; // ε_5 := | x_5 - sqrt(a) | ≤ 2**(e-72)   -- general case with k = 36
            xn = (xn + a / xn) >> 1; // ε_6 := | x_6 - sqrt(a) | ≤ 2**(e-144)  -- general case with k = 72

            // Because e ≤ 128 (as discussed during the first estimation phase), we know have reached a precision
            // ε_6 ≤ 2**(e-144) < 1. Given we're operating on integers, then we can ensure that xn is now either
            // sqrt(a) or sqrt(a) + 1.
            return xn - SafeCast.toUint(xn > a / xn);
        }
    }

    /**
     * @dev Calculates sqrt(a), following the selected rounding direction.
     */
    function sqrt(uint256 a, Rounding rounding) internal pure returns (uint256) {
        unchecked {
            uint256 result = sqrt(a);
            return result + SafeCast.toUint(unsignedRoundsUp(rounding) && result * result < a);
        }
    }

    /**
     * @dev Return the log in base 2 of a positive value rounded towards zero.
     * Returns 0 if given 0.
     */
    function log2(uint256 x) internal pure returns (uint256 r) {
        // If value has upper 128 bits set, log2 result is at least 128
        r = SafeCast.toUint(x > 0xffffffffffffffffffffffffffffffff) << 7;
        // If upper 64 bits of 128-bit half set, add 64 to result
        r |= SafeCast.toUint((x >> r) > 0xffffffffffffffff) << 6;
        // If upper 32 bits of 64-bit half set, add 32 to result
        r |= SafeCast.toUint((x >> r) > 0xffffffff) << 5;
        // If upper 16 bits of 32-bit half set, add 16 to result
        r |= SafeCast.toUint((x >> r) > 0xffff) << 4;
        // If upper 8 bits of 16-bit half set, add 8 to result
        r |= SafeCast.toUint((x >> r) > 0xff) << 3;
        // If upper 4 bits of 8-bit half set, add 4 to result
        r |= SafeCast.toUint((x >> r) > 0xf) << 2;

        // Shifts value right by the current result and use it as an index into this lookup table:
        //
        // | x (4 bits) |  index  | table[index] = MSB position |
        // |------------|---------|-----------------------------|
        // |    0000    |    0    |        table[0] = 0         |
        // |    0001    |    1    |        table[1] = 0         |
        // |    0010    |    2    |        table[2] = 1         |
        // |    0011    |    3    |        table[3] = 1         |
        // |    0100    |    4    |        table[4] = 2         |
        // |    0101    |    5    |        table[5] = 2         |
        // |    0110    |    6    |        table[6] = 2         |
        // |    0111    |    7    |        table[7] = 2         |
        // |    1000    |    8    |        table[8] = 3         |
        // |    1001    |    9    |        table[9] = 3         |
        // |    1010    |   10    |        table[10] = 3        |
        // |    1011    |   11    |        table[11] = 3        |
        // |    1100    |   12    |        table[12] = 3        |
        // |    1101    |   13    |        table[13] = 3        |
        // |    1110    |   14    |        table[14] = 3        |
        // |    1111    |   15    |        table[15] = 3        |
        //
        // The lookup table is represented as a 32-byte value with the MSB positions for 0-15 in the last 16 bytes.
        assembly ("memory-safe") {
            r := or(r, byte(shr(r, x), 0x0000010102020202030303030303030300000000000000000000000000000000))
        }
    }

    /**
     * @dev Return the log in base 2, following the selected rounding direction, of a positive value.
     * Returns 0 if given 0.
     */
    function log2(uint256 value, Rounding rounding) internal pure returns (uint256) {
        unchecked {
            uint256 result = log2(value);
            return result + SafeCast.toUint(unsignedRoundsUp(rounding) && 1 << result < value);
        }
    }

    /**
     * @dev Return the log in base 10 of a positive value rounded towards zero.
     * Returns 0 if given 0.
     */
    function log10(uint256 value) internal pure returns (uint256) {
        uint256 result = 0;
        unchecked {
            if (value >= 10 ** 64) {
                value /= 10 ** 64;
                result += 64;
            }
            if (value >= 10 ** 32) {
                value /= 10 ** 32;
                result += 32;
            }
            if (value >= 10 ** 16) {
                value /= 10 ** 16;
                result += 16;
            }
            if (value >= 10 ** 8) {
                value /= 10 ** 8;
                result += 8;
            }
            if (value >= 10 ** 4) {
                value /= 10 ** 4;
                result += 4;
            }
            if (value >= 10 ** 2) {
                value /= 10 ** 2;
                result += 2;
            }
            if (value >= 10 ** 1) {
                result += 1;
            }
        }
        return result;
    }

    /**
     * @dev Return the log in base 10, following the selected rounding direction, of a positive value.
     * Returns 0 if given 0.
     */
    function log10(uint256 value, Rounding rounding) internal pure returns (uint256) {
        unchecked {
            uint256 result = log10(value);
            return result + SafeCast.toUint(unsignedRoundsUp(rounding) && 10 ** result < value);
        }
    }

    /**
     * @dev Return the log in base 256 of a positive value rounded towards zero.
     * Returns 0 if given 0.
     *
     * Adding one to the result gives the number of pairs of hex symbols needed to represent `value` as a hex string.
     */
    function log256(uint256 x) internal pure returns (uint256 r) {
        // If value has upper 128 bits set, log2 result is at least 128
        r = SafeCast.toUint(x > 0xffffffffffffffffffffffffffffffff) << 7;
        // If upper 64 bits of 128-bit half set, add 64 to result
        r |= SafeCast.toUint((x >> r) > 0xffffffffffffffff) << 6;
        // If upper 32 bits of 64-bit half set, add 32 to result
        r |= SafeCast.toUint((x >> r) > 0xffffffff) << 5;
        // If upper 16 bits of 32-bit half set, add 16 to result
        r |= SafeCast.toUint((x >> r) > 0xffff) << 4;
        // Add 1 if upper 8 bits of 16-bit half set, and divide accumulated result by 8
        return (r >> 3) | SafeCast.toUint((x >> r) > 0xff);
    }

    /**
     * @dev Return the log in base 256, following the selected rounding direction, of a positive value.
     * Returns 0 if given 0.
     */
    function log256(uint256 value, Rounding rounding) internal pure returns (uint256) {
        unchecked {
            uint256 result = log256(value);
            return result + SafeCast.toUint(unsignedRoundsUp(rounding) && 1 << (result << 3) < value);
        }
    }

    /**
     * @dev Returns whether a provided rounding mode is considered rounding up for unsigned integers.
     */
    function unsignedRoundsUp(Rounding rounding) internal pure returns (bool) {
        return uint8(rounding) % 2 == 1;
    }
}

// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity >=0.8.15;

import {ROLESv1} from "src/modules/ROLES/ROLES.v1.sol";
import "src/Kernel.sol";

/// @notice Abstract contract to have the `onlyRole` modifier
/// @dev    Inheriting this automatically makes ROLES module a dependency
abstract contract RolesConsumer {
    ROLESv1 public ROLES;

    modifier onlyRole(bytes32 role_) {
        ROLES.requireRole(role_, msg.sender);
        _;
    }
}

/// @notice Module that holds multisig roles needed by various policies.
contract OlympusRoles is ROLESv1 {
    //============================================================================================//
    //                                        MODULE SETUP                                        //
    //============================================================================================//

    constructor(Kernel kernel_) Module(kernel_) {}

    /// @inheritdoc Module
    function KEYCODE() public pure override returns (Keycode) {
        return toKeycode("ROLES");
    }

    /// @inheritdoc Module
    function VERSION() external pure override returns (uint8 major, uint8 minor) {
        major = 1;
        minor = 0;
    }

    //============================================================================================//
    //                                       CORE FUNCTIONS                                       //
    //============================================================================================//

    /// @inheritdoc ROLESv1
    function saveRole(bytes32 role_, address addr_) external override permissioned {
        if (hasRole[addr_][role_]) revert ROLES_AddressAlreadyHasRole(addr_, role_);

        ensureValidRole(role_);

        // Grant role to the address
        hasRole[addr_][role_] = true;

        emit RoleGranted(role_, addr_);
    }

    /// @inheritdoc ROLESv1
    function removeRole(bytes32 role_, address addr_) external override permissioned {
        if (!hasRole[addr_][role_]) revert ROLES_AddressDoesNotHaveRole(addr_, role_);

        hasRole[addr_][role_] = false;

        emit RoleRevoked(role_, addr_);
    }

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

    /// @inheritdoc ROLESv1
    function requireRole(bytes32 role_, address caller_) external view override {
        if (!hasRole[caller_][role_]) revert ROLES_RequireRole(role_);
    }

    /// @inheritdoc ROLESv1
    function ensureValidRole(bytes32 role_) public pure override {
        for (uint256 i = 0; i < 32; ) {
            bytes1 char = role_[i];
            if ((char < 0x61 || char > 0x7A) && char != 0x5f && char != 0x00) {
                revert ROLES_InvalidRole(role_); // a-z only
            }
            unchecked {
                i++;
            }
        }
    }
}

File 33 of 35 : RoleDefinitions.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

/// @dev Allows enabling/disabling the protocol/policies in an emergency
bytes32 constant EMERGENCY_ROLE = "emergency";
/// @dev Administrative access, e.g. configuration parameters. Typically assigned to on-chain governance.
bytes32 constant ADMIN_ROLE = "admin";
/// @dev Managerial access, e.g. managing specific protocol parameters. Typically assigned to a multisig/council.
bytes32 constant MANAGER_ROLE = "manager";
/// @dev Heart role, e.g. performing periodic tasks.
bytes32 constant HEART_ROLE = "heart";

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (utils/Panic.sol)

pragma solidity ^0.8.20;

/**
 * @dev Helper library for emitting standardized panic codes.
 *
 * ```solidity
 * contract Example {
 *      using Panic for uint256;
 *
 *      // Use any of the declared internal constants
 *      function foo() { Panic.GENERIC.panic(); }
 *
 *      // Alternatively
 *      function foo() { Panic.panic(Panic.GENERIC); }
 * }
 * ```
 *
 * Follows the list from https://github.com/ethereum/solidity/blob/v0.8.24/libsolutil/ErrorCodes.h[libsolutil].
 *
 * _Available since v5.1._
 */
// slither-disable-next-line unused-state
library Panic {
    /// @dev generic / unspecified error
    uint256 internal constant GENERIC = 0x00;
    /// @dev used by the assert() builtin
    uint256 internal constant ASSERT = 0x01;
    /// @dev arithmetic underflow or overflow
    uint256 internal constant UNDER_OVERFLOW = 0x11;
    /// @dev division or modulo by zero
    uint256 internal constant DIVISION_BY_ZERO = 0x12;
    /// @dev enum conversion error
    uint256 internal constant ENUM_CONVERSION_ERROR = 0x21;
    /// @dev invalid encoding in storage
    uint256 internal constant STORAGE_ENCODING_ERROR = 0x22;
    /// @dev empty array pop
    uint256 internal constant EMPTY_ARRAY_POP = 0x31;
    /// @dev array out of bounds access
    uint256 internal constant ARRAY_OUT_OF_BOUNDS = 0x32;
    /// @dev resource error (too large allocation or too large array)
    uint256 internal constant RESOURCE_ERROR = 0x41;
    /// @dev calling invalid internal function
    uint256 internal constant INVALID_INTERNAL_FUNCTION = 0x51;

    /// @dev Reverts with a panic code. Recommended to use with
    /// the internal constants with predefined codes.
    function panic(uint256 code) internal pure {
        assembly ("memory-safe") {
            mstore(0x00, 0x4e487b71)
            mstore(0x20, code)
            revert(0x1c, 0x24)
        }
    }
}

File 35 of 35 : SafeCast.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (utils/math/SafeCast.sol)
// This file was procedurally generated from scripts/generate/templates/SafeCast.js.

pragma solidity ^0.8.20;

/**
 * @dev Wrappers over Solidity's uintXX/intXX/bool casting operators with added overflow
 * checks.
 *
 * Downcasting from uint256/int256 in Solidity does not revert on overflow. This can
 * easily result in undesired exploitation or bugs, since developers usually
 * assume that overflows raise errors. `SafeCast` restores this intuition by
 * reverting the transaction when such an operation overflows.
 *
 * Using this library instead of the unchecked operations eliminates an entire
 * class of bugs, so it's recommended to use it always.
 */
library SafeCast {
    /**
     * @dev Value doesn't fit in an uint of `bits` size.
     */
    error SafeCastOverflowedUintDowncast(uint8 bits, uint256 value);

    /**
     * @dev An int value doesn't fit in an uint of `bits` size.
     */
    error SafeCastOverflowedIntToUint(int256 value);

    /**
     * @dev Value doesn't fit in an int of `bits` size.
     */
    error SafeCastOverflowedIntDowncast(uint8 bits, int256 value);

    /**
     * @dev An uint value doesn't fit in an int of `bits` size.
     */
    error SafeCastOverflowedUintToInt(uint256 value);

    /**
     * @dev Returns the downcasted uint248 from uint256, reverting on
     * overflow (when the input is greater than largest uint248).
     *
     * Counterpart to Solidity's `uint248` operator.
     *
     * Requirements:
     *
     * - input must fit into 248 bits
     */
    function toUint248(uint256 value) internal pure returns (uint248) {
        if (value > type(uint248).max) {
            revert SafeCastOverflowedUintDowncast(248, value);
        }
        return uint248(value);
    }

    /**
     * @dev Returns the downcasted uint240 from uint256, reverting on
     * overflow (when the input is greater than largest uint240).
     *
     * Counterpart to Solidity's `uint240` operator.
     *
     * Requirements:
     *
     * - input must fit into 240 bits
     */
    function toUint240(uint256 value) internal pure returns (uint240) {
        if (value > type(uint240).max) {
            revert SafeCastOverflowedUintDowncast(240, value);
        }
        return uint240(value);
    }

    /**
     * @dev Returns the downcasted uint232 from uint256, reverting on
     * overflow (when the input is greater than largest uint232).
     *
     * Counterpart to Solidity's `uint232` operator.
     *
     * Requirements:
     *
     * - input must fit into 232 bits
     */
    function toUint232(uint256 value) internal pure returns (uint232) {
        if (value > type(uint232).max) {
            revert SafeCastOverflowedUintDowncast(232, value);
        }
        return uint232(value);
    }

    /**
     * @dev Returns the downcasted uint224 from uint256, reverting on
     * overflow (when the input is greater than largest uint224).
     *
     * Counterpart to Solidity's `uint224` operator.
     *
     * Requirements:
     *
     * - input must fit into 224 bits
     */
    function toUint224(uint256 value) internal pure returns (uint224) {
        if (value > type(uint224).max) {
            revert SafeCastOverflowedUintDowncast(224, value);
        }
        return uint224(value);
    }

    /**
     * @dev Returns the downcasted uint216 from uint256, reverting on
     * overflow (when the input is greater than largest uint216).
     *
     * Counterpart to Solidity's `uint216` operator.
     *
     * Requirements:
     *
     * - input must fit into 216 bits
     */
    function toUint216(uint256 value) internal pure returns (uint216) {
        if (value > type(uint216).max) {
            revert SafeCastOverflowedUintDowncast(216, value);
        }
        return uint216(value);
    }

    /**
     * @dev Returns the downcasted uint208 from uint256, reverting on
     * overflow (when the input is greater than largest uint208).
     *
     * Counterpart to Solidity's `uint208` operator.
     *
     * Requirements:
     *
     * - input must fit into 208 bits
     */
    function toUint208(uint256 value) internal pure returns (uint208) {
        if (value > type(uint208).max) {
            revert SafeCastOverflowedUintDowncast(208, value);
        }
        return uint208(value);
    }

    /**
     * @dev Returns the downcasted uint200 from uint256, reverting on
     * overflow (when the input is greater than largest uint200).
     *
     * Counterpart to Solidity's `uint200` operator.
     *
     * Requirements:
     *
     * - input must fit into 200 bits
     */
    function toUint200(uint256 value) internal pure returns (uint200) {
        if (value > type(uint200).max) {
            revert SafeCastOverflowedUintDowncast(200, value);
        }
        return uint200(value);
    }

    /**
     * @dev Returns the downcasted uint192 from uint256, reverting on
     * overflow (when the input is greater than largest uint192).
     *
     * Counterpart to Solidity's `uint192` operator.
     *
     * Requirements:
     *
     * - input must fit into 192 bits
     */
    function toUint192(uint256 value) internal pure returns (uint192) {
        if (value > type(uint192).max) {
            revert SafeCastOverflowedUintDowncast(192, value);
        }
        return uint192(value);
    }

    /**
     * @dev Returns the downcasted uint184 from uint256, reverting on
     * overflow (when the input is greater than largest uint184).
     *
     * Counterpart to Solidity's `uint184` operator.
     *
     * Requirements:
     *
     * - input must fit into 184 bits
     */
    function toUint184(uint256 value) internal pure returns (uint184) {
        if (value > type(uint184).max) {
            revert SafeCastOverflowedUintDowncast(184, value);
        }
        return uint184(value);
    }

    /**
     * @dev Returns the downcasted uint176 from uint256, reverting on
     * overflow (when the input is greater than largest uint176).
     *
     * Counterpart to Solidity's `uint176` operator.
     *
     * Requirements:
     *
     * - input must fit into 176 bits
     */
    function toUint176(uint256 value) internal pure returns (uint176) {
        if (value > type(uint176).max) {
            revert SafeCastOverflowedUintDowncast(176, value);
        }
        return uint176(value);
    }

    /**
     * @dev Returns the downcasted uint168 from uint256, reverting on
     * overflow (when the input is greater than largest uint168).
     *
     * Counterpart to Solidity's `uint168` operator.
     *
     * Requirements:
     *
     * - input must fit into 168 bits
     */
    function toUint168(uint256 value) internal pure returns (uint168) {
        if (value > type(uint168).max) {
            revert SafeCastOverflowedUintDowncast(168, value);
        }
        return uint168(value);
    }

    /**
     * @dev Returns the downcasted uint160 from uint256, reverting on
     * overflow (when the input is greater than largest uint160).
     *
     * Counterpart to Solidity's `uint160` operator.
     *
     * Requirements:
     *
     * - input must fit into 160 bits
     */
    function toUint160(uint256 value) internal pure returns (uint160) {
        if (value > type(uint160).max) {
            revert SafeCastOverflowedUintDowncast(160, value);
        }
        return uint160(value);
    }

    /**
     * @dev Returns the downcasted uint152 from uint256, reverting on
     * overflow (when the input is greater than largest uint152).
     *
     * Counterpart to Solidity's `uint152` operator.
     *
     * Requirements:
     *
     * - input must fit into 152 bits
     */
    function toUint152(uint256 value) internal pure returns (uint152) {
        if (value > type(uint152).max) {
            revert SafeCastOverflowedUintDowncast(152, value);
        }
        return uint152(value);
    }

    /**
     * @dev Returns the downcasted uint144 from uint256, reverting on
     * overflow (when the input is greater than largest uint144).
     *
     * Counterpart to Solidity's `uint144` operator.
     *
     * Requirements:
     *
     * - input must fit into 144 bits
     */
    function toUint144(uint256 value) internal pure returns (uint144) {
        if (value > type(uint144).max) {
            revert SafeCastOverflowedUintDowncast(144, value);
        }
        return uint144(value);
    }

    /**
     * @dev Returns the downcasted uint136 from uint256, reverting on
     * overflow (when the input is greater than largest uint136).
     *
     * Counterpart to Solidity's `uint136` operator.
     *
     * Requirements:
     *
     * - input must fit into 136 bits
     */
    function toUint136(uint256 value) internal pure returns (uint136) {
        if (value > type(uint136).max) {
            revert SafeCastOverflowedUintDowncast(136, value);
        }
        return uint136(value);
    }

    /**
     * @dev Returns the downcasted uint128 from uint256, reverting on
     * overflow (when the input is greater than largest uint128).
     *
     * Counterpart to Solidity's `uint128` operator.
     *
     * Requirements:
     *
     * - input must fit into 128 bits
     */
    function toUint128(uint256 value) internal pure returns (uint128) {
        if (value > type(uint128).max) {
            revert SafeCastOverflowedUintDowncast(128, value);
        }
        return uint128(value);
    }

    /**
     * @dev Returns the downcasted uint120 from uint256, reverting on
     * overflow (when the input is greater than largest uint120).
     *
     * Counterpart to Solidity's `uint120` operator.
     *
     * Requirements:
     *
     * - input must fit into 120 bits
     */
    function toUint120(uint256 value) internal pure returns (uint120) {
        if (value > type(uint120).max) {
            revert SafeCastOverflowedUintDowncast(120, value);
        }
        return uint120(value);
    }

    /**
     * @dev Returns the downcasted uint112 from uint256, reverting on
     * overflow (when the input is greater than largest uint112).
     *
     * Counterpart to Solidity's `uint112` operator.
     *
     * Requirements:
     *
     * - input must fit into 112 bits
     */
    function toUint112(uint256 value) internal pure returns (uint112) {
        if (value > type(uint112).max) {
            revert SafeCastOverflowedUintDowncast(112, value);
        }
        return uint112(value);
    }

    /**
     * @dev Returns the downcasted uint104 from uint256, reverting on
     * overflow (when the input is greater than largest uint104).
     *
     * Counterpart to Solidity's `uint104` operator.
     *
     * Requirements:
     *
     * - input must fit into 104 bits
     */
    function toUint104(uint256 value) internal pure returns (uint104) {
        if (value > type(uint104).max) {
            revert SafeCastOverflowedUintDowncast(104, value);
        }
        return uint104(value);
    }

    /**
     * @dev Returns the downcasted uint96 from uint256, reverting on
     * overflow (when the input is greater than largest uint96).
     *
     * Counterpart to Solidity's `uint96` operator.
     *
     * Requirements:
     *
     * - input must fit into 96 bits
     */
    function toUint96(uint256 value) internal pure returns (uint96) {
        if (value > type(uint96).max) {
            revert SafeCastOverflowedUintDowncast(96, value);
        }
        return uint96(value);
    }

    /**
     * @dev Returns the downcasted uint88 from uint256, reverting on
     * overflow (when the input is greater than largest uint88).
     *
     * Counterpart to Solidity's `uint88` operator.
     *
     * Requirements:
     *
     * - input must fit into 88 bits
     */
    function toUint88(uint256 value) internal pure returns (uint88) {
        if (value > type(uint88).max) {
            revert SafeCastOverflowedUintDowncast(88, value);
        }
        return uint88(value);
    }

    /**
     * @dev Returns the downcasted uint80 from uint256, reverting on
     * overflow (when the input is greater than largest uint80).
     *
     * Counterpart to Solidity's `uint80` operator.
     *
     * Requirements:
     *
     * - input must fit into 80 bits
     */
    function toUint80(uint256 value) internal pure returns (uint80) {
        if (value > type(uint80).max) {
            revert SafeCastOverflowedUintDowncast(80, value);
        }
        return uint80(value);
    }

    /**
     * @dev Returns the downcasted uint72 from uint256, reverting on
     * overflow (when the input is greater than largest uint72).
     *
     * Counterpart to Solidity's `uint72` operator.
     *
     * Requirements:
     *
     * - input must fit into 72 bits
     */
    function toUint72(uint256 value) internal pure returns (uint72) {
        if (value > type(uint72).max) {
            revert SafeCastOverflowedUintDowncast(72, value);
        }
        return uint72(value);
    }

    /**
     * @dev Returns the downcasted uint64 from uint256, reverting on
     * overflow (when the input is greater than largest uint64).
     *
     * Counterpart to Solidity's `uint64` operator.
     *
     * Requirements:
     *
     * - input must fit into 64 bits
     */
    function toUint64(uint256 value) internal pure returns (uint64) {
        if (value > type(uint64).max) {
            revert SafeCastOverflowedUintDowncast(64, value);
        }
        return uint64(value);
    }

    /**
     * @dev Returns the downcasted uint56 from uint256, reverting on
     * overflow (when the input is greater than largest uint56).
     *
     * Counterpart to Solidity's `uint56` operator.
     *
     * Requirements:
     *
     * - input must fit into 56 bits
     */
    function toUint56(uint256 value) internal pure returns (uint56) {
        if (value > type(uint56).max) {
            revert SafeCastOverflowedUintDowncast(56, value);
        }
        return uint56(value);
    }

    /**
     * @dev Returns the downcasted uint48 from uint256, reverting on
     * overflow (when the input is greater than largest uint48).
     *
     * Counterpart to Solidity's `uint48` operator.
     *
     * Requirements:
     *
     * - input must fit into 48 bits
     */
    function toUint48(uint256 value) internal pure returns (uint48) {
        if (value > type(uint48).max) {
            revert SafeCastOverflowedUintDowncast(48, value);
        }
        return uint48(value);
    }

    /**
     * @dev Returns the downcasted uint40 from uint256, reverting on
     * overflow (when the input is greater than largest uint40).
     *
     * Counterpart to Solidity's `uint40` operator.
     *
     * Requirements:
     *
     * - input must fit into 40 bits
     */
    function toUint40(uint256 value) internal pure returns (uint40) {
        if (value > type(uint40).max) {
            revert SafeCastOverflowedUintDowncast(40, value);
        }
        return uint40(value);
    }

    /**
     * @dev Returns the downcasted uint32 from uint256, reverting on
     * overflow (when the input is greater than largest uint32).
     *
     * Counterpart to Solidity's `uint32` operator.
     *
     * Requirements:
     *
     * - input must fit into 32 bits
     */
    function toUint32(uint256 value) internal pure returns (uint32) {
        if (value > type(uint32).max) {
            revert SafeCastOverflowedUintDowncast(32, value);
        }
        return uint32(value);
    }

    /**
     * @dev Returns the downcasted uint24 from uint256, reverting on
     * overflow (when the input is greater than largest uint24).
     *
     * Counterpart to Solidity's `uint24` operator.
     *
     * Requirements:
     *
     * - input must fit into 24 bits
     */
    function toUint24(uint256 value) internal pure returns (uint24) {
        if (value > type(uint24).max) {
            revert SafeCastOverflowedUintDowncast(24, value);
        }
        return uint24(value);
    }

    /**
     * @dev Returns the downcasted uint16 from uint256, reverting on
     * overflow (when the input is greater than largest uint16).
     *
     * Counterpart to Solidity's `uint16` operator.
     *
     * Requirements:
     *
     * - input must fit into 16 bits
     */
    function toUint16(uint256 value) internal pure returns (uint16) {
        if (value > type(uint16).max) {
            revert SafeCastOverflowedUintDowncast(16, value);
        }
        return uint16(value);
    }

    /**
     * @dev Returns the downcasted uint8 from uint256, reverting on
     * overflow (when the input is greater than largest uint8).
     *
     * Counterpart to Solidity's `uint8` operator.
     *
     * Requirements:
     *
     * - input must fit into 8 bits
     */
    function toUint8(uint256 value) internal pure returns (uint8) {
        if (value > type(uint8).max) {
            revert SafeCastOverflowedUintDowncast(8, value);
        }
        return uint8(value);
    }

    /**
     * @dev Converts a signed int256 into an unsigned uint256.
     *
     * Requirements:
     *
     * - input must be greater than or equal to 0.
     */
    function toUint256(int256 value) internal pure returns (uint256) {
        if (value < 0) {
            revert SafeCastOverflowedIntToUint(value);
        }
        return uint256(value);
    }

    /**
     * @dev Returns the downcasted int248 from int256, reverting on
     * overflow (when the input is less than smallest int248 or
     * greater than largest int248).
     *
     * Counterpart to Solidity's `int248` operator.
     *
     * Requirements:
     *
     * - input must fit into 248 bits
     */
    function toInt248(int256 value) internal pure returns (int248 downcasted) {
        downcasted = int248(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(248, value);
        }
    }

    /**
     * @dev Returns the downcasted int240 from int256, reverting on
     * overflow (when the input is less than smallest int240 or
     * greater than largest int240).
     *
     * Counterpart to Solidity's `int240` operator.
     *
     * Requirements:
     *
     * - input must fit into 240 bits
     */
    function toInt240(int256 value) internal pure returns (int240 downcasted) {
        downcasted = int240(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(240, value);
        }
    }

    /**
     * @dev Returns the downcasted int232 from int256, reverting on
     * overflow (when the input is less than smallest int232 or
     * greater than largest int232).
     *
     * Counterpart to Solidity's `int232` operator.
     *
     * Requirements:
     *
     * - input must fit into 232 bits
     */
    function toInt232(int256 value) internal pure returns (int232 downcasted) {
        downcasted = int232(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(232, value);
        }
    }

    /**
     * @dev Returns the downcasted int224 from int256, reverting on
     * overflow (when the input is less than smallest int224 or
     * greater than largest int224).
     *
     * Counterpart to Solidity's `int224` operator.
     *
     * Requirements:
     *
     * - input must fit into 224 bits
     */
    function toInt224(int256 value) internal pure returns (int224 downcasted) {
        downcasted = int224(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(224, value);
        }
    }

    /**
     * @dev Returns the downcasted int216 from int256, reverting on
     * overflow (when the input is less than smallest int216 or
     * greater than largest int216).
     *
     * Counterpart to Solidity's `int216` operator.
     *
     * Requirements:
     *
     * - input must fit into 216 bits
     */
    function toInt216(int256 value) internal pure returns (int216 downcasted) {
        downcasted = int216(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(216, value);
        }
    }

    /**
     * @dev Returns the downcasted int208 from int256, reverting on
     * overflow (when the input is less than smallest int208 or
     * greater than largest int208).
     *
     * Counterpart to Solidity's `int208` operator.
     *
     * Requirements:
     *
     * - input must fit into 208 bits
     */
    function toInt208(int256 value) internal pure returns (int208 downcasted) {
        downcasted = int208(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(208, value);
        }
    }

    /**
     * @dev Returns the downcasted int200 from int256, reverting on
     * overflow (when the input is less than smallest int200 or
     * greater than largest int200).
     *
     * Counterpart to Solidity's `int200` operator.
     *
     * Requirements:
     *
     * - input must fit into 200 bits
     */
    function toInt200(int256 value) internal pure returns (int200 downcasted) {
        downcasted = int200(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(200, value);
        }
    }

    /**
     * @dev Returns the downcasted int192 from int256, reverting on
     * overflow (when the input is less than smallest int192 or
     * greater than largest int192).
     *
     * Counterpart to Solidity's `int192` operator.
     *
     * Requirements:
     *
     * - input must fit into 192 bits
     */
    function toInt192(int256 value) internal pure returns (int192 downcasted) {
        downcasted = int192(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(192, value);
        }
    }

    /**
     * @dev Returns the downcasted int184 from int256, reverting on
     * overflow (when the input is less than smallest int184 or
     * greater than largest int184).
     *
     * Counterpart to Solidity's `int184` operator.
     *
     * Requirements:
     *
     * - input must fit into 184 bits
     */
    function toInt184(int256 value) internal pure returns (int184 downcasted) {
        downcasted = int184(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(184, value);
        }
    }

    /**
     * @dev Returns the downcasted int176 from int256, reverting on
     * overflow (when the input is less than smallest int176 or
     * greater than largest int176).
     *
     * Counterpart to Solidity's `int176` operator.
     *
     * Requirements:
     *
     * - input must fit into 176 bits
     */
    function toInt176(int256 value) internal pure returns (int176 downcasted) {
        downcasted = int176(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(176, value);
        }
    }

    /**
     * @dev Returns the downcasted int168 from int256, reverting on
     * overflow (when the input is less than smallest int168 or
     * greater than largest int168).
     *
     * Counterpart to Solidity's `int168` operator.
     *
     * Requirements:
     *
     * - input must fit into 168 bits
     */
    function toInt168(int256 value) internal pure returns (int168 downcasted) {
        downcasted = int168(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(168, value);
        }
    }

    /**
     * @dev Returns the downcasted int160 from int256, reverting on
     * overflow (when the input is less than smallest int160 or
     * greater than largest int160).
     *
     * Counterpart to Solidity's `int160` operator.
     *
     * Requirements:
     *
     * - input must fit into 160 bits
     */
    function toInt160(int256 value) internal pure returns (int160 downcasted) {
        downcasted = int160(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(160, value);
        }
    }

    /**
     * @dev Returns the downcasted int152 from int256, reverting on
     * overflow (when the input is less than smallest int152 or
     * greater than largest int152).
     *
     * Counterpart to Solidity's `int152` operator.
     *
     * Requirements:
     *
     * - input must fit into 152 bits
     */
    function toInt152(int256 value) internal pure returns (int152 downcasted) {
        downcasted = int152(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(152, value);
        }
    }

    /**
     * @dev Returns the downcasted int144 from int256, reverting on
     * overflow (when the input is less than smallest int144 or
     * greater than largest int144).
     *
     * Counterpart to Solidity's `int144` operator.
     *
     * Requirements:
     *
     * - input must fit into 144 bits
     */
    function toInt144(int256 value) internal pure returns (int144 downcasted) {
        downcasted = int144(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(144, value);
        }
    }

    /**
     * @dev Returns the downcasted int136 from int256, reverting on
     * overflow (when the input is less than smallest int136 or
     * greater than largest int136).
     *
     * Counterpart to Solidity's `int136` operator.
     *
     * Requirements:
     *
     * - input must fit into 136 bits
     */
    function toInt136(int256 value) internal pure returns (int136 downcasted) {
        downcasted = int136(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(136, value);
        }
    }

    /**
     * @dev Returns the downcasted int128 from int256, reverting on
     * overflow (when the input is less than smallest int128 or
     * greater than largest int128).
     *
     * Counterpart to Solidity's `int128` operator.
     *
     * Requirements:
     *
     * - input must fit into 128 bits
     */
    function toInt128(int256 value) internal pure returns (int128 downcasted) {
        downcasted = int128(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(128, value);
        }
    }

    /**
     * @dev Returns the downcasted int120 from int256, reverting on
     * overflow (when the input is less than smallest int120 or
     * greater than largest int120).
     *
     * Counterpart to Solidity's `int120` operator.
     *
     * Requirements:
     *
     * - input must fit into 120 bits
     */
    function toInt120(int256 value) internal pure returns (int120 downcasted) {
        downcasted = int120(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(120, value);
        }
    }

    /**
     * @dev Returns the downcasted int112 from int256, reverting on
     * overflow (when the input is less than smallest int112 or
     * greater than largest int112).
     *
     * Counterpart to Solidity's `int112` operator.
     *
     * Requirements:
     *
     * - input must fit into 112 bits
     */
    function toInt112(int256 value) internal pure returns (int112 downcasted) {
        downcasted = int112(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(112, value);
        }
    }

    /**
     * @dev Returns the downcasted int104 from int256, reverting on
     * overflow (when the input is less than smallest int104 or
     * greater than largest int104).
     *
     * Counterpart to Solidity's `int104` operator.
     *
     * Requirements:
     *
     * - input must fit into 104 bits
     */
    function toInt104(int256 value) internal pure returns (int104 downcasted) {
        downcasted = int104(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(104, value);
        }
    }

    /**
     * @dev Returns the downcasted int96 from int256, reverting on
     * overflow (when the input is less than smallest int96 or
     * greater than largest int96).
     *
     * Counterpart to Solidity's `int96` operator.
     *
     * Requirements:
     *
     * - input must fit into 96 bits
     */
    function toInt96(int256 value) internal pure returns (int96 downcasted) {
        downcasted = int96(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(96, value);
        }
    }

    /**
     * @dev Returns the downcasted int88 from int256, reverting on
     * overflow (when the input is less than smallest int88 or
     * greater than largest int88).
     *
     * Counterpart to Solidity's `int88` operator.
     *
     * Requirements:
     *
     * - input must fit into 88 bits
     */
    function toInt88(int256 value) internal pure returns (int88 downcasted) {
        downcasted = int88(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(88, value);
        }
    }

    /**
     * @dev Returns the downcasted int80 from int256, reverting on
     * overflow (when the input is less than smallest int80 or
     * greater than largest int80).
     *
     * Counterpart to Solidity's `int80` operator.
     *
     * Requirements:
     *
     * - input must fit into 80 bits
     */
    function toInt80(int256 value) internal pure returns (int80 downcasted) {
        downcasted = int80(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(80, value);
        }
    }

    /**
     * @dev Returns the downcasted int72 from int256, reverting on
     * overflow (when the input is less than smallest int72 or
     * greater than largest int72).
     *
     * Counterpart to Solidity's `int72` operator.
     *
     * Requirements:
     *
     * - input must fit into 72 bits
     */
    function toInt72(int256 value) internal pure returns (int72 downcasted) {
        downcasted = int72(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(72, value);
        }
    }

    /**
     * @dev Returns the downcasted int64 from int256, reverting on
     * overflow (when the input is less than smallest int64 or
     * greater than largest int64).
     *
     * Counterpart to Solidity's `int64` operator.
     *
     * Requirements:
     *
     * - input must fit into 64 bits
     */
    function toInt64(int256 value) internal pure returns (int64 downcasted) {
        downcasted = int64(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(64, value);
        }
    }

    /**
     * @dev Returns the downcasted int56 from int256, reverting on
     * overflow (when the input is less than smallest int56 or
     * greater than largest int56).
     *
     * Counterpart to Solidity's `int56` operator.
     *
     * Requirements:
     *
     * - input must fit into 56 bits
     */
    function toInt56(int256 value) internal pure returns (int56 downcasted) {
        downcasted = int56(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(56, value);
        }
    }

    /**
     * @dev Returns the downcasted int48 from int256, reverting on
     * overflow (when the input is less than smallest int48 or
     * greater than largest int48).
     *
     * Counterpart to Solidity's `int48` operator.
     *
     * Requirements:
     *
     * - input must fit into 48 bits
     */
    function toInt48(int256 value) internal pure returns (int48 downcasted) {
        downcasted = int48(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(48, value);
        }
    }

    /**
     * @dev Returns the downcasted int40 from int256, reverting on
     * overflow (when the input is less than smallest int40 or
     * greater than largest int40).
     *
     * Counterpart to Solidity's `int40` operator.
     *
     * Requirements:
     *
     * - input must fit into 40 bits
     */
    function toInt40(int256 value) internal pure returns (int40 downcasted) {
        downcasted = int40(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(40, value);
        }
    }

    /**
     * @dev Returns the downcasted int32 from int256, reverting on
     * overflow (when the input is less than smallest int32 or
     * greater than largest int32).
     *
     * Counterpart to Solidity's `int32` operator.
     *
     * Requirements:
     *
     * - input must fit into 32 bits
     */
    function toInt32(int256 value) internal pure returns (int32 downcasted) {
        downcasted = int32(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(32, value);
        }
    }

    /**
     * @dev Returns the downcasted int24 from int256, reverting on
     * overflow (when the input is less than smallest int24 or
     * greater than largest int24).
     *
     * Counterpart to Solidity's `int24` operator.
     *
     * Requirements:
     *
     * - input must fit into 24 bits
     */
    function toInt24(int256 value) internal pure returns (int24 downcasted) {
        downcasted = int24(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(24, value);
        }
    }

    /**
     * @dev Returns the downcasted int16 from int256, reverting on
     * overflow (when the input is less than smallest int16 or
     * greater than largest int16).
     *
     * Counterpart to Solidity's `int16` operator.
     *
     * Requirements:
     *
     * - input must fit into 16 bits
     */
    function toInt16(int256 value) internal pure returns (int16 downcasted) {
        downcasted = int16(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(16, value);
        }
    }

    /**
     * @dev Returns the downcasted int8 from int256, reverting on
     * overflow (when the input is less than smallest int8 or
     * greater than largest int8).
     *
     * Counterpart to Solidity's `int8` operator.
     *
     * Requirements:
     *
     * - input must fit into 8 bits
     */
    function toInt8(int256 value) internal pure returns (int8 downcasted) {
        downcasted = int8(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(8, value);
        }
    }

    /**
     * @dev Converts an unsigned uint256 into a signed int256.
     *
     * Requirements:
     *
     * - input must be less than or equal to maxInt256.
     */
    function toInt256(uint256 value) internal pure returns (int256) {
        // Note: Unsafe cast below is okay because `type(int256).max` is guaranteed to be positive
        if (value > uint256(type(int256).max)) {
            revert SafeCastOverflowedUintToInt(value);
        }
        return int256(value);
    }

    /**
     * @dev Cast a boolean (false or true) to a uint256 (0 or 1) with no jump.
     */
    function toUint(bool b) internal pure returns (uint256 u) {
        assembly ("memory-safe") {
            u := iszero(iszero(b))
        }
    }
}

Settings
{
  "remappings": [
    "interfaces/=src/interfaces/",
    "modules/=src/modules/",
    "policies/=src/policies/",
    "libraries/=src/libraries/",
    "test/=src/test/",
    "test/mocks/=src/test/mocks/",
    "test/lib/=src/test/lib/",
    "@base64-1.1.0/=dependencies/base64-1.1.0/",
    "@surl-1.0.0/=dependencies/surl-1.0.0/src/",
    "@chainlink-ccip-1.6.0/=dependencies/chainlink-ccip-1.6.0/contracts/src/v0.8/",
    "@chainlink-local-0.2.5/=dependencies/chainlink-local-0.2.5/src/",
    "@chainlink/contracts-ccip/contracts/=dependencies/chainlink-ccip-1.6.0/contracts/src/v0.8/ccip/",
    "@chainlink/contracts/=dependencies/chainlink-ccip-1.6.0/contracts/",
    "@forge-std-1.9.6/=dependencies/forge-std-1.9.6/src/",
    "forge-std/=dependencies/forge-std-1.9.6/src/",
    "@safe-utils-0.0.13/=dependencies/safe-utils-0.0.13/src/",
    "@base58-solidity-1.0.3/=dependencies/base58-solidity-1.0.3/contracts/",
    "@clones-with-immutable-args-1.1.2/=dependencies/clones-with-immutable-args-1.1.2/src/",
    "clones/=dependencies/clones-with-immutable-args-1.1.2/src/",
    "@layer-zero-endpoint-v1-0.0.6/=dependencies/layer-zero-endpoint-v1-0.0.6/contracts/",
    "layer-zero/=dependencies/layer-zero-endpoint-v1-0.0.6/contracts/",
    "@solmate-6.2.0/=dependencies/solmate-6.2.0/src/",
    "solmate/=dependencies/solmate-6.2.0/src/",
    "@openzeppelin-4.8.0/=dependencies/openzeppelin-4.8.0/contracts/",
    "@openzeppelin/contracts/=dependencies/openzeppelin-4.8.0/contracts/",
    "openzeppelin/=dependencies/openzeppelin-4.8.0/contracts/",
    "@openzeppelin-5.3.0/=dependencies/openzeppelin-new-5.3.0/contracts/",
    "proposal-sim/=dependencies/forge-proposal-simulator-1.0.1/",
    "dependencies/forge-proposal-simulator-1.0.1/:@addresses/=dependencies/forge-proposal-simulator-1.0.1/addresses/",
    "dependencies/forge-proposal-simulator-1.0.1/:@utils/=dependencies/forge-proposal-simulator-1.0.1/utils/",
    "dependencies/forge-proposal-simulator-1.0.1/:@proposals/=dependencies/forge-proposal-simulator-1.0.1/proposals/",
    "Governors/=dependencies/forge-proposal-simulator-1.0.1/Governors/",
    "safe-smart-account/=dependencies/safe-smart-account-1.4.1/contracts/",
    "solidity-http/=dependencies/solidity-http-0.0.6/src/",
    "@addresses/=dependencies/forge-proposal-simulator-1.0.1/addresses/",
    "@chainlink/local/src/=dependencies/chainlink-local-0.2.5/src/",
    "@examples/=dependencies/forge-proposal-simulator-1.0.1/examples/",
    "@proposals/=dependencies/forge-proposal-simulator-1.0.1/proposals/",
    "@script/=dependencies/forge-proposal-simulator-1.0.1/script/",
    "@test/=dependencies/forge-proposal-simulator-1.0.1/test/",
    "@utils/=dependencies/forge-proposal-simulator-1.0.1/utils/",
    "base58-solidity-1.0.3/=dependencies/base58-solidity-1.0.3/contracts/",
    "base64-1.1.0/=dependencies/base64-1.1.0/",
    "chainlink-ccip-1.6.0/=dependencies/chainlink-ccip-1.6.0/",
    "chainlink-local-0.2.5/=dependencies/chainlink-local-0.2.5/src/",
    "clones-with-immutable-args-1.1.2/=dependencies/clones-with-immutable-args-1.1.2/src/",
    "comp-governance/=dependencies/forge-proposal-simulator-1.0.1/lib/compound-governance/contracts/",
    "ds-test/=dependencies/clones-with-immutable-args-1.1.2/lib/ds-test/src/",
    "forge-proposal-simulator-1.0.1/=dependencies/forge-proposal-simulator-1.0.1/",
    "forge-std-1.9.6/=dependencies/forge-std-1.9.6/src/",
    "layer-zero-endpoint-v1-0.0.6/=dependencies/layer-zero-endpoint-v1-0.0.6/contracts/",
    "openzeppelin-4.8.0/=dependencies/openzeppelin-4.8.0/",
    "openzeppelin-new-5.3.0/=dependencies/openzeppelin-new-5.3.0/",
    "safe-smart-account-1.4.1/=dependencies/safe-smart-account-1.4.1/",
    "safe-utils-0.0.13/=dependencies/safe-utils-0.0.13/src/",
    "solidity-http-0.0.6/=dependencies/solidity-http-0.0.6/src/",
    "solidity-stringutils/=dependencies/solidity-http-0.0.6/lib/solidity-stringutils/",
    "solmate-6.2.0/=dependencies/solmate-6.2.0/src/",
    "surl-1.0.0/=dependencies/surl-1.0.0/src/"
  ],
  "optimizer": {
    "enabled": true,
    "runs": 10000
  },
  "metadata": {
    "useLiteralContent": false,
    "bytecodeHash": "ipfs",
    "appendCBOR": true
  },
  "outputSelection": {
    "*": {
      "*": [
        "evm.bytecode",
        "evm.deployedBytecode",
        "devdoc",
        "userdoc",
        "metadata",
        "abi"
      ]
    }
  },
  "evmVersion": "prague",
  "viaIR": false
}

Contract Security Audit

Contract ABI

API
[{"inputs":[{"internalType":"address","name":"kernel_","type":"address"},{"internalType":"address","name":"depositManager_","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[{"internalType":"string","name":"reason_","type":"string"}],"name":"DEPOS_InvalidParams","type":"error"},{"inputs":[{"internalType":"uint256","name":"positionId_","type":"uint256"}],"name":"DEPOS_NotOwner","type":"error"},{"inputs":[{"internalType":"address","name":"caller_","type":"address"}],"name":"KernelAdapter_OnlyKernel","type":"error"},{"inputs":[],"name":"NotAuthorised","type":"error"},{"inputs":[],"name":"NotDisabled","type":"error"},{"inputs":[],"name":"NotEnabled","type":"error"},{"inputs":[{"internalType":"Keycode","name":"keycode_","type":"bytes5"}],"name":"Policy_ModuleDoesNotExist","type":"error"},{"inputs":[{"internalType":"bytes","name":"expected_","type":"bytes"}],"name":"Policy_WrongModuleVersion","type":"error"},{"inputs":[{"internalType":"bytes32","name":"role_","type":"bytes32"}],"name":"ROLES_RequireRole","type":"error"},{"inputs":[{"internalType":"address","name":"user","type":"address"},{"internalType":"uint16","name":"redemptionId","type":"uint16"}],"name":"RedemptionVault_AlreadyRedeemed","type":"error"},{"inputs":[{"internalType":"address","name":"facility","type":"address"}],"name":"RedemptionVault_FacilityExists","type":"error"},{"inputs":[{"internalType":"address","name":"facility","type":"address"}],"name":"RedemptionVault_FacilityNotRegistered","type":"error"},{"inputs":[{"internalType":"address","name":"asset","type":"address"},{"internalType":"address","name":"facility","type":"address"}],"name":"RedemptionVault_InterestRateNotSet","type":"error"},{"inputs":[{"internalType":"address","name":"user","type":"address"},{"internalType":"uint16","name":"redemptionId","type":"uint16"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"RedemptionVault_InvalidAmount","type":"error"},{"inputs":[{"internalType":"address","name":"depositManager","type":"address"}],"name":"RedemptionVault_InvalidDepositManager","type":"error"},{"inputs":[{"internalType":"address","name":"facility","type":"address"}],"name":"RedemptionVault_InvalidFacility","type":"error"},{"inputs":[{"internalType":"address","name":"user","type":"address"},{"internalType":"uint16","name":"redemptionId","type":"uint16"}],"name":"RedemptionVault_InvalidLoan","type":"error"},{"inputs":[{"internalType":"address","name":"user","type":"address"},{"internalType":"uint16","name":"redemptionId","type":"uint16"}],"name":"RedemptionVault_InvalidRedemptionId","type":"error"},{"inputs":[{"internalType":"address","name":"user","type":"address"},{"internalType":"uint16","name":"redemptionId","type":"uint16"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"RedemptionVault_LoanAmountExceeded","type":"error"},{"inputs":[{"internalType":"address","name":"user","type":"address"},{"internalType":"uint16","name":"redemptionId","type":"uint16"}],"name":"RedemptionVault_LoanIncorrectState","type":"error"},{"inputs":[{"internalType":"address","name":"user","type":"address"},{"internalType":"uint16","name":"redemptionId","type":"uint16"},{"internalType":"uint256","name":"actualAmount","type":"uint256"},{"internalType":"uint256","name":"maxAmount","type":"uint256"}],"name":"RedemptionVault_MaxSlippageExceeded","type":"error"},{"inputs":[{"internalType":"uint16","name":"rate","type":"uint16"}],"name":"RedemptionVault_OutOfBounds","type":"error"},{"inputs":[{"internalType":"address","name":"user","type":"address"},{"internalType":"uint16","name":"redemptionId","type":"uint16"},{"internalType":"uint48","name":"redeemableAt","type":"uint48"}],"name":"RedemptionVault_TooEarly","type":"error"},{"inputs":[{"internalType":"address","name":"user","type":"address"},{"internalType":"uint16","name":"redemptionId","type":"uint16"}],"name":"RedemptionVault_UnpaidLoan","type":"error"},{"inputs":[],"name":"RedemptionVault_ZeroAddress","type":"error"},{"inputs":[],"name":"RedemptionVault_ZeroAmount","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"asset","type":"address"},{"indexed":true,"internalType":"address","name":"facility","type":"address"},{"indexed":false,"internalType":"uint16","name":"rate","type":"uint16"}],"name":"AnnualInterestRateSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint16","name":"percent","type":"uint16"}],"name":"ClaimDefaultRewardPercentageSet","type":"event"},{"anonymous":false,"inputs":[],"name":"Disabled","type":"event"},{"anonymous":false,"inputs":[],"name":"Enabled","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"facility","type":"address"}],"name":"FacilityAuthorized","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"facility","type":"address"}],"name":"FacilityDeauthorized","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":true,"internalType":"uint16","name":"redemptionId","type":"uint16"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"address","name":"facility","type":"address"}],"name":"LoanCreated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":true,"internalType":"uint16","name":"redemptionId","type":"uint16"},{"indexed":false,"internalType":"uint256","name":"principal","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"interest","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"remainingCollateral","type":"uint256"}],"name":"LoanDefaulted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":true,"internalType":"uint16","name":"redemptionId","type":"uint16"},{"indexed":false,"internalType":"uint256","name":"newDueDate","type":"uint256"}],"name":"LoanExtended","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":true,"internalType":"uint16","name":"redemptionId","type":"uint16"},{"indexed":false,"internalType":"uint256","name":"principal","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"interest","type":"uint256"}],"name":"LoanRepaid","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"asset","type":"address"},{"indexed":true,"internalType":"address","name":"facility","type":"address"},{"indexed":false,"internalType":"uint16","name":"percent","type":"uint16"}],"name":"MaxBorrowPercentageSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":true,"internalType":"uint16","name":"redemptionId","type":"uint16"},{"indexed":true,"internalType":"address","name":"depositToken","type":"address"},{"indexed":false,"internalType":"uint8","name":"depositPeriod","type":"uint8"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"remainingAmount","type":"uint256"}],"name":"RedemptionCancelled","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":true,"internalType":"uint16","name":"redemptionId","type":"uint16"},{"indexed":true,"internalType":"address","name":"depositToken","type":"address"},{"indexed":false,"internalType":"uint8","name":"depositPeriod","type":"uint8"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"RedemptionFinished","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":true,"internalType":"uint16","name":"redemptionId","type":"uint16"},{"indexed":true,"internalType":"address","name":"depositToken","type":"address"},{"indexed":false,"internalType":"uint8","name":"depositPeriod","type":"uint8"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"address","name":"facility","type":"address"}],"name":"RedemptionStarted","type":"event"},{"inputs":[],"name":"DEPOS","outputs":[{"internalType":"contract DEPOSv1","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"DEPOSIT_MANAGER","outputs":[{"internalType":"contract IDepositManager","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"ONE_HUNDRED_PERCENT","outputs":[{"internalType":"uint16","name":"","type":"uint16"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"ROLES","outputs":[{"internalType":"contract ROLESv1","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"TRSRY","outputs":[{"internalType":"contract TRSRYv1","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"facility_","type":"address"}],"name":"authorizeFacility","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint16","name":"redemptionId_","type":"uint16"}],"name":"borrowAgainstRedemption","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint16","name":"redemptionId_","type":"uint16"},{"internalType":"uint256","name":"amount_","type":"uint256"}],"name":"cancelRedemption","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract Kernel","name":"newKernel_","type":"address"}],"name":"changeKernel","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"user_","type":"address"},{"internalType":"uint16","name":"redemptionId_","type":"uint16"}],"name":"claimDefaultedLoan","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"configureDependencies","outputs":[{"internalType":"Keycode[]","name":"dependencies","type":"bytes5[]"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"facility_","type":"address"}],"name":"deauthorizeFacility","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes","name":"disableData_","type":"bytes"}],"name":"disable","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes","name":"enableData_","type":"bytes"}],"name":"enable","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint16","name":"redemptionId_","type":"uint16"},{"internalType":"uint8","name":"months_","type":"uint8"}],"name":"extendLoan","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint16","name":"redemptionId_","type":"uint16"}],"name":"finishRedemption","outputs":[{"internalType":"uint256","name":"actualAmount","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract IERC20","name":"asset_","type":"address"},{"internalType":"address","name":"facility_","type":"address"}],"name":"getAnnualInterestRate","outputs":[{"internalType":"uint16","name":"","type":"uint16"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getAuthorizedFacilities","outputs":[{"internalType":"address[]","name":"","type":"address[]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getClaimDefaultRewardPercentage","outputs":[{"internalType":"uint16","name":"","type":"uint16"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract IERC20","name":"asset_","type":"address"},{"internalType":"address","name":"facility_","type":"address"}],"name":"getMaxBorrowPercentage","outputs":[{"internalType":"uint16","name":"","type":"uint16"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"user_","type":"address"},{"internalType":"uint16","name":"redemptionId_","type":"uint16"}],"name":"getRedemptionLoan","outputs":[{"components":[{"internalType":"uint256","name":"initialPrincipal","type":"uint256"},{"internalType":"uint256","name":"principal","type":"uint256"},{"internalType":"uint256","name":"interest","type":"uint256"},{"internalType":"uint48","name":"dueDate","type":"uint48"},{"internalType":"bool","name":"isDefaulted","type":"bool"}],"internalType":"struct IDepositRedemptionVault.Loan","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"user_","type":"address"},{"internalType":"uint16","name":"redemptionId_","type":"uint16"}],"name":"getUserRedemption","outputs":[{"components":[{"internalType":"address","name":"depositToken","type":"address"},{"internalType":"uint8","name":"depositPeriod","type":"uint8"},{"internalType":"uint48","name":"redeemableAt","type":"uint48"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"address","name":"facility","type":"address"},{"internalType":"uint256","name":"positionId","type":"uint256"}],"internalType":"struct IDepositRedemptionVault.UserRedemption","name":"redemption","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"user_","type":"address"}],"name":"getUserRedemptionCount","outputs":[{"internalType":"uint16","name":"count","type":"uint16"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"user_","type":"address"}],"name":"getUserRedemptions","outputs":[{"components":[{"internalType":"address","name":"depositToken","type":"address"},{"internalType":"uint8","name":"depositPeriod","type":"uint8"},{"internalType":"uint48","name":"redeemableAt","type":"uint48"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"address","name":"facility","type":"address"},{"internalType":"uint256","name":"positionId","type":"uint256"}],"internalType":"struct IDepositRedemptionVault.UserRedemption[]","name":"","type":"tuple[]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"isActive","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"facility_","type":"address"}],"name":"isAuthorizedFacility","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"isEnabled","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"kernel","outputs":[{"internalType":"contract Kernel","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"user_","type":"address"},{"internalType":"uint16","name":"redemptionId_","type":"uint16"}],"name":"previewBorrowAgainstRedemption","outputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint48","name":"","type":"uint48"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"user_","type":"address"},{"internalType":"uint16","name":"redemptionId_","type":"uint16"},{"internalType":"uint8","name":"months_","type":"uint8"}],"name":"previewExtendLoan","outputs":[{"internalType":"uint48","name":"","type":"uint48"},{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint16","name":"redemptionId_","type":"uint16"},{"internalType":"uint256","name":"amount_","type":"uint256"},{"internalType":"uint256","name":"maxSlippage_","type":"uint256"}],"name":"repayLoan","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"requestPermissions","outputs":[{"components":[{"internalType":"Keycode","name":"keycode","type":"bytes5"},{"internalType":"bytes4","name":"funcSelector","type":"bytes4"}],"internalType":"struct Permissions[]","name":"permissions","type":"tuple[]"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"contract IERC20","name":"asset_","type":"address"},{"internalType":"address","name":"facility_","type":"address"},{"internalType":"uint16","name":"rate_","type":"uint16"}],"name":"setAnnualInterestRate","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint16","name":"percent_","type":"uint16"}],"name":"setClaimDefaultRewardPercentage","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract IERC20","name":"asset_","type":"address"},{"internalType":"address","name":"facility_","type":"address"},{"internalType":"uint16","name":"percent_","type":"uint16"}],"name":"setMaxBorrowPercentage","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"positionId_","type":"uint256"},{"internalType":"uint256","name":"amount_","type":"uint256"}],"name":"startRedemption","outputs":[{"internalType":"uint16","name":"redemptionId","type":"uint16"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract IERC20","name":"depositToken_","type":"address"},{"internalType":"uint8","name":"depositPeriod_","type":"uint8"},{"internalType":"uint256","name":"amount_","type":"uint256"},{"internalType":"address","name":"facility_","type":"address"}],"name":"startRedemption","outputs":[{"internalType":"uint16","name":"redemptionId","type":"uint16"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes4","name":"interfaceId","type":"bytes4"}],"name":"supportsInterface","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"}]

60a06040526001600255348015610014575f5ffd5b50604051615b81380380615b8183398101604081905261003391610111565b5f80546001600160a01b0319166001600160a01b03848116919091179091556040516301ffc9a760e01b8152637e3680dd60e01b6004820152908216906301ffc9a790602401602060405180830381865afa158015610094573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906100b89190610142565b6100e457604051631cc6a4ad60e31b81526001600160a01b038216600482015260240160405180910390fd5b6001600160a01b031660805250610168565b80516001600160a01b038116811461010c575f5ffd5b919050565b5f5f60408385031215610122575f5ffd5b61012b836100f6565b9150610139602084016100f6565b90509250929050565b5f60208284031215610152575f5ffd5b81518015158114610161575f5ffd5b9392505050565b60805161599f6101e25f395f8181610364015281816108ca01528181610948015281816109f601528181610bf401528181611101015281816111710152818161121f0152818161137a015281816117f3015281816118d701528181612875015281816129e9015281816145aa0152614682015261599f5ff3fe608060405234801561000f575f5ffd5b5060043610610283575f3560e01c80636aa633b611610157578063cb865d8f116100d2578063f2463ee811610088578063f911b6bc1161006e578063f911b6bc1461062d578063f91fde3614610640578063ff6e4e2714610653575f5ffd5b8063f2463ee8146105c6578063f337cbc1146105fc575f5ffd5b8063d4aae0c4116100b8578063d4aae0c414610598578063dd0081c7146105aa578063e13fe767146105b3575f5ffd5b8063cb865d8f1461054c578063ce9ddccc1461056c575f5ffd5b8063923cb95211610127578063bd96a8791161010d578063bd96a87914610506578063c4e71eaf14610519578063c938156614610539575f5ffd5b8063923cb952146104de5780639459b875146104f1575f5ffd5b80636aa633b61461048b5780637977ca901461049f57806380cee4ab146104b25780638fc925aa146104cb575f5ffd5b806337a9c983116102015780635414396b116101b757806359f4c4261161019d57806359f4c426146104075780635dc54e23146104655780635eaf5d6714610478575f5ffd5b80635414396b146103ec5780635924be70146103f8575f5ffd5b806343fd097e116101e757806343fd097e146103b15780634657b36c146103c457806348595010146103d7575f5ffd5b806337a9c9831461035f5780634330a75c1461039e575f5ffd5b80630c959556116102565780631ef306e31161023c5780631ef306e31461031e57806322f3e2d41461034457806331dd41e31461034c575f5ffd5b80630c959556146102f857806314b0a3391461030b575f5ffd5b80630105b65b1461028757806301ffc9a71461029c57806308b9b2a9146102c45780630b3a580c146102e5575b5f5ffd5b61029a6102953660046150a1565b610666565b005b6102af6102aa3660046150d4565b610e0f565b60405190151581526020015b60405180910390f35b6102d76102d2366004615113565b610eb6565b6040519081526020016102bb565b61029a6102f336600461512c565b611467565b61029a610306366004615154565b611a40565b6102af6103193660046151c2565b611b03565b61033161032c3660046151dd565b611b0f565b60405161ffff90911681526020016102bb565b6102af61201c565b6102d761035a366004615113565b6120a4565b6103867f000000000000000000000000000000000000000000000000000000000000000081565b6040516001600160a01b0390911681526020016102bb565b6103316103ac3660046151fd565b612457565b6103316103bf3660046151fd565b612480565b61029a6103d23660046151c2565b61248e565b6103df61250c565b6040516102bb9190615234565b60055461ffff16610331565b60606040516102bb919061527f565b61041a6104153660046150a1565b612518565b6040516102bb91905f60a08201905082518252602083015160208301526040830151604083015265ffffffffffff606084015116606083015260808301511515608083015292915050565b61029a610473366004615306565b6125bf565b61029a610486366004615336565b612acb565b6001546102af90600160a01b900460ff1681565b61029a6104ad3660046151c2565b612c80565b600554610386906201000090046001600160a01b031681565b61029a6104d9366004615154565b612f00565b600154610386906001600160a01b031681565b6104f9612faf565b6040516102bb919061537a565b61029a6105143660046153e1565b6131f8565b61052c6105273660046151c2565b613509565b6040516102bb919061540b565b61029a610547366004615113565b61367b565b61055f61055a3660046150a1565b613783565b6040516102bb919061549f565b61033161057a3660046151c2565b6001600160a01b03165f9081526007602052604090205461ffff1690565b5f54610386906001600160a01b031681565b61033161271081565b61029a6105c13660046151c2565b613892565b6105d96105d43660046150a1565b613973565b60408051938452602084019290925265ffffffffffff16908201526060016102bb565b61060f61060a3660046154ff565b61398d565b6040805165ffffffffffff90931683526020830191909152016102bb565b61033161063b366004615545565b613bb5565b600654610386906001600160a01b031681565b61029a610661366004615336565b613efb565b6002546001146106d7576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600a60248201527f5245454e5452414e43590000000000000000000000000000000000000000000060448201526064015b60405180910390fd5b600280556106e36140a7565b81816106ef82826140ec565b5f5f5f6106fc8787614163565b5f908152600860209081526040808320600b9092529091206002820154919450925061073191506001600160a01b03166141a6565b600381015465ffffffffffff165f0361078b576040517f55e6177a0000000000000000000000000000000000000000000000000000000081526001600160a01b038716600482015261ffff861660248201526044016106ce565b600381015465ffffffffffff164210806107b3575060038101546601000000000000900460ff165b806107c057506001810154155b1561080c576040517f285f89e10000000000000000000000000000000000000000000000000000000081526001600160a01b038716600482015261ffff861660248201526044016106ce565b6001808201546002830154835492850154919290915f9161082c916155c2565b6003850180547fffffffffffffffffffffffffffffffffffffffffffffffffff00ffffffffffff1666010000000000001790555f6001860181905560028087018290558754908801546040517f11df9fa20000000000000000000000000000000000000000000000000000000081526001600160a01b038084166004830152600160a01b90930460ff166024820152908216604482015292935090917f0000000000000000000000000000000000000000000000000000000000000000909116906311df9fa290606401602060405180830381865afa158015610911573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061093591906155d5565b90505f61094285846155ec565b90505f5f7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663bc3dc04e6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156109a2573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906109c691906155ff565b6040517f426a84930000000000000000000000000000000000000000000000000000000081526001600160a01b037f00000000000000000000000000000000000000000000000000000000000000008116600483015260248201879052604482018690529192509082169063426a8493906064016020604051808303815f875af1158015610a56573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610a7a9190615629565b508615610b1557600289015489546040517f21e079a60000000000000000000000000000000000000000000000000000000081526001600160a01b038083166004830152600160a01b90920460ff166024820152604481018a90523060648201529116906321e079a6906084015f604051808303815f87803b158015610afe575f5ffd5b505af1158015610b10573d5f5f3e3d5ffd5b505050505b8415610bc457600289015489546040517f757e9ae70000000000000000000000000000000000000000000000000000000081526001600160a01b038083166004830152600160a01b90920460ff1660248201526044810188905230606482015291169063757e9ae7906084016020604051808303815f875af1158015610b9d573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610bc191906155d5565b91505b6040517f426a84930000000000000000000000000000000000000000000000000000000081526001600160a01b037f000000000000000000000000000000000000000000000000000000000000000081166004830152602482018690525f604483015282169063426a8493906064016020604051808303815f875af1158015610c4f573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610c739190615629565b50610c80905086856155ec565b886001015f828254610c9291906155c2565b90915550506005545f90610cae90839061ffff166127106141f5565b90505f610cbb82846155c2565b90508115610cd9578954610cd9906001600160a01b031633846142a1565b8015610d02576005548a54610d02916001600160a01b03918216916201000090910416836142a1565b61ffff8d166001600160a01b038f167fdd9b9a8dbb6213e735782fa30d86ba4895b54644dda4d03ce809954c0a04d8678a8a610d3e828c6155ec565b6040805193845260208401929092529082015260600160405180910390a3895f015f9054906101000a90046001600160a01b03166001600160a01b03168d61ffff168f6001600160a01b03167f651175795d5e55543ac975d01bf28fd07964198e4ecffb623256b03e9f7a87568d5f0160149054906101000a900460ff168c8b610dc891906155ec565b8f60010154604051610df29392919060ff9390931683526020830191909152604082015260600190565b60405180910390a450506001600255505050505050505050505050565b5f7fffffffff0000000000000000000000000000000000000000000000000000000082167f01ffc9a7000000000000000000000000000000000000000000000000000000001480610ea157507fffffffff0000000000000000000000000000000000000000000000000000000082167f5512aa4a00000000000000000000000000000000000000000000000000000000145b80610eb05750610eb0826143ff565b92915050565b5f600254600114610f23576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600a60248201527f5245454e5452414e43590000000000000000000000000000000000000000000060448201526064016106ce565b60028055610f2f6140a7565b3382610f3b82826140ec565b5f610f463386614163565b5f818152600860205260409020600281015491925090610f6e906001600160a01b03166141a6565b80600101545f03610fb7576040517f11c258d100000000000000000000000000000000000000000000000000000000815233600482015261ffff871660248201526044016106ce565b80547501000000000000000000000000000000000000000000900465ffffffffffff164210156110485780546040517fb1e4b4aa00000000000000000000000000000000000000000000000000000000815233600482015261ffff88166024820152750100000000000000000000000000000000000000000090910465ffffffffffff1660448201526064016106ce565b5f828152600b60205260409020600101541561109c576040517fb0f135b000000000000000000000000000000000000000000000000000000000815233600482015261ffff871660248201526044016106ce565b6001810180545f91829055825460028401546040517f11df9fa20000000000000000000000000000000000000000000000000000000081526001600160a01b038084166004830152600160a01b90930460ff16602482015290821660448201529192917f0000000000000000000000000000000000000000000000000000000000000000909116906311df9fa290606401602060405180830381865afa158015611148573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061116c91906155d5565b90505f7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663bc3dc04e6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156111cb573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906111ef91906155ff565b6040517f426a84930000000000000000000000000000000000000000000000000000000081526001600160a01b037f00000000000000000000000000000000000000000000000000000000000000008116600483015260248201859052604482018690529192509082169063426a8493906064016020604051808303815f875af115801561127f573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906112a39190615629565b50600284015484546040517f757e9ae70000000000000000000000000000000000000000000000000000000081526001600160a01b038083166004830152600160a01b90920460ff1660248201526044810186905233606482015291169063757e9ae7906084016020604051808303815f875af1158015611326573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061134a91906155d5565b6040517f426a84930000000000000000000000000000000000000000000000000000000081526001600160a01b037f000000000000000000000000000000000000000000000000000000000000000081166004830152602482018590525f60448301529199509082169063426a8493906064016020604051808303815f875af11580156113d9573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906113fd9190615629565b50835460408051600160a01b830460ff168152602081018690526001600160a01b039092169161ffff8c169133917fda66127b6b8e933c8290917b0744c4fc122e4ba27df3f33b2f8f5dcb4ac71f06910160405180910390a4505050505050506001600255919050565b6002546001146114d3576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600a60248201527f5245454e5452414e43590000000000000000000000000000000000000000000060448201526064016106ce565b600280556114df6140a7565b33826114eb82826140ec565b5f6114f63386614163565b5f81815260086020526040902060028101549192509061151e906001600160a01b03166141a6565b845f03611557576040517f1b60ca6900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b80600101548511156115a8576040517f3ab03ca900000000000000000000000000000000000000000000000000000000815233600482015261ffff87166024820152604481018690526064016106ce565b5f828152600b6020526040902060010154156115fc576040517fb0f135b000000000000000000000000000000000000000000000000000000000815233600482015261ffff871660248201526044016106ce565b84816001015f82825461160f91906155c2565b9091555050600281015481546040517faad721050000000000000000000000000000000000000000000000000000000081526001600160a01b038083166004830152600160a01b90920460ff1660248201526044810188905291169063aad72105906064015f604051808303815f87803b15801561168b575f5ffd5b505af115801561169d573d5f5f3e3d5ffd5b505050505f198160030154146117f05760065460038201546040517feb02c301000000000000000000000000000000000000000000000000000000008152600481019190915233916001600160a01b03169063eb02c301906024015f60405180830381865afa158015611712573d5f5f3e3d5ffd5b505050506040513d5f823e601f3d9081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0168201604052611757919081019061576d565b602001516001600160a01b0316036117f057600281015460038201546040517fe97ee9550000000000000000000000000000000000000000000000000000000081526001600160a01b039092169163e97ee955916117c2918990600401918252602082015260400190565b5f604051808303815f87803b1580156117d9575f5ffd5b505af11580156117eb573d5f5f3e3d5ffd5b505050505b5f7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663bc3dc04e6040518163ffffffff1660e01b8152600401602060405180830381865afa15801561184d573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061187191906155ff565b825460028401546040517f11df9fa20000000000000000000000000000000000000000000000000000000081526001600160a01b038084166004830152600160a01b90930460ff16602482015290821660448201529192508083169163095bcdb69133917f0000000000000000000000000000000000000000000000000000000000000000909116906311df9fa290606401602060405180830381865afa15801561191e573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061194291906155d5565b6040517fffffffff0000000000000000000000000000000000000000000000000000000060e085901b1681526001600160a01b0390921660048301526024820152604481018990526064016020604051808303815f875af11580156119a9573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906119cd9190615629565b50815460018301546040805160ff600160a01b8504168152602081018a905280820192909252516001600160a01b039092169161ffff8a169133917f651175795d5e55543ac975d01bf28fd07964198e4ecffb623256b03e9f7a87569181900360600190a4505060016002555050505050565b611a4933614496565b611aa1576040517f7de30f410000000000000000000000000000000000000000000000000000000081527f61646d696e00000000000000000000000000000000000000000000000000000060048201526024016106ce565b611aa9614542565b600180547fffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffff16600160a01b1790556040517fc0f961051f97b04c496472d11cb6170d844e4b2c9dfd3b602a4fa0139712d484905f90a15050565b5f610eb0600983614586565b5f600254600114611b7c576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600a60248201527f5245454e5452414e43590000000000000000000000000000000000000000000060448201526064016106ce565b60028055611b886140a7565b815f03611bc1576040517f1b60ca6900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6006546040517feb02c301000000000000000000000000000000000000000000000000000000008152600481018590525f916001600160a01b03169063eb02c301906024015f60405180830381865afa158015611c20573d5f5f3e3d5ffd5b505050506040513d5f823e601f3d9081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0168201604052611c65919081019061576d565b60208101519091506001600160a01b03163314611cb1576040517f45734ef7000000000000000000000000000000000000000000000000000000008152600481018590526024016106ce565b8060800151831115611d1f576040517f929ad8ed00000000000000000000000000000000000000000000000000000000815260206004820152600660248201527f616d6f756e74000000000000000000000000000000000000000000000000000060448201526064016106ce565b604081015160608201518251611d34816141a6565b335f908152600760205260408120805461ffff1691611d5283615856565b91906101000a81548161ffff021916908361ffff16021790555094506040518060c00160405280846001600160a01b031681526020018360ff1681526020018560c0015165ffffffffffff168152602001878152602001826001600160a01b031681526020018881525060085f611dc93389614163565b815260208082019290925260409081015f208351815493850151858401516001600160a01b039283167fffffffffffffffffffffff00000000000000000000000000000000000000000090961695909517600160a01b60ff92831602177fffffffffff000000000000ffffffffffffffffffffffffffffffffffffffffff16750100000000000000000000000000000000000000000065ffffffffffff909616959095029490941782556060850151600183015560808501516002830180547fffffffffffffffffffffffff00000000000000000000000000000000000000001691831691909117905560a090940151600390910155517f2f34546b000000000000000000000000000000000000000000000000000000008152858316600482015290841660248201526044810188905290821690632f34546b906064015f604051808303815f87803b158015611f1e575f5ffd5b505af1158015611f30573d5f5f3e3d5ffd5b50506040517fa9cf27de000000000000000000000000000000000000000000000000000000008152600481018a9052602481018990526001600160a01b038416925063a9cf27de91506044015f604051808303815f87803b158015611f93575f5ffd5b505af1158015611fa5573d5f5f3e3d5ffd5b50505050611fb5838383896145a7565b6040805160ff84168152602081018890526001600160a01b038381168284015291519185169161ffff88169133917f73249ca0e3cb0c8adf0ce7e639a3f6b22bc27a30a943fd8133a1fe062265c3019181900360600190a450505050600160025592915050565b5f80546040517fe52223bb0000000000000000000000000000000000000000000000000000000081523060048201526001600160a01b039091169063e52223bb90602401602060405180830381865afa15801561207b573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061209f9190615629565b905090565b5f600254600114612111576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600a60248201527f5245454e5452414e43590000000000000000000000000000000000000000000060448201526064016106ce565b6002805561211d6140a7565b338261212982826140ec565b5f6121343386614163565b5f818152600860209081526040808320600b909252909120600301549192509065ffffffffffff161561219f576040517f285f89e100000000000000000000000000000000000000000000000000000000815233600482015261ffff871660248201526044016106ce565b805460028201546004915f916121c1916001600160a01b039081169116614789565b815260208101919091526040015f9081205461ffff16900361222957805460028201546040517f06082a180000000000000000000000000000000000000000000000000000000081526001600160a01b039283166004820152911660248201526044016106ce565b5f5f5f612236338a6147b0565b925092509250825f03612275576040517f1b60ca6900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6040805160a081018252848152602080820186815282840186815265ffffffffffff868116606086019081525f608087018181528d8252600b90965287812087518155945160018601559251600280860191909155905160039094018054955115156601000000000000027fffffffffffffffffffffffffffffffffffffffffffffffffff00000000000000909616949092169390931793909317909255870154875493517fae04c1070000000000000000000000000000000000000000000000000000000081526001600160a01b038086166004830152600160a01b90950460ff166024820152604481018890523360648201529293919291169063ae04c107906084016020604051808303815f875af1158015612396573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906123ba91906155d5565b9050805f036123f5576040517f1b60ca6900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6002860154604080518381526001600160a01b03909216602083015261ffff8d169133917ff5cbb4792d2c67cbe6360dcea810195870fbc966ffaf062a7e7713127050c1e7910160405180910390a360016002559a9950505050505050505050565b5f60035f6124658585614789565b815260208101919091526040015f205461ffff169392505050565b5f60045f6124658585614789565b5f546001600160a01b031633146124d3576040517f14fa403c0000000000000000000000000000000000000000000000000000000081523360048201526024016106ce565b5f80547fffffffffffffffffffffffff0000000000000000000000000000000000000000166001600160a01b0392909216919091179055565b606061209f60096148ed565b61254f6040518060a001604052805f81526020015f81526020015f81526020015f65ffffffffffff1681526020015f151581525090565b600b5f61255c8585614163565b815260208082019290925260409081015f20815160a081018352815481526001820154938101939093526002810154918301919091526003015465ffffffffffff811660608301526601000000000000900460ff16151560808201529392505050565b60025460011461262b576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600a60248201527f5245454e5452414e43590000000000000000000000000000000000000000000060448201526064016106ce565b600280556126376140a7565b338361264382826140ec565b835f0361267c576040517f1b60ca6900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f6126873387614163565b5f81815260086020908152604091829020825160c08101845281546001600160a01b038082168352600160a01b820460ff16948301949094527501000000000000000000000000000000000000000000900465ffffffffffff16938101939093526001810154606084015260028101549091166080830181905260039091015460a083015291925090612719906141a6565b5f828152600b602052604081206003810154909165ffffffffffff909116900361277b576040517f55e6177a00000000000000000000000000000000000000000000000000000000815233600482015261ffff891660248201526044016106ce565b600381015465ffffffffffff16421015806127a4575060038101546601000000000000900460ff165b806127b157506001810154155b156127f4576040517f285f89e100000000000000000000000000000000000000000000000000000000815233600482015261ffff891660248201526044016106ce565b815161280b906001600160a01b031633308a6148f9565b5f5f8260020154891161282057889150612834565b60028301549150612831828a6155c2565b90505b811561285d57600554845161285d916001600160a01b03918216916201000090910416846142a1565b5f8115612a0e57845161289a906001600160a01b03167f000000000000000000000000000000000000000000000000000000000000000084614a58565b60808501518551602087015160018701546040517f1500b2d10000000000000000000000000000000000000000000000000000000081526001600160a01b03938416600482015260ff9092166024830152604482018690526064820152306084820152911690631500b2d19060a4016020604051808303815f875af1158015612925573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061294991906155d5565b90508360010154821015801561296d575088846001015461296a91906155ec565b81115b156129d857338b828b876001015461298591906155ec565b6040517fbf4661960000000000000000000000000000000000000000000000000000000081526001600160a01b03909416600485015261ffff9092166024840152604483015260648201526084016106ce565b8451612a0e906001600160a01b03167f00000000000000000000000000000000000000000000000000000000000000005f614a58565b8215612a435783600201548311612a255782612a2b565b83600201545b846002015f828254612a3d91906155c2565b90915550505b8015612a785783600101548111612a5a5780612a60565b83600101545b846001015f828254612a7291906155c2565b90915550505b604080518281526020810185905261ffff8d169133917f4ba59a99c14dcd6586c2ac1537ac071480a5e5ee5441e8fc83818fccc642c6ef910160405180910390a350506001600255505050505050505050565b612ad36140a7565b612adc33614baf565b158015612aef5750612aed33614496565b155b15612b26576040517f1648fd0100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6001600160a01b038316612b66576040517f7dbe27de00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6001600160a01b038216612ba6576040517f7dbe27de00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61271061ffff82161115612bec576040517fa92683a800000000000000000000000000000000000000000000000000000000815261ffff821660048201526024016106ce565b8060035f612bfa8686614789565b815260208082019290925260409081015f2080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00001661ffff9485161790555191831682526001600160a01b0384811692908616917f7e3ffd1e23bc4b3ff0b2ffee6b46237d60fa2fd705005fd9d7431eff52508ff991015b60405180910390a3505050565b612c8933614496565b612ce1576040517f7de30f410000000000000000000000000000000000000000000000000000000081527f61646d696e00000000000000000000000000000000000000000000000000000060048201526024016106ce565b6001600160a01b038116612d2c576040517f49a2f9690000000000000000000000000000000000000000000000000000000081526001600160a01b03821660048201526024016106ce565b612d37600982614586565b15612d79576040517f5ea5e3860000000000000000000000000000000000000000000000000000000081526001600160a01b03821660048201526024016106ce565b604080517e52f9aa0000000000000000000000000000000000000000000000000000000060248083019190915282518083039091018152604490910182526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167f01ffc9a70000000000000000000000000000000000000000000000000000000017905290515f9182916001600160a01b03851691612e1991615876565b5f60405180830381855afa9150503d805f8114612e51576040519150601f19603f3d011682016040523d82523d5f602084013e612e56565b606091505b5091509150811580612e79575080806020019051810190612e779190615629565b155b15612ebb576040517f49a2f9690000000000000000000000000000000000000000000000000000000081526001600160a01b03841660048201526024016106ce565b50612ec99050600982614c20565b506040516001600160a01b038216907f5574568d3b2c858d01dd672f106c56b1a2b8708170e09b6551500138ebf2adad905f90a250565b612f0933614c34565b158015612f1c5750612f1a33614496565b155b15612f53576040517f1648fd0100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b612f5b6140a7565b600180547fffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffff1690556040517f75884cdadc4a89e8b545db800057f06ec7f5338a08183c7ba515f2bfdd9fe1e1905f90a15050565b604080516003808252608082019092526060916020820183803683370190505090507f5452535259000000000000000000000000000000000000000000000000000000815f815181106130045761300461588c565b7fffffffffff0000000000000000000000000000000000000000000000000000009092166020928302919091019091015261305c7f524f4c455300000000000000000000000000000000000000000000000000000090565b8160018151811061306f5761306f61588c565b7fffffffffff000000000000000000000000000000000000000000000000000000909216602092830291909101909101526130c77f4445504f5300000000000000000000000000000000000000000000000000000090565b816002815181106130da576130da61588c565b60200260200101907affffffffffffffffffffffffffffffffffffffffffffffffffffff191690817affffffffffffffffffffffffffffffffffffffffffffffffffffff191681525050613146815f815181106131395761313961588c565b6020026020010151614ca5565b600560026101000a8154816001600160a01b0302191690836001600160a01b03160217905550613182816001815181106131395761313961588c565b60015f6101000a8154816001600160a01b0302191690836001600160a01b031602179055506131bd816002815181106131395761313961588c565b600680547fffffffffffffffffffffffff0000000000000000000000000000000000000000166001600160a01b039290921691909117905590565b600254600114613264576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600a60248201527f5245454e5452414e43590000000000000000000000000000000000000000000060448201526064016106ce565b600280556132706140a7565b338261327c82826140ec565b8260ff165f036132b8576040517f1b60ca6900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f6132c33386614163565b5f81815260086020908152604091829020825160c08101845281546001600160a01b038082168352600160a01b820460ff16948301949094527501000000000000000000000000000000000000000000900465ffffffffffff16938101939093526001810154606084015260028101549091166080830181905260039091015460a083015291925090613355906141a6565b5f828152600b602052604081206003810154909165ffffffffffff90911690036133b7576040517f55e6177a00000000000000000000000000000000000000000000000000000000815233600482015261ffff881660248201526044016106ce565b600381015465ffffffffffff16421015806133e0575060038101546601000000000000900460ff165b806133ed57506001810154155b15613430576040517f285f89e100000000000000000000000000000000000000000000000000000000815233600482015261ffff881660248201526044016106ce565b81516080830151600183015460038401545f93849361345c93919290919065ffffffffffff168c614db0565b6005548651929450909250613487916001600160a01b039081169133916201000090910416846148f9565b6003830180547fffffffffffffffffffffffffffffffffffffffffffffffffffff0000000000001665ffffffffffff841690811790915560405190815261ffff8a169033907fb7ba6522ad736820c140f6d62d345ba66f38167ead41236d73d550fe38b686a49060200160405180910390a35050600160025550505050505050565b6001600160a01b0381165f9081526007602052604081205460609161ffff909116908167ffffffffffffffff81111561354457613544615642565b6040519080825280602002602001820160405280156135a257816020015b6040805160c0810182525f8082526020808301829052928201819052606082018190526080820181905260a082015282525f199092019101816135625790505b5090505f5b8261ffff168161ffff1610156136735760085f6135c48784614163565b815260208082019290925260409081015f20815160c08101835281546001600160a01b038082168352600160a01b820460ff16958301959095527501000000000000000000000000000000000000000000900465ffffffffffff1692810192909252600181015460608301526002810154909216608082015260039091015460a08201528251839061ffff84169081106136605761366061588c565b60209081029190910101526001016135a7565b509392505050565b6136836140a7565b61368c33614baf565b15801561369f575061369d33614496565b155b156136d6576040517f1648fd0100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61271061ffff8216111561371c576040517fa92683a800000000000000000000000000000000000000000000000000000000815261ffff821660048201526024016106ce565b600580547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00001661ffff83169081179091556040519081527fb181f70209132d8ea3e8d73753ea50373ecc5139cf202f35ae868d70a62168669060200160405180910390a150565b6040805160c0810182525f80825260208201819052918101829052606081018290526080810182905260a081019190915260085f6137c18585614163565b815260208082019290925260409081015f20815160c08101835281546001600160a01b03808216808452600160a01b830460ff1696840196909652750100000000000000000000000000000000000000000090910465ffffffffffff169382019390935260018201546060820152600282015490921660808301526003015460a08201529150610eb0576040517f82ca08840000000000000000000000000000000000000000000000000000000081526001600160a01b038416600482015261ffff831660248201526044016106ce565b61389b33614c34565b1580156138ae57506138ac33614496565b155b156138e5576040517f1648fd0100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6138f0600982614586565b613931576040517f6e8f26200000000000000000000000000000000000000000000000000000000081526001600160a01b03821660048201526024016106ce565b61393c600982614e6d565b506040516001600160a01b038216907f0f1b21b9bcb408ed1f2c561dd7d8a2d19ee85a67928144f6a5623e1e6b89195e905f90a250565b5f5f5f61398085856147b0565b9250925092509250925092565b5f5f848461399b82826140ec565b8460ff165f036139d7576040517f1b60ca6900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f6139e28888614163565b5f818152600860209081526040808320815160c08101835281546001600160a01b03808216835260ff600160a01b830481168488015265ffffffffffff750100000000000000000000000000000000000000000090930483168487015260018086015460608087019190915260028088015490941660808088019190915260039788015460a0808901919091528c8c52600b8b52898c208a519182018b5280548252938401549a81019a909a529382015497890197909752909401549182169486018590526601000000000000909104909216151591840191909152939450909103613b0f576040517f55e6177a0000000000000000000000000000000000000000000000000000000081526001600160a01b038b16600482015261ffff8a1660248201526044016106ce565b806060015165ffffffffffff1642101580613b2b575080608001515b80613b3857506020810151155b15613b84576040517f285f89e10000000000000000000000000000000000000000000000000000000081526001600160a01b038b16600482015261ffff8a1660248201526044016106ce565b5f5f613ba2845f01518560800151856020015186606001518e614db0565b909d909c509a5050505050505050505050565b5f600254600114613c22576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600a60248201527f5245454e5452414e43590000000000000000000000000000000000000000000060448201526064016106ce565b60028055613c2e6140a7565b81613c38816141a6565b835f03613c71576040517f1b60ca6900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b335f908152600760205260408120805461ffff1691613c8f83615856565b91906101000a81548161ffff021916908361ffff16021790555091506040518060c00160405280876001600160a01b031681526020018660ff16815260200162278d008760ff16613ce091906158b9565b613cea90426158da565b65ffffffffffff168152602001858152602001846001600160a01b031681526020015f1981525060085f613d1e3386614163565b815260208082019290925260409081015f208351815493850151858401516001600160a01b039283167fffffffffffffffffffffff00000000000000000000000000000000000000000090961695909517600160a01b60ff92831602177fffffffffff000000000000ffffffffffffffffffffffffffffffffffffffffff16750100000000000000000000000000000000000000000065ffffffffffff909616959095029490941782556060850151600183015560808501516002830180547fffffffffffffffffffffffff00000000000000000000000000000000000000001691831691909117905560a090940151600390910155517f2f34546b000000000000000000000000000000000000000000000000000000008152888316600482015290871660248201526044810186905290841690632f34546b906064015f604051808303815f87803b158015613e73575f5ffd5b505af1158015613e85573d5f5f3e3d5ffd5b50505050613e95868685876145a7565b6040805160ff87168152602081018690526001600160a01b038581168284015291519188169161ffff85169133917f73249ca0e3cb0c8adf0ce7e639a3f6b22bc27a30a943fd8133a1fe062265c3019181900360600190a4506001600255949350505050565b613f036140a7565b613f0c33614baf565b158015613f1f5750613f1d33614496565b155b15613f56576040517f1648fd0100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6001600160a01b038316613f96576040517f7dbe27de00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6001600160a01b038216613fd6576040517f7dbe27de00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61271061ffff8216111561401c576040517fa92683a800000000000000000000000000000000000000000000000000000000815261ffff821660048201526024016106ce565b8060045f61402a8686614789565b815260208082019290925260409081015f2080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00001661ffff9485161790555191831682526001600160a01b0384811692908616917f58b0d598b5df293251c50ecd1a55d2464abf92116113a4fe50c876d75f9f60b49101612c73565b600154600160a01b900460ff166140ea576040517f7ea8ac5600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b565b5f6008816140fa8585614163565b815260208101919091526040015f20546001600160a01b03160361415f576040517f82ca08840000000000000000000000000000000000000000000000000000000081526001600160a01b038316600482015261ffff821660248201526044016106ce565b5050565b604080516001600160a01b038416602082015261ffff8316918101919091525f906060015b60405160208183030381529060405280519060200120905092915050565b6141b1600982614586565b6141f2576040517f6e8f26200000000000000000000000000000000000000000000000000000000081526001600160a01b03821660048201526024016106ce565b50565b5f80805f19858709858702925082811083820303915050805f03614229575f841161421e575f5ffd5b50829004905061429a565b808411614234575f5ffd5b5f848688098519600190810187169687900496828603819004959092119093035f82900391909104909201919091029190911760038402600290811880860282030280860282030280860282030280860282030280860282030280860290910302029150505b9392505050565b604080516001600160a01b038481166024830152604480830185905283518084039091018152606490920183526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fa9059cbb0000000000000000000000000000000000000000000000000000000017905291515f9283929087169161432a9190615876565b5f604051808303815f865af19150503d805f8114614363576040519150601f19603f3d011682016040523d82523d5f602084013e614368565b606091505b50915091508180156143925750805115806143925750808060200190518101906143929190615629565b6143f8576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600f60248201527f5452414e534645525f4641494c4544000000000000000000000000000000000060448201526064016106ce565b5050505050565b5f7fffffffff0000000000000000000000000000000000000000000000000000000082167f01ffc9a7000000000000000000000000000000000000000000000000000000001480610eb057507fffffffff0000000000000000000000000000000000000000000000000000000082167fe9fa834a000000000000000000000000000000000000000000000000000000001492915050565b6001546040517fac4ab3fb0000000000000000000000000000000000000000000000000000000081526001600160a01b0383811660048301527f61646d696e00000000000000000000000000000000000000000000000000000060248301525f92169063ac4ab3fb906044015b602060405180830381865afa15801561451e573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610eb09190615629565b600154600160a01b900460ff16156140ea576040517f5b3d539f00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6001600160a01b0381165f908152600183016020526040812054151561429a565b5f7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663bc3dc04e6040518163ffffffff1660e01b8152600401602060405180830381865afa158015614604573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061462891906155ff565b6040517f11df9fa20000000000000000000000000000000000000000000000000000000081526001600160a01b03878116600483015260ff8716602483015285811660448301529192508183169163fe99049a91339130917f0000000000000000000000000000000000000000000000000000000000000000909116906311df9fa290606401602060405180830381865afa1580156146c9573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906146ed91906155d5565b6040517fffffffff0000000000000000000000000000000000000000000000000000000060e086901b1681526001600160a01b0393841660048201529290911660248301526044820152606481018590526084016020604051808303815f875af115801561475d573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906147819190615629565b505050505050565b604080516001600160a01b0380851660208301528316918101919091525f90606001614188565b5f5f5f5f6147be8686614163565b5f81815260086020908152604091829020825160c08101845281546001600160a01b038082168352600160a01b820460ff16948301949094527501000000000000000000000000000000000000000000900465ffffffffffff16938101939093526001810154606084015260028101549091166080830181905260039091015460a083015291925090614850906141a6565b5f614862825f01518360800151614789565b5f818152600360205260408120546060850151929350909161488a9161ffff166127106141f5565b5f8381526004602090815260408220549086015192935090916148b891849161ffff9091169060ff16614e81565b90505f62278d00856020015160ff166148d191906158b9565b6148db90426158da565b929b919a509198509650505050505050565b60605f61429a83614eac565b604080516001600160a01b0385811660248301528481166044830152606480830185905283518084039091018152608490920183526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167f23b872dd0000000000000000000000000000000000000000000000000000000017905291515f9283929088169161498a9190615876565b5f604051808303815f865af19150503d805f81146149c3576040519150601f19603f3d011682016040523d82523d5f602084013e6149c8565b606091505b50915091508180156149f25750805115806149f25750808060200190518101906149f29190615629565b614781576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601460248201527f5452414e534645525f46524f4d5f4641494c454400000000000000000000000060448201526064016106ce565b604080516001600160a01b038481166024830152604480830185905283518084039091018152606490920183526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167f095ea7b30000000000000000000000000000000000000000000000000000000017905291515f92839290871691614ae19190615876565b5f604051808303815f865af19150503d805f8114614b1a576040519150601f19603f3d011682016040523d82523d5f602084013e614b1f565b606091505b5091509150818015614b49575080511580614b49575080806020019051810190614b499190615629565b6143f8576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600e60248201527f415050524f56455f4641494c454400000000000000000000000000000000000060448201526064016106ce565b6001546040517fac4ab3fb0000000000000000000000000000000000000000000000000000000081526001600160a01b0383811660048301527f6d616e616765720000000000000000000000000000000000000000000000000060248301525f92169063ac4ab3fb90604401614503565b5f61429a836001600160a01b038416614f05565b6001546040517fac4ab3fb0000000000000000000000000000000000000000000000000000000081526001600160a01b0383811660048301527f656d657267656e6379000000000000000000000000000000000000000000000060248301525f92169063ac4ab3fb90604401614503565b5f80546040517fb4dc00b40000000000000000000000000000000000000000000000000000000081527fffffffffff0000000000000000000000000000000000000000000000000000008416600482015282916001600160a01b03169063b4dc00b490602401602060405180830381865afa158015614d26573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190614d4a91906155ff565b90506001600160a01b038116610eb0576040517f5c3fa9cd0000000000000000000000000000000000000000000000000000000081527fffffffffff000000000000000000000000000000000000000000000000000000841660048201526024016106ce565b5f5f614dbb866141a6565b5f60045f614dc98a8a614789565b815260208101919091526040015f9081205461ffff169150819003614e2d576040517f06082a180000000000000000000000000000000000000000000000000000000081526001600160a01b03808a166004830152881660248201526044016106ce565b5f614e40878361ffff168760ff16614e81565b90505f614e5362278d0060ff88166158b9565b614e5d90886158da565b9a91995090975050505050505050565b5f61429a836001600160a01b038416614f51565b5f614ea4614e8f83856158f8565b614e9c612710600c6158f8565b86919061503b565b949350505050565b6060815f01805480602002602001604051908101604052809291908181526020018280548015614ef957602002820191905f5260205f20905b815481526020019060010190808311614ee5575b50505050509050919050565b5f818152600183016020526040812054614f4a57508154600181810184555f848152602080822090930184905584548482528286019093526040902091909155610eb0565b505f610eb0565b5f818152600183016020526040812054801561502b575f614f736001836155c2565b85549091505f90614f86906001906155c2565b9050808214614fe5575f865f018281548110614fa457614fa461588c565b905f5260205f200154905080875f018481548110614fc457614fc461588c565b5f918252602080832090910192909255918252600188019052604090208390555b8554869080614ff657614ff661590f565b600190038181905f5260205f20015f90559055856001015f8681526020019081526020015f205f905560019350505050610eb0565b5f915050610eb0565b5092915050565b5f6150478484846141f5565b90505f82806150585761505861593c565b848609111561429a575f19811061506d575f5ffd5b6001019392505050565b6001600160a01b03811681146141f2575f5ffd5b803561ffff8116811461509c575f5ffd5b919050565b5f5f604083850312156150b2575f5ffd5b82356150bd81615077565b91506150cb6020840161508b565b90509250929050565b5f602082840312156150e4575f5ffd5b81357fffffffff000000000000000000000000000000000000000000000000000000008116811461429a575f5ffd5b5f60208284031215615123575f5ffd5b61429a8261508b565b5f5f6040838503121561513d575f5ffd5b6151468361508b565b946020939093013593505050565b5f5f60208385031215615165575f5ffd5b823567ffffffffffffffff81111561517b575f5ffd5b8301601f8101851361518b575f5ffd5b803567ffffffffffffffff8111156151a1575f5ffd5b8560208284010111156151b2575f5ffd5b6020919091019590945092505050565b5f602082840312156151d2575f5ffd5b813561429a81615077565b5f5f604083850312156151ee575f5ffd5b50508035926020909101359150565b5f5f6040838503121561520e575f5ffd5b823561521981615077565b9150602083013561522981615077565b809150509250929050565b602080825282518282018190525f918401906040840190835b818110156152745783516001600160a01b031683526020938401939092019160010161524d565b509095945050505050565b602080825282518282018190525f918401906040840190835b8181101561527457835180517fffffffffff0000000000000000000000000000000000000000000000000000001684526020908101517fffffffff00000000000000000000000000000000000000000000000000000000168185015290930192604090920191600101615298565b5f5f5f60608486031215615318575f5ffd5b6153218461508b565b95602085013595506040909401359392505050565b5f5f5f60608486031215615348575f5ffd5b833561535381615077565b9250602084013561536381615077565b91506153716040850161508b565b90509250925092565b602080825282518282018190525f918401906040840190835b818110156152745783517fffffffffff00000000000000000000000000000000000000000000000000000016835260209384019390920191600101615393565b60ff811681146141f2575f5ffd5b5f5f604083850312156153f2575f5ffd5b6153fb8361508b565b91506020830135615229816153d3565b602080825282518282018190525f918401906040840190835b81811015615274576154898385516001600160a01b03815116825260ff602082015116602083015265ffffffffffff6040820151166040830152606081015160608301526001600160a01b03608082015116608083015260a081015160a08301525050565b6020939093019260c09290920191600101615424565b60c08101610eb082846001600160a01b03815116825260ff602082015116602083015265ffffffffffff6040820151166040830152606081015160608301526001600160a01b03608082015116608083015260a081015160a08301525050565b5f5f5f60608486031215615511575f5ffd5b833561551c81615077565b925061552a6020850161508b565b9150604084013561553a816153d3565b809150509250925092565b5f5f5f5f60808587031215615558575f5ffd5b843561556381615077565b93506020850135615573816153d3565b925060408501359150606085013561558a81615077565b939692955090935050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b81810381811115610eb057610eb0615595565b5f602082840312156155e5575f5ffd5b5051919050565b80820180821115610eb057610eb0615595565b5f6020828403121561560f575f5ffd5b815161429a81615077565b8051801515811461509c575f5ffd5b5f60208284031215615639575f5ffd5b61429a8261561a565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b604051610120810167ffffffffffffffff8111828210171561569357615693615642565b60405290565b805161509c81615077565b805161509c816153d3565b805165ffffffffffff8116811461509c575f5ffd5b5f82601f8301126156d3575f5ffd5b815167ffffffffffffffff8111156156ed576156ed615642565b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0908116603f0116810167ffffffffffffffff8111828210171561573a5761573a615642565b604052818152838201602001851015615751575f5ffd5b8160208501602083015e5f918101602001919091529392505050565b5f6020828403121561577d575f5ffd5b815167ffffffffffffffff811115615793575f5ffd5b820161012081850312156157a5575f5ffd5b6157ad61566f565b6157b682615699565b81526157c460208301615699565b60208201526157d560408301615699565b60408201526157e6606083016156a4565b60608201526080828101519082015260a0808301519082015261580b60c083016156af565b60c082015261581c60e0830161561a565b60e082015261010082015167ffffffffffffffff81111561583b575f5ffd5b615847868285016156c4565b61010083015250949350505050565b5f61ffff821661ffff810361586d5761586d615595565b60010192915050565b5f82518060208501845e5f920191825250919050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52603260045260245ffd5b65ffffffffffff818116838216029081169081811461503457615034615595565b65ffffffffffff8181168382160190811115610eb057610eb0615595565b8082028115828204841417610eb057610eb0615595565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52603160045260245ffd5b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601260045260245ffdfea2646970667358221220516fbfb21c97e7b455c0263349a24b863a16ffc5b8a7eeb81128f94690c4c9fd64736f6c634300081e00330000000000000000000000002286d7f9639e8158fad1169e76d1fbc38247f54b000000000000000000000000cb4e21eb404d80f3e1db781aad9ad6a1217fbbf2

Deployed Bytecode

0x608060405234801561000f575f5ffd5b5060043610610283575f3560e01c80636aa633b611610157578063cb865d8f116100d2578063f2463ee811610088578063f911b6bc1161006e578063f911b6bc1461062d578063f91fde3614610640578063ff6e4e2714610653575f5ffd5b8063f2463ee8146105c6578063f337cbc1146105fc575f5ffd5b8063d4aae0c4116100b8578063d4aae0c414610598578063dd0081c7146105aa578063e13fe767146105b3575f5ffd5b8063cb865d8f1461054c578063ce9ddccc1461056c575f5ffd5b8063923cb95211610127578063bd96a8791161010d578063bd96a87914610506578063c4e71eaf14610519578063c938156614610539575f5ffd5b8063923cb952146104de5780639459b875146104f1575f5ffd5b80636aa633b61461048b5780637977ca901461049f57806380cee4ab146104b25780638fc925aa146104cb575f5ffd5b806337a9c983116102015780635414396b116101b757806359f4c4261161019d57806359f4c426146104075780635dc54e23146104655780635eaf5d6714610478575f5ffd5b80635414396b146103ec5780635924be70146103f8575f5ffd5b806343fd097e116101e757806343fd097e146103b15780634657b36c146103c457806348595010146103d7575f5ffd5b806337a9c9831461035f5780634330a75c1461039e575f5ffd5b80630c959556116102565780631ef306e31161023c5780631ef306e31461031e57806322f3e2d41461034457806331dd41e31461034c575f5ffd5b80630c959556146102f857806314b0a3391461030b575f5ffd5b80630105b65b1461028757806301ffc9a71461029c57806308b9b2a9146102c45780630b3a580c146102e5575b5f5ffd5b61029a6102953660046150a1565b610666565b005b6102af6102aa3660046150d4565b610e0f565b60405190151581526020015b60405180910390f35b6102d76102d2366004615113565b610eb6565b6040519081526020016102bb565b61029a6102f336600461512c565b611467565b61029a610306366004615154565b611a40565b6102af6103193660046151c2565b611b03565b61033161032c3660046151dd565b611b0f565b60405161ffff90911681526020016102bb565b6102af61201c565b6102d761035a366004615113565b6120a4565b6103867f000000000000000000000000cb4e21eb404d80f3e1db781aad9ad6a1217fbbf281565b6040516001600160a01b0390911681526020016102bb565b6103316103ac3660046151fd565b612457565b6103316103bf3660046151fd565b612480565b61029a6103d23660046151c2565b61248e565b6103df61250c565b6040516102bb9190615234565b60055461ffff16610331565b60606040516102bb919061527f565b61041a6104153660046150a1565b612518565b6040516102bb91905f60a08201905082518252602083015160208301526040830151604083015265ffffffffffff606084015116606083015260808301511515608083015292915050565b61029a610473366004615306565b6125bf565b61029a610486366004615336565b612acb565b6001546102af90600160a01b900460ff1681565b61029a6104ad3660046151c2565b612c80565b600554610386906201000090046001600160a01b031681565b61029a6104d9366004615154565b612f00565b600154610386906001600160a01b031681565b6104f9612faf565b6040516102bb919061537a565b61029a6105143660046153e1565b6131f8565b61052c6105273660046151c2565b613509565b6040516102bb919061540b565b61029a610547366004615113565b61367b565b61055f61055a3660046150a1565b613783565b6040516102bb919061549f565b61033161057a3660046151c2565b6001600160a01b03165f9081526007602052604090205461ffff1690565b5f54610386906001600160a01b031681565b61033161271081565b61029a6105c13660046151c2565b613892565b6105d96105d43660046150a1565b613973565b60408051938452602084019290925265ffffffffffff16908201526060016102bb565b61060f61060a3660046154ff565b61398d565b6040805165ffffffffffff90931683526020830191909152016102bb565b61033161063b366004615545565b613bb5565b600654610386906001600160a01b031681565b61029a610661366004615336565b613efb565b6002546001146106d7576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600a60248201527f5245454e5452414e43590000000000000000000000000000000000000000000060448201526064015b60405180910390fd5b600280556106e36140a7565b81816106ef82826140ec565b5f5f5f6106fc8787614163565b5f908152600860209081526040808320600b9092529091206002820154919450925061073191506001600160a01b03166141a6565b600381015465ffffffffffff165f0361078b576040517f55e6177a0000000000000000000000000000000000000000000000000000000081526001600160a01b038716600482015261ffff861660248201526044016106ce565b600381015465ffffffffffff164210806107b3575060038101546601000000000000900460ff165b806107c057506001810154155b1561080c576040517f285f89e10000000000000000000000000000000000000000000000000000000081526001600160a01b038716600482015261ffff861660248201526044016106ce565b6001808201546002830154835492850154919290915f9161082c916155c2565b6003850180547fffffffffffffffffffffffffffffffffffffffffffffffffff00ffffffffffff1666010000000000001790555f6001860181905560028087018290558754908801546040517f11df9fa20000000000000000000000000000000000000000000000000000000081526001600160a01b038084166004830152600160a01b90930460ff166024820152908216604482015292935090917f000000000000000000000000cb4e21eb404d80f3e1db781aad9ad6a1217fbbf2909116906311df9fa290606401602060405180830381865afa158015610911573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061093591906155d5565b90505f61094285846155ec565b90505f5f7f000000000000000000000000cb4e21eb404d80f3e1db781aad9ad6a1217fbbf26001600160a01b031663bc3dc04e6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156109a2573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906109c691906155ff565b6040517f426a84930000000000000000000000000000000000000000000000000000000081526001600160a01b037f000000000000000000000000cb4e21eb404d80f3e1db781aad9ad6a1217fbbf28116600483015260248201879052604482018690529192509082169063426a8493906064016020604051808303815f875af1158015610a56573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610a7a9190615629565b508615610b1557600289015489546040517f21e079a60000000000000000000000000000000000000000000000000000000081526001600160a01b038083166004830152600160a01b90920460ff166024820152604481018a90523060648201529116906321e079a6906084015f604051808303815f87803b158015610afe575f5ffd5b505af1158015610b10573d5f5f3e3d5ffd5b505050505b8415610bc457600289015489546040517f757e9ae70000000000000000000000000000000000000000000000000000000081526001600160a01b038083166004830152600160a01b90920460ff1660248201526044810188905230606482015291169063757e9ae7906084016020604051808303815f875af1158015610b9d573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610bc191906155d5565b91505b6040517f426a84930000000000000000000000000000000000000000000000000000000081526001600160a01b037f000000000000000000000000cb4e21eb404d80f3e1db781aad9ad6a1217fbbf281166004830152602482018690525f604483015282169063426a8493906064016020604051808303815f875af1158015610c4f573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610c739190615629565b50610c80905086856155ec565b886001015f828254610c9291906155c2565b90915550506005545f90610cae90839061ffff166127106141f5565b90505f610cbb82846155c2565b90508115610cd9578954610cd9906001600160a01b031633846142a1565b8015610d02576005548a54610d02916001600160a01b03918216916201000090910416836142a1565b61ffff8d166001600160a01b038f167fdd9b9a8dbb6213e735782fa30d86ba4895b54644dda4d03ce809954c0a04d8678a8a610d3e828c6155ec565b6040805193845260208401929092529082015260600160405180910390a3895f015f9054906101000a90046001600160a01b03166001600160a01b03168d61ffff168f6001600160a01b03167f651175795d5e55543ac975d01bf28fd07964198e4ecffb623256b03e9f7a87568d5f0160149054906101000a900460ff168c8b610dc891906155ec565b8f60010154604051610df29392919060ff9390931683526020830191909152604082015260600190565b60405180910390a450506001600255505050505050505050505050565b5f7fffffffff0000000000000000000000000000000000000000000000000000000082167f01ffc9a7000000000000000000000000000000000000000000000000000000001480610ea157507fffffffff0000000000000000000000000000000000000000000000000000000082167f5512aa4a00000000000000000000000000000000000000000000000000000000145b80610eb05750610eb0826143ff565b92915050565b5f600254600114610f23576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600a60248201527f5245454e5452414e43590000000000000000000000000000000000000000000060448201526064016106ce565b60028055610f2f6140a7565b3382610f3b82826140ec565b5f610f463386614163565b5f818152600860205260409020600281015491925090610f6e906001600160a01b03166141a6565b80600101545f03610fb7576040517f11c258d100000000000000000000000000000000000000000000000000000000815233600482015261ffff871660248201526044016106ce565b80547501000000000000000000000000000000000000000000900465ffffffffffff164210156110485780546040517fb1e4b4aa00000000000000000000000000000000000000000000000000000000815233600482015261ffff88166024820152750100000000000000000000000000000000000000000090910465ffffffffffff1660448201526064016106ce565b5f828152600b60205260409020600101541561109c576040517fb0f135b000000000000000000000000000000000000000000000000000000000815233600482015261ffff871660248201526044016106ce565b6001810180545f91829055825460028401546040517f11df9fa20000000000000000000000000000000000000000000000000000000081526001600160a01b038084166004830152600160a01b90930460ff16602482015290821660448201529192917f000000000000000000000000cb4e21eb404d80f3e1db781aad9ad6a1217fbbf2909116906311df9fa290606401602060405180830381865afa158015611148573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061116c91906155d5565b90505f7f000000000000000000000000cb4e21eb404d80f3e1db781aad9ad6a1217fbbf26001600160a01b031663bc3dc04e6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156111cb573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906111ef91906155ff565b6040517f426a84930000000000000000000000000000000000000000000000000000000081526001600160a01b037f000000000000000000000000cb4e21eb404d80f3e1db781aad9ad6a1217fbbf28116600483015260248201859052604482018690529192509082169063426a8493906064016020604051808303815f875af115801561127f573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906112a39190615629565b50600284015484546040517f757e9ae70000000000000000000000000000000000000000000000000000000081526001600160a01b038083166004830152600160a01b90920460ff1660248201526044810186905233606482015291169063757e9ae7906084016020604051808303815f875af1158015611326573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061134a91906155d5565b6040517f426a84930000000000000000000000000000000000000000000000000000000081526001600160a01b037f000000000000000000000000cb4e21eb404d80f3e1db781aad9ad6a1217fbbf281166004830152602482018590525f60448301529199509082169063426a8493906064016020604051808303815f875af11580156113d9573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906113fd9190615629565b50835460408051600160a01b830460ff168152602081018690526001600160a01b039092169161ffff8c169133917fda66127b6b8e933c8290917b0744c4fc122e4ba27df3f33b2f8f5dcb4ac71f06910160405180910390a4505050505050506001600255919050565b6002546001146114d3576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600a60248201527f5245454e5452414e43590000000000000000000000000000000000000000000060448201526064016106ce565b600280556114df6140a7565b33826114eb82826140ec565b5f6114f63386614163565b5f81815260086020526040902060028101549192509061151e906001600160a01b03166141a6565b845f03611557576040517f1b60ca6900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b80600101548511156115a8576040517f3ab03ca900000000000000000000000000000000000000000000000000000000815233600482015261ffff87166024820152604481018690526064016106ce565b5f828152600b6020526040902060010154156115fc576040517fb0f135b000000000000000000000000000000000000000000000000000000000815233600482015261ffff871660248201526044016106ce565b84816001015f82825461160f91906155c2565b9091555050600281015481546040517faad721050000000000000000000000000000000000000000000000000000000081526001600160a01b038083166004830152600160a01b90920460ff1660248201526044810188905291169063aad72105906064015f604051808303815f87803b15801561168b575f5ffd5b505af115801561169d573d5f5f3e3d5ffd5b505050505f198160030154146117f05760065460038201546040517feb02c301000000000000000000000000000000000000000000000000000000008152600481019190915233916001600160a01b03169063eb02c301906024015f60405180830381865afa158015611712573d5f5f3e3d5ffd5b505050506040513d5f823e601f3d9081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0168201604052611757919081019061576d565b602001516001600160a01b0316036117f057600281015460038201546040517fe97ee9550000000000000000000000000000000000000000000000000000000081526001600160a01b039092169163e97ee955916117c2918990600401918252602082015260400190565b5f604051808303815f87803b1580156117d9575f5ffd5b505af11580156117eb573d5f5f3e3d5ffd5b505050505b5f7f000000000000000000000000cb4e21eb404d80f3e1db781aad9ad6a1217fbbf26001600160a01b031663bc3dc04e6040518163ffffffff1660e01b8152600401602060405180830381865afa15801561184d573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061187191906155ff565b825460028401546040517f11df9fa20000000000000000000000000000000000000000000000000000000081526001600160a01b038084166004830152600160a01b90930460ff16602482015290821660448201529192508083169163095bcdb69133917f000000000000000000000000cb4e21eb404d80f3e1db781aad9ad6a1217fbbf2909116906311df9fa290606401602060405180830381865afa15801561191e573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061194291906155d5565b6040517fffffffff0000000000000000000000000000000000000000000000000000000060e085901b1681526001600160a01b0390921660048301526024820152604481018990526064016020604051808303815f875af11580156119a9573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906119cd9190615629565b50815460018301546040805160ff600160a01b8504168152602081018a905280820192909252516001600160a01b039092169161ffff8a169133917f651175795d5e55543ac975d01bf28fd07964198e4ecffb623256b03e9f7a87569181900360600190a4505060016002555050505050565b611a4933614496565b611aa1576040517f7de30f410000000000000000000000000000000000000000000000000000000081527f61646d696e00000000000000000000000000000000000000000000000000000060048201526024016106ce565b611aa9614542565b600180547fffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffff16600160a01b1790556040517fc0f961051f97b04c496472d11cb6170d844e4b2c9dfd3b602a4fa0139712d484905f90a15050565b5f610eb0600983614586565b5f600254600114611b7c576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600a60248201527f5245454e5452414e43590000000000000000000000000000000000000000000060448201526064016106ce565b60028055611b886140a7565b815f03611bc1576040517f1b60ca6900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6006546040517feb02c301000000000000000000000000000000000000000000000000000000008152600481018590525f916001600160a01b03169063eb02c301906024015f60405180830381865afa158015611c20573d5f5f3e3d5ffd5b505050506040513d5f823e601f3d9081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0168201604052611c65919081019061576d565b60208101519091506001600160a01b03163314611cb1576040517f45734ef7000000000000000000000000000000000000000000000000000000008152600481018590526024016106ce565b8060800151831115611d1f576040517f929ad8ed00000000000000000000000000000000000000000000000000000000815260206004820152600660248201527f616d6f756e74000000000000000000000000000000000000000000000000000060448201526064016106ce565b604081015160608201518251611d34816141a6565b335f908152600760205260408120805461ffff1691611d5283615856565b91906101000a81548161ffff021916908361ffff16021790555094506040518060c00160405280846001600160a01b031681526020018360ff1681526020018560c0015165ffffffffffff168152602001878152602001826001600160a01b031681526020018881525060085f611dc93389614163565b815260208082019290925260409081015f208351815493850151858401516001600160a01b039283167fffffffffffffffffffffff00000000000000000000000000000000000000000090961695909517600160a01b60ff92831602177fffffffffff000000000000ffffffffffffffffffffffffffffffffffffffffff16750100000000000000000000000000000000000000000065ffffffffffff909616959095029490941782556060850151600183015560808501516002830180547fffffffffffffffffffffffff00000000000000000000000000000000000000001691831691909117905560a090940151600390910155517f2f34546b000000000000000000000000000000000000000000000000000000008152858316600482015290841660248201526044810188905290821690632f34546b906064015f604051808303815f87803b158015611f1e575f5ffd5b505af1158015611f30573d5f5f3e3d5ffd5b50506040517fa9cf27de000000000000000000000000000000000000000000000000000000008152600481018a9052602481018990526001600160a01b038416925063a9cf27de91506044015f604051808303815f87803b158015611f93575f5ffd5b505af1158015611fa5573d5f5f3e3d5ffd5b50505050611fb5838383896145a7565b6040805160ff84168152602081018890526001600160a01b038381168284015291519185169161ffff88169133917f73249ca0e3cb0c8adf0ce7e639a3f6b22bc27a30a943fd8133a1fe062265c3019181900360600190a450505050600160025592915050565b5f80546040517fe52223bb0000000000000000000000000000000000000000000000000000000081523060048201526001600160a01b039091169063e52223bb90602401602060405180830381865afa15801561207b573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061209f9190615629565b905090565b5f600254600114612111576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600a60248201527f5245454e5452414e43590000000000000000000000000000000000000000000060448201526064016106ce565b6002805561211d6140a7565b338261212982826140ec565b5f6121343386614163565b5f818152600860209081526040808320600b909252909120600301549192509065ffffffffffff161561219f576040517f285f89e100000000000000000000000000000000000000000000000000000000815233600482015261ffff871660248201526044016106ce565b805460028201546004915f916121c1916001600160a01b039081169116614789565b815260208101919091526040015f9081205461ffff16900361222957805460028201546040517f06082a180000000000000000000000000000000000000000000000000000000081526001600160a01b039283166004820152911660248201526044016106ce565b5f5f5f612236338a6147b0565b925092509250825f03612275576040517f1b60ca6900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6040805160a081018252848152602080820186815282840186815265ffffffffffff868116606086019081525f608087018181528d8252600b90965287812087518155945160018601559251600280860191909155905160039094018054955115156601000000000000027fffffffffffffffffffffffffffffffffffffffffffffffffff00000000000000909616949092169390931793909317909255870154875493517fae04c1070000000000000000000000000000000000000000000000000000000081526001600160a01b038086166004830152600160a01b90950460ff166024820152604481018890523360648201529293919291169063ae04c107906084016020604051808303815f875af1158015612396573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906123ba91906155d5565b9050805f036123f5576040517f1b60ca6900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6002860154604080518381526001600160a01b03909216602083015261ffff8d169133917ff5cbb4792d2c67cbe6360dcea810195870fbc966ffaf062a7e7713127050c1e7910160405180910390a360016002559a9950505050505050505050565b5f60035f6124658585614789565b815260208101919091526040015f205461ffff169392505050565b5f60045f6124658585614789565b5f546001600160a01b031633146124d3576040517f14fa403c0000000000000000000000000000000000000000000000000000000081523360048201526024016106ce565b5f80547fffffffffffffffffffffffff0000000000000000000000000000000000000000166001600160a01b0392909216919091179055565b606061209f60096148ed565b61254f6040518060a001604052805f81526020015f81526020015f81526020015f65ffffffffffff1681526020015f151581525090565b600b5f61255c8585614163565b815260208082019290925260409081015f20815160a081018352815481526001820154938101939093526002810154918301919091526003015465ffffffffffff811660608301526601000000000000900460ff16151560808201529392505050565b60025460011461262b576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600a60248201527f5245454e5452414e43590000000000000000000000000000000000000000000060448201526064016106ce565b600280556126376140a7565b338361264382826140ec565b835f0361267c576040517f1b60ca6900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f6126873387614163565b5f81815260086020908152604091829020825160c08101845281546001600160a01b038082168352600160a01b820460ff16948301949094527501000000000000000000000000000000000000000000900465ffffffffffff16938101939093526001810154606084015260028101549091166080830181905260039091015460a083015291925090612719906141a6565b5f828152600b602052604081206003810154909165ffffffffffff909116900361277b576040517f55e6177a00000000000000000000000000000000000000000000000000000000815233600482015261ffff891660248201526044016106ce565b600381015465ffffffffffff16421015806127a4575060038101546601000000000000900460ff165b806127b157506001810154155b156127f4576040517f285f89e100000000000000000000000000000000000000000000000000000000815233600482015261ffff891660248201526044016106ce565b815161280b906001600160a01b031633308a6148f9565b5f5f8260020154891161282057889150612834565b60028301549150612831828a6155c2565b90505b811561285d57600554845161285d916001600160a01b03918216916201000090910416846142a1565b5f8115612a0e57845161289a906001600160a01b03167f000000000000000000000000cb4e21eb404d80f3e1db781aad9ad6a1217fbbf284614a58565b60808501518551602087015160018701546040517f1500b2d10000000000000000000000000000000000000000000000000000000081526001600160a01b03938416600482015260ff9092166024830152604482018690526064820152306084820152911690631500b2d19060a4016020604051808303815f875af1158015612925573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061294991906155d5565b90508360010154821015801561296d575088846001015461296a91906155ec565b81115b156129d857338b828b876001015461298591906155ec565b6040517fbf4661960000000000000000000000000000000000000000000000000000000081526001600160a01b03909416600485015261ffff9092166024840152604483015260648201526084016106ce565b8451612a0e906001600160a01b03167f000000000000000000000000cb4e21eb404d80f3e1db781aad9ad6a1217fbbf25f614a58565b8215612a435783600201548311612a255782612a2b565b83600201545b846002015f828254612a3d91906155c2565b90915550505b8015612a785783600101548111612a5a5780612a60565b83600101545b846001015f828254612a7291906155c2565b90915550505b604080518281526020810185905261ffff8d169133917f4ba59a99c14dcd6586c2ac1537ac071480a5e5ee5441e8fc83818fccc642c6ef910160405180910390a350506001600255505050505050505050565b612ad36140a7565b612adc33614baf565b158015612aef5750612aed33614496565b155b15612b26576040517f1648fd0100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6001600160a01b038316612b66576040517f7dbe27de00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6001600160a01b038216612ba6576040517f7dbe27de00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61271061ffff82161115612bec576040517fa92683a800000000000000000000000000000000000000000000000000000000815261ffff821660048201526024016106ce565b8060035f612bfa8686614789565b815260208082019290925260409081015f2080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00001661ffff9485161790555191831682526001600160a01b0384811692908616917f7e3ffd1e23bc4b3ff0b2ffee6b46237d60fa2fd705005fd9d7431eff52508ff991015b60405180910390a3505050565b612c8933614496565b612ce1576040517f7de30f410000000000000000000000000000000000000000000000000000000081527f61646d696e00000000000000000000000000000000000000000000000000000060048201526024016106ce565b6001600160a01b038116612d2c576040517f49a2f9690000000000000000000000000000000000000000000000000000000081526001600160a01b03821660048201526024016106ce565b612d37600982614586565b15612d79576040517f5ea5e3860000000000000000000000000000000000000000000000000000000081526001600160a01b03821660048201526024016106ce565b604080517e52f9aa0000000000000000000000000000000000000000000000000000000060248083019190915282518083039091018152604490910182526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167f01ffc9a70000000000000000000000000000000000000000000000000000000017905290515f9182916001600160a01b03851691612e1991615876565b5f60405180830381855afa9150503d805f8114612e51576040519150601f19603f3d011682016040523d82523d5f602084013e612e56565b606091505b5091509150811580612e79575080806020019051810190612e779190615629565b155b15612ebb576040517f49a2f9690000000000000000000000000000000000000000000000000000000081526001600160a01b03841660048201526024016106ce565b50612ec99050600982614c20565b506040516001600160a01b038216907f5574568d3b2c858d01dd672f106c56b1a2b8708170e09b6551500138ebf2adad905f90a250565b612f0933614c34565b158015612f1c5750612f1a33614496565b155b15612f53576040517f1648fd0100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b612f5b6140a7565b600180547fffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffff1690556040517f75884cdadc4a89e8b545db800057f06ec7f5338a08183c7ba515f2bfdd9fe1e1905f90a15050565b604080516003808252608082019092526060916020820183803683370190505090507f5452535259000000000000000000000000000000000000000000000000000000815f815181106130045761300461588c565b7fffffffffff0000000000000000000000000000000000000000000000000000009092166020928302919091019091015261305c7f524f4c455300000000000000000000000000000000000000000000000000000090565b8160018151811061306f5761306f61588c565b7fffffffffff000000000000000000000000000000000000000000000000000000909216602092830291909101909101526130c77f4445504f5300000000000000000000000000000000000000000000000000000090565b816002815181106130da576130da61588c565b60200260200101907affffffffffffffffffffffffffffffffffffffffffffffffffffff191690817affffffffffffffffffffffffffffffffffffffffffffffffffffff191681525050613146815f815181106131395761313961588c565b6020026020010151614ca5565b600560026101000a8154816001600160a01b0302191690836001600160a01b03160217905550613182816001815181106131395761313961588c565b60015f6101000a8154816001600160a01b0302191690836001600160a01b031602179055506131bd816002815181106131395761313961588c565b600680547fffffffffffffffffffffffff0000000000000000000000000000000000000000166001600160a01b039290921691909117905590565b600254600114613264576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600a60248201527f5245454e5452414e43590000000000000000000000000000000000000000000060448201526064016106ce565b600280556132706140a7565b338261327c82826140ec565b8260ff165f036132b8576040517f1b60ca6900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f6132c33386614163565b5f81815260086020908152604091829020825160c08101845281546001600160a01b038082168352600160a01b820460ff16948301949094527501000000000000000000000000000000000000000000900465ffffffffffff16938101939093526001810154606084015260028101549091166080830181905260039091015460a083015291925090613355906141a6565b5f828152600b602052604081206003810154909165ffffffffffff90911690036133b7576040517f55e6177a00000000000000000000000000000000000000000000000000000000815233600482015261ffff881660248201526044016106ce565b600381015465ffffffffffff16421015806133e0575060038101546601000000000000900460ff165b806133ed57506001810154155b15613430576040517f285f89e100000000000000000000000000000000000000000000000000000000815233600482015261ffff881660248201526044016106ce565b81516080830151600183015460038401545f93849361345c93919290919065ffffffffffff168c614db0565b6005548651929450909250613487916001600160a01b039081169133916201000090910416846148f9565b6003830180547fffffffffffffffffffffffffffffffffffffffffffffffffffff0000000000001665ffffffffffff841690811790915560405190815261ffff8a169033907fb7ba6522ad736820c140f6d62d345ba66f38167ead41236d73d550fe38b686a49060200160405180910390a35050600160025550505050505050565b6001600160a01b0381165f9081526007602052604081205460609161ffff909116908167ffffffffffffffff81111561354457613544615642565b6040519080825280602002602001820160405280156135a257816020015b6040805160c0810182525f8082526020808301829052928201819052606082018190526080820181905260a082015282525f199092019101816135625790505b5090505f5b8261ffff168161ffff1610156136735760085f6135c48784614163565b815260208082019290925260409081015f20815160c08101835281546001600160a01b038082168352600160a01b820460ff16958301959095527501000000000000000000000000000000000000000000900465ffffffffffff1692810192909252600181015460608301526002810154909216608082015260039091015460a08201528251839061ffff84169081106136605761366061588c565b60209081029190910101526001016135a7565b509392505050565b6136836140a7565b61368c33614baf565b15801561369f575061369d33614496565b155b156136d6576040517f1648fd0100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61271061ffff8216111561371c576040517fa92683a800000000000000000000000000000000000000000000000000000000815261ffff821660048201526024016106ce565b600580547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00001661ffff83169081179091556040519081527fb181f70209132d8ea3e8d73753ea50373ecc5139cf202f35ae868d70a62168669060200160405180910390a150565b6040805160c0810182525f80825260208201819052918101829052606081018290526080810182905260a081019190915260085f6137c18585614163565b815260208082019290925260409081015f20815160c08101835281546001600160a01b03808216808452600160a01b830460ff1696840196909652750100000000000000000000000000000000000000000090910465ffffffffffff169382019390935260018201546060820152600282015490921660808301526003015460a08201529150610eb0576040517f82ca08840000000000000000000000000000000000000000000000000000000081526001600160a01b038416600482015261ffff831660248201526044016106ce565b61389b33614c34565b1580156138ae57506138ac33614496565b155b156138e5576040517f1648fd0100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6138f0600982614586565b613931576040517f6e8f26200000000000000000000000000000000000000000000000000000000081526001600160a01b03821660048201526024016106ce565b61393c600982614e6d565b506040516001600160a01b038216907f0f1b21b9bcb408ed1f2c561dd7d8a2d19ee85a67928144f6a5623e1e6b89195e905f90a250565b5f5f5f61398085856147b0565b9250925092509250925092565b5f5f848461399b82826140ec565b8460ff165f036139d7576040517f1b60ca6900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f6139e28888614163565b5f818152600860209081526040808320815160c08101835281546001600160a01b03808216835260ff600160a01b830481168488015265ffffffffffff750100000000000000000000000000000000000000000090930483168487015260018086015460608087019190915260028088015490941660808088019190915260039788015460a0808901919091528c8c52600b8b52898c208a519182018b5280548252938401549a81019a909a529382015497890197909752909401549182169486018590526601000000000000909104909216151591840191909152939450909103613b0f576040517f55e6177a0000000000000000000000000000000000000000000000000000000081526001600160a01b038b16600482015261ffff8a1660248201526044016106ce565b806060015165ffffffffffff1642101580613b2b575080608001515b80613b3857506020810151155b15613b84576040517f285f89e10000000000000000000000000000000000000000000000000000000081526001600160a01b038b16600482015261ffff8a1660248201526044016106ce565b5f5f613ba2845f01518560800151856020015186606001518e614db0565b909d909c509a5050505050505050505050565b5f600254600114613c22576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600a60248201527f5245454e5452414e43590000000000000000000000000000000000000000000060448201526064016106ce565b60028055613c2e6140a7565b81613c38816141a6565b835f03613c71576040517f1b60ca6900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b335f908152600760205260408120805461ffff1691613c8f83615856565b91906101000a81548161ffff021916908361ffff16021790555091506040518060c00160405280876001600160a01b031681526020018660ff16815260200162278d008760ff16613ce091906158b9565b613cea90426158da565b65ffffffffffff168152602001858152602001846001600160a01b031681526020015f1981525060085f613d1e3386614163565b815260208082019290925260409081015f208351815493850151858401516001600160a01b039283167fffffffffffffffffffffff00000000000000000000000000000000000000000090961695909517600160a01b60ff92831602177fffffffffff000000000000ffffffffffffffffffffffffffffffffffffffffff16750100000000000000000000000000000000000000000065ffffffffffff909616959095029490941782556060850151600183015560808501516002830180547fffffffffffffffffffffffff00000000000000000000000000000000000000001691831691909117905560a090940151600390910155517f2f34546b000000000000000000000000000000000000000000000000000000008152888316600482015290871660248201526044810186905290841690632f34546b906064015f604051808303815f87803b158015613e73575f5ffd5b505af1158015613e85573d5f5f3e3d5ffd5b50505050613e95868685876145a7565b6040805160ff87168152602081018690526001600160a01b038581168284015291519188169161ffff85169133917f73249ca0e3cb0c8adf0ce7e639a3f6b22bc27a30a943fd8133a1fe062265c3019181900360600190a4506001600255949350505050565b613f036140a7565b613f0c33614baf565b158015613f1f5750613f1d33614496565b155b15613f56576040517f1648fd0100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6001600160a01b038316613f96576040517f7dbe27de00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6001600160a01b038216613fd6576040517f7dbe27de00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61271061ffff8216111561401c576040517fa92683a800000000000000000000000000000000000000000000000000000000815261ffff821660048201526024016106ce565b8060045f61402a8686614789565b815260208082019290925260409081015f2080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00001661ffff9485161790555191831682526001600160a01b0384811692908616917f58b0d598b5df293251c50ecd1a55d2464abf92116113a4fe50c876d75f9f60b49101612c73565b600154600160a01b900460ff166140ea576040517f7ea8ac5600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b565b5f6008816140fa8585614163565b815260208101919091526040015f20546001600160a01b03160361415f576040517f82ca08840000000000000000000000000000000000000000000000000000000081526001600160a01b038316600482015261ffff821660248201526044016106ce565b5050565b604080516001600160a01b038416602082015261ffff8316918101919091525f906060015b60405160208183030381529060405280519060200120905092915050565b6141b1600982614586565b6141f2576040517f6e8f26200000000000000000000000000000000000000000000000000000000081526001600160a01b03821660048201526024016106ce565b50565b5f80805f19858709858702925082811083820303915050805f03614229575f841161421e575f5ffd5b50829004905061429a565b808411614234575f5ffd5b5f848688098519600190810187169687900496828603819004959092119093035f82900391909104909201919091029190911760038402600290811880860282030280860282030280860282030280860282030280860282030280860290910302029150505b9392505050565b604080516001600160a01b038481166024830152604480830185905283518084039091018152606490920183526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fa9059cbb0000000000000000000000000000000000000000000000000000000017905291515f9283929087169161432a9190615876565b5f604051808303815f865af19150503d805f8114614363576040519150601f19603f3d011682016040523d82523d5f602084013e614368565b606091505b50915091508180156143925750805115806143925750808060200190518101906143929190615629565b6143f8576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600f60248201527f5452414e534645525f4641494c4544000000000000000000000000000000000060448201526064016106ce565b5050505050565b5f7fffffffff0000000000000000000000000000000000000000000000000000000082167f01ffc9a7000000000000000000000000000000000000000000000000000000001480610eb057507fffffffff0000000000000000000000000000000000000000000000000000000082167fe9fa834a000000000000000000000000000000000000000000000000000000001492915050565b6001546040517fac4ab3fb0000000000000000000000000000000000000000000000000000000081526001600160a01b0383811660048301527f61646d696e00000000000000000000000000000000000000000000000000000060248301525f92169063ac4ab3fb906044015b602060405180830381865afa15801561451e573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610eb09190615629565b600154600160a01b900460ff16156140ea576040517f5b3d539f00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6001600160a01b0381165f908152600183016020526040812054151561429a565b5f7f000000000000000000000000cb4e21eb404d80f3e1db781aad9ad6a1217fbbf26001600160a01b031663bc3dc04e6040518163ffffffff1660e01b8152600401602060405180830381865afa158015614604573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061462891906155ff565b6040517f11df9fa20000000000000000000000000000000000000000000000000000000081526001600160a01b03878116600483015260ff8716602483015285811660448301529192508183169163fe99049a91339130917f000000000000000000000000cb4e21eb404d80f3e1db781aad9ad6a1217fbbf2909116906311df9fa290606401602060405180830381865afa1580156146c9573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906146ed91906155d5565b6040517fffffffff0000000000000000000000000000000000000000000000000000000060e086901b1681526001600160a01b0393841660048201529290911660248301526044820152606481018590526084016020604051808303815f875af115801561475d573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906147819190615629565b505050505050565b604080516001600160a01b0380851660208301528316918101919091525f90606001614188565b5f5f5f5f6147be8686614163565b5f81815260086020908152604091829020825160c08101845281546001600160a01b038082168352600160a01b820460ff16948301949094527501000000000000000000000000000000000000000000900465ffffffffffff16938101939093526001810154606084015260028101549091166080830181905260039091015460a083015291925090614850906141a6565b5f614862825f01518360800151614789565b5f818152600360205260408120546060850151929350909161488a9161ffff166127106141f5565b5f8381526004602090815260408220549086015192935090916148b891849161ffff9091169060ff16614e81565b90505f62278d00856020015160ff166148d191906158b9565b6148db90426158da565b929b919a509198509650505050505050565b60605f61429a83614eac565b604080516001600160a01b0385811660248301528481166044830152606480830185905283518084039091018152608490920183526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167f23b872dd0000000000000000000000000000000000000000000000000000000017905291515f9283929088169161498a9190615876565b5f604051808303815f865af19150503d805f81146149c3576040519150601f19603f3d011682016040523d82523d5f602084013e6149c8565b606091505b50915091508180156149f25750805115806149f25750808060200190518101906149f29190615629565b614781576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601460248201527f5452414e534645525f46524f4d5f4641494c454400000000000000000000000060448201526064016106ce565b604080516001600160a01b038481166024830152604480830185905283518084039091018152606490920183526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167f095ea7b30000000000000000000000000000000000000000000000000000000017905291515f92839290871691614ae19190615876565b5f604051808303815f865af19150503d805f8114614b1a576040519150601f19603f3d011682016040523d82523d5f602084013e614b1f565b606091505b5091509150818015614b49575080511580614b49575080806020019051810190614b499190615629565b6143f8576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600e60248201527f415050524f56455f4641494c454400000000000000000000000000000000000060448201526064016106ce565b6001546040517fac4ab3fb0000000000000000000000000000000000000000000000000000000081526001600160a01b0383811660048301527f6d616e616765720000000000000000000000000000000000000000000000000060248301525f92169063ac4ab3fb90604401614503565b5f61429a836001600160a01b038416614f05565b6001546040517fac4ab3fb0000000000000000000000000000000000000000000000000000000081526001600160a01b0383811660048301527f656d657267656e6379000000000000000000000000000000000000000000000060248301525f92169063ac4ab3fb90604401614503565b5f80546040517fb4dc00b40000000000000000000000000000000000000000000000000000000081527fffffffffff0000000000000000000000000000000000000000000000000000008416600482015282916001600160a01b03169063b4dc00b490602401602060405180830381865afa158015614d26573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190614d4a91906155ff565b90506001600160a01b038116610eb0576040517f5c3fa9cd0000000000000000000000000000000000000000000000000000000081527fffffffffff000000000000000000000000000000000000000000000000000000841660048201526024016106ce565b5f5f614dbb866141a6565b5f60045f614dc98a8a614789565b815260208101919091526040015f9081205461ffff169150819003614e2d576040517f06082a180000000000000000000000000000000000000000000000000000000081526001600160a01b03808a166004830152881660248201526044016106ce565b5f614e40878361ffff168760ff16614e81565b90505f614e5362278d0060ff88166158b9565b614e5d90886158da565b9a91995090975050505050505050565b5f61429a836001600160a01b038416614f51565b5f614ea4614e8f83856158f8565b614e9c612710600c6158f8565b86919061503b565b949350505050565b6060815f01805480602002602001604051908101604052809291908181526020018280548015614ef957602002820191905f5260205f20905b815481526020019060010190808311614ee5575b50505050509050919050565b5f818152600183016020526040812054614f4a57508154600181810184555f848152602080822090930184905584548482528286019093526040902091909155610eb0565b505f610eb0565b5f818152600183016020526040812054801561502b575f614f736001836155c2565b85549091505f90614f86906001906155c2565b9050808214614fe5575f865f018281548110614fa457614fa461588c565b905f5260205f200154905080875f018481548110614fc457614fc461588c565b5f918252602080832090910192909255918252600188019052604090208390555b8554869080614ff657614ff661590f565b600190038181905f5260205f20015f90559055856001015f8681526020019081526020015f205f905560019350505050610eb0565b5f915050610eb0565b5092915050565b5f6150478484846141f5565b90505f82806150585761505861593c565b848609111561429a575f19811061506d575f5ffd5b6001019392505050565b6001600160a01b03811681146141f2575f5ffd5b803561ffff8116811461509c575f5ffd5b919050565b5f5f604083850312156150b2575f5ffd5b82356150bd81615077565b91506150cb6020840161508b565b90509250929050565b5f602082840312156150e4575f5ffd5b81357fffffffff000000000000000000000000000000000000000000000000000000008116811461429a575f5ffd5b5f60208284031215615123575f5ffd5b61429a8261508b565b5f5f6040838503121561513d575f5ffd5b6151468361508b565b946020939093013593505050565b5f5f60208385031215615165575f5ffd5b823567ffffffffffffffff81111561517b575f5ffd5b8301601f8101851361518b575f5ffd5b803567ffffffffffffffff8111156151a1575f5ffd5b8560208284010111156151b2575f5ffd5b6020919091019590945092505050565b5f602082840312156151d2575f5ffd5b813561429a81615077565b5f5f604083850312156151ee575f5ffd5b50508035926020909101359150565b5f5f6040838503121561520e575f5ffd5b823561521981615077565b9150602083013561522981615077565b809150509250929050565b602080825282518282018190525f918401906040840190835b818110156152745783516001600160a01b031683526020938401939092019160010161524d565b509095945050505050565b602080825282518282018190525f918401906040840190835b8181101561527457835180517fffffffffff0000000000000000000000000000000000000000000000000000001684526020908101517fffffffff00000000000000000000000000000000000000000000000000000000168185015290930192604090920191600101615298565b5f5f5f60608486031215615318575f5ffd5b6153218461508b565b95602085013595506040909401359392505050565b5f5f5f60608486031215615348575f5ffd5b833561535381615077565b9250602084013561536381615077565b91506153716040850161508b565b90509250925092565b602080825282518282018190525f918401906040840190835b818110156152745783517fffffffffff00000000000000000000000000000000000000000000000000000016835260209384019390920191600101615393565b60ff811681146141f2575f5ffd5b5f5f604083850312156153f2575f5ffd5b6153fb8361508b565b91506020830135615229816153d3565b602080825282518282018190525f918401906040840190835b81811015615274576154898385516001600160a01b03815116825260ff602082015116602083015265ffffffffffff6040820151166040830152606081015160608301526001600160a01b03608082015116608083015260a081015160a08301525050565b6020939093019260c09290920191600101615424565b60c08101610eb082846001600160a01b03815116825260ff602082015116602083015265ffffffffffff6040820151166040830152606081015160608301526001600160a01b03608082015116608083015260a081015160a08301525050565b5f5f5f60608486031215615511575f5ffd5b833561551c81615077565b925061552a6020850161508b565b9150604084013561553a816153d3565b809150509250925092565b5f5f5f5f60808587031215615558575f5ffd5b843561556381615077565b93506020850135615573816153d3565b925060408501359150606085013561558a81615077565b939692955090935050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b81810381811115610eb057610eb0615595565b5f602082840312156155e5575f5ffd5b5051919050565b80820180821115610eb057610eb0615595565b5f6020828403121561560f575f5ffd5b815161429a81615077565b8051801515811461509c575f5ffd5b5f60208284031215615639575f5ffd5b61429a8261561a565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b604051610120810167ffffffffffffffff8111828210171561569357615693615642565b60405290565b805161509c81615077565b805161509c816153d3565b805165ffffffffffff8116811461509c575f5ffd5b5f82601f8301126156d3575f5ffd5b815167ffffffffffffffff8111156156ed576156ed615642565b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0908116603f0116810167ffffffffffffffff8111828210171561573a5761573a615642565b604052818152838201602001851015615751575f5ffd5b8160208501602083015e5f918101602001919091529392505050565b5f6020828403121561577d575f5ffd5b815167ffffffffffffffff811115615793575f5ffd5b820161012081850312156157a5575f5ffd5b6157ad61566f565b6157b682615699565b81526157c460208301615699565b60208201526157d560408301615699565b60408201526157e6606083016156a4565b60608201526080828101519082015260a0808301519082015261580b60c083016156af565b60c082015261581c60e0830161561a565b60e082015261010082015167ffffffffffffffff81111561583b575f5ffd5b615847868285016156c4565b61010083015250949350505050565b5f61ffff821661ffff810361586d5761586d615595565b60010192915050565b5f82518060208501845e5f920191825250919050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52603260045260245ffd5b65ffffffffffff818116838216029081169081811461503457615034615595565b65ffffffffffff8181168382160190811115610eb057610eb0615595565b8082028115828204841417610eb057610eb0615595565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52603160045260245ffd5b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601260045260245ffdfea2646970667358221220516fbfb21c97e7b455c0263349a24b863a16ffc5b8a7eeb81128f94690c4c9fd64736f6c634300081e0033

Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)

0000000000000000000000002286d7f9639e8158fad1169e76d1fbc38247f54b000000000000000000000000cb4e21eb404d80f3e1db781aad9ad6a1217fbbf2

-----Decoded View---------------
Arg [0] : kernel_ (address): 0x2286d7f9639e8158FaD1169e76d1FbC38247f54b
Arg [1] : depositManager_ (address): 0xcb4E21Eb404d80F3e1dB781aAd9AD6A1217fbbf2

-----Encoded View---------------
2 Constructor Arguments found :
Arg [0] : 0000000000000000000000002286d7f9639e8158fad1169e76d1fbc38247f54b
Arg [1] : 000000000000000000000000cb4e21eb404d80f3e1db781aad9ad6a1217fbbf2


Block Uncle Number Difficulty Gas Used Reward
View All Uncles
Loading...
Loading
Loading...
Loading
Loading...
Loading
[ Download: CSV Export  ]

A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.