ETH Price: $2,036.91 (+3.09%)
 

Overview

ETH Balance

0 ETH

Eth Value

$0.00

Token Holdings

More Info

Private Name Tags

Multichain Info

No addresses found
Transaction Hash
Method
Block
From
To
Set Strategist239600902025-12-07 9:33:5985 days ago1765100039IN
0x8D09EF60...3CDf1Feb6
0 ETH0.000004390.15056761
Set Rewards239583912025-12-07 3:50:1185 days ago1765079411IN
0x8D09EF60...3CDf1Feb6
0 ETH0.00002520.37905241
Set Max Report D...239583002025-12-07 3:31:5985 days ago1765078319IN
0x8D09EF60...3CDf1Feb6
0 ETH0.00000950.30534197
Set Keeper239461512025-12-05 10:27:1187 days ago1764930431IN
0x8D09EF60...3CDf1Feb6
0 ETH0.000000830.0271965

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

Similar Match Source Code
This contract matches the deployed Bytecode of the Source Code for Contract 0x3a045924...a135BD920
The constructor portion of the code might be different and could alter the actual behaviour of the contract

Contract Name:
StrategyMorphoV1Vault

Compiler Version
v0.8.29+commit.ab55807c

Optimization Enabled:
Yes with 250 runs

Other Settings:
cancun EvmVersion

Contract Source Code (Solidity Standard Json-Input format)

// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.8.29 <0.9.0;

import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import { IERC20Metadata } from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol";
import { SafeERC20 } from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import { Math } from "@openzeppelin/contracts/utils/math/Math.sol";
import { EnumerableSet } from "@openzeppelin/contracts/utils/structs/EnumerableSet.sol";

import { BaseCooldownStrategy } from "@superearn/core/BaseCooldownStrategy.sol";
import { IMetaMorpho } from "@metamorpho/src/interfaces/IMetaMorpho.sol";

// https://docs.morpho.org/get-started/resources/contracts/morpho-vaults/
// https://github.com/morpho-org/metamorpho

/**
 * @title StrategyMorphoV1Vault
 */
contract StrategyMorphoV1Vault is BaseCooldownStrategy {
    using SafeERC20 for IERC20;
    using EnumerableSet for EnumerableSet.UintSet;

    // Custom errors
    error RedeemAmountMismatch();

    // Constants
    uint256 private immutable MAX_SUPPLY_THRESHOLD;

    // State variables
    IMetaMorpho public immutable metaMorpho;
    uint256 private immutable oneUsdt;
    uint256 private immutable oneERC4626;
    uint256 private lastRedeemIndex;
    mapping(uint256 => uint256) public redeemAmounts;

    /**
     * @notice Initializes the StrategyMorphoV1Vault with MetaMorphoVaultV1 integration
     * @param _vault Address of the Yearn vault that will manage this strategy
     * @dev Sets up the MetaMorphoVaultV1 instance and configures strategy parameters
     */
    constructor(
        address _vault,
        address _metaMorpho
    )
        BaseCooldownStrategy(_vault, address(_metaMorpho), address(IMetaMorpho(_metaMorpho).asset()))
    {
        // Initialize MetaMorphoVaultV1
        metaMorpho = IMetaMorpho(_metaMorpho);
        AMOUNT_TO_SHARE_BUFFER = 0;

        minReportDelay = 0;
        maxReportDelay = 7 days;

        // externalUnderlyingToken should be USDT
        oneERC4626 = 10 ** IERC20Metadata(address(externalShareToken)).decimals();
        oneUsdt = 10 ** IERC20Metadata(address(externalUnderlyingToken)).decimals();

        MAX_SUPPLY_THRESHOLD = 1_000_000_000 * oneUsdt;
    }

    /**
     * @notice Strategy name
     */
    function name() external view override returns (string memory) {
        return string.concat("StrategyMorphoV1Vault ", IERC20Metadata(address(metaMorpho)).name());
    }

    // ============================================
    // ABSTRACT FUNCTION IMPLEMENTATIONS
    // ============================================

    function getCooldownPeriod() public view virtual override returns (uint256 cooldownPeriod) {
        cooldownPeriod = 0;
    }

    function requestDeposit(uint256 assets)
        internal
        virtual
        override
        returns (bool success, uint256 shares, uint256 filledAssets)
    {
        if (!beforeExternalDeposit(assets)) return (false, 0, 0);

        uint256 beforeUnderlyingBalance = externalUnderlyingToken.balanceOf(address(this));
        uint256 beforeShareBalance = externalShareToken.balanceOf(address(this));

        externalUnderlyingToken.forceApprove(address(metaMorpho), assets);
        try metaMorpho.deposit(assets, address(this)) {
            success = true;
            shares = externalShareToken.balanceOf(address(this)) - beforeShareBalance;
            filledAssets = beforeUnderlyingBalance - externalUnderlyingToken.balanceOf(address(this));
        } catch {
            success = false; // assign explicitly
        }
    }

    function requestRedeem(uint256 shares)
        internal
        virtual
        override
        returns (bool success, uint256 redeemId, uint256 redeemUnderlyingAmount, uint256 cooldownPeriod)
    {
        if (!beforeExternalRedeem(shares)) return (false, 0, 0, 0);

        // don't need to approve; it will be burned
        uint256 balanceDelta = externalUnderlyingToken.balanceOf(address(this));
        try metaMorpho.redeem(shares, address(this), address(this)) returns (uint256 redeemAmount) {
            success = true;
            redeemAmounts[++lastRedeemIndex] = redeemAmount;
            (redeemId,,, redeemUnderlyingAmount,) = getRedeemDetail(getLastRedeemIndex());

            balanceDelta = externalUnderlyingToken.balanceOf(address(this)) - balanceDelta;
            if (balanceDelta != redeemAmount) {
                revert RedeemAmountMismatch();
            }
        } catch {
            success = false; // assign explicitly
        }

        cooldownPeriod = getCooldownPeriod();
    }

    /**
     * @dev Cannot reach here for ERC4626
     */
    function requestClaim(uint256 /* redeemIndex */ ) internal virtual override returns (bool, uint256) {
        assert(false);
    }

    /**
     * @dev see ERC4626
     */
    function previewDeposit(uint256 assets) public view virtual override returns (uint256 shares) {
        return metaMorpho.previewDeposit(assets);
    }

    /**
     * @dev see ERC4626
     */
    function previewMint(uint256 shares) public view virtual override returns (uint256 assets) {
        return metaMorpho.previewMint(shares);
    }

    /**
     * @dev see ERC4626
     */
    function previewWithdraw(uint256 assets) public view virtual override returns (uint256 shares) {
        return metaMorpho.previewWithdraw(assets);
    }

    /**
     * @dev see ERC4626
     */
    function previewRedeem(uint256 shares) public view virtual override returns (uint256 assets) {
        return metaMorpho.previewRedeem(shares);
    }

    /**
     * @dev The redeemIndex in metaMorpho starts from 1 and indicates the last valid registered index.
     *      When set to 0, it means there are no redeems. Therefore, redeemIndex can also be viewed as redeemLength.
     */
    function getLastRedeemIndex() internal view virtual override returns (uint256) {
        return lastRedeemIndex;
    }

    function getRedeemDetail(uint256 redeemIndex)
        public
        view
        virtual
        override
        returns (
            uint256 redeemId,
            uint256 redeemTimestamp,
            address redeemUser,
            uint256 redeemUnderlyingAmount,
            bool redeemIsDone
        )
    {
        redeemId = redeemIndex;
        // redeemTimestamp = 0;
        redeemUser = address(this);
        redeemUnderlyingAmount = redeemAmounts[redeemId];
        redeemIsDone = redeemId > 0;
    }

    function getSupplyCap() public view virtual override returns (uint256 supplyAssetsCap, uint256 availableAssets) {
        supplyAssetsCap = metaMorpho.maxDeposit(address(this));
        availableAssets = Math.min(supplyAssetsCap, MAX_SUPPLY_THRESHOLD);
    }

    function isPredepositAlreadyClaimed(uint256 predepositId) external view virtual override returns (bool isClaimed) {
        uint256 redeemIndex = externalRedeemIndexes[predepositId];
        if (redeemIndex == 0) return false;
        (,,,, isClaimed) = getRedeemDetail(redeemIndex);
    }

    function predepositDebtRetrievable(uint256 predepositId)
        external
        view
        virtual
        override
        returns (bool isRetrievable)
    {
        isRetrievable = false;
    }

    function ethToWant(uint256 /* _amtInWei */ ) public view virtual override returns (uint256) {
        return 0;
    }

    function beforeExternalDeposit(uint256 assets) internal view virtual override returns (bool valid) {
        valid = metaMorpho.previewDeposit(assets) > 0;
    }

    function beforeExternalRedeem(uint256 shares) internal view virtual override returns (bool valid) {
        valid = metaMorpho.previewRedeem(shares) > 0;
    }

    // ============================================
    // YEARN STRATEGY FUNCTIONS
    // ============================================

    function prepareReturn(uint256 _debtOutstanding)
        internal
        virtual
        override
        returns (uint256 _profit, uint256 _loss, uint256 _debtPayment)
    {
        // 1. Calculate current total assets
        uint256 totalAssets = estimatedTotalAssets();
        uint256 totalDebt = getStrategyParams().totalDebt;

        // 2. Estimate debt repayment and profit/loss
        _debtPayment = Math.min(_debtOutstanding, totalAssets);
        totalAssets -= _debtPayment;
        totalDebt -= _debtPayment;

        if (totalAssets > totalDebt) {
            _profit = totalAssets - totalDebt;
            // _loss = 0;
        } else {
            // _profit = 0;
            _loss = totalDebt - totalAssets;
        }

        // 3. Actual repayment: Since the expected and actual repayment amounts may differ during liquidation,
        // recalculate accurately based on the realizable amount

        // yVault enforces: want.balanceOf(address(this)) >= _profit + _debtPayment
        (uint256 toReturn,) = liquidatePosition(_profit + _debtPayment);

        totalAssets = estimatedTotalAssets();
        totalDebt = getStrategyParams().totalDebt;

        _debtPayment = Math.min(_debtPayment, toReturn);
        totalAssets -= _debtPayment;
        totalDebt -= _debtPayment;
        if (totalAssets > totalDebt) {
            _profit = Math.min(toReturn - _debtPayment, totalAssets - totalDebt);
            _loss = 0;
        } else {
            _profit = 0;
            _loss = totalDebt - totalAssets;
        }
    }

    function liquidatePosition(uint256 _amountNeeded)
        internal
        virtual
        override
        nonReentrant
        returns (uint256 _liquidatedAmount, uint256 _loss)
    {
        // pendingInvested is the asset waiting to be invested and is already liquidated
        uint256 pendingInvested = want.balanceOf(address(this));
        if (pendingInvested >= _amountNeeded) {
            return (_amountNeeded, 0);
        }

        // The shortage is additionally liquidated from invested shares
        (,, uint256 lossShares) = premintCooldownVault(_amountNeeded - pendingInvested);

        // should `_liquidatedAmount + _loss <= _amountNeeded`
        uint256 available = want.balanceOf(address(this));
        if (_amountNeeded > available) {
            _liquidatedAmount = available;
            _loss = Math.min(_amountNeeded - available, lossShares);
        } else {
            _liquidatedAmount = _amountNeeded;
            // _loss = 0;
        }
    }

    function liquidateAllPositions() internal virtual override nonReentrant returns (uint256 _amountFreed) {
        premintCooldownVault(estimatedTotalAssets() + oneUsdt);
        _amountFreed = want.balanceOf(address(this));
    }

    function adjustPosition(uint256 _debtOutstanding) internal virtual override nonReentrant {
        if (emergencyExit) return;

        uint256 pendingInvested = want.balanceOf(address(this));
        if (_debtOutstanding >= pendingInvested) return;

        uint256 toInvestShares;
        unchecked {
            uint256 _free = cooldownVault.maxInstantRedeem(address(this));
            uint256 _maxInvest = pendingInvested - _debtOutstanding;
            toInvestShares = Math.min(_free, _maxInvest);
            (, uint256 _availableUsdt) = getSupplyCap();
            toInvestShares = Math.min(toInvestShares, cooldownVault.previewDeposit(_availableUsdt));
        }

        uint256 toInvestAssets = cooldownVault.instantRedeem(toInvestShares);
        if (remainingPredepositDebt > shortfallTolerance) {
            toInvestAssets = 0;
        }

        (,, uint256 filledAssets) = requestDeposit(toInvestAssets);
        uint256 unfilledAssets = toInvestAssets - filledAssets;

        if (unfilledAssets > 0) {
            externalUnderlyingToken.forceApprove(address(cooldownVault), unfilledAssets);
            cooldownVault.deposit(unfilledAssets, address(this));
        }

        emit AdjustPosition(this.getUtilizationRate(), filledAssets, unfilledAssets, estimatedTotalAssets());
    }

    function prepareMigration(address _newStrategy) internal virtual override {
        _requireNoOutstandingDebt();
        externalShareToken.safeTransfer(_newStrategy, externalShareToken.balanceOf(address(this)));
        externalUnderlyingToken.safeTransfer(_newStrategy, externalUnderlyingToken.balanceOf(address(this)));
    }

    function protectedTokens() internal view virtual override returns (address[] memory) {
        address[] memory protected = new address[](2);
        protected[0] = address(externalShareToken); // ERC4626
        protected[1] = address(externalUnderlyingToken);
        return protected;
    }

    function tendTrigger(uint256 /* callCostInWei */ ) public view virtual override returns (bool) {
        if (emergencyExit) return false;

        // Additional investment is possible when utilizationRate is not 100%
        uint256 pendingInvested = want.balanceOf(address(this));
        if (pendingInvested == 0) return false;

        // Check if there are actually tokens available for investment
        uint256 idleBalance = cooldownVault.idleBalance();
        return beforeExternalDeposit(Math.min(idleBalance, pendingInvested));
    }

    function harvestTrigger(uint256 callCostInWei) public view virtual override returns (bool) {
        bool trigger = super.harvestTrigger(callCostInWei);
        uint256 lastReport = getStrategyParams().lastReport;
        if (trigger) {
            if (forceHarvestTriggerOnce) return true;
            if ((block.timestamp - lastReport) >= maxReportDelay) return true;
        } else {
            return false;
        }
        if (block.timestamp == lastReport) return false;

        // 1. Calculate current total assets
        uint256 totalAssets = estimatedTotalAssets();
        uint256 totalDebt = getStrategyParams().totalDebt;
        uint256 credit = vault.creditAvailable();

        // 2. Estimate debt repayment and profit/loss
        uint256 _debtPayment = 0;
        uint256 _debtOutstanding = vault.debtOutstanding();
        if (totalAssets > _debtOutstanding) {
            _debtPayment = _debtOutstanding;
        } else {
            _debtPayment = totalAssets;
        }
        totalAssets -= _debtPayment;
        totalDebt -= _debtPayment;

        uint256 _profit = 0;
        if (totalAssets > totalDebt) {
            _profit = totalAssets - totalDebt;
        }

        uint256 halfCreditThreshold = creditThreshold >> 1;
        uint256 totalAvail = _debtPayment + _profit;
        if (totalAvail < credit) {
            // receive want from vault
            return (credit - totalAvail) >= halfCreditThreshold;
        } else if (totalAvail > credit) {
            // send want to vault
            return (totalAvail - credit) >= halfCreditThreshold;
        }

        return false;
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (token/ERC20/IERC20.sol)

pragma solidity ^0.8.20;

/**
 * @dev Interface of the ERC-20 standard as defined in the ERC.
 */
interface IERC20 {
    /**
     * @dev Emitted when `value` tokens are moved from one account (`from`) to
     * another (`to`).
     *
     * Note that `value` may be zero.
     */
    event Transfer(address indexed from, address indexed to, uint256 value);

    /**
     * @dev Emitted when the allowance of a `spender` for an `owner` is set by
     * a call to {approve}. `value` is the new allowance.
     */
    event Approval(address indexed owner, address indexed spender, uint256 value);

    /**
     * @dev Returns the value of tokens in existence.
     */
    function totalSupply() external view returns (uint256);

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

    /**
     * @dev Moves a `value` amount of tokens from the caller's account to `to`.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * Emits a {Transfer} event.
     */
    function transfer(address to, uint256 value) external returns (bool);

    /**
     * @dev Returns the remaining number of tokens that `spender` will be
     * allowed to spend on behalf of `owner` through {transferFrom}. This is
     * zero by default.
     *
     * This value changes when {approve} or {transferFrom} are called.
     */
    function allowance(address owner, address spender) external view returns (uint256);

    /**
     * @dev Sets a `value` amount of tokens as the allowance of `spender` over the
     * caller's tokens.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * IMPORTANT: Beware that changing an allowance with this method brings the risk
     * that someone may use both the old and the new allowance by unfortunate
     * transaction ordering. One possible solution to mitigate this race
     * condition is to first reduce the spender's allowance to 0 and set the
     * desired value afterwards:
     * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
     *
     * Emits an {Approval} event.
     */
    function approve(address spender, uint256 value) external returns (bool);

    /**
     * @dev Moves a `value` amount of tokens from `from` to `to` using the
     * allowance mechanism. `value` is then deducted from the caller's
     * allowance.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * Emits a {Transfer} event.
     */
    function transferFrom(address from, address to, uint256 value) external returns (bool);
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (token/ERC20/extensions/IERC20Metadata.sol)

pragma solidity ^0.8.20;

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

/**
 * @dev Interface for the optional metadata functions from the ERC-20 standard.
 */
interface IERC20Metadata is IERC20 {
    /**
     * @dev Returns the name of the token.
     */
    function name() external view returns (string memory);

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

    /**
     * @dev Returns the decimals places of the token.
     */
    function decimals() external view returns (uint8);
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.3.0) (token/ERC20/utils/SafeERC20.sol)

pragma solidity ^0.8.20;

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

/**
 * @title SafeERC20
 * @dev Wrappers around ERC-20 operations that throw on failure (when the token
 * contract returns false). Tokens that return no value (and instead revert or
 * throw on failure) are also supported, non-reverting calls are assumed to be
 * successful.
 * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,
 * which allows you to call the safe operations as `token.safeTransfer(...)`, etc.
 */
library SafeERC20 {
    /**
     * @dev An operation with an ERC-20 token failed.
     */
    error SafeERC20FailedOperation(address token);

    /**
     * @dev Indicates a failed `decreaseAllowance` request.
     */
    error SafeERC20FailedDecreaseAllowance(address spender, uint256 currentAllowance, uint256 requestedDecrease);

    /**
     * @dev Transfer `value` amount of `token` from the calling contract to `to`. If `token` returns no value,
     * non-reverting calls are assumed to be successful.
     */
    function safeTransfer(IERC20 token, address to, uint256 value) internal {
        _callOptionalReturn(token, abi.encodeCall(token.transfer, (to, value)));
    }

    /**
     * @dev Transfer `value` amount of `token` from `from` to `to`, spending the approval given by `from` to the
     * calling contract. If `token` returns no value, non-reverting calls are assumed to be successful.
     */
    function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal {
        _callOptionalReturn(token, abi.encodeCall(token.transferFrom, (from, to, value)));
    }

    /**
     * @dev Variant of {safeTransfer} that returns a bool instead of reverting if the operation is not successful.
     */
    function trySafeTransfer(IERC20 token, address to, uint256 value) internal returns (bool) {
        return _callOptionalReturnBool(token, abi.encodeCall(token.transfer, (to, value)));
    }

    /**
     * @dev Variant of {safeTransferFrom} that returns a bool instead of reverting if the operation is not successful.
     */
    function trySafeTransferFrom(IERC20 token, address from, address to, uint256 value) internal returns (bool) {
        return _callOptionalReturnBool(token, abi.encodeCall(token.transferFrom, (from, to, value)));
    }

    /**
     * @dev Increase the calling contract's allowance toward `spender` by `value`. If `token` returns no value,
     * non-reverting calls are assumed to be successful.
     *
     * IMPORTANT: If the token implements ERC-7674 (ERC-20 with temporary allowance), and if the "client"
     * smart contract uses ERC-7674 to set temporary allowances, then the "client" smart contract should avoid using
     * this function. Performing a {safeIncreaseAllowance} or {safeDecreaseAllowance} operation on a token contract
     * that has a non-zero temporary allowance (for that particular owner-spender) will result in unexpected behavior.
     */
    function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal {
        uint256 oldAllowance = token.allowance(address(this), spender);
        forceApprove(token, spender, oldAllowance + value);
    }

    /**
     * @dev Decrease the calling contract's allowance toward `spender` by `requestedDecrease`. If `token` returns no
     * value, non-reverting calls are assumed to be successful.
     *
     * IMPORTANT: If the token implements ERC-7674 (ERC-20 with temporary allowance), and if the "client"
     * smart contract uses ERC-7674 to set temporary allowances, then the "client" smart contract should avoid using
     * this function. Performing a {safeIncreaseAllowance} or {safeDecreaseAllowance} operation on a token contract
     * that has a non-zero temporary allowance (for that particular owner-spender) will result in unexpected behavior.
     */
    function safeDecreaseAllowance(IERC20 token, address spender, uint256 requestedDecrease) internal {
        unchecked {
            uint256 currentAllowance = token.allowance(address(this), spender);
            if (currentAllowance < requestedDecrease) {
                revert SafeERC20FailedDecreaseAllowance(spender, currentAllowance, requestedDecrease);
            }
            forceApprove(token, spender, currentAllowance - requestedDecrease);
        }
    }

    /**
     * @dev Set the calling contract's allowance toward `spender` to `value`. If `token` returns no value,
     * non-reverting calls are assumed to be successful. Meant to be used with tokens that require the approval
     * to be set to zero before setting it to a non-zero value, such as USDT.
     *
     * NOTE: If the token implements ERC-7674, this function will not modify any temporary allowance. This function
     * only sets the "standard" allowance. Any temporary allowance will remain active, in addition to the value being
     * set here.
     */
    function forceApprove(IERC20 token, address spender, uint256 value) internal {
        bytes memory approvalCall = abi.encodeCall(token.approve, (spender, value));

        if (!_callOptionalReturnBool(token, approvalCall)) {
            _callOptionalReturn(token, abi.encodeCall(token.approve, (spender, 0)));
            _callOptionalReturn(token, approvalCall);
        }
    }

    /**
     * @dev Performs an {ERC1363} transferAndCall, with a fallback to the simple {ERC20} transfer if the target has no
     * code. This can be used to implement an {ERC721}-like safe transfer that rely on {ERC1363} checks when
     * targeting contracts.
     *
     * Reverts if the returned value is other than `true`.
     */
    function transferAndCallRelaxed(IERC1363 token, address to, uint256 value, bytes memory data) internal {
        if (to.code.length == 0) {
            safeTransfer(token, to, value);
        } else if (!token.transferAndCall(to, value, data)) {
            revert SafeERC20FailedOperation(address(token));
        }
    }

    /**
     * @dev Performs an {ERC1363} transferFromAndCall, with a fallback to the simple {ERC20} transferFrom if the target
     * has no code. This can be used to implement an {ERC721}-like safe transfer that rely on {ERC1363} checks when
     * targeting contracts.
     *
     * Reverts if the returned value is other than `true`.
     */
    function transferFromAndCallRelaxed(
        IERC1363 token,
        address from,
        address to,
        uint256 value,
        bytes memory data
    ) internal {
        if (to.code.length == 0) {
            safeTransferFrom(token, from, to, value);
        } else if (!token.transferFromAndCall(from, to, value, data)) {
            revert SafeERC20FailedOperation(address(token));
        }
    }

    /**
     * @dev Performs an {ERC1363} approveAndCall, with a fallback to the simple {ERC20} approve if the target has no
     * code. This can be used to implement an {ERC721}-like safe transfer that rely on {ERC1363} checks when
     * targeting contracts.
     *
     * NOTE: When the recipient address (`to`) has no code (i.e. is an EOA), this function behaves as {forceApprove}.
     * Opposedly, when the recipient address (`to`) has code, this function only attempts to call {ERC1363-approveAndCall}
     * once without retrying, and relies on the returned value to be true.
     *
     * Reverts if the returned value is other than `true`.
     */
    function approveAndCallRelaxed(IERC1363 token, address to, uint256 value, bytes memory data) internal {
        if (to.code.length == 0) {
            forceApprove(token, to, value);
        } else if (!token.approveAndCall(to, value, data)) {
            revert SafeERC20FailedOperation(address(token));
        }
    }

    /**
     * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
     * on the return value: the return value is optional (but if data is returned, it must not be false).
     * @param token The token targeted by the call.
     * @param data The call data (encoded using abi.encode or one of its variants).
     *
     * This is a variant of {_callOptionalReturnBool} that reverts if call fails to meet the requirements.
     */
    function _callOptionalReturn(IERC20 token, bytes memory data) private {
        uint256 returnSize;
        uint256 returnValue;
        assembly ("memory-safe") {
            let success := call(gas(), token, 0, add(data, 0x20), mload(data), 0, 0x20)
            // bubble errors
            if iszero(success) {
                let ptr := mload(0x40)
                returndatacopy(ptr, 0, returndatasize())
                revert(ptr, returndatasize())
            }
            returnSize := returndatasize()
            returnValue := mload(0)
        }

        if (returnSize == 0 ? address(token).code.length == 0 : returnValue != 1) {
            revert SafeERC20FailedOperation(address(token));
        }
    }

    /**
     * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
     * on the return value: the return value is optional (but if data is returned, it must not be false).
     * @param token The token targeted by the call.
     * @param data The call data (encoded using abi.encode or one of its variants).
     *
     * This is a variant of {_callOptionalReturn} that silently catches all reverts and returns a bool instead.
     */
    function _callOptionalReturnBool(IERC20 token, bytes memory data) private returns (bool) {
        bool success;
        uint256 returnSize;
        uint256 returnValue;
        assembly ("memory-safe") {
            success := call(gas(), token, 0, add(data, 0x20), mload(data), 0, 0x20)
            returnSize := returndatasize()
            returnValue := mload(0)
        }
        return success && (returnSize == 0 ? address(token).code.length > 0 : returnValue == 1);
    }
}

File 5 of 36 : 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: 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;
    }
}

File 7 of 36 : BaseCooldownStrategy.sol
// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.8.29 <0.9.0;

import { ReentrancyGuard } from "@openzeppelin/contracts/utils/ReentrancyGuard.sol";
import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import { IERC20Metadata } from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol";
import { SafeERC20 } from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import { ICooldownVault } from "@superearn/interface/ICooldownVault.sol";
import { Math } from "@openzeppelin/contracts/utils/math/Math.sol";
import { StrategyParams } from "@superearn/interface/IVault.sol";
import { BaseStrategy } from "@yearn-vaults/BaseStrategy.sol";
import { IStrategyCooldownAware } from "@superearn/interface/IStrategyCooldownAware.sol";
import { TimelockExecutionLib } from "@superearn/core/lib/TimelockExecutionLib.sol";

/**
 * @title BaseCooldownStrategy
 * @notice Abstract base contract for strategies that interact with external vaults requiring cooldown periods
 * @dev This contract handles the coordination between CooldownVault predeposits and external vault redemptions.
 *      It manages the complex flow of:
 *      1. Requesting redemptions from external vaults
 *      2. Predepositing CooldownVaults for immediate liquidity
 *      3. Repaying predeposit debt once external redemptions are claimable
 *      Inheriting strategies must implement the abstract functions to integrate with specific external vaults.
 */
abstract contract BaseCooldownStrategy is IStrategyCooldownAware, BaseStrategy, ReentrancyGuard {
    using SafeERC20 for IERC20;
    using TimelockExecutionLib for TimelockExecutionLib.TimelockStorage;

    // === Custom Errors ===
    error InvalidExternalToken();
    error OnlyCooldownVault();
    error InvalidExternalRedeem();
    error InvalidDebtClaimState();
    error CannotRepayByFailedClaim();
    error InsufficientUnderlyingBalance(uint256 requested, uint256 available);
    error ZeroAssets();
    error RepayAmountExceedsShortfall(uint256 requested, uint256 outstanding);
    error OutstandingPredepositDebt();

    uint256 private constant BASIS_POINTS = 10_000;
    uint256 internal immutable AMOUNT_TO_SHARE_BUFFER;

    /// @notice Minimum timelock delay (12 hours)
    uint256 public constant MIN_TIMELOCK_DELAY = 12 hours;
    /// @notice Maximum timelock delay (30 days)
    uint256 public constant MAX_TIMELOCK_DELAY = 30 days;
    /// @notice Execution window duration after timelock expires (7 days)
    uint256 public constant EXECUTION_WINDOW = 7 days;

    /// @notice Restricts function access to CooldownVault contract only
    modifier onlyCooldownVault() {
        if (msg.sender != address(cooldownVault)) revert OnlyCooldownVault();
        _;
    }

    /// @notice Maps predeposit IDs to their corresponding external vault redeem indexes
    mapping(uint256 predepositId => uint256 externalRedeemIndex) public externalRedeemIndexes;

    /// @notice Token received from external vault redemptions (usually same as want().underlyingToken())
    IERC20 public immutable externalShareToken;
    IERC20 public immutable externalUnderlyingToken;
    ICooldownVault public immutable cooldownVault;

    /// @notice Default: 100 tokens (e.g., 100 USDT)
    uint256 public shortfallTolerance;
    /// @notice Total shortfall of the predeposit debt
    /// @notice Timelock execution storage (pendingExecution, allowedTargets, timelockDelay)
    TimelockExecutionLib.TimelockStorage internal _timelockStorage;

    /// @notice Accumulated shortfall from predeposit repayments (mirrors cooldownVault.strategyShortfall)
    /// @dev This is only the unpaid portion when repayPredepositDebt() underpays; it is not the full
    /// predeposit debt. Starts at 0, increases by (expected - claimed), and is cleared when
    /// repayRemainingPredepositDebt() calls cooldownVault.retrieveShortfall(). Used to avoid
    /// overstating assets in estimatedTotalAssets() while the vault still records the shortfall inside
    /// strategyDebtOutstanding.
    uint256 public remainingPredepositDebt;

    /**
     * @notice Initializes the base cooldown strategy
     * @param _vault Address of the Yearn vault
     * @param _externalShareToken Address of the external vault's share token
     * @param _externalUnderlyingToken Address of the token received from external vault redemptions
     * @dev Verifies that external underlying token matches CooldownVault's underlying
     */
    constructor(address _vault, address _externalShareToken, address _externalUnderlyingToken) BaseStrategy(_vault) {
        if (_externalShareToken == address(0)) revert InvalidExternalToken();
        externalShareToken = IERC20(_externalShareToken);

        externalUnderlyingToken = IERC20(_externalUnderlyingToken);

        cooldownVault = ICooldownVault(address(want));

        if (_externalUnderlyingToken != cooldownVault.asset()) {
            revert InvalidExternalToken();
        }

        shortfallTolerance = 100 * 10 ** IERC20Metadata(address(want)).decimals();

        _timelockStorage.timelockDelay = 2 days; // OpenZeppelin standard default
    }

    // ============================================
    // ABSTRACT FUNCTIONS
    // ============================================

    /**
     * @notice Returns the cooldown period of the external vault
     * @return cooldownPeriod The cooldown period in seconds
     */
    function getCooldownPeriod() public view virtual override returns (uint256 cooldownPeriod);

    /**
     * @notice Requests a deposit of assets into the external vault
     * @param assets Amount of underlying tokens to deposit
     * @return success True if the deposit request was successful
     * @return shares Number of external vault shares received from the deposit
     * @return filledAssets Amount of underlying tokens filled from the deposit
     * @dev This function is called internally to invest idle assets into the external vault.
     *      The implementation should handle the actual deposit interaction with the external vault.
     *      Inheriting contracts must complete the implementation after the beforeExternalDeposit check.
     *      Example: In StrategyAvalonUSDTVault, this mints sUSDT shares from the Avalon vault.
     */
    function requestDeposit(uint256 assets)
        internal
        virtual
        returns (bool success, uint256 shares, uint256 filledAssets)
    {
        if (!beforeExternalDeposit(assets)) return (false, 0, 0);
        // Comment: Inheriting contracts must complete the implementation
    }

    /**
     * @notice Requests a redemption of shares from the external vault
     * @param shares Number of external vault shares to redeem
     * @return success True if the redemption request was successful
     * @return redeemId The unique identifier of the redemption request in the external vault
     * @return redeemUnderlyingAmount The amount of underlying tokens to be received upon redemption
     * @return cooldownPeriod The cooldown period in seconds
     * @dev This function initiates the cooldown period for withdrawing from the external vault.
     *      The implementation should handle the actual redemption request with the external vault.
     *      Inheriting contracts must complete the implementation after the beforeExternalRedeem check.
     *      Example: In StrategyAvalonUSDTVault, this burns sUSDT shares and registers a redemption request.
     */
    function requestRedeem(uint256 shares)
        internal
        virtual
        returns (bool success, uint256 redeemId, uint256 redeemUnderlyingAmount, uint256 cooldownPeriod)
    {
        if (!beforeExternalRedeem(shares)) return (false, 0, 0, 0);
        // Comment: Inheriting contracts must complete the implementation
    }

    /**
     * @notice Claims a completed redemption from the external vault
     * @param redeemIndex The index/ID of the redemption to claim
     * @dev This internal function makes permissive requests
     *      Validation should be handled where this function is used
     */
    function requestClaim(uint256 redeemIndex) internal virtual returns (bool success, uint256 claimedAmount);

    /**
     * @notice Converts assets to shares for the external DeFi vault this strategy integrates with
     * @param assets Amount of underlying tokens to convert
     * @return shares Equivalent amount of external vault shares
     * @dev If the external vault is ERC4626-compliant or supports precise rounding,
     *      this function should use Math.Rounding.Floor (returns fewer shares, favorable to vault)
     */
    function previewDeposit(uint256 assets) public view virtual override returns (uint256 shares);

    /// @notice See previewDeposit. Uses Math.Rounding.Ceil (requires more assets, favorable to vault)
    function previewMint(uint256 shares) public view virtual override returns (uint256 assets);

    /// @notice See previewDeposit. Uses Math.Rounding.Ceil (requires more shares, favorable to vault)
    function previewWithdraw(uint256 assets) public view virtual override returns (uint256 shares);

    /// @notice See previewDeposit. Uses Math.Rounding.Floor (returns fewer assets, favorable to vault)
    function previewRedeem(uint256 shares) public view virtual override returns (uint256 assets);

    /**
     * @notice Gets the last redemption index from external vault
     * @return The index/ID of the most recent redemption request
     * @dev Used to track which external redemption corresponds to a predeposit
     */
    function getLastRedeemIndex() internal view virtual returns (uint256);

    /**
     * @notice Gets detailed information about a specific redemption request
     * @param redeemIndex The index/ID of the redemption to query
     * @return redeemId The unique identifier of the redemption request
     * @return redeemTimestamp The timestamp when the redemption was requested
     * @return redeemUser The address that initiated the redemption
     * @return redeemUnderlyingAmount The amount of underlying tokens to be redeemed
     * @return redeemIsDone Whether the redemption has been completed
     * @dev Implementation should query the external vault for redemption details
     */
    function getRedeemDetail(uint256 redeemIndex)
        public
        view
        virtual
        override
        returns (
            uint256 redeemId,
            uint256 redeemTimestamp,
            address redeemUser,
            uint256 redeemUnderlyingAmount,
            bool redeemIsDone
        );

    /**
     * @notice Gets the supply cap and available capacity from the external vault
     * @return supplyAssetsCap The maximum amount that can be supplied to the external vault
     * @return availableAssets The amount still available to be supplied before reaching the cap
     */
    function getSupplyCap() public view virtual override returns (uint256 supplyAssetsCap, uint256 availableAssets);

    /**
     * @notice Checks if the external vault redemption linked to a predeposit has been claimed
     * @param predepositId The predeposit ID to check
     * @return isClaimed True if the external redemption was already claimed from the external vault
     * @dev Critical security function for protocols with permissionless claim mechanisms.
     *      Some external protocols allow anyone to claim redemptions (e.g., for meta-tx support),
     *      which breaks CooldownVault's assumption that claims only happen through retrieveDebt().
     *      This function ensures CooldownVault can verify if a predeposit was properly completed
     *      even when the claim happened outside the expected flow, preventing loss of tracking
     *      and ensuring correct debt settlement for users.
     */
    function isPredepositAlreadyClaimed(uint256 predepositId) external view virtual override returns (bool isClaimed);

    /**
     * @notice Checks if a predeposit debt can be retrieved from external vault
     * @param predepositId The predeposit ID to check
     * @return isRetrievable True if the corresponding external redemption has completed its cooldown
     */
    function predepositDebtRetrievable(uint256 predepositId)
        external
        view
        virtual
        override
        returns (bool isRetrievable);

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

    /**
     * @notice Validates whether a deposit to the external vault should proceed
     * @param assets Amount of underlying tokens to deposit
     * @return valid True if the deposit should proceed, false otherwise
     * @dev This hook is called before executing requestDeposit to allow strategy-specific validation.
     *      Implementations can check minimum amounts, supply caps, or other conditions.
     *      Example: In StrategyAvalonUSDTVault, validates that assets >= 1 USDT (Avalon's minimum).
     */
    function beforeExternalDeposit(uint256 assets) internal view virtual returns (bool valid);

    /**
     * @notice Validates whether a redemption from the external vault should proceed
     * @param shares Number of external vault shares to redeem
     * @return valid True if the redemption should proceed, false otherwise
     * @dev This hook is called before executing requestRedeem to allow strategy-specific validation.
     *      Implementations can check minimum amounts, availability, or other conditions.
     *      Example: In StrategyAvalonUSDTVault, validates that shares >= 1.0 sUSDT (Avalon's minimum).
     */
    function beforeExternalRedeem(uint256 shares) internal view virtual returns (bool valid);

    /**
     * @notice Handles the preminting of CooldownVaults with coordinated external redemption
     * @param sharesNeeded Maximum amount of CooldownVault shares to premint
     * @dev The function name uses "premint" instead of "predeposit" to maintain consistency
     *      with ERC4626 standard naming conventions. In ERC4626, when the parameter represents
     *      shares (as opposed to assets), the function uses "mint" rather than "deposit".
     *      This naming choice intentionally aligns with the ERC4626 pattern for share-based operations.
     *      This function:
     *      1. Initiates redemption from external vault
     *      2. Records the redemption index
     *      3. Predeposits CooldownVaults for immediate liquidity
     *      4. Links predeposit ID to external redemption for future debt repayment
     */
    function premintCooldownVault(uint256 sharesNeeded)
        internal
        virtual
        returns (uint256 predepositId, uint256 preShares, uint256 lossShares)
    {
        // Request redeem from external vault
        bool redeemSuccess;
        uint256 redeemIndex;
        uint256 redeemUsdt;
        uint256 cooldownPeriod;
        {
            // When requesting based on shares, the actual amount received is often less than expected.
            // Receiving slightly more than this is not an issue, so we add a buffer of 10 amount.
            uint256 _needUsdt = cooldownVault.previewMint(sharesNeeded);
            uint256 _needSusdt = previewWithdraw(_needUsdt + AMOUNT_TO_SHARE_BUFFER);
            uint256 _susdtBalance = externalShareToken.balanceOf(address(this));
            uint256 shares = Math.min(_needSusdt, _susdtBalance);

            (redeemSuccess, redeemIndex, redeemUsdt, cooldownPeriod) = requestRedeem(shares);
            if (!redeemSuccess) return (0, 0, 0);
        }
        if (!emergencyExit && redeemIndex == 0) revert InvalidExternalRedeem();

        // Normal deposit without predeposit - for vaults with no cooldown period (like standard ERC4626)
        if (cooldownPeriod == 0) {
            externalUnderlyingToken.forceApprove(address(cooldownVault), redeemUsdt);
            preShares = cooldownVault.deposit(redeemUsdt, address(this));
            lossShares = sharesNeeded > preShares ? (sharesNeeded - preShares) : 0;
            return (0, preShares, lossShares);
        }

        // Predeposit cooldown tokens
        (predepositId, preShares) = cooldownVault.predeposit(redeemUsdt);
        if (predepositId == 0) return (0, 0, 0);
        lossShares = sharesNeeded > preShares ? (sharesNeeded - preShares) : 0;

        externalRedeemIndexes[predepositId] = redeemIndex;

        emit Preminted(predepositId, redeemUsdt, preShares, redeemIndex);
    }

    /**
     * @notice Repays debt for a predeposited CooldownVault by claiming from external vault
     * @param predepositId The ID of the predeposit to repay
     * @dev This function is called by CooldownVault contract when a user's cooldown expires.
     *      The CooldownVault contract should verify predepositDebtRetrievable before calling.
     *      Process:
     *      1. Claims the completed redemption from external vault
     *      2. Transfers claimed tokens to CooldownVault contract
     *      3. CooldownVault then transfers to the user
     */
    function repayPredepositDebt(uint256 predepositId)
        external
        virtual
        override
        onlyCooldownVault
        nonReentrant
        returns (uint256 repayAmount)
    {
        (,, uint256 predepositDebt,,, bool predepositRepaymentFinished) = cooldownVault.predepositRequests(predepositId);
        if (predepositRepaymentFinished) {
            revert InvalidDebtClaimState();
        }

        // Claim from external vault
        uint256 redeemIndex = externalRedeemIndexes[predepositId];
        (,,, uint256 redeemUnderlyingAmount, bool isDoneActually) = getRedeemDetail(redeemIndex);

        uint256 claimedAmount;
        if (isDoneActually) {
            // Already claimed externally (permissionless claim protocol).
            // Strategy must NOT auto-reinvest underlying tokens before debt repayment.
            claimedAmount = Math.min(externalUnderlyingToken.balanceOf(address(this)), redeemUnderlyingAmount);
        } else {
            bool claimSuccess;
            (claimSuccess, claimedAmount) = requestClaim(redeemIndex);
            if (!claimSuccess) revert CannotRepayByFailedClaim();
        }

        // Transfer to cooldown token
        if (claimedAmount < predepositDebt) {
            repayAmount = claimedAmount;
            remainingPredepositDebt += predepositDebt - claimedAmount;
        } else {
            repayAmount = predepositDebt;
        }

        externalUnderlyingToken.safeTransfer(msg.sender, repayAmount);
        emit PredepositDebtRepaid(predepositId, repayAmount);
    }

    /**
     * @notice Repays accumulated shortfall from previous predeposit debt repayments
     * @param repayAmount Amount of underlying tokens to repay toward the shortfall
     */
    function repayRemainingPredepositDebt(uint256 repayAmount) internal virtual {
        uint256 balance = externalUnderlyingToken.balanceOf(address(this));
        if (repayAmount == 0) revert ZeroAssets();
        if (repayAmount > remainingPredepositDebt) {
            revert RepayAmountExceedsShortfall(repayAmount, remainingPredepositDebt);
        }
        if (repayAmount > balance) {
            revert InsufficientUnderlyingBalance(repayAmount, balance);
        }

        externalUnderlyingToken.safeIncreaseAllowance(address(cooldownVault), repayAmount);
        cooldownVault.retrieveShortfall(repayAmount);
        remainingPredepositDebt -= repayAmount;
        emit RemainingPredepositDebtRepaid(repayAmount);
    }

    // ============================================
    // GOVERNANCE FUNCTIONS
    // ============================================

    /**
     * @notice Allows authorized users to initiate an emergency redemption from the external vault
     * @param shares Number of external vault shares to redeem
     * @return success True if the redemption request was successful
     * @return redeemId The unique identifier of the redemption request
     * @return redeemUnderlyingAmount The amount of underlying tokens to be received
     * @return cooldownPeriod The cooldown period in seconds
     * @dev Only callable by authorized addresses (governance/management)
     */
    function emergencyRedeem(uint256 shares)
        external
        virtual
        override
        onlyGovernance
        returns (bool success, uint256 redeemId, uint256 redeemUnderlyingAmount, uint256 cooldownPeriod)
    {
        return requestRedeem(shares);
    }

    /**
     * @notice Allows authorized users to claim a completed redemption from the external vault
     * @param redeemIndex The index/ID of the redemption to claim
     * @return success True if the claim was successful
     * @return claimedAmount The amount of underlying tokens claimed
     * @dev External function restricted to authorized addresses for emergency situations
     */
    function emergencyClaim(uint256 redeemIndex)
        external
        virtual
        override
        onlyGovernance
        returns (bool success, uint256 claimedAmount)
    {
        return requestClaim(redeemIndex);
    }

    /// @notice Governance function to manually repay shortfall. See repayRemainingPredepositDebt()
    function emergencyRepayRemainingPredepositDebt(uint256 repayAmount) external virtual onlyGovernance {
        repayRemainingPredepositDebt(repayAmount);
    }

    /**
     * @notice Submit arbitrary external calls for future execution (supports batch calls)
     * @dev Delegates to TimelockExecutionLib for logic. See library for full documentation.
     * @param targets Array of contract addresses to call (must be whitelisted, cannot be self)
     * @param calldatas Array of encoded function call data
     */
    function submitExecution(
        address[] calldata targets,
        bytes[] calldata calldatas
    )
        external
        virtual
        override
        onlyGovernance
    {
        bytes4[] memory allowedSelfCallSelectors = new bytes4[](2);
        (allowedSelfCallSelectors[0], allowedSelfCallSelectors[1]) =
            (this._setTimelockDelay.selector, this._setShortfallTolerance.selector);

        _timelockStorage.submitExecution(targets, calldatas, allowedSelfCallSelectors);
    }

    /**
     * @notice Execute the pending external calls
     * @dev Delegates to TimelockExecutionLib for logic. See library for full documentation.
     * @return success Whether all external calls succeeded
     * @return returnData Array of return data from each external call
     */
    function acceptExecution()
        external
        virtual
        override
        onlyGovernance
        returns (bool success, bytes memory returnData)
    {
        return _timelockStorage.acceptExecution();
    }

    /**
     * @notice Cancel the pending execution
     * @dev Delegates to TimelockExecutionLib for logic. See library for full documentation.
     */
    function cancelExecution() external virtual override onlyAuthorized {
        _timelockStorage.cancelExecution();
    }

    /**
     * @notice Add or remove a target address from the whitelist
     * @dev Delegates to TimelockExecutionLib for logic. See library for full documentation.
     * @param target The address to update in the whitelist
     * @param allowed Whether the target should be allowed
     */
    function setAllowedTarget(address target, bool allowed) external virtual override onlyGovernance {
        _timelockStorage.setAllowedTarget(target, allowed);
    }

    /**
     * @notice Updates the timelock delay for governance executions
     * @dev Only callable by the contract itself through submitExecution() -> acceptExecution() flow.
     * @param newDelay The new timelock delay in seconds (must be between MIN and MAX)
     */
    function _setTimelockDelay(uint256 newDelay) external virtual {
        if (msg.sender != address(this)) {
            revert TimelockExecutionLib.InvalidExecutionState("ONLY_SELF");
        }
        _timelockStorage.setTimelockDelay(newDelay);
    }

    /**
     * @notice Updates the shortfall tolerance
     * @dev Only callable by the contract itself through submitExecution() -> acceptExecution() flow.
     * @param newTolerance The new shortfall tolerance value
     */
    function _setShortfallTolerance(uint256 newTolerance) external virtual {
        if (msg.sender != address(this)) {
            revert TimelockExecutionLib.InvalidExecutionState("ONLY_SELF");
        }

        uint256 oldTolerance = shortfallTolerance;
        shortfallTolerance = newTolerance;

        emit ShortfallToleranceUpdated(oldTolerance, newTolerance);
    }

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

    /// @notice Returns the pending execution details
    function pendingExecution() external view returns (TimelockExecutionLib.PendingExecution memory) {
        return _timelockStorage.pendingExecution;
    }

    /// @notice Returns whether a target address is allowed for execution
    function allowedTargets(address target) external view returns (bool) {
        return _timelockStorage.allowedTargets[target];
    }

    /// @notice Returns the current timelock delay
    function timelockDelay() external view returns (uint256) {
        return _timelockStorage.timelockDelay;
    }

    /**
     * @notice Calculates the utilization rate of the strategy's assets
     * @return The utilization rate in basis points (10000 = 100%)
     * @dev Utilization rate = (totalAssets - pendingInvested) / totalAssets * 10000
     *      where pendingInvested is the amount of CooldownVaults waiting to be invested
     */
    function getUtilizationRate() external view virtual override returns (uint256) {
        uint256 totalAssets = estimatedTotalAssets();
        uint256 pendingInvested = want.balanceOf(address(this));

        if (totalAssets == 0) return 0;
        return Math.mulDiv(totalAssets - pendingInvested, BASIS_POINTS, totalAssets);
    }

    function getStrategyParams() internal view virtual returns (StrategyParams memory) {
        return vault.strategies(address(this));
    }

    /**
     * @notice Estimates total assets managed by this strategy (in CooldownVault shares)
     * @return Total estimated assets, accounting for shortfall
     */
    function estimatedTotalAssets() public view virtual override returns (uint256) {
        uint256 pendingInvested = want.balanceOf(address(this));

        uint256 previewInWant;
        {
            uint256 underlyingToPreview = externalUnderlyingToken.balanceOf(address(this));

            uint256 shareBalance = externalShareToken.balanceOf(address(this));
            underlyingToPreview += previewRedeem(shareBalance);

            previewInWant = cooldownVault.previewDeposit(underlyingToPreview);
        }

        uint256 totalAsset = pendingInvested + previewInWant;
        if (remainingPredepositDebt > totalAsset) {
            return 0;
        } else {
            return totalAsset - remainingPredepositDebt;
        }
    }

    /// @dev Migration guard: ensure this strategy has no outstanding predeposit debt or shortfall
    function _requireNoOutstandingDebt() internal view {
        if (cooldownVault.strategyShortfall(address(this)) > 0) revert OutstandingPredepositDebt();
        if (cooldownVault.strategyDebtOutstanding(address(this)) > 0) revert OutstandingPredepositDebt();
    }
}

File 8 of 36 : IMetaMorpho.sol
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.5.0;

import {IMorpho, Id, MarketParams} from "../../lib/morpho-blue/src/interfaces/IMorpho.sol";
import {IERC4626} from "../../lib/openzeppelin-contracts/contracts/interfaces/IERC4626.sol";
import {IERC20Permit} from "../../lib/openzeppelin-contracts/contracts/token/ERC20/extensions/IERC20Permit.sol";

import {MarketConfig, PendingUint192, PendingAddress} from "../libraries/PendingLib.sol";

struct MarketAllocation {
    /// @notice The market to allocate.
    MarketParams marketParams;
    /// @notice The amount of assets to allocate.
    uint256 assets;
}

interface IMulticall {
    function multicall(bytes[] calldata) external returns (bytes[] memory);
}

interface IOwnable {
    function owner() external view returns (address);
    function transferOwnership(address) external;
    function renounceOwnership() external;
    function acceptOwnership() external;
    function pendingOwner() external view returns (address);
}

/// @dev This interface is used for factorizing IMetaMorphoStaticTyping and IMetaMorpho.
/// @dev Consider using the IMetaMorpho interface instead of this one.
interface IMetaMorphoBase {
    /// @notice The address of the Morpho contract.
    function MORPHO() external view returns (IMorpho);
    function DECIMALS_OFFSET() external view returns (uint8);

    /// @notice The address of the curator.
    function curator() external view returns (address);

    /// @notice Stores whether an address is an allocator or not.
    function isAllocator(address target) external view returns (bool);

    /// @notice The current guardian. Can be set even without the timelock set.
    function guardian() external view returns (address);

    /// @notice The current fee.
    function fee() external view returns (uint96);

    /// @notice The fee recipient.
    function feeRecipient() external view returns (address);

    /// @notice The skim recipient.
    function skimRecipient() external view returns (address);

    /// @notice The current timelock.
    function timelock() external view returns (uint256);

    /// @dev Stores the order of markets on which liquidity is supplied upon deposit.
    /// @dev Can contain any market. A market is skipped as soon as its supply cap is reached.
    function supplyQueue(uint256) external view returns (Id);

    /// @notice Returns the length of the supply queue.
    function supplyQueueLength() external view returns (uint256);

    /// @dev Stores the order of markets from which liquidity is withdrawn upon withdrawal.
    /// @dev Always contain all non-zero cap markets as well as all markets on which the vault supplies liquidity,
    /// without duplicate.
    function withdrawQueue(uint256) external view returns (Id);

    /// @notice Returns the length of the withdraw queue.
    function withdrawQueueLength() external view returns (uint256);

    /// @notice Stores the total assets managed by this vault when the fee was last accrued.
    /// @dev May be greater than `totalAssets()` due to removal of markets with non-zero supply or socialized bad debt.
    /// This difference will decrease the fee accrued until one of the functions updating `lastTotalAssets` is
    /// triggered (deposit/mint/withdraw/redeem/setFee/setFeeRecipient).
    function lastTotalAssets() external view returns (uint256);

    /// @notice Submits a `newTimelock`.
    /// @dev Warning: Reverts if a timelock is already pending. Revoke the pending timelock to overwrite it.
    /// @dev In case the new timelock is higher than the current one, the timelock is set immediately.
    function submitTimelock(uint256 newTimelock) external;

    /// @notice Accepts the pending timelock.
    function acceptTimelock() external;

    /// @notice Revokes the pending timelock.
    /// @dev Does not revert if there is no pending timelock.
    function revokePendingTimelock() external;

    /// @notice Submits a `newSupplyCap` for the market defined by `marketParams`.
    /// @dev Warning: Reverts if a cap is already pending. Revoke the pending cap to overwrite it.
    /// @dev Warning: Reverts if a market removal is pending.
    /// @dev In case the new cap is lower than the current one, the cap is set immediately.
    function submitCap(MarketParams memory marketParams, uint256 newSupplyCap) external;

    /// @notice Accepts the pending cap of the market defined by `marketParams`.
    function acceptCap(MarketParams memory marketParams) external;

    /// @notice Revokes the pending cap of the market defined by `id`.
    /// @dev Does not revert if there is no pending cap.
    function revokePendingCap(Id id) external;

    /// @notice Submits a forced market removal from the vault, eventually losing all funds supplied to the market.
    /// @notice Funds can be recovered by enabling this market again and withdrawing from it (using `reallocate`),
    /// but funds will be distributed pro-rata to the shares at the time of withdrawal, not at the time of removal.
    /// @notice This forced removal is expected to be used as an emergency process in case a market constantly reverts.
    /// To softly remove a sane market, the curator role is expected to bundle a reallocation that empties the market
    /// first (using `reallocate`), followed by the removal of the market (using `updateWithdrawQueue`).
    /// @dev Warning: Removing a market with non-zero supply will instantly impact the vault's price per share.
    /// @dev Warning: Reverts for non-zero cap or if there is a pending cap. Successfully submitting a zero cap will
    /// prevent such reverts.
    function submitMarketRemoval(MarketParams memory marketParams) external;

    /// @notice Revokes the pending removal of the market defined by `id`.
    /// @dev Does not revert if there is no pending market removal.
    function revokePendingMarketRemoval(Id id) external;

    /// @notice Submits a `newGuardian`.
    /// @notice Warning: a malicious guardian could disrupt the vault's operation, and would have the power to revoke
    /// any pending guardian.
    /// @dev In case there is no guardian, the gardian is set immediately.
    /// @dev Warning: Submitting a gardian will overwrite the current pending gardian.
    function submitGuardian(address newGuardian) external;

    /// @notice Accepts the pending guardian.
    function acceptGuardian() external;

    /// @notice Revokes the pending guardian.
    function revokePendingGuardian() external;

    /// @notice Skims the vault `token` balance to `skimRecipient`.
    function skim(address) external;

    /// @notice Sets `newAllocator` as an allocator or not (`newIsAllocator`).
    function setIsAllocator(address newAllocator, bool newIsAllocator) external;

    /// @notice Sets `curator` to `newCurator`.
    function setCurator(address newCurator) external;

    /// @notice Sets the `fee` to `newFee`.
    function setFee(uint256 newFee) external;

    /// @notice Sets `feeRecipient` to `newFeeRecipient`.
    function setFeeRecipient(address newFeeRecipient) external;

    /// @notice Sets `skimRecipient` to `newSkimRecipient`.
    function setSkimRecipient(address newSkimRecipient) external;

    /// @notice Sets `supplyQueue` to `newSupplyQueue`.
    /// @param newSupplyQueue is an array of enabled markets, and can contain duplicate markets, but it would only
    /// increase the cost of depositing to the vault.
    function setSupplyQueue(Id[] calldata newSupplyQueue) external;

    /// @notice Updates the withdraw queue. Some markets can be removed, but no market can be added.
    /// @notice Removing a market requires the vault to have 0 supply on it, or to have previously submitted a removal
    /// for this market (with the function `submitMarketRemoval`).
    /// @notice Warning: Anyone can supply on behalf of the vault so the call to `updateWithdrawQueue` that expects a
    /// market to be empty can be griefed by a front-run. To circumvent this, the allocator can simply bundle a
    /// reallocation that withdraws max from this market with a call to `updateWithdrawQueue`.
    /// @dev Warning: Removing a market with supply will decrease the fee accrued until one of the functions updating
    /// `lastTotalAssets` is triggered (deposit/mint/withdraw/redeem/setFee/setFeeRecipient).
    /// @dev Warning: `updateWithdrawQueue` is not idempotent. Submitting twice the same tx will change the queue twice.
    /// @param indexes The indexes of each market in the previous withdraw queue, in the new withdraw queue's order.
    function updateWithdrawQueue(uint256[] calldata indexes) external;

    /// @notice Reallocates the vault's liquidity so as to reach a given allocation of assets on each given market.
    /// @notice The allocator can withdraw from any market, even if it's not in the withdraw queue, as long as the loan
    /// token of the market is the same as the vault's asset.
    /// @dev The behavior of the reallocation can be altered by state changes, including:
    /// - Deposits on the vault that supplies to markets that are expected to be supplied to during reallocation.
    /// - Withdrawals from the vault that withdraws from markets that are expected to be withdrawn from during
    /// reallocation.
    /// - Donations to the vault on markets that are expected to be supplied to during reallocation.
    /// - Withdrawals from markets that are expected to be withdrawn from during reallocation.
    /// @dev Sender is expected to pass `assets = type(uint256).max` with the last MarketAllocation of `allocations` to
    /// supply all the remaining withdrawn liquidity, which would ensure that `totalWithdrawn` = `totalSupplied`.
    function reallocate(MarketAllocation[] calldata allocations) external;
}

/// @dev This interface is inherited by MetaMorpho so that function signatures are checked by the compiler.
/// @dev Consider using the IMetaMorpho interface instead of this one.
interface IMetaMorphoStaticTyping is IMetaMorphoBase {
    /// @notice Returns the current configuration of each market.
    function config(Id) external view returns (uint184 cap, bool enabled, uint64 removableAt);

    /// @notice Returns the pending guardian.
    function pendingGuardian() external view returns (address guardian, uint64 validAt);

    /// @notice Returns the pending cap for each market.
    function pendingCap(Id) external view returns (uint192 value, uint64 validAt);

    /// @notice Returns the pending timelock.
    function pendingTimelock() external view returns (uint192 value, uint64 validAt);
}

/// @title IMetaMorpho
/// @author Morpho Labs
/// @custom:contact security@morpho.org
/// @dev Use this interface for MetaMorpho to have access to all the functions with the appropriate function signatures.
interface IMetaMorpho is IMetaMorphoBase, IERC4626, IERC20Permit, IOwnable, IMulticall {
    /// @notice Returns the current configuration of each market.
    function config(Id) external view returns (MarketConfig memory);

    /// @notice Returns the pending guardian.
    function pendingGuardian() external view returns (PendingAddress memory);

    /// @notice Returns the pending cap for each market.
    function pendingCap(Id) external view returns (PendingUint192 memory);

    /// @notice Returns the pending timelock.
    function pendingTimelock() external view returns (PendingUint192 memory);
}

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

pragma solidity ^0.8.20;

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

/**
 * @title IERC1363
 * @dev Interface of the ERC-1363 standard as defined in the https://eips.ethereum.org/EIPS/eip-1363[ERC-1363].
 *
 * Defines an extension interface for ERC-20 tokens that supports executing code on a recipient contract
 * after `transfer` or `transferFrom`, or code on a spender contract after `approve`, in a single transaction.
 */
interface IERC1363 is IERC20, IERC165 {
    /*
     * Note: the ERC-165 identifier for this interface is 0xb0202a11.
     * 0xb0202a11 ===
     *   bytes4(keccak256('transferAndCall(address,uint256)')) ^
     *   bytes4(keccak256('transferAndCall(address,uint256,bytes)')) ^
     *   bytes4(keccak256('transferFromAndCall(address,address,uint256)')) ^
     *   bytes4(keccak256('transferFromAndCall(address,address,uint256,bytes)')) ^
     *   bytes4(keccak256('approveAndCall(address,uint256)')) ^
     *   bytes4(keccak256('approveAndCall(address,uint256,bytes)'))
     */

    /**
     * @dev Moves a `value` amount of tokens from the caller's account to `to`
     * and then calls {IERC1363Receiver-onTransferReceived} on `to`.
     * @param to The address which you want to transfer to.
     * @param value The amount of tokens to be transferred.
     * @return A boolean value indicating whether the operation succeeded unless throwing.
     */
    function transferAndCall(address to, uint256 value) external returns (bool);

    /**
     * @dev Moves a `value` amount of tokens from the caller's account to `to`
     * and then calls {IERC1363Receiver-onTransferReceived} on `to`.
     * @param to The address which you want to transfer to.
     * @param value The amount of tokens to be transferred.
     * @param data Additional data with no specified format, sent in call to `to`.
     * @return A boolean value indicating whether the operation succeeded unless throwing.
     */
    function transferAndCall(address to, uint256 value, bytes calldata data) external returns (bool);

    /**
     * @dev Moves a `value` amount of tokens from `from` to `to` using the allowance mechanism
     * and then calls {IERC1363Receiver-onTransferReceived} on `to`.
     * @param from The address which you want to send tokens from.
     * @param to The address which you want to transfer to.
     * @param value The amount of tokens to be transferred.
     * @return A boolean value indicating whether the operation succeeded unless throwing.
     */
    function transferFromAndCall(address from, address to, uint256 value) external returns (bool);

    /**
     * @dev Moves a `value` amount of tokens from `from` to `to` using the allowance mechanism
     * and then calls {IERC1363Receiver-onTransferReceived} on `to`.
     * @param from The address which you want to send tokens from.
     * @param to The address which you want to transfer to.
     * @param value The amount of tokens to be transferred.
     * @param data Additional data with no specified format, sent in call to `to`.
     * @return A boolean value indicating whether the operation succeeded unless throwing.
     */
    function transferFromAndCall(address from, address to, uint256 value, bytes calldata data) external returns (bool);

    /**
     * @dev Sets a `value` amount of tokens as the allowance of `spender` over the
     * caller's tokens and then calls {IERC1363Spender-onApprovalReceived} on `spender`.
     * @param spender The address which will spend the funds.
     * @param value The amount of tokens to be spent.
     * @return A boolean value indicating whether the operation succeeded unless throwing.
     */
    function approveAndCall(address spender, uint256 value) external returns (bool);

    /**
     * @dev Sets a `value` amount of tokens as the allowance of `spender` over the
     * caller's tokens and then calls {IERC1363Spender-onApprovalReceived} on `spender`.
     * @param spender The address which will spend the funds.
     * @param value The amount of tokens to be spent.
     * @param data Additional data with no specified format, sent in call to `spender`.
     * @return A boolean value indicating whether the operation succeeded unless throwing.
     */
    function approveAndCall(address spender, uint256 value, bytes calldata data) external returns (bool);
}

// 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 11 of 36 : 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))
        }
    }
}

File 12 of 36 : 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: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (utils/ReentrancyGuard.sol)

pragma solidity ^0.8.20;

/**
 * @dev Contract module that helps prevent reentrant calls to a function.
 *
 * Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier
 * available, which can be applied to functions to make sure there are no nested
 * (reentrant) calls to them.
 *
 * Note that because there is a single `nonReentrant` guard, functions marked as
 * `nonReentrant` may not call one another. This can be worked around by making
 * those functions `private`, and then adding `external` `nonReentrant` entry
 * points to them.
 *
 * TIP: If EIP-1153 (transient storage) is available on the chain you're deploying at,
 * consider using {ReentrancyGuardTransient} instead.
 *
 * TIP: If you would like to learn more about reentrancy and alternative ways
 * to protect against it, check out our blog post
 * https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul].
 */
abstract contract ReentrancyGuard {
    // Booleans are more expensive than uint256 or any type that takes up a full
    // word because each write operation emits an extra SLOAD to first read the
    // slot's contents, replace the bits taken up by the boolean, and then write
    // back. This is the compiler's defense against contract upgrades and
    // pointer aliasing, and it cannot be disabled.

    // The values being non-zero value makes deployment a bit more expensive,
    // but in exchange the refund on every call to nonReentrant will be lower in
    // amount. Since refunds are capped to a percentage of the total
    // transaction's gas, it is best to keep them low in cases like this one, to
    // increase the likelihood of the full refund coming into effect.
    uint256 private constant NOT_ENTERED = 1;
    uint256 private constant ENTERED = 2;

    uint256 private _status;

    /**
     * @dev Unauthorized reentrant call.
     */
    error ReentrancyGuardReentrantCall();

    constructor() {
        _status = NOT_ENTERED;
    }

    /**
     * @dev Prevents a contract from calling itself, directly or indirectly.
     * Calling a `nonReentrant` function from another `nonReentrant`
     * function is not supported. It is possible to prevent this from happening
     * by making the `nonReentrant` function external, and making it call a
     * `private` function that does the actual work.
     */
    modifier nonReentrant() {
        _nonReentrantBefore();
        _;
        _nonReentrantAfter();
    }

    function _nonReentrantBefore() private {
        // On the first call to nonReentrant, _status will be NOT_ENTERED
        if (_status == ENTERED) {
            revert ReentrancyGuardReentrantCall();
        }

        // Any calls to nonReentrant after this point will fail
        _status = ENTERED;
    }

    function _nonReentrantAfter() private {
        // By storing the original value once again, a refund is triggered (see
        // https://eips.ethereum.org/EIPS/eip-2200)
        _status = NOT_ENTERED;
    }

    /**
     * @dev Returns true if the reentrancy guard is currently set to "entered", which indicates there is a
     * `nonReentrant` function in the call stack.
     */
    function _reentrancyGuardEntered() internal view returns (bool) {
        return _status == ENTERED;
    }
}

File 14 of 36 : ICooldownVault.sol
// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.8.29 <0.9.0;

import { IERC4626Upgradeable } from "@openzeppelin/contracts-upgradeable/interfaces/IERC4626Upgradeable.sol";
import { IHealthCheck } from "@superearn/interface/IHealthCheck.sol";

interface ICooldownVault is IERC4626Upgradeable {
    struct RedeemRequest {
        address receiver;
        uint256 assets;
        uint256 cooldownRequestedTime;
        uint256 cooldownPeriod; // Stored cooldown period at request time
        // Note: claimableAt could be derived as (cooldownRequestedTime + cooldownPeriod),
        // but both fields are kept separately for frontend logging and diagnostics.
        bool claimed;
    }

    struct PredepositRequest {
        address strategy;
        uint256 shares;
        uint256 debtAssets;
        uint256 cooldownRequestedTime;
        uint256 cooldownPeriod;
        bool claimed;
    }

    event RedeemRequested(
        address indexed caller,
        address indexed receiver,
        uint256 indexed requestId,
        uint256 assets,
        uint256 shares,
        uint256 requestedTime
    );
    event Claimed(address indexed caller, uint256 indexed requestId, uint256 assets, uint256 claimable);
    event InstantRedemption(address indexed caller, uint256 shares, uint256 assets);
    event CooldownPeriodUpdated(uint256 newCooldownPeriod);
    event CooldownPeriodSubmitted(uint256 newCooldownPeriod);
    event GovernanceTransferred(address indexed previousGovernance, address indexed newGovernance);
    event GovernanceTransferSubmitted(address indexed newGovernance);
    event UpdateManagement(address indexed newManagement);
    event Recovered(address indexed to, uint256 assets);
    event RecoverClaimLoss(address indexed to, uint256 shares);
    event StrategyAdded(address indexed strategy);
    event StrategyRemoved(address indexed strategy);
    event PredepositRequested(
        address indexed strategy,
        uint256 indexed predepositId,
        uint256 shares,
        uint256 debtAssets,
        uint256 cooldownPeriod
    );
    event DebtRetrieved(
        address indexed strategy, uint256 indexed predepositId, uint256 debtAssets, uint256 debtPayment
    );
    event MaxLossThresholdUpdated(uint256 oldThreshold, uint256 newThreshold);
    event StrangeCooldownPeriod(uint256 maxCooldownPeriod, uint256 newCooldownPeriod);
    event SetDoHealthCheck(bool doHealthCheck);
    event HealthCheckUpdated(address indexed oldHealthCheck, address indexed newHealthCheck);
    event StrategyDebtShortfall(address indexed strategy, uint256 indexed predepositId, uint256 shortfall);
    event StrategyDebtRepaid(address indexed strategy, uint256 repay, uint256 remainingShortfall);
    event AuthorizedAddressAdded(address indexed authorizedAddress);
    event AuthorizedAddressRemoved(address indexed authorizedAddress);

    // ERC4626 Core Functions (inherited from IERC4626)
    // function asset() external view returns (address);
    // function totalAssets() external view returns (uint256);
    // function convertToShares(uint256 assets) external view returns (uint256);
    // function convertToAssets(uint256 shares) external view returns (uint256);
    // function maxDeposit(address receiver) external view returns (uint256);
    // function previewDeposit(uint256 assets) external view returns (uint256);
    // function deposit(uint256 assets, address receiver) external returns (uint256);
    // function maxMint(address receiver) external view returns (uint256);
    // function previewMint(uint256 shares) external view returns (uint256);
    // function mint(uint256 shares, address receiver) external returns (uint256);
    // function maxWithdraw(address owner) external view returns (uint256);
    // function previewWithdraw(uint256 assets) external view returns (uint256);
    // function withdsraw(uint256 assets, address receiver, address owner) external returns (uint256);
    // function maxRedeem(address owner) external view returns (uint256);
    // function previewRedeem(uint256 shares) external view returns (uint256);
    // function redeem(uint256 shares, address receiver, address owner) external returns (uint256);

    // ERC20 Functions (inherited from IERC20 through IERC4626)
    // function totalSupply() external view returns (uint256);
    // function balanceOf(address account) external view returns (uint256);
    // function transfer(address to, uint256 amount) external returns (bool);
    // function allowance(address owner, address spender) external view returns (uint256);
    // function approve(address spender, uint256 amount) external returns (bool);
    // function transferFrom(address from, address to, uint256 amount) external returns (bool);

    // ERC20Metadata Functions (inherited from IERC20Metadata through IERC4626)
    // function name() external view returns (string memory);
    // function symbol() external view returns (string memory);
    // function decimals() external view returns (uint8);

    // CooldownVault specific functions
    function claim(uint256 requestId, uint256 maxLossBps) external returns (uint256 claimable);
    function instantRedeem(uint256 shares) external returns (uint256 assets);
    function predeposit(uint256 assets) external returns (uint256 predepositId, uint256 shares);
    function retrieveDebt(uint256 predepositId) external;
    function retrieveShortfall(uint256 assets) external;

    // Governance and management
    function cooldownPeriod() external view returns (uint256 period);
    function governance() external view returns (address governanceAddress);
    function pendingGovernance() external view returns (address pendingAddress);
    function management() external view returns (address managementAddress);

    function doHealthCheck() external view returns (bool);
    function healthCheck() external view returns (IHealthCheck healthCheckAddress);
    function submitGovernanceTransfer(address newGovernance) external;
    function acceptGovernanceTransfer() external;
    function setManagement(address newManagement) external;

    function recover() external returns (uint256 assets);
    function recoverClaimLoss() external returns (uint256 shares);
    function submitCooldownPeriod(uint256 _cooldownPeriod) external;
    function acceptCooldownPeriod() external;
    function pause() external;
    function unpause() external;
    function setMaxLossThresholdBps(uint256 _maxLossThresholdBps) external;
    function setDoHealthCheck(bool newDoHealthCheck) external;
    function setHealthCheck(address newHealthCheck) external;

    // Strategy management
    function addStrategy(address strategy) external;
    function removeStrategy(address strategy) external;
    function isStrategy(address strategy) external view returns (bool result);
    function getStrategies() external view returns (address[] memory strategies);

    // Authorized addresses
    function getAuthorizedAddresses() external view returns (address[] memory authorizedAddresses);
    function addAuthorizedAddress(address authorizedAddress) external;
    function removeAuthorizedAddress(address authorizedAddress) external;

    // View functions - State variables
    function lastRequestId() external view returns (uint256 requestId);
    function lastPredepositId() external view returns (uint256 predepositId);
    function pendingCooldownPeriod() external view returns (uint256 period);
    function hasPendingCooldownPeriod() external view returns (bool hasPending);
    function totalDebt() external view returns (uint256 debt);
    function totalLockedAssets() external view returns (uint256 lockedAssets);
    function maxLossThresholdBps() external view returns (uint256 thresholdBps);

    // View functions - Computed values
    function assetBalance() external view returns (uint256 balance);
    function idleBalance() external view returns (uint256 idle);
    function strategyShortfall(address strategy) external view returns (uint256 outstanding);
    function strategyDebtOutstanding(address strategy) external view returns (uint256 outstanding);
    function pendingAssets(address receiver) external view returns (uint256 pending);
    function maxInstantRedeem(address caller) external view returns (uint256 maxShares);

    // View functions - Request tracking
    function getRedeemRequest(uint256 requestId) external view returns (RedeemRequest memory request);
    function getPredepositRequest(uint256 predepositId) external view returns (PredepositRequest memory request);
    function getUnclaimedRedeemRequestIds() external view returns (uint256[] memory requestIds);
    function getUnclaimedRedeemRequestIds(
        uint256 limit,
        uint256 skip
    )
        external
        view
        returns (uint256[] memory requestIds);
    function getUnclaimedPredepositRequestIds() external view returns (uint256[] memory requestIds);
    function getUnclaimedPredepositRequestIds(
        uint256 limit,
        uint256 skip
    )
        external
        view
        returns (uint256[] memory requestIds);
    function redeemRequests(uint256 requestId)
        external
        view
        returns (address receiver, uint256 assets, uint256 cooldownRequestedTime, uint256 cooldownPeriod, bool claimed);
    function predepositRequests(uint256 predepositId)
        external
        view
        returns (
            address strategy,
            uint256 shares,
            uint256 debtAssets,
            uint256 cooldownRequestedTime,
            uint256 cooldownPeriod,
            bool claimed
        );
}

File 15 of 36 : IVault.sol
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.29 <0.9.0;

import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";

// Strategy Parameters Struct
struct StrategyParams {
    uint256 performanceFee; // Strategist's fee (basis points)
    uint256 activation; // Activation block.timestamp
    uint256 debtRatio; // Maximum borrow amount (in BPS of total assets)
    uint256 minDebtPerHarvest; // Lower limit on the increase of debt since last harvest
    uint256 maxDebtPerHarvest; // Upper limit on the increase of debt since last harvest
    uint256 lastReport; // block.timestamp of the last time a report occurred
    uint256 totalDebt; // Total outstanding debt that Strategy has
    uint256 totalGain; // Total returns that Strategy has realized for Vault
    uint256 totalLoss; // Total losses that Strategy has realized for Vault
}

/**
 * @title IVault
 * @notice Interface for Yearn Vault contract
 * @dev This interface corresponds to Vault.vy version 0.3.3
 */
interface IVault is IERC20 {
    // Vault Events
    event Deposit(address indexed recipient, uint256 shares, uint256 amount);
    event Withdraw(address indexed recipient, uint256 shares, uint256 amount);
    event Sweep(address indexed token, uint256 amount);
    event LockedProfitDegradationUpdated(uint256 value);

    // Strategy Events
    event StrategyAdded(
        address indexed strategy,
        uint256 debtRatio,
        uint256 minDebtPerHarvest,
        uint256 maxDebtPerHarvest,
        uint256 performanceFee
    );
    event StrategyReported(
        address indexed strategy,
        uint256 gain,
        uint256 loss,
        uint256 debtPaid,
        uint256 totalGain,
        uint256 totalLoss,
        uint256 totalDebt,
        uint256 debtAdded,
        uint256 debtRatio
    );
    event FeeReport(uint256 management_fee, uint256 performance_fee, uint256 strategist_fee, uint256 duration);
    event WithdrawFromStrategy(address indexed strategy, uint256 totalDebt, uint256 loss);
    event StrategyUpdateDebtRatio(address indexed strategy, uint256 debtRatio);
    event StrategyUpdateMinDebtPerHarvest(address indexed strategy, uint256 minDebtPerHarvest);
    event StrategyUpdateMaxDebtPerHarvest(address indexed strategy, uint256 maxDebtPerHarvest);
    event StrategyUpdatePerformanceFee(address indexed strategy, uint256 performanceFee);
    event StrategyMigrated(address indexed oldVersion, address indexed newVersion);
    event StrategyRevoked(address indexed strategy);
    event StrategyRemovedFromQueue(address indexed strategy);
    event StrategyAddedToQueue(address indexed strategy);

    // Governance Events
    event UpdateGovernance(address governance);
    event UpdateManagement(address management);
    event UpdateRewards(address rewards);
    event UpdateDepositLimit(uint256 depositLimit);
    event UpdatePerformanceFee(uint256 performanceFee);
    event UpdateManagementFee(uint256 managementFee);
    event UpdateGuardian(address guardian);
    event EmergencyShutdown(bool active);
    event UpdateWithdrawalQueue(address[20] queue);
    event NewPendingGovernance(address indexed pendingGovernance);

    // ERC20 View functions for public variables
    function name() external view returns (string memory);
    function symbol() external view returns (string memory);
    function decimals() external view returns (uint256);
    function balanceOf(address) external view returns (uint256);
    function allowance(address, address) external view returns (uint256);
    function totalSupply() external view returns (uint256);

    // Vault View functions for public variables
    function token() external view returns (address);
    function governance() external view returns (address);
    function management() external view returns (address);
    function guardian() external view returns (address);
    function strategies(address) external view returns (StrategyParams memory);
    function withdrawalQueue(uint256) external view returns (address);
    function emergencyShutdown() external view returns (bool);
    function depositLimit() external view returns (uint256);
    function debtRatio() external view returns (uint256);
    function totalIdle() external view returns (uint256);
    function totalDebt() external view returns (uint256);
    function lastReport() external view returns (uint256);
    function activation() external view returns (uint256);
    function lockedProfit() external view returns (uint256);
    function lockedProfitDegradation() external view returns (uint256);
    function rewards() external view returns (address);
    function managementFee() external view returns (uint256);
    function performanceFee() external view returns (uint256);
    function nonces(address) external view returns (uint256);

    // Initialization
    function initialize(
        address token,
        address governance,
        address rewards,
        string memory nameOverride,
        string memory symbolOverride,
        address guardian,
        address management
    )
        external;

    // Core functions
    function apiVersion() external pure returns (string memory);
    function DOMAIN_SEPARATOR() external view returns (bytes32);

    // ERC20 functions
    function transfer(address receiver, uint256 amount) external returns (bool);
    function transferFrom(address sender, address receiver, uint256 amount) external returns (bool);
    function approve(address spender, uint256 amount) external returns (bool);
    function increaseAllowance(address spender, uint256 amount) external returns (bool);
    function decreaseAllowance(address spender, uint256 amount) external returns (bool);
    function permit(
        address owner,
        address spender,
        uint256 amount,
        uint256 expiry,
        bytes memory signature
    )
        external
        returns (bool);

    // Vault core functions
    function totalAssets() external view returns (uint256);

    // NOTE: Vyper produces multiple signatures for a given function with "default" args
    function deposit() external returns (uint256);
    function deposit(uint256 amount) external returns (uint256);
    function deposit(uint256 _amount, address recipient) external returns (uint256);

    // NOTE: Vyper produces multiple signatures for a given function with "default" args
    function withdraw() external returns (uint256);
    function withdraw(uint256 maxShares) external returns (uint256);
    function withdraw(uint256 maxShares, address recipient) external returns (uint256);
    function withdraw(uint256 maxShares, address recipient, uint256 maxLossBps) external returns (uint256);

    function pricePerShare() external view returns (uint256);
    function maxAvailableShares() external view returns (uint256);
    function availableDepositLimit() external view returns (uint256);

    // Strategy functions
    function debtOutstanding() external view returns (uint256);
    function debtOutstanding(address strategy) external view returns (uint256);

    function creditAvailable() external view returns (uint256);
    function creditAvailable(address strategy) external view returns (uint256);

    function expectedReturn() external view returns (uint256);
    function expectedReturn(address strategy) external view returns (uint256);

    function report(uint256 gain, uint256 loss, uint256 _debtPayment) external returns (uint256);

    // Governance functions
    function setName(string memory name) external;
    function setSymbol(string memory symbol) external;
    function setGovernance(address governance) external;
    function acceptGovernance() external;
    function setManagement(address management) external;
    function setRewards(address rewards) external;
    function setLockedProfitDegradation(uint256 degradation) external;
    function setDepositLimit(uint256 limit) external;
    function setPerformanceFee(uint256 fee) external;
    function setManagementFee(uint256 fee) external;
    function setGuardian(address guardian) external;
    function setEmergencyShutdown(bool active) external;
    function setWithdrawalQueue(address[20] memory queue) external;

    // Strategy management functions
    function addStrategy(
        address strategy,
        uint256 debtRatio,
        uint256 minDebtPerHarvest,
        uint256 maxDebtPerHarvest,
        uint256 performanceFee
    )
        external;
    function updateStrategyDebtRatio(address strategy, uint256 debtRatio) external;
    function updateStrategyMinDebtPerHarvest(address strategy, uint256 minDebtPerHarvest) external;
    function updateStrategyMaxDebtPerHarvest(address strategy, uint256 maxDebtPerHarvest) external;
    function updateStrategyPerformanceFee(address strategy, uint256 performanceFee) external;
    function migrateStrategy(address oldVersion, address newVersion) external;

    function revokeStrategy() external;
    function revokeStrategy(address strategy) external;

    function addStrategyToQueue(address strategy) external;
    function removeStrategyFromQueue(address strategy) external;

    // Utility functions
    function sweep(address token, uint256 amount) external;
}

File 16 of 36 : BaseStrategy.sol
// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.8.29 <0.9.0;

import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import { SafeERC20 } from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import { IBaseFeeOracle } from "@superearn/interface/IBaseFeeOracle.sol";
import { IStrategy } from "@superearn/interface/IStrategy.sol";
import { IVault, StrategyParams } from "@superearn/interface/IVault.sol";
import { IHealthCheck } from "@superearn/interface/IHealthCheck.sol";

/**
 * @title Yearn Base Strategy
 * @author yearn.finance
 * @notice
 *  BaseStrategy implements all of the required functionality to interoperate
 *  closely with the Vault contract. This contract should be inherited and the
 *  abstract methods implemented to adapt the Strategy to the particular needs
 *  it has to create a return.
 *
 *  Of special interest is the relationship between `harvest()` and
 *  `vault.report()'. `harvest()` may be called simply because enough time has
 *  elapsed since the last report, and not because any funds need to be moved
 *  or positions adjusted. This is critical so that the Vault may maintain an
 *  accurate picture of the Strategy's performance. See  `vault.report()`,
 *  `harvest()`, and `harvestTrigger()` for further details.
 */
abstract contract BaseStrategy is IStrategy {
    using SafeERC20 for IERC20;

    error StrategyAlreadyInitialized();
    error FailedHealthCheck();
    error NotVault();
    error NotOriginal();
    error SweepTokenIsWant();
    error SweepTokenIsVaultShares();
    error TokenProtected();

    string public metadataURI;

    // health checks
    bool public doHealthCheck;
    address public healthCheck;

    /**
     * @notice
     *  Used to track which version of `StrategyAPI` this Strategy
     *  implements.
     * @dev The Strategy's version must match the Vault's `API_VERSION`.
     * @return A string which holds the current API version of this contract.
     */
    function apiVersion() public pure returns (string memory) {
        return "0.4.6";
    }

    /**
     * @notice This Strategy's name.
     * @dev
     *  You can use this field to manage the "version" of this Strategy, e.g.
     *  `StrategySomethingOrOtherV1`. However, "API Version" is managed by
     *  `apiVersion()` function above.
     * @return This Strategy's name.
     */
    function name() external view virtual returns (string memory);

    /**
     * @notice
     *  The amount (priced in want) of the total assets managed by this strategy should not count
     *  towards Yearn's TVL calculations.
     * @dev
     *  You can override this field to set it to a non-zero value if some of the assets of this
     *  Strategy is somehow delegated inside another part of of Yearn's ecosystem e.g. another Vault.
     *  Note that this value must be strictly less than or equal to the amount provided by
     *  `estimatedTotalAssets()` below, as the TVL calc will be total assets minus delegated assets.
     *  Also note that this value is used to determine the total assets under management by this
     *  strategy, for the purposes of computing the management fee in `Vault`
     * @return
     *  The amount of assets this strategy manages that should not be included in Yearn's Total Value
     *  Locked (TVL) calculation across it's ecosystem.
     */
    function delegatedAssets() external view virtual returns (uint256) {
        return 0;
    }

    IVault public vault;
    address public strategist;
    address public rewards;
    address public keeper;

    IERC20 public want;

    // The minimum number of seconds between harvest calls. See
    // `setMinReportDelay()` for more details.
    uint256 public minReportDelay;

    // The maximum number of seconds between harvest calls. See
    // `setMaxReportDelay()` for more details.
    uint256 public maxReportDelay;

    // See note on `setEmergencyExit()`.
    bool public emergencyExit;

    // See note on `isBaseFeeOracleAcceptable()`.
    address public baseFeeOracle;

    // See note on `setCreditThreshold()`
    uint256 public creditThreshold;

    // See note on `setForceHarvestTriggerOnce`
    bool public forceHarvestTriggerOnce;

    // modifiers
    modifier onlyAuthorized() {
        _onlyAuthorized();
        _;
    }

    modifier onlyEmergencyAuthorized() {
        _onlyEmergencyAuthorized();
        _;
    }

    modifier onlyStrategist() {
        _onlyStrategist();
        _;
    }

    modifier onlyGovernance() {
        _onlyGovernance();
        _;
    }

    modifier onlyRewarder() {
        _onlyRewarder();
        _;
    }

    modifier onlyKeepers() {
        _onlyKeepers();
        _;
    }

    modifier onlyVaultManagers() {
        _onlyVaultManagers();
        _;
    }

    function _onlyAuthorized() internal {
        require(msg.sender == strategist || msg.sender == governance());
    }

    function _onlyEmergencyAuthorized() internal {
        require(
            msg.sender == strategist || msg.sender == governance() || msg.sender == vault.guardian()
                || msg.sender == vault.management()
        );
    }

    function _onlyStrategist() internal {
        require(msg.sender == strategist);
    }

    function _onlyGovernance() internal {
        require(msg.sender == governance());
    }

    function _onlyRewarder() internal {
        require(msg.sender == governance() || msg.sender == strategist);
    }

    function _onlyKeepers() internal {
        require(
            msg.sender == keeper || msg.sender == strategist || msg.sender == governance()
                || msg.sender == vault.guardian() || msg.sender == vault.management()
        );
    }

    function _onlyVaultManagers() internal {
        require(msg.sender == vault.management() || msg.sender == governance());
    }

    constructor(address _vault) {
        _initialize(_vault, msg.sender, msg.sender, msg.sender);
    }

    /**
     * @notice
     *  Initializes the Strategy, this is called only once, when the
     *  contract is deployed.
     * @dev `_vault` should implement `IVault`.
     * @param _vault The address of the Vault responsible for this Strategy.
     * @param _strategist The address to assign as `strategist`.
     * The strategist is able to change the reward address
     * @param _rewards  The address to use for pulling rewards.
     * @param _keeper The adddress of the _keeper. _keeper
     * can harvest and tend a strategy.
     */
    function _initialize(address _vault, address _strategist, address _rewards, address _keeper) internal {
        if (address(want) != address(0)) revert StrategyAlreadyInitialized();

        vault = IVault(_vault);
        want = IERC20(vault.token());
        want.forceApprove(_vault, type(uint256).max); // Give Vault unlimited access (might save gas)
        strategist = _strategist;
        rewards = _rewards;
        keeper = _keeper;

        // initialize variables
        maxReportDelay = 30 days;
        creditThreshold = 1_000_000 * 10 ** vault.decimals(); // set this high by default so we don't get tons of false
            // triggers if not changed

        vault.approve(rewards, type(uint256).max); // Allow rewards to be pulled
    }

    function setHealthCheck(address _healthCheck) external onlyVaultManagers {
        emit SetHealthCheck(_healthCheck);
        healthCheck = _healthCheck;
    }

    function setDoHealthCheck(bool _doHealthCheck) external onlyVaultManagers {
        emit SetDoHealthCheck(_doHealthCheck);
        doHealthCheck = _doHealthCheck;
    }

    /**
     * @notice
     *  Used to change `strategist`.
     *
     *  This may only be called by governance or the existing strategist.
     * @param _strategist The new address to assign as `strategist`.
     */
    function setStrategist(address _strategist) external onlyAuthorized {
        require(_strategist != address(0));
        strategist = _strategist;
        emit UpdatedStrategist(_strategist);
    }

    /**
     * @notice
     *  Used to change `keeper`.
     *
     *  `keeper` is the only address that may call `tend()` or `harvest()`,
     *  other than `governance()` or `strategist`. However, unlike
     *  `governance()` or `strategist`, `keeper` may *only* call `tend()`
     *  and `harvest()`, and no other authorized functions, following the
     *  principle of least privilege.
     *
     *  This may only be called by governance or the strategist.
     * @param _keeper The new address to assign as `keeper`.
     */
    function setKeeper(address _keeper) external onlyAuthorized {
        require(_keeper != address(0));
        keeper = _keeper;
        emit UpdatedKeeper(_keeper);
    }

    /**
     * @notice
     *  Used to change `rewards`. EOA or smart contract which has the permission
     *  to pull rewards from the vault.
     *
     *  This may only be called by the strategist.
     * @param _rewards The address to use for pulling rewards.
     */
    function setRewards(address _rewards) external onlyRewarder {
        require(_rewards != address(0));
        vault.approve(rewards, 0);
        rewards = _rewards;
        vault.approve(rewards, type(uint256).max);
        emit UpdatedRewards(_rewards);
    }

    /**
     * @notice
     *  Used to change `minReportDelay`. `minReportDelay` is the minimum number
     *  of blocks that should pass for `harvest()` to be called.
     *
     *  For external keepers (such as the Keep3r network), this is the minimum
     *  time between jobs to wait. (see `harvestTrigger()`
     *  for more details.)
     *
     *  This may only be called by governance or the strategist.
     * @param _delay The minimum number of seconds to wait between harvests.
     */
    function setMinReportDelay(uint256 _delay) external onlyAuthorized {
        minReportDelay = _delay;
        emit UpdatedMinReportDelay(_delay);
    }

    /**
     * @notice
     *  Used to change `maxReportDelay`. `maxReportDelay` is the maximum number
     *  of blocks that should pass for `harvest()` to be called.
     *
     *  For external keepers (such as the Keep3r network), this is the maximum
     *  time between jobs to wait. (see `harvestTrigger()`
     *  for more details.)
     *
     *  This may only be called by governance or the strategist.
     * @param _delay The maximum number of seconds to wait between harvests.
     */
    function setMaxReportDelay(uint256 _delay) external onlyAuthorized {
        maxReportDelay = _delay;
        emit UpdatedMaxReportDelay(_delay);
    }

    /**
     * @notice
     *  Used to ensure that any significant credit a strategy has from the
     *  vault will be automatically harvested.
     *
     *  This may only be called by governance or management.
     * @param _creditThreshold The number of want tokens that will
     *  automatically trigger a harvest.
     */
    function setCreditThreshold(uint256 _creditThreshold) external onlyVaultManagers {
        creditThreshold = _creditThreshold;
        emit UpdatedCreditThreshold(_creditThreshold);
    }

    /**
     * @notice
     *  Used to automatically trigger a harvest by our keepers. Can be
     *  useful if gas prices are too high now, and we want to harvest
     *  later once prices have lowered.
     *
     *  This may only be called by governance or management.
     * @param _forceHarvestTriggerOnce Value of true tells keepers to harvest
     *  our strategy
     */
    function setForceHarvestTriggerOnce(bool _forceHarvestTriggerOnce) external onlyVaultManagers {
        forceHarvestTriggerOnce = _forceHarvestTriggerOnce;
        emit ForcedHarvestTrigger(_forceHarvestTriggerOnce);
    }

    /**
     * @notice
     *  Used to set our baseFeeOracle, which checks the network's current base
     *  fee price to determine whether it is an optimal time to harvest or tend.
     *
     *  This may only be called by governance or management.
     * @param _baseFeeOracle Address of our baseFeeOracle
     */
    function setBaseFeeOracle(address _baseFeeOracle) external onlyVaultManagers {
        baseFeeOracle = _baseFeeOracle;
        emit UpdatedBaseFeeOracle(_baseFeeOracle);
    }

    /**
     * @notice
     *  Used to change `metadataURI`. `metadataURI` is used to store the URI
     * of the file describing the strategy.
     *
     *  This may only be called by governance or the strategist.
     * @param _metadataURI The URI that describe the strategy.
     */
    function setMetadataURI(string calldata _metadataURI) external onlyAuthorized {
        metadataURI = _metadataURI;
        emit UpdatedMetadataURI(_metadataURI);
    }

    /**
     * Resolve governance address from Vault contract, used to make assertions
     * on protected functions in the Strategy.
     */
    function governance() internal view returns (address) {
        return vault.governance();
    }

    /**
     * @notice
     *  Provide an accurate conversion from `_amtInWei` (denominated in wei)
     *  to `want` (using the native decimal characteristics of `want`).
     * @dev
     *  Care must be taken when working with decimals to assure that the conversion
     *  is compatible. As an example:
     *
     *      given 1e17 wei (0.1 ETH) as input, and want is USDC (6 decimals),
     *      with USDC/ETH = 1800, this should give back 1800000000 (180 USDC)
     *
     * @param _amtInWei The amount (in wei/1e-18 ETH) to convert to `want`
     * @return The amount in `want` of `_amtInEth` converted to `want`
     *
     */
    function ethToWant(uint256 _amtInWei) public view virtual returns (uint256);

    /**
     * @notice
     *  Provide an accurate estimate for the total amount of assets
     *  (principle + return) that this Strategy is currently managing,
     *  denominated in terms of `want` tokens.
     *
     *  This total should be "realizable" e.g. the total value that could
     *  *actually* be obtained from this Strategy if it were to divest its
     *  entire position based on current on-chain conditions.
     * @dev
     *  Care must be taken in using this function, since it relies on external
     *  systems, which could be manipulated by the attacker to give an inflated
     *  (or reduced) value produced by this function, based on current on-chain
     *  conditions (e.g. this function is possible to influence through
     *  flashloan attacks, oracle manipulations, or other DeFi attack
     *  mechanisms).
     *
     *  It is up to governance to use this function to correctly order this
     *  Strategy relative to its peers in the withdrawal queue to minimize
     *  losses for the Vault based on sudden withdrawals. This value should be
     *  higher than the total debt of the Strategy and higher than its expected
     *  value to be "safe".
     * @return The estimated total assets in this Strategy.
     */
    function estimatedTotalAssets() public view virtual returns (uint256);

    /*
     * @notice
     *  Provide an indication of whether this strategy is currently "active"
     *  in that it is managing an active position, or will manage a position in
     *  the future. This should correlate to `harvest()` activity, so that Harvest
     *  events can be tracked externally by indexing agents.
     * @return True if the strategy is actively managing a position.
     */
    function isActive() public view returns (bool) {
        return vault.strategies(address(this)).debtRatio > 0 || estimatedTotalAssets() > 0;
    }

    /**
     * Perform any Strategy unwinding or other calls necessary to capture the
     * "free return" this Strategy has generated since the last time its core
     * position(s) were adjusted. Examples include unwrapping extra rewards.
     * This call is only used during "normal operation" of a Strategy, and
     * should be optimized to minimize losses as much as possible.
     *
     * This method returns any realized profits and/or realized losses
     * incurred, and should return the total amounts of profits/losses/debt
     * payments (in `want` tokens) for the Vault's accounting (e.g.
     * `want.balanceOf(this) >= _debtPayment + _profit`).
     *
     * `_debtOutstanding` will be 0 if the Strategy is not past the configured
     * debt limit, otherwise its value will be how far past the debt limit
     * the Strategy is. The Strategy's debt limit is configured in the Vault.
     *
     * NOTE: `_debtPayment` should be less than or equal to `_debtOutstanding`.
     *       It is okay for it to be less than `_debtOutstanding`, as that
     *       should only used as a guide for how much is left to pay back.
     *       Payments should be made to minimize loss from slippage, debt,
     *       withdrawal fees, etc.
     *
     * See `vault.debtOutstanding()`.
     */
    function prepareReturn(uint256 _debtOutstanding)
        internal
        virtual
        returns (uint256 _profit, uint256 _loss, uint256 _debtPayment);

    /**
     * Perform any adjustments to the core position(s) of this Strategy given
     * what change the Vault made in the "investable capital" available to the
     * Strategy. Note that all "free capital" in the Strategy after the report
     * was made is available for reinvestment. Also note that this number
     * could be 0, and you should handle that scenario accordingly.
     *
     * See comments regarding `_debtOutstanding` on `prepareReturn()`.
     */
    function adjustPosition(uint256 _debtOutstanding) internal virtual;

    /**
     * Liquidate up to `_amountNeeded` of `want` of this strategy's positions,
     * irregardless of slippage. Any excess will be re-invested with `adjustPosition()`.
     * This function should return the amount of `want` tokens made available by the
     * liquidation. If there is a difference between them, `_loss` indicates whether the
     * difference is due to a realized loss, or if there is some other sitution at play
     * (e.g. locked funds) where the amount made available is less than what is needed.
     *
     * NOTE: The invariant `_liquidatedAmount + _loss <= _amountNeeded` should always be maintained
     */
    function liquidatePosition(uint256 _amountNeeded)
        internal
        virtual
        returns (uint256 _liquidatedAmount, uint256 _loss);

    /**
     * Liquidate everything and returns the amount that got freed.
     * This function is used during emergency exit instead of `prepareReturn()` to
     * liquidate all of the Strategy's positions back to the Vault.
     */
    function liquidateAllPositions() internal virtual returns (uint256 _amountFreed);

    /**
     * @notice
     *  Provide a signal to the keeper that `tend()` should be called. The
     *  keeper will provide the estimated gas cost that they would pay to call
     *  `tend()`, and this function should use that estimate to make a
     *  determination if calling it is "worth it" for the keeper. This is not
     *  the only consideration into issuing this trigger, for example if the
     *  position would be negatively affected if `tend()` is not called
     *  shortly, then this can return `true` even if the keeper might be
     *  "at a loss" (keepers are always reimbursed by Yearn).
     * @dev
     *  `callCostInWei` must be priced in terms of `wei` (1e-18 ETH).
     *
     *  This call and `harvestTrigger()` should never return `true` at the same
     *  time.
     * @param callCostInWei The keeper's estimated gas cost to call `tend()` (in wei).
     * @return `true` if `tend()` should be called, `false` otherwise.
     */
    function tendTrigger(uint256 callCostInWei) public view virtual returns (bool) {
        // We usually don't need tend, but if there are positions that need
        // active maintainence, overriding this function is how you would
        // signal for that.
        // If your implementation uses the cost of the call in want, you can
        // use uint256 callCost = ethToWant(callCostInWei);
        // It is highly suggested to use the baseFeeOracle here as well.

        return false;
    }

    /**
     * @notice
     *  Adjust the Strategy's position. The purpose of tending isn't to
     *  realize gains, but to maximize yield by reinvesting any returns.
     *
     *  See comments on `adjustPosition()`.
     *
     *  This may only be called by governance, the strategist, or the keeper.
     */
    function tend() external onlyKeepers {
        // Don't take profits with this call, but adjust for better gains
        adjustPosition(vault.debtOutstanding());
    }

    /**
     * @notice
     *  Provide a signal to the keeper that `harvest()` should be called. The
     *  keeper will provide the estimated gas cost that they would pay to call
     *  `harvest()`, and this function should use that estimate to make a
     *  determination if calling it is "worth it" for the keeper. This is not
     *  the only consideration into issuing this trigger, for example if the
     *  position would be negatively affected if `harvest()` is not called
     *  shortly, then this can return `true` even if the keeper might be "at a
     *  loss" (keepers are always reimbursed by Yearn).
     * @dev
     *  `callCostInWei` must be priced in terms of `wei` (1e-18 ETH).
     *
     *  This call and `tendTrigger` should never return `true` at the
     *  same time.
     *
     *  See `maxReportDelay`, `creditThreshold` to adjust the
     *  strategist-controlled parameters that will influence whether this call
     *  returns `true` or not. These parameters will be used in conjunction
     *  with the parameters reported to the Vault (see `params`) to determine
     *  if calling `harvest()` is merited.
     *
     *  This trigger also checks the network's base fee to avoid harvesting during
     *  times of high network congestion.
     *
     *  Consider use of super.harvestTrigger() in any override to build on top
     *  of this logic instead of replacing it. For example, if using `minReportDelay`.
     *
     *  It is expected that an external system will check `harvestTrigger()`.
     *  This could be a script run off a desktop or cloud bot (e.g.
     *  https://github.com/iearn-finance/yearn-vaults/blob/main/scripts/keep.py),
     *  or via an integration with the Keep3r network (e.g.
     *  https://github.com/Macarse/GenericKeep3rV2/blob/master/contracts/keep3r/GenericKeep3rV2.sol).
     * @param callCostInWei The keeper's estimated gas cost to call `harvest()` (in wei).
     * @return `true` if `harvest()` should be called, `false` otherwise.
     */
    function harvestTrigger(uint256 callCostInWei) public view virtual returns (bool) {
        // Should not trigger if strategy is not active (no assets or no debtRatio)
        if (!isActive()) return false;

        // check if the base fee gas price is higher than we allow. if it is, block harvests.
        if (!isBaseFeeAcceptable()) return false;

        // trigger if we want to manually harvest, but only if our gas price is acceptable
        if (forceHarvestTriggerOnce) return true;

        // Should trigger if hasn't been called in a while
        StrategyParams memory params = vault.strategies(address(this));
        if ((block.timestamp - params.lastReport) >= maxReportDelay) return true;

        // harvest our credit if it's above our threshold or return false
        return (vault.creditAvailable() > creditThreshold);
    }

    /**
     * @notice
     *  Check if the current network base fee is below our external target. If
     *  not, then harvestTrigger will return false.
     * @return `true` if `harvest()` should be allowed, `false` otherwise.
     */
    function isBaseFeeAcceptable() public view returns (bool) {
        if (baseFeeOracle == address(0)) return true;
        else return IBaseFeeOracle(baseFeeOracle).isCurrentBaseFeeAcceptable();
    }

    /**
     * @notice
     *  Harvests the Strategy, recognizing any profits or losses and adjusting
     *  the Strategy's position.
     *
     *  In the rare case the Strategy is in emergency shutdown, this will exit
     *  the Strategy's position.
     *
     *  This may only be called by governance, the strategist, or the keeper.
     * @dev
     *  When `harvest()` is called, the Strategy reports to the Vault (via
     *  `vault.report()`), so in some cases `harvest()` must be called in order
     *  to take in profits, to borrow newly available funds from the Vault, or
     *  otherwise adjust its position. In other cases `harvest()` must be
     *  called to report to the Vault on the Strategy's position, especially if
     *  any losses have occurred.
     */
    function harvest() external onlyKeepers {
        uint256 profit = 0;
        uint256 loss = 0;
        uint256 debtOutstanding = vault.debtOutstanding();
        uint256 debtPayment = 0;
        if (emergencyExit) {
            // Free up as much capital as possible
            uint256 amountFreed = liquidateAllPositions();
            if (amountFreed < debtOutstanding) {
                loss = debtOutstanding - amountFreed;
            } else if (amountFreed > debtOutstanding) {
                profit = amountFreed - debtOutstanding;
            }
            debtPayment = debtOutstanding - loss;
        } else {
            // Free up returns for Vault to pull
            (profit, loss, debtPayment) = prepareReturn(debtOutstanding);
        }

        // we're done harvesting, so reset our trigger if we used it
        forceHarvestTriggerOnce = false;
        emit ForcedHarvestTrigger(false);

        // Allow Vault to take up to the "harvested" balance of this contract,
        // which is the amount it has earned since the last time it reported to
        // the Vault.
        uint256 totalDebt = vault.strategies(address(this)).totalDebt;
        debtOutstanding = vault.report(profit, loss, debtPayment);

        // Check if free returns are left, and re-invest them
        adjustPosition(debtOutstanding);

        // call healthCheck contract
        if (doHealthCheck && healthCheck != address(0)) {
            if (!IHealthCheck(healthCheck).check(profit, loss, debtPayment, debtOutstanding, totalDebt)) {
                revert FailedHealthCheck();
            }
        } else {
            emit SetDoHealthCheck(true);
            doHealthCheck = true;
        }

        emit Harvested(profit, loss, debtPayment, debtOutstanding);
    }

    /**
     * @notice
     *  Withdraws `_amountNeeded` to `vault`.
     *
     *  This may only be called by the Vault.
     * @param _amountNeeded How much `want` to withdraw.
     * @return _loss Any realized losses
     */
    function withdraw(uint256 _amountNeeded) external returns (uint256 _loss) {
        if (msg.sender != address(vault)) revert NotVault();
        // Liquidate as much as possible to `want`, up to `_amountNeeded`
        uint256 amountFreed;
        (amountFreed, _loss) = liquidatePosition(_amountNeeded);
        // Send it directly back (NOTE: Using `msg.sender` saves some gas here)
        want.safeTransfer(msg.sender, amountFreed);
        // NOTE: Reinvest anything leftover on next `tend`/`harvest`
    }

    /**
     * Do anything necessary to prepare this Strategy for migration, such as
     * transferring any reserve or LP tokens, CDPs, or other tokens or stores of
     * value.
     */
    function prepareMigration(address _newStrategy) internal virtual;

    /**
     * @notice
     *  Transfers all `want` from this Strategy to `_newStrategy`.
     *
     *  This may only be called by the Vault.
     * @dev
     * The new Strategy's Vault must be the same as this Strategy's Vault.
     *  The migration process should be carefully performed to make sure all
     * the assets are migrated to the new address, which should have never
     * interacted with the vault before.
     * @param _newStrategy The Strategy to migrate to.
     */
    function migrate(address _newStrategy) external {
        require(msg.sender == address(vault));
        require(BaseStrategy(_newStrategy).vault() == vault);
        prepareMigration(_newStrategy);
        want.safeTransfer(_newStrategy, want.balanceOf(address(this)));
    }

    /**
     * @notice
     *  Activates emergency exit. Once activated, the Strategy will exit its
     *  position upon the next harvest, depositing all funds into the Vault as
     *  quickly as is reasonable given on-chain conditions.
     *
     *  This may only be called by governance or the strategist.
     * @dev
     *  See `vault.setEmergencyShutdown()` and `harvest()` for further details.
     */
    function setEmergencyExit() external onlyEmergencyAuthorized {
        emergencyExit = true;
        if (vault.strategies(address(this)).debtRatio != 0) {
            vault.revokeStrategy();
        }

        emit EmergencyExitEnabled();
    }

    /**
     * Override this to add all tokens/tokenized positions this contract
     * manages on a *persistent* basis (e.g. not just for swapping back to
     * want ephemerally).
     *
     * NOTE: Do *not* include `want`, already included in `sweep` below.
     *
     * Example:
     * ```
     *    function protectedTokens() internal override view returns (address[] memory) {
     *      address[] memory protected = new address[](3);
     *      protected[0] = tokenA;
     *      protected[1] = tokenB;
     *      protected[2] = tokenC;
     *      return protected;
     *    }
     * ```
     */
    function protectedTokens() internal view virtual returns (address[] memory);

    /**
     * @notice
     *  Removes tokens from this Strategy that are not the type of tokens
     *  managed by this Strategy. This may be used in case of accidentally
     *  sending the wrong kind of token to this Strategy.
     *
     *  Tokens will be sent to `governance()`.
     *
     *  This will fail if an attempt is made to sweep `want`, or any tokens
     *  that are protected by this Strategy.
     *
     *  This may only be called by governance.
     * @dev
     *  Implement `protectedTokens()` to specify any additional tokens that
     *  should be protected from sweeping in addition to `want`.
     * @param _token The token to transfer out of this vault.
     */
    function sweep(address _token) external onlyGovernance {
        if (_token == address(want)) revert SweepTokenIsWant();
        if (_token == address(vault)) revert SweepTokenIsVaultShares();

        address[] memory _protectedTokens = protectedTokens();
        for (uint256 i; i < _protectedTokens.length; i++) {
            if (_token == _protectedTokens[i]) revert TokenProtected();
        }

        IERC20(_token).safeTransfer(governance(), IERC20(_token).balanceOf(address(this)));
    }
}

abstract contract BaseStrategyInitializable is BaseStrategy {
    bool public isOriginal = true;

    event Cloned(address indexed clone);

    constructor(address _vault) BaseStrategy(_vault) { }

    function initialize(address _vault, address _strategist, address _rewards, address _keeper) external virtual {
        _initialize(_vault, _strategist, _rewards, _keeper);
    }

    function clone(address _vault) external returns (address) {
        return clone(_vault, msg.sender, msg.sender, msg.sender);
    }

    function clone(
        address _vault,
        address _strategist,
        address _rewards,
        address _keeper
    )
        public
        returns (address newStrategy)
    {
        if (!isOriginal) revert NotOriginal();
        // Copied from https://github.com/optionality/clone-factory/blob/master/contracts/CloneFactory.sol
        bytes20 addressBytes = bytes20(address(this));

        assembly {
            // EIP-1167 bytecode
            let clone_code := mload(0x40)
            mstore(clone_code, 0x3d602d80600a3d3981f3363d3d373d3d3d363d73000000000000000000000000)
            mstore(add(clone_code, 0x14), addressBytes)
            mstore(add(clone_code, 0x28), 0x5af43d82803e903d91602b57fd5bf30000000000000000000000000000000000)
            newStrategy := create(0, clone_code, 0x37)
        }

        BaseStrategyInitializable(newStrategy).initialize(_vault, _strategist, _rewards, _keeper);

        emit Cloned(newStrategy);
    }
}

// SPDX-License-Identifier: MIT
pragma solidity >=0.8.29 <0.9.0;

interface IStrategyCooldownAware {
    // Events
    event Preminted(
        uint256 indexed predepositId, uint256 debtAssets, uint256 predepositedShares, uint256 externalRedeemIndex
    );
    event PredepositDebtRepaid(uint256 indexed predepositId, uint256 repayAmount);
    event AdjustPosition(
        uint256 utilizationRateBps,
        uint256 filledUnderlyingAmount,
        uint256 unfilledUnderlyingAmount,
        uint256 estimatedTotalAssets
    );
    event RemainingPredepositDebtRepaid(uint256 amount);
    event ShortfallToleranceUpdated(uint256 oldTolerance, uint256 newTolerance);

    // Core functions
    function getRedeemDetail(uint256 redeemIndex)
        external
        view
        returns (
            uint256 redeemId,
            uint256 redeemTimestamp,
            address redeemUser,
            uint256 redeemUnderlyingAmount,
            bool redeemIsDone
        );
    function isPredepositAlreadyClaimed(uint256 predepositId) external view returns (bool isClaimed);
    function predepositDebtRetrievable(uint256 predepositId) external view returns (bool isRetrievable);
    function repayPredepositDebt(uint256 predepositId) external returns (uint256 repayAmount);
    function externalRedeemIndexes(uint256 predepositId) external view returns (uint256);
    function getUtilizationRate() external view returns (uint256);

    // Additional view functions
    function getCooldownPeriod() external view returns (uint256 cooldownPeriod);
    function previewDeposit(uint256 assets) external view returns (uint256 shares);
    function previewMint(uint256 shares) external view returns (uint256 assets);
    function previewWithdraw(uint256 assets) external view returns (uint256 shares);
    function previewRedeem(uint256 shares) external view returns (uint256 assets);
    function getSupplyCap() external view returns (uint256 supplyAssetsCap, uint256 availableAssets);

    // Governance execution functions
    function emergencyRedeem(uint256 shares)
        external
        returns (bool success, uint256 redeemId, uint256 redeemUnderlyingAmount, uint256 cooldownPeriod);
    function emergencyClaim(uint256 redeemIndex) external returns (bool success, uint256 claimedAmount);
    function submitExecution(address[] calldata targets, bytes[] calldata calldatas) external;
    function acceptExecution() external returns (bool success, bytes memory returnData);
    function cancelExecution() external;
    function setAllowedTarget(address target, bool allowed) external;
}

// SPDX-License-Identifier: BUSL-1.1
pragma solidity >=0.8.29 <0.9.0;

/**
 * @title TimelockExecutionLib
 * @notice External library for governance timelock execution functions
 * @dev Extracted from BaseCooldownStrategy to reduce contract size.
 *      Uses external functions for separate deployment via DELEGATECALL.
 */
library TimelockExecutionLib {
    using TimelockExecutionLib for TimelockStorage;

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

    /// @notice Pending execution data for timelock
    struct PendingExecution {
        address[] targets;
        bytes[] calldatas;
        uint256 executeAfter;
    }

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

    event ExecutionSubmitted(address[] targets, bytes[] calldatas, uint256 executeAfter);
    event ExecutionAccepted(address[] targets, bytes[] calldatas, bytes[] returnData);
    event ExecutionCancelled(address[] targets);
    event TargetAllowlistUpdated(address indexed target, bool allowed);
    event TimelockDelayUpdated(uint256 oldDelay, uint256 newDelay);

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

    error InvalidExecutionState(string reason);
    error ExecutionFailed();

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

    /// @notice Minimum timelock delay (12 hours)
    uint256 public constant MIN_TIMELOCK_DELAY = 12 hours;
    /// @notice Maximum timelock delay (30 days)
    uint256 public constant MAX_TIMELOCK_DELAY = 30 days;
    /// @notice Execution window duration after timelock expires (7 days)
    uint256 public constant EXECUTION_WINDOW = 7 days;

    // ============================================
    // STORAGE STRUCT
    // ============================================

    /// @notice Storage layout for timelock execution state
    /// @dev Used to pass storage references to library functions
    struct TimelockStorage {
        PendingExecution pendingExecution;
        mapping(address => bool) allowedTargets;
        uint256 timelockDelay;
    }

    // ============================================
    // EXTERNAL FUNCTIONS
    // ============================================

    /**
     * @notice Submit arbitrary external calls for future execution (supports batch calls)
     * @param self Storage reference to timelock state
     * @param targets Array of contract addresses to call (must be whitelisted, cannot be self)
     * @param calldatas Array of encoded function call data
     * @param allowedSelfCallSelectors such as _setTimelockDelay function (for self-call validation)
     */
    function submitExecution(
        TimelockStorage storage self,
        address[] calldata targets,
        bytes[] calldata calldatas,
        bytes4[] calldata allowedSelfCallSelectors
    )
        external
    {
        // Prevent overwriting existing pending execution
        if (self.pendingExecution.targets.length > 0) {
            revert InvalidExecutionState("PENDING_EXISTS");
        }

        // Validate arrays have same length
        if (targets.length == 0) revert InvalidExecutionState("EMPTY_ARRAYS");
        if (targets.length != calldatas.length) revert InvalidExecutionState("ARRAY_MISMATCH");

        // Check if all targets are allowed
        // Special handling for address(this): only _setTimelockDelay() is allowed
        for (uint256 i = 0; i < targets.length; i++) {
            if (targets[i] == address(this)) {
                // For self-calls, validate function selector
                if (!_isAllowedSelector(bytes4(calldatas[i]), allowedSelfCallSelectors)) {
                    revert InvalidExecutionState("INVALID_SELF_CALL");
                }
            } else if (!self.allowedTargets[targets[i]]) {
                revert InvalidExecutionState("NOTALLOWED");
            }
        }

        // Calculate timelock timestamp following OpenZeppelin pattern
        uint256 executeAfter = block.timestamp + self.timelockDelay;

        // Store pending execution
        self.pendingExecution = PendingExecution({ targets: targets, calldatas: calldatas, executeAfter: executeAfter });

        emit ExecutionSubmitted(targets, calldatas, executeAfter);
    }

    /**
     * @notice Check if a selector is in the allowed list
     * @param selector The function selector to check
     * @param allowedSelectors Array of allowed selectors
     * @return True if selector is allowed
     */
    function _isAllowedSelector(bytes4 selector, bytes4[] memory allowedSelectors) private pure returns (bool) {
        for (uint256 i = 0; i < allowedSelectors.length; i++) {
            if (selector == allowedSelectors[i]) return true;
        }
        return false;
    }

    /**
     * @notice Execute the pending external calls
     * @param self Storage reference to timelock state
     * @return success Whether all external calls succeeded
     * @return returnData Encoded array of return data from each external call
     */
    function acceptExecution(TimelockStorage storage self) external returns (bool success, bytes memory returnData) {
        // Get execution details and check if pending exists
        address[] memory targets = self.pendingExecution.targets;
        if (targets.length == 0) revert InvalidExecutionState("NOPENDING");

        // Validate timelock window (OpenZeppelin pattern)
        if (block.timestamp < self.pendingExecution.executeAfter) {
            revert InvalidExecutionState("TIMELOCKED");
        }
        uint256 expireAt = self.pendingExecution.executeAfter + EXECUTION_WINDOW;
        if (block.timestamp > expireAt) {
            revert InvalidExecutionState("EXPIRED");
        }

        // Get call data
        bytes[] memory calldatas = self.pendingExecution.calldatas;

        // Clear pending execution state
        delete self.pendingExecution;

        // Execute all calls in batch
        bytes[] memory returnDataArray = new bytes[](targets.length);
        for (uint256 i = 0; i < targets.length; i++) {
            (bool callSuccess, bytes memory callReturnData) = targets[i].call(calldatas[i]);
            if (!callSuccess) revert ExecutionFailed();
            returnDataArray[i] = callReturnData;
        }

        // Encode return data array for backward compatibility
        returnData = abi.encode(returnDataArray);
        success = true;

        emit ExecutionAccepted(targets, calldatas, returnDataArray);
    }

    /**
     * @notice Cancel the pending execution
     * @param self Storage reference to timelock state
     */
    function cancelExecution(TimelockStorage storage self) external {
        // Get targets and check if pending exists
        address[] memory targets = self.pendingExecution.targets;
        if (targets.length == 0) revert InvalidExecutionState("NOPENDING");

        // Clear pending execution state
        delete self.pendingExecution;

        emit ExecutionCancelled(targets);
    }

    /**
     * @notice Add or remove a target address from the whitelist
     * @param self Storage reference to timelock state
     * @param target The address to update in the whitelist
     * @param allowed Whether the target should be allowed
     */
    function setAllowedTarget(TimelockStorage storage self, address target, bool allowed) external {
        // Prevent whitelisting self
        if (target == address(this)) revert InvalidExecutionState("SELFWHITELIST");

        // Update allowlist
        self.allowedTargets[target] = allowed;

        emit TargetAllowlistUpdated(target, allowed);
    }

    /**
     * @notice Updates the timelock delay for governance executions
     * @param self Storage reference to timelock state
     * @param newDelay The new timelock delay in seconds (must be between MIN and MAX)
     */
    function setTimelockDelay(TimelockStorage storage self, uint256 newDelay) external {
        // Validate delay is within acceptable bounds
        if (newDelay < MIN_TIMELOCK_DELAY || newDelay > MAX_TIMELOCK_DELAY) {
            revert InvalidExecutionState("INVALID_DELAY");
        }

        uint256 oldDelay = self.timelockDelay;
        self.timelockDelay = newDelay;

        emit TimelockDelayUpdated(oldDelay, newDelay);
    }
}

// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.5.0;

type Id is bytes32;

struct MarketParams {
    address loanToken;
    address collateralToken;
    address oracle;
    address irm;
    uint256 lltv;
}

/// @dev Warning: For `feeRecipient`, `supplyShares` does not contain the accrued shares since the last interest
/// accrual.
struct Position {
    uint256 supplyShares;
    uint128 borrowShares;
    uint128 collateral;
}

/// @dev Warning: `totalSupplyAssets` does not contain the accrued interest since the last interest accrual.
/// @dev Warning: `totalBorrowAssets` does not contain the accrued interest since the last interest accrual.
/// @dev Warning: `totalSupplyShares` does not contain the additional shares accrued by `feeRecipient` since the last
/// interest accrual.
struct Market {
    uint128 totalSupplyAssets;
    uint128 totalSupplyShares;
    uint128 totalBorrowAssets;
    uint128 totalBorrowShares;
    uint128 lastUpdate;
    uint128 fee;
}

struct Authorization {
    address authorizer;
    address authorized;
    bool isAuthorized;
    uint256 nonce;
    uint256 deadline;
}

struct Signature {
    uint8 v;
    bytes32 r;
    bytes32 s;
}

/// @dev This interface is used for factorizing IMorphoStaticTyping and IMorpho.
/// @dev Consider using the IMorpho interface instead of this one.
interface IMorphoBase {
    /// @notice The EIP-712 domain separator.
    /// @dev Warning: Every EIP-712 signed message based on this domain separator can be reused on another chain sharing
    /// the same chain id because the domain separator would be the same.
    function DOMAIN_SEPARATOR() external view returns (bytes32);

    /// @notice The owner of the contract.
    /// @dev It has the power to change the owner.
    /// @dev It has the power to set fees on markets and set the fee recipient.
    /// @dev It has the power to enable but not disable IRMs and LLTVs.
    function owner() external view returns (address);

    /// @notice The fee recipient of all markets.
    /// @dev The recipient receives the fees of a given market through a supply position on that market.
    function feeRecipient() external view returns (address);

    /// @notice Whether the `irm` is enabled.
    function isIrmEnabled(address irm) external view returns (bool);

    /// @notice Whether the `lltv` is enabled.
    function isLltvEnabled(uint256 lltv) external view returns (bool);

    /// @notice Whether `authorized` is authorized to modify `authorizer`'s position on all markets.
    /// @dev Anyone is authorized to modify their own positions, regardless of this variable.
    function isAuthorized(address authorizer, address authorized) external view returns (bool);

    /// @notice The `authorizer`'s current nonce. Used to prevent replay attacks with EIP-712 signatures.
    function nonce(address authorizer) external view returns (uint256);

    /// @notice Sets `newOwner` as `owner` of the contract.
    /// @dev Warning: No two-step transfer ownership.
    /// @dev Warning: The owner can be set to the zero address.
    function setOwner(address newOwner) external;

    /// @notice Enables `irm` as a possible IRM for market creation.
    /// @dev Warning: It is not possible to disable an IRM.
    function enableIrm(address irm) external;

    /// @notice Enables `lltv` as a possible LLTV for market creation.
    /// @dev Warning: It is not possible to disable a LLTV.
    function enableLltv(uint256 lltv) external;

    /// @notice Sets the `newFee` for the given market `marketParams`.
    /// @param newFee The new fee, scaled by WAD.
    /// @dev Warning: The recipient can be the zero address.
    function setFee(MarketParams memory marketParams, uint256 newFee) external;

    /// @notice Sets `newFeeRecipient` as `feeRecipient` of the fee.
    /// @dev Warning: If the fee recipient is set to the zero address, fees will accrue there and will be lost.
    /// @dev Modifying the fee recipient will allow the new recipient to claim any pending fees not yet accrued. To
    /// ensure that the current recipient receives all due fees, accrue interest manually prior to making any changes.
    function setFeeRecipient(address newFeeRecipient) external;

    /// @notice Creates the market `marketParams`.
    /// @dev Here is the list of assumptions on the market's dependencies (tokens, IRM and oracle) that guarantees
    /// Morpho behaves as expected:
    /// - The token should be ERC-20 compliant, except that it can omit return values on `transfer` and `transferFrom`.
    /// - The token balance of Morpho should only decrease on `transfer` and `transferFrom`. In particular, tokens with
    /// burn functions are not supported.
    /// - The token should not re-enter Morpho on `transfer` nor `transferFrom`.
    /// - The token balance of the sender (resp. receiver) should decrease (resp. increase) by exactly the given amount
    /// on `transfer` and `transferFrom`. In particular, tokens with fees on transfer are not supported.
    /// - The IRM should not re-enter Morpho.
    /// - The oracle should return a price with the correct scaling.
    /// @dev Here is a list of properties on the market's dependencies that could break Morpho's liveness properties
    /// (funds could get stuck):
    /// - The token can revert on `transfer` and `transferFrom` for a reason other than an approval or balance issue.
    /// - A very high amount of assets (~1e35) supplied or borrowed can make the computation of `toSharesUp` and
    /// `toSharesDown` overflow.
    /// - The IRM can revert on `borrowRate`.
    /// - A very high borrow rate returned by the IRM can make the computation of `interest` in `_accrueInterest`
    /// overflow.
    /// - The oracle can revert on `price`. Note that this can be used to prevent `borrow`, `withdrawCollateral` and
    /// `liquidate` from being used under certain market conditions.
    /// - A very high price returned by the oracle can make the computation of `maxBorrow` in `_isHealthy` overflow, or
    /// the computation of `assetsRepaid` in `liquidate` overflow.
    /// @dev The borrow share price of a market with less than 1e4 assets borrowed can be decreased by manipulations, to
    /// the point where `totalBorrowShares` is very large and borrowing overflows.
    function createMarket(MarketParams memory marketParams) external;

    /// @notice Supplies `assets` or `shares` on behalf of `onBehalf`, optionally calling back the caller's
    /// `onMorphoSupply` function with the given `data`.
    /// @dev Either `assets` or `shares` should be zero. Most use cases should rely on `assets` as an input so the
    /// caller is guaranteed to have `assets` tokens pulled from their balance, but the possibility to mint a specific
    /// amount of shares is given for full compatibility and precision.
    /// @dev Supplying a large amount can revert for overflow.
    /// @dev Supplying an amount of shares may lead to supply more or fewer assets than expected due to slippage.
    /// Consider using the `assets` parameter to avoid this.
    /// @param marketParams The market to supply assets to.
    /// @param assets The amount of assets to supply.
    /// @param shares The amount of shares to mint.
    /// @param onBehalf The address that will own the increased supply position.
    /// @param data Arbitrary data to pass to the `onMorphoSupply` callback. Pass empty data if not needed.
    /// @return assetsSupplied The amount of assets supplied.
    /// @return sharesSupplied The amount of shares minted.
    function supply(
        MarketParams memory marketParams,
        uint256 assets,
        uint256 shares,
        address onBehalf,
        bytes memory data
    ) external returns (uint256 assetsSupplied, uint256 sharesSupplied);

    /// @notice Withdraws `assets` or `shares` on behalf of `onBehalf` and sends the assets to `receiver`.
    /// @dev Either `assets` or `shares` should be zero. To withdraw max, pass the `shares`'s balance of `onBehalf`.
    /// @dev `msg.sender` must be authorized to manage `onBehalf`'s positions.
    /// @dev Withdrawing an amount corresponding to more shares than supplied will revert for underflow.
    /// @dev It is advised to use the `shares` input when withdrawing the full position to avoid reverts due to
    /// conversion roundings between shares and assets.
    /// @param marketParams The market to withdraw assets from.
    /// @param assets The amount of assets to withdraw.
    /// @param shares The amount of shares to burn.
    /// @param onBehalf The address of the owner of the supply position.
    /// @param receiver The address that will receive the withdrawn assets.
    /// @return assetsWithdrawn The amount of assets withdrawn.
    /// @return sharesWithdrawn The amount of shares burned.
    function withdraw(
        MarketParams memory marketParams,
        uint256 assets,
        uint256 shares,
        address onBehalf,
        address receiver
    ) external returns (uint256 assetsWithdrawn, uint256 sharesWithdrawn);

    /// @notice Borrows `assets` or `shares` on behalf of `onBehalf` and sends the assets to `receiver`.
    /// @dev Either `assets` or `shares` should be zero. Most use cases should rely on `assets` as an input so the
    /// caller is guaranteed to borrow `assets` of tokens, but the possibility to mint a specific amount of shares is
    /// given for full compatibility and precision.
    /// @dev `msg.sender` must be authorized to manage `onBehalf`'s positions.
    /// @dev Borrowing a large amount can revert for overflow.
    /// @dev Borrowing an amount of shares may lead to borrow fewer assets than expected due to slippage.
    /// Consider using the `assets` parameter to avoid this.
    /// @param marketParams The market to borrow assets from.
    /// @param assets The amount of assets to borrow.
    /// @param shares The amount of shares to mint.
    /// @param onBehalf The address that will own the increased borrow position.
    /// @param receiver The address that will receive the borrowed assets.
    /// @return assetsBorrowed The amount of assets borrowed.
    /// @return sharesBorrowed The amount of shares minted.
    function borrow(
        MarketParams memory marketParams,
        uint256 assets,
        uint256 shares,
        address onBehalf,
        address receiver
    ) external returns (uint256 assetsBorrowed, uint256 sharesBorrowed);

    /// @notice Repays `assets` or `shares` on behalf of `onBehalf`, optionally calling back the caller's
    /// `onMorphoReplay` function with the given `data`.
    /// @dev Either `assets` or `shares` should be zero. To repay max, pass the `shares`'s balance of `onBehalf`.
    /// @dev Repaying an amount corresponding to more shares than borrowed will revert for underflow.
    /// @dev It is advised to use the `shares` input when repaying the full position to avoid reverts due to conversion
    /// roundings between shares and assets.
    /// @dev An attacker can front-run a repay with a small repay making the transaction revert for underflow.
    /// @param marketParams The market to repay assets to.
    /// @param assets The amount of assets to repay.
    /// @param shares The amount of shares to burn.
    /// @param onBehalf The address of the owner of the debt position.
    /// @param data Arbitrary data to pass to the `onMorphoRepay` callback. Pass empty data if not needed.
    /// @return assetsRepaid The amount of assets repaid.
    /// @return sharesRepaid The amount of shares burned.
    function repay(
        MarketParams memory marketParams,
        uint256 assets,
        uint256 shares,
        address onBehalf,
        bytes memory data
    ) external returns (uint256 assetsRepaid, uint256 sharesRepaid);

    /// @notice Supplies `assets` of collateral on behalf of `onBehalf`, optionally calling back the caller's
    /// `onMorphoSupplyCollateral` function with the given `data`.
    /// @dev Interest are not accrued since it's not required and it saves gas.
    /// @dev Supplying a large amount can revert for overflow.
    /// @param marketParams The market to supply collateral to.
    /// @param assets The amount of collateral to supply.
    /// @param onBehalf The address that will own the increased collateral position.
    /// @param data Arbitrary data to pass to the `onMorphoSupplyCollateral` callback. Pass empty data if not needed.
    function supplyCollateral(MarketParams memory marketParams, uint256 assets, address onBehalf, bytes memory data)
        external;

    /// @notice Withdraws `assets` of collateral on behalf of `onBehalf` and sends the assets to `receiver`.
    /// @dev `msg.sender` must be authorized to manage `onBehalf`'s positions.
    /// @dev Withdrawing an amount corresponding to more collateral than supplied will revert for underflow.
    /// @param marketParams The market to withdraw collateral from.
    /// @param assets The amount of collateral to withdraw.
    /// @param onBehalf The address of the owner of the collateral position.
    /// @param receiver The address that will receive the collateral assets.
    function withdrawCollateral(MarketParams memory marketParams, uint256 assets, address onBehalf, address receiver)
        external;

    /// @notice Liquidates the given `repaidShares` of debt asset or seize the given `seizedAssets` of collateral on the
    /// given market `marketParams` of the given `borrower`'s position, optionally calling back the caller's
    /// `onMorphoLiquidate` function with the given `data`.
    /// @dev Either `seizedAssets` or `repaidShares` should be zero.
    /// @dev Seizing more than the collateral balance will underflow and revert without any error message.
    /// @dev Repaying more than the borrow balance will underflow and revert without any error message.
    /// @dev An attacker can front-run a liquidation with a small repay making the transaction revert for underflow.
    /// @param marketParams The market of the position.
    /// @param borrower The owner of the position.
    /// @param seizedAssets The amount of collateral to seize.
    /// @param repaidShares The amount of shares to repay.
    /// @param data Arbitrary data to pass to the `onMorphoLiquidate` callback. Pass empty data if not needed.
    /// @return The amount of assets seized.
    /// @return The amount of assets repaid.
    function liquidate(
        MarketParams memory marketParams,
        address borrower,
        uint256 seizedAssets,
        uint256 repaidShares,
        bytes memory data
    ) external returns (uint256, uint256);

    /// @notice Executes a flash loan.
    /// @dev Flash loans have access to the whole balance of the contract (the liquidity and deposited collateral of all
    /// markets combined, plus donations).
    /// @dev Warning: Not ERC-3156 compliant but compatibility is easily reached:
    /// - `flashFee` is zero.
    /// - `maxFlashLoan` is the token's balance of this contract.
    /// - The receiver of `assets` is the caller.
    /// @param token The token to flash loan.
    /// @param assets The amount of assets to flash loan.
    /// @param data Arbitrary data to pass to the `onMorphoFlashLoan` callback.
    function flashLoan(address token, uint256 assets, bytes calldata data) external;

    /// @notice Sets the authorization for `authorized` to manage `msg.sender`'s positions.
    /// @param authorized The authorized address.
    /// @param newIsAuthorized The new authorization status.
    function setAuthorization(address authorized, bool newIsAuthorized) external;

    /// @notice Sets the authorization for `authorization.authorized` to manage `authorization.authorizer`'s positions.
    /// @dev Warning: Reverts if the signature has already been submitted.
    /// @dev The signature is malleable, but it has no impact on the security here.
    /// @dev The nonce is passed as argument to be able to revert with a different error message.
    /// @param authorization The `Authorization` struct.
    /// @param signature The signature.
    function setAuthorizationWithSig(Authorization calldata authorization, Signature calldata signature) external;

    /// @notice Accrues interest for the given market `marketParams`.
    function accrueInterest(MarketParams memory marketParams) external;

    /// @notice Returns the data stored on the different `slots`.
    function extSloads(bytes32[] memory slots) external view returns (bytes32[] memory);
}

/// @dev This interface is inherited by Morpho so that function signatures are checked by the compiler.
/// @dev Consider using the IMorpho interface instead of this one.
interface IMorphoStaticTyping is IMorphoBase {
    /// @notice The state of the position of `user` on the market corresponding to `id`.
    /// @dev Warning: For `feeRecipient`, `supplyShares` does not contain the accrued shares since the last interest
    /// accrual.
    function position(Id id, address user)
        external
        view
        returns (uint256 supplyShares, uint128 borrowShares, uint128 collateral);

    /// @notice The state of the market corresponding to `id`.
    /// @dev Warning: `totalSupplyAssets` does not contain the accrued interest since the last interest accrual.
    /// @dev Warning: `totalBorrowAssets` does not contain the accrued interest since the last interest accrual.
    /// @dev Warning: `totalSupplyShares` does not contain the accrued shares by `feeRecipient` since the last interest
    /// accrual.
    function market(Id id)
        external
        view
        returns (
            uint128 totalSupplyAssets,
            uint128 totalSupplyShares,
            uint128 totalBorrowAssets,
            uint128 totalBorrowShares,
            uint128 lastUpdate,
            uint128 fee
        );

    /// @notice The market params corresponding to `id`.
    /// @dev This mapping is not used in Morpho. It is there to enable reducing the cost associated to calldata on layer
    /// 2s by creating a wrapper contract with functions that take `id` as input instead of `marketParams`.
    function idToMarketParams(Id id)
        external
        view
        returns (address loanToken, address collateralToken, address oracle, address irm, uint256 lltv);
}

/// @title IMorpho
/// @author Morpho Labs
/// @custom:contact security@morpho.org
/// @dev Use this interface for Morpho to have access to all the functions with the appropriate function signatures.
interface IMorpho is IMorphoBase {
    /// @notice The state of the position of `user` on the market corresponding to `id`.
    /// @dev Warning: For `feeRecipient`, `p.supplyShares` does not contain the accrued shares since the last interest
    /// accrual.
    function position(Id id, address user) external view returns (Position memory p);

    /// @notice The state of the market corresponding to `id`.
    /// @dev Warning: `m.totalSupplyAssets` does not contain the accrued interest since the last interest accrual.
    /// @dev Warning: `m.totalBorrowAssets` does not contain the accrued interest since the last interest accrual.
    /// @dev Warning: `m.totalSupplyShares` does not contain the accrued shares by `feeRecipient` since the last
    /// interest accrual.
    function market(Id id) external view returns (Market memory m);

    /// @notice The market params corresponding to `id`.
    /// @dev This mapping is not used in Morpho. It is there to enable reducing the cost associated to calldata on layer
    /// 2s by creating a wrapper contract with functions that take `id` as input instead of `marketParams`.
    function idToMarketParams(Id id) external view returns (MarketParams memory);
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (interfaces/IERC4626.sol)

pragma solidity ^0.8.20;

import {IERC20} from "../token/ERC20/IERC20.sol";
import {IERC20Metadata} from "../token/ERC20/extensions/IERC20Metadata.sol";

/**
 * @dev Interface of the ERC4626 "Tokenized Vault Standard", as defined in
 * https://eips.ethereum.org/EIPS/eip-4626[ERC-4626].
 */
interface IERC4626 is IERC20, IERC20Metadata {
    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
    );

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

    /**
     * @dev Returns the total amount of the underlying asset that is “managed” by Vault.
     *
     * - 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);

    /**
     * @dev 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.
     *
     * - 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);

    /**
     * @dev 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.
     *
     * - 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);

    /**
     * @dev Returns the maximum amount of the underlying asset that can be deposited into the Vault for the receiver,
     * through a deposit call.
     *
     * - 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);

    /**
     * @dev Allows an on-chain or off-chain user to simulate the effects of their deposit at the current block, given
     * current on-chain conditions.
     *
     * - 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);

    /**
     * @dev Mints shares Vault shares to receiver by depositing exactly amount of underlying tokens.
     *
     * - 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);

    /**
     * @dev Returns the maximum amount of the Vault shares that can be minted for the receiver, through a mint call.
     * - 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);

    /**
     * @dev Allows an on-chain or off-chain user to simulate the effects of their mint at the current block, given
     * current on-chain conditions.
     *
     * - 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);

    /**
     * @dev Mints exactly shares Vault shares to receiver by depositing amount of underlying tokens.
     *
     * - 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);

    /**
     * @dev Returns the maximum amount of the underlying asset that can be withdrawn from the owner balance in the
     * Vault, through a withdraw call.
     *
     * - 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);

    /**
     * @dev Allows an on-chain or off-chain user to simulate the effects of their withdrawal at the current block,
     * given current on-chain conditions.
     *
     * - 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);

    /**
     * @dev Burns shares from owner and sends exactly assets of underlying tokens to receiver.
     *
     * - 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);

    /**
     * @dev Returns the maximum amount of Vault shares that can be redeemed from the owner balance in the Vault,
     * through a redeem call.
     *
     * - 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);

    /**
     * @dev Allows an on-chain or off-chain user to simulate the effects of their redeemption at the current block,
     * given current on-chain conditions.
     *
     * - 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);

    /**
     * @dev Burns exactly shares from owner and sends assets of underlying tokens to receiver.
     *
     * - 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
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/extensions/IERC20Permit.sol)

pragma solidity ^0.8.20;

/**
 * @dev Interface of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in
 * https://eips.ethereum.org/EIPS/eip-2612[EIP-2612].
 *
 * Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by
 * presenting a message signed by the account. By not relying on {IERC20-approve}, the token holder account doesn't
 * need to send a transaction, and thus is not required to hold Ether at all.
 *
 * ==== Security Considerations
 *
 * There are two important considerations concerning the use of `permit`. The first is that a valid permit signature
 * expresses an allowance, and it should not be assumed to convey additional meaning. In particular, it should not be
 * considered as an intention to spend the allowance in any specific way. The second is that because permits have
 * built-in replay protection and can be submitted by anyone, they can be frontrun. A protocol that uses permits should
 * take this into consideration and allow a `permit` call to fail. Combining these two aspects, a pattern that may be
 * generally recommended is:
 *
 * ```solidity
 * function doThingWithPermit(..., uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s) public {
 *     try token.permit(msg.sender, address(this), value, deadline, v, r, s) {} catch {}
 *     doThing(..., value);
 * }
 *
 * function doThing(..., uint256 value) public {
 *     token.safeTransferFrom(msg.sender, address(this), value);
 *     ...
 * }
 * ```
 *
 * Observe that: 1) `msg.sender` is used as the owner, leaving no ambiguity as to the signer intent, and 2) the use of
 * `try/catch` allows the permit to fail and makes the code tolerant to frontrunning. (See also
 * {SafeERC20-safeTransferFrom}).
 *
 * Additionally, note that smart contract wallets (such as Argent or Safe) are not able to produce permit signatures, so
 * contracts should have entry points that don't rely on permit.
 */
interface IERC20Permit {
    /**
     * @dev Sets `value` as the allowance of `spender` over ``owner``'s tokens,
     * given ``owner``'s signed approval.
     *
     * IMPORTANT: The same issues {IERC20-approve} has related to transaction
     * ordering also apply here.
     *
     * Emits an {Approval} event.
     *
     * Requirements:
     *
     * - `spender` cannot be the zero address.
     * - `deadline` must be a timestamp in the future.
     * - `v`, `r` and `s` must be a valid `secp256k1` signature from `owner`
     * over the EIP712-formatted function arguments.
     * - the signature must use ``owner``'s current nonce (see {nonces}).
     *
     * For more information on the signature format, see the
     * https://eips.ethereum.org/EIPS/eip-2612#specification[relevant EIP
     * section].
     *
     * CAUTION: See Security Considerations above.
     */
    function permit(
        address owner,
        address spender,
        uint256 value,
        uint256 deadline,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) external;

    /**
     * @dev Returns the current nonce for `owner`. This value must be
     * included whenever a signature is generated for {permit}.
     *
     * Every successful call to {permit} increases ``owner``'s nonce by one. This
     * prevents a signature from being used multiple times.
     */
    function nonces(address owner) external view returns (uint256);

    /**
     * @dev Returns the domain separator used in the encoding of the signature for {permit}, as defined by {EIP712}.
     */
    // solhint-disable-next-line func-name-mixedcase
    function DOMAIN_SEPARATOR() external view returns (bytes32);
}

// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.8.0;

struct MarketConfig {
    /// @notice The maximum amount of assets that can be allocated to the market.
    uint184 cap;
    /// @notice Whether the market is in the withdraw queue.
    bool enabled;
    /// @notice The timestamp at which the market can be instantly removed from the withdraw queue.
    uint64 removableAt;
}

struct PendingUint192 {
    /// @notice The pending value to set.
    uint192 value;
    /// @notice The timestamp at which the pending value becomes valid.
    uint64 validAt;
}

struct PendingAddress {
    /// @notice The pending value to set.
    address value;
    /// @notice The timestamp at which the pending value becomes valid.
    uint64 validAt;
}

/// @title PendingLib
/// @author Morpho Labs
/// @custom:contact security@morpho.org
/// @notice Library to manage pending values and their validity timestamp.
library PendingLib {
    /// @dev Updates `pending`'s value to `newValue` and its corresponding `validAt` timestamp.
    /// @dev Assumes `timelock` <= `MAX_TIMELOCK`.
    function update(PendingUint192 storage pending, uint184 newValue, uint256 timelock) internal {
        pending.value = newValue;
        // Safe "unchecked" cast because timelock <= MAX_TIMELOCK.
        pending.validAt = uint64(block.timestamp + timelock);
    }

    /// @dev Updates `pending`'s value to `newValue` and its corresponding `validAt` timestamp.
    /// @dev Assumes `timelock` <= `MAX_TIMELOCK`.
    function update(PendingAddress storage pending, address newValue, uint256 timelock) internal {
        pending.value = newValue;
        // Safe "unchecked" cast because timelock <= MAX_TIMELOCK.
        pending.validAt = uint64(block.timestamp + timelock);
    }
}

File 23 of 36 : IERC20.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (interfaces/IERC20.sol)

pragma solidity ^0.8.20;

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

File 24 of 36 : 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: 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
        }
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (interfaces/IERC4626.sol)

pragma solidity ^0.8.0;

import "../token/ERC20/IERC20Upgradeable.sol";
import "../token/ERC20/extensions/IERC20MetadataUpgradeable.sol";

/**
 * @dev Interface of the ERC4626 "Tokenized Vault Standard", as defined in
 * https://eips.ethereum.org/EIPS/eip-4626[ERC-4626].
 *
 * _Available since v4.7._
 */
interface IERC4626Upgradeable is IERC20Upgradeable, IERC20MetadataUpgradeable {
    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
    );

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

    /**
     * @dev Returns the total amount of the underlying asset that is “managed” by Vault.
     *
     * - 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);

    /**
     * @dev 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.
     *
     * - 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);

    /**
     * @dev 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.
     *
     * - 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);

    /**
     * @dev Returns the maximum amount of the underlying asset that can be deposited into the Vault for the receiver,
     * through a deposit call.
     *
     * - 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);

    /**
     * @dev Allows an on-chain or off-chain user to simulate the effects of their deposit at the current block, given
     * current on-chain conditions.
     *
     * - 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);

    /**
     * @dev Mints shares Vault shares to receiver by depositing exactly amount of underlying tokens.
     *
     * - 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);

    /**
     * @dev Returns the maximum amount of the Vault shares that can be minted for the receiver, through a mint call.
     * - 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);

    /**
     * @dev Allows an on-chain or off-chain user to simulate the effects of their mint at the current block, given
     * current on-chain conditions.
     *
     * - 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);

    /**
     * @dev Mints exactly shares Vault shares to receiver by depositing amount of underlying tokens.
     *
     * - 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);

    /**
     * @dev Returns the maximum amount of the underlying asset that can be withdrawn from the owner balance in the
     * Vault, through a withdraw call.
     *
     * - 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);

    /**
     * @dev Allows an on-chain or off-chain user to simulate the effects of their withdrawal at the current block,
     * given current on-chain conditions.
     *
     * - 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);

    /**
     * @dev Burns shares from owner and sends exactly assets of underlying tokens to receiver.
     *
     * - 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);

    /**
     * @dev Returns the maximum amount of Vault shares that can be redeemed from the owner balance in the Vault,
     * through a redeem call.
     *
     * - 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);

    /**
     * @dev Allows an on-chain or off-chain user to simulate the effects of their redeemption at the current block,
     * given current on-chain conditions.
     *
     * - 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);

    /**
     * @dev Burns exactly shares from owner and sends assets of underlying tokens to receiver.
     *
     * - 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: GPL-3.0
pragma solidity >=0.8.29 <0.9.0;

interface IHealthCheck {
    // Events
    event GovernanceUpdated(address indexed newGovernance);
    event ManagementUpdated(address indexed newManagement);
    event DefaultProfitLimitUpdated(uint256 newProfitLimitRatio);
    event DefaultLossLimitUpdated(uint256 newLossLimitRatio);
    event StrategyLimitsUpdated(
        address indexed strategy, uint256 profitLimitRatio, uint256 lossLimitRatio, bool exists
    );
    event CustomCheckUpdated(address indexed strategy, address indexed customCheck);

    function check(
        uint256 profit,
        uint256 loss,
        uint256 debtPayment,
        uint256 debtOutstanding,
        uint256 totalDebt
    )
        external
        view
        returns (bool);
}

interface IStrategyHealthCheck {
    function check(
        uint256 profit,
        uint256 loss,
        uint256 debtPayment,
        uint256 debtOutstanding,
        uint256 totalDebt,
        address callerStrategy
    )
        external
        view
        returns (bool);
}

// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.8.29 <0.9.0;

interface IBaseFee {
    function basefee_global() external view returns (uint256);
}

interface IBaseFeeOracle {
    function isCurrentBaseFeeAcceptable() external view returns (bool);
}

// SPDX-License-Identifier: MIT
pragma solidity >=0.8.29 <0.9.0;

import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import { IVault } from "./IVault.sol";

interface IStrategy {
    // Events
    event Harvested(uint256 profit, uint256 loss, uint256 debtPayment, uint256 debtOutstanding);
    event UpdatedStrategist(address newStrategist);
    event UpdatedKeeper(address newKeeper);
    event UpdatedRewards(address rewards);
    event UpdatedMinReportDelay(uint256 delay);
    event UpdatedMaxReportDelay(uint256 delay);
    event UpdatedBaseFeeOracle(address baseFeeOracle);
    event UpdatedCreditThreshold(uint256 creditThreshold);
    event ForcedHarvestTrigger(bool triggerState);
    event EmergencyExitEnabled();
    event UpdatedMetadataURI(string metadataURI);
    event SetHealthCheck(address);
    event SetDoHealthCheck(bool);

    // Functions
    function name() external view returns (string memory);
    function vault() external view returns (IVault);
    function want() external view returns (IERC20);
    function apiVersion() external pure returns (string memory);
    function strategist() external view returns (address);
    function keeper() external view returns (address);
    function isActive() external view returns (bool);
    function delegatedAssets() external view returns (uint256);
    function estimatedTotalAssets() external view returns (uint256);
    function tendTrigger(uint256 callCost) external view returns (bool);
    function tend() external;
    function harvestTrigger(uint256 callCost) external view returns (bool);
    function harvest() external;
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/IERC20.sol)

pragma solidity ^0.8.20;

/**
 * @dev Interface of the ERC20 standard as defined in the EIP.
 */
interface IERC20 {
    /**
     * @dev Emitted when `value` tokens are moved from one account (`from`) to
     * another (`to`).
     *
     * Note that `value` may be zero.
     */
    event Transfer(address indexed from, address indexed to, uint256 value);

    /**
     * @dev Emitted when the allowance of a `spender` for an `owner` is set by
     * a call to {approve}. `value` is the new allowance.
     */
    event Approval(address indexed owner, address indexed spender, uint256 value);

    /**
     * @dev Returns the value of tokens in existence.
     */
    function totalSupply() external view returns (uint256);

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

    /**
     * @dev Moves a `value` amount of tokens from the caller's account to `to`.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * Emits a {Transfer} event.
     */
    function transfer(address to, uint256 value) external returns (bool);

    /**
     * @dev Returns the remaining number of tokens that `spender` will be
     * allowed to spend on behalf of `owner` through {transferFrom}. This is
     * zero by default.
     *
     * This value changes when {approve} or {transferFrom} are called.
     */
    function allowance(address owner, address spender) external view returns (uint256);

    /**
     * @dev Sets a `value` amount of tokens as the allowance of `spender` over the
     * caller's tokens.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * IMPORTANT: Beware that changing an allowance with this method brings the risk
     * that someone may use both the old and the new allowance by unfortunate
     * transaction ordering. One possible solution to mitigate this race
     * condition is to first reduce the spender's allowance to 0 and set the
     * desired value afterwards:
     * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
     *
     * Emits an {Approval} event.
     */
    function approve(address spender, uint256 value) external returns (bool);

    /**
     * @dev Moves a `value` amount of tokens from `from` to `to` using the
     * allowance mechanism. `value` is then deducted from the caller's
     * allowance.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * Emits a {Transfer} event.
     */
    function transferFrom(address from, address to, uint256 value) external returns (bool);
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/extensions/IERC20Metadata.sol)

pragma solidity ^0.8.20;

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

/**
 * @dev Interface for the optional metadata functions from the ERC20 standard.
 */
interface IERC20Metadata is IERC20 {
    /**
     * @dev Returns the name of the token.
     */
    function name() external view returns (string memory);

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

    /**
     * @dev Returns the decimals places of the token.
     */
    function decimals() external view returns (uint8);
}

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

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (token/ERC20/IERC20.sol)

pragma solidity ^0.8.0;

/**
 * @dev Interface of the ERC20 standard as defined in the EIP.
 */
interface IERC20Upgradeable {
    /**
     * @dev Emitted when `value` tokens are moved from one account (`from`) to
     * another (`to`).
     *
     * Note that `value` may be zero.
     */
    event Transfer(address indexed from, address indexed to, uint256 value);

    /**
     * @dev Emitted when the allowance of a `spender` for an `owner` is set by
     * a call to {approve}. `value` is the new allowance.
     */
    event Approval(address indexed owner, address indexed spender, uint256 value);

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

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

    /**
     * @dev Moves `amount` tokens from the caller's account to `to`.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * Emits a {Transfer} event.
     */
    function transfer(address to, uint256 amount) external returns (bool);

    /**
     * @dev Returns the remaining number of tokens that `spender` will be
     * allowed to spend on behalf of `owner` through {transferFrom}. This is
     * zero by default.
     *
     * This value changes when {approve} or {transferFrom} are called.
     */
    function allowance(address owner, address spender) external view returns (uint256);

    /**
     * @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * IMPORTANT: Beware that changing an allowance with this method brings the risk
     * that someone may use both the old and the new allowance by unfortunate
     * transaction ordering. One possible solution to mitigate this race
     * condition is to first reduce the spender's allowance to 0 and set the
     * desired value afterwards:
     * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
     *
     * Emits an {Approval} event.
     */
    function approve(address spender, uint256 amount) external returns (bool);

    /**
     * @dev Moves `amount` tokens from `from` to `to` using the
     * allowance mechanism. `amount` is then deducted from the caller's
     * allowance.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * Emits a {Transfer} event.
     */
    function transferFrom(address from, address to, uint256 amount) external returns (bool);
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (token/ERC20/extensions/IERC20Metadata.sol)

pragma solidity ^0.8.0;

import "../IERC20Upgradeable.sol";

/**
 * @dev Interface for the optional metadata functions from the ERC20 standard.
 *
 * _Available since v4.1._
 */
interface IERC20MetadataUpgradeable is IERC20Upgradeable {
    /**
     * @dev Returns the name of the token.
     */
    function name() external view returns (string memory);

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

    /**
     * @dev Returns the decimals places of the token.
     */
    function decimals() external view returns (uint8);
}

Settings
{
  "remappings": [
    "@openzeppelin/contracts@5.0.2/=node_modules/@openzeppelin/contracts-5.0.2/",
    "@openzeppelin/contracts/=node_modules/@openzeppelin/contracts/",
    "@openzeppelin/contracts-upgradeable/=node_modules/@openzeppelin/contracts-upgradeable-4.9.4/",
    "@openzeppelin/contracts@5.0.2/=node_modules/@openzeppelin/contracts-5.0.2/",
    "@metamorpho/src/=lib/metamorpho/src/",
    "forge-std/src/=lib/forge-std/src/",
    "forge-std/=lib/forge-std/src/",
    "@superearn/=src/superearn/",
    "@yearn-vaults/=src/yearn-vaults/",
    "@mock/=src/mock/",
    "@runespear/=src/superearn/v2/messaging/runespear/",
    "solady/=node_modules/solady/",
    "@bisonai/orakl-contracts/=node_modules/@bisonai/orakl-contracts/",
    "@arbitrum/=node_modules/@arbitrum/",
    "@chainlink/=node_modules/@chainlink/",
    "@eth-optimism/=node_modules/@chainlink/contracts/node_modules/@eth-optimism/",
    "@offchainlabs/=node_modules/@offchainlabs/",
    "@scroll-tech/=node_modules/@scroll-tech/",
    "@uniswap/=node_modules/@uniswap/",
    "@zksync/=node_modules/@zksync/",
    "base64-sol/=node_modules/base64-sol/",
    "chainlink-evm/=lib/chainlink-evm/",
    "ds-test/=lib/metamorpho/lib/forge-std/lib/ds-test/src/",
    "erc4626-tests/=lib/metamorpho/lib/erc4626-tests/",
    "forge-gas-snapshot/=node_modules/@uniswap/v4-periphery/lib/permit2/lib/forge-gas-snapshot/",
    "hardhat/=node_modules/hardhat/",
    "metamorpho/=lib/metamorpho/src/",
    "morpho-blue/=lib/metamorpho/lib/morpho-blue/",
    "murky/=lib/metamorpho/lib/universal-rewards-distributor/lib/murky/src/",
    "openzeppelin-contracts/=lib/metamorpho/lib/openzeppelin-contracts/",
    "openzeppelin/=lib/metamorpho/lib/universal-rewards-distributor/lib/openzeppelin-contracts/contracts/",
    "permit2/=node_modules/@uniswap/v4-periphery/lib/permit2/",
    "solmate/=node_modules/@uniswap/v4-core/lib/solmate/",
    "universal-rewards-distributor/=lib/metamorpho/lib/universal-rewards-distributor/src/",
    "v4-core/=node_modules/@uniswap/v4-periphery/lib/v4-core/"
  ],
  "optimizer": {
    "enabled": true,
    "runs": 250
  },
  "metadata": {
    "useLiteralContent": false,
    "bytecodeHash": "none",
    "appendCBOR": true
  },
  "outputSelection": {
    "*": {
      "*": [
        "evm.bytecode",
        "evm.deployedBytecode",
        "devdoc",
        "userdoc",
        "metadata",
        "abi"
      ]
    }
  },
  "evmVersion": "cancun",
  "viaIR": true,
  "libraries": {
    "src/superearn/core/StrategyMorphoV1Vault.sol": {
      "TimelockExecutionLib": "0xecb5d39de95345ae62c9bd8c87ad1297a32bcfa0"
    }
  }
}

Contract Security Audit

Contract ABI

API
[{"inputs":[{"internalType":"address","name":"_vault","type":"address"},{"internalType":"address","name":"_metaMorpho","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"CannotRepayByFailedClaim","type":"error"},{"inputs":[],"name":"FailedHealthCheck","type":"error"},{"inputs":[{"internalType":"uint256","name":"requested","type":"uint256"},{"internalType":"uint256","name":"available","type":"uint256"}],"name":"InsufficientUnderlyingBalance","type":"error"},{"inputs":[],"name":"InvalidDebtClaimState","type":"error"},{"inputs":[{"internalType":"string","name":"reason","type":"string"}],"name":"InvalidExecutionState","type":"error"},{"inputs":[],"name":"InvalidExternalRedeem","type":"error"},{"inputs":[],"name":"InvalidExternalToken","type":"error"},{"inputs":[],"name":"NotOriginal","type":"error"},{"inputs":[],"name":"NotVault","type":"error"},{"inputs":[],"name":"OnlyCooldownVault","type":"error"},{"inputs":[],"name":"OutstandingPredepositDebt","type":"error"},{"inputs":[],"name":"RedeemAmountMismatch","type":"error"},{"inputs":[],"name":"ReentrancyGuardReentrantCall","type":"error"},{"inputs":[{"internalType":"uint256","name":"requested","type":"uint256"},{"internalType":"uint256","name":"outstanding","type":"uint256"}],"name":"RepayAmountExceedsShortfall","type":"error"},{"inputs":[{"internalType":"address","name":"token","type":"address"}],"name":"SafeERC20FailedOperation","type":"error"},{"inputs":[],"name":"StrategyAlreadyInitialized","type":"error"},{"inputs":[],"name":"SweepTokenIsVaultShares","type":"error"},{"inputs":[],"name":"SweepTokenIsWant","type":"error"},{"inputs":[],"name":"TokenProtected","type":"error"},{"inputs":[],"name":"ZeroAssets","type":"error"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"utilizationRateBps","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"filledUnderlyingAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"unfilledUnderlyingAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"estimatedTotalAssets","type":"uint256"}],"name":"AdjustPosition","type":"event"},{"anonymous":false,"inputs":[],"name":"EmergencyExitEnabled","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"bool","name":"triggerState","type":"bool"}],"name":"ForcedHarvestTrigger","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"profit","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"loss","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"debtPayment","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"debtOutstanding","type":"uint256"}],"name":"Harvested","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"predepositId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"repayAmount","type":"uint256"}],"name":"PredepositDebtRepaid","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"predepositId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"debtAssets","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"predepositedShares","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"externalRedeemIndex","type":"uint256"}],"name":"Preminted","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"RemainingPredepositDebtRepaid","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"bool","name":"","type":"bool"}],"name":"SetDoHealthCheck","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"","type":"address"}],"name":"SetHealthCheck","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"oldTolerance","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"newTolerance","type":"uint256"}],"name":"ShortfallToleranceUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"baseFeeOracle","type":"address"}],"name":"UpdatedBaseFeeOracle","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"creditThreshold","type":"uint256"}],"name":"UpdatedCreditThreshold","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"newKeeper","type":"address"}],"name":"UpdatedKeeper","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"delay","type":"uint256"}],"name":"UpdatedMaxReportDelay","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"string","name":"metadataURI","type":"string"}],"name":"UpdatedMetadataURI","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"delay","type":"uint256"}],"name":"UpdatedMinReportDelay","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"rewards","type":"address"}],"name":"UpdatedRewards","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"newStrategist","type":"address"}],"name":"UpdatedStrategist","type":"event"},{"inputs":[],"name":"EXECUTION_WINDOW","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MAX_TIMELOCK_DELAY","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MIN_TIMELOCK_DELAY","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"newTolerance","type":"uint256"}],"name":"_setShortfallTolerance","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"newDelay","type":"uint256"}],"name":"_setTimelockDelay","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"acceptExecution","outputs":[{"internalType":"bool","name":"success","type":"bool"},{"internalType":"bytes","name":"returnData","type":"bytes"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"target","type":"address"}],"name":"allowedTargets","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"apiVersion","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"baseFeeOracle","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"cancelExecution","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"cooldownVault","outputs":[{"internalType":"contract ICooldownVault","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"creditThreshold","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"delegatedAssets","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"doHealthCheck","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"redeemIndex","type":"uint256"}],"name":"emergencyClaim","outputs":[{"internalType":"bool","name":"success","type":"bool"},{"internalType":"uint256","name":"claimedAmount","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"emergencyExit","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"shares","type":"uint256"}],"name":"emergencyRedeem","outputs":[{"internalType":"bool","name":"success","type":"bool"},{"internalType":"uint256","name":"redeemId","type":"uint256"},{"internalType":"uint256","name":"redeemUnderlyingAmount","type":"uint256"},{"internalType":"uint256","name":"cooldownPeriod","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"repayAmount","type":"uint256"}],"name":"emergencyRepayRemainingPredepositDebt","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"estimatedTotalAssets","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"ethToWant","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"predepositId","type":"uint256"}],"name":"externalRedeemIndexes","outputs":[{"internalType":"uint256","name":"externalRedeemIndex","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"externalShareToken","outputs":[{"internalType":"contract IERC20","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"externalUnderlyingToken","outputs":[{"internalType":"contract IERC20","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"forceHarvestTriggerOnce","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getCooldownPeriod","outputs":[{"internalType":"uint256","name":"cooldownPeriod","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"redeemIndex","type":"uint256"}],"name":"getRedeemDetail","outputs":[{"internalType":"uint256","name":"redeemId","type":"uint256"},{"internalType":"uint256","name":"redeemTimestamp","type":"uint256"},{"internalType":"address","name":"redeemUser","type":"address"},{"internalType":"uint256","name":"redeemUnderlyingAmount","type":"uint256"},{"internalType":"bool","name":"redeemIsDone","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getSupplyCap","outputs":[{"internalType":"uint256","name":"supplyAssetsCap","type":"uint256"},{"internalType":"uint256","name":"availableAssets","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getUtilizationRate","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"harvest","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"callCostInWei","type":"uint256"}],"name":"harvestTrigger","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"healthCheck","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"isActive","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"isBaseFeeAcceptable","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"predepositId","type":"uint256"}],"name":"isPredepositAlreadyClaimed","outputs":[{"internalType":"bool","name":"isClaimed","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"keeper","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"maxReportDelay","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"metaMorpho","outputs":[{"internalType":"contract IMetaMorpho","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"metadataURI","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_newStrategy","type":"address"}],"name":"migrate","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"minReportDelay","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"pendingExecution","outputs":[{"components":[{"internalType":"address[]","name":"targets","type":"address[]"},{"internalType":"bytes[]","name":"calldatas","type":"bytes[]"},{"internalType":"uint256","name":"executeAfter","type":"uint256"}],"internalType":"struct TimelockExecutionLib.PendingExecution","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"predepositId","type":"uint256"}],"name":"predepositDebtRetrievable","outputs":[{"internalType":"bool","name":"isRetrievable","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"assets","type":"uint256"}],"name":"previewDeposit","outputs":[{"internalType":"uint256","name":"shares","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"shares","type":"uint256"}],"name":"previewMint","outputs":[{"internalType":"uint256","name":"assets","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"shares","type":"uint256"}],"name":"previewRedeem","outputs":[{"internalType":"uint256","name":"assets","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"assets","type":"uint256"}],"name":"previewWithdraw","outputs":[{"internalType":"uint256","name":"shares","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"redeemAmounts","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"remainingPredepositDebt","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"predepositId","type":"uint256"}],"name":"repayPredepositDebt","outputs":[{"internalType":"uint256","name":"repayAmount","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"rewards","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"target","type":"address"},{"internalType":"bool","name":"allowed","type":"bool"}],"name":"setAllowedTarget","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_baseFeeOracle","type":"address"}],"name":"setBaseFeeOracle","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_creditThreshold","type":"uint256"}],"name":"setCreditThreshold","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bool","name":"_doHealthCheck","type":"bool"}],"name":"setDoHealthCheck","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"setEmergencyExit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bool","name":"_forceHarvestTriggerOnce","type":"bool"}],"name":"setForceHarvestTriggerOnce","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_healthCheck","type":"address"}],"name":"setHealthCheck","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_keeper","type":"address"}],"name":"setKeeper","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_delay","type":"uint256"}],"name":"setMaxReportDelay","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string","name":"_metadataURI","type":"string"}],"name":"setMetadataURI","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_delay","type":"uint256"}],"name":"setMinReportDelay","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_rewards","type":"address"}],"name":"setRewards","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_strategist","type":"address"}],"name":"setStrategist","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"shortfallTolerance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"strategist","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address[]","name":"targets","type":"address[]"},{"internalType":"bytes[]","name":"calldatas","type":"bytes[]"}],"name":"submitExecution","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_token","type":"address"}],"name":"sweep","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"tend","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"tendTrigger","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"timelockDelay","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"vault","outputs":[{"internalType":"contract IVault","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"want","outputs":[{"internalType":"contract IERC20","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_amountNeeded","type":"uint256"}],"name":"withdraw","outputs":[{"internalType":"uint256","name":"_loss","type":"uint256"}],"stateMutability":"nonpayable","type":"function"}]

0x61018080604052346104fe576040816153b380380380916100208285610659565b8339810103126104fe5761003381610690565b906001600160a01b039061004990602001610690565b6040516338d52e0f60e01b8152911691602082600481865afa9182156104b2575f9261061d575b50600654906001600160a01b03821661060f57600280546001600160a01b0319166001600160a01b03929092169182179055604051637e062a3560e11b8152909190602081600481865afa9081156104b2575f916105d5575b5060018060a01b0316809160018060a01b031916176006556040519060205f81840163095ea7b360e01b81528560248601528119604486015260448552610111606486610659565b84519082855af15f513d826105b9575b505015610574575b505060038054336001600160a01b031991821681179092556004805482168317815560058054909216909217905562278d0060085560025460405163313ce56760e01b81526001600160a01b03909116925090602090829081855afa9081156104b2575f91610542575b50604d811161046f57600a0a80620f42400290620f424082040361046f57600a5560405163095ea7b360e01b81523360048201525f19602482015290602090829060449082905f905af180156104b25761050a575b506001600c5581156104bd5760a08290526001600160a01b0390811660c081905260065490911660e08190526040516338d52e0f60e01b8152909190602081600481865afa9081156104b2575f916104cc575b506001600160a01b0316036104bd5760206004916040519283809263313ce56760e01b82525afa80156104b257610279915f91610483575b506106bd565b8060640290606482040361046f57600e556202a300601355610120525f608081905260075562093a8060085560a05160405163313ce56760e01b815290602090829060049082906001600160a01b03165afa80156104b2576102e1915f9161048357506106bd565b6101605260c05160405163313ce56760e01b815290602090829060049082906001600160a01b03165afa80156104b257610321915f9161048357506106bd565b806101405280633b9aca000290633b9aca0082040361046f5761010052604051614c8c90816107278239608051816145e7015260a051818181610e050152818161109001528181612c64015281816136f50152818161462d0152614956015260c051818181610e65015281816116790152818161196a015281816119d70152818161242c01528181612c98015281816136a001528181613b95015281816140ea0152818161479d01526148fd015260e051818181610cd801528181610d7a015281816118b8015281816124980152818161333e0152818161376401528181613f4f0152614593015261010051816130830152610120518181816106ba0152818161128f015281816112e301528181612a4101528181612fca01528181613048015281816132ad01528181613b2f0152818161432201526149ae01526101405181611d0b015261016051815050f35b634e487b7160e01b5f52601160045260245ffd5b6104a5915060203d6020116104ab575b61049d8183610659565b8101906106a4565b5f610273565b503d610493565b6040513d5f823e3d90fd5b63c09faddb60e01b5f5260045ffd5b90506020813d602011610502575b816104e760209383610659565b810103126104fe576104f890610690565b5f61023b565b5f80fd5b3d91506104da565b6020813d60201161053a575b8161052360209383610659565b810103126104fe5751801515036104fe575f6101e8565b3d9150610516565b90506020813d60201161056c575b8161055d60209383610659565b810103126104fe57515f610193565b3d9150610550565b6105ac6105b1936040519063095ea7b360e01b602083015260248201525f6044820152604481526105a6606482610659565b826106ce565b6106ce565b5f8080610129565b9091506105cd5750803b15155b5f80610121565b6001146105c6565b90506020813d602011610607575b816105f060209383610659565b810103126104fe5761060190610690565b5f6100c9565b3d91506105e3565b6236173760e01b5f5260045ffd5b9091506020813d602011610651575b8161063960209383610659565b810103126104fe5761064a90610690565b905f610070565b3d915061062c565b601f909101601f19168101906001600160401b0382119082101761067c57604052565b634e487b7160e01b5f52604160045260245ffd5b51906001600160a01b03821682036104fe57565b908160209103126104fe575160ff811681036104fe5790565b60ff16604d811161046f57600a0a90565b905f602091828151910182855af1156104b2575f513d61071d57506001600160a01b0381163b155b6106fd5750565b635274afe760e01b5f9081526001600160a01b0391909116600452602490fd5b600114156106f656fe60806040526004361015610011575f80fd5b5f5f3560e01c806301504adf14612dad57806301681a6214612bf657806303ee438c14612b4257806306fdde0314612a1d578063083f01fa146127df5780630a28a477146127c05780630ada4dab146127605780630d674c731461273657806311bc8245146126bd578063169070eb146126a05780631f1fcd51146126795780631fe4a68614612652578063203618141461262b578063217b5042146123fa57806322f3e2d4146123df578063258294101461239657806328b7ccf7146123785780632a083ca31461235a5780632e1a7d4d146122f657806339a172a8146122a85780633d61b286146122675780633e65164a14612249578063440368a3146121b25780634641257d14611c985780634883a19c14611c7a5780634a417a5314611c5f5780634cdad50614611c405780635641ec0314611c1d57806361bc2e0814611bbe578063650d188014611ba25780636718835f14611b7f5780636c610dcc1461169d5780637086f5f214611b61578063748747e614611ae957806374f1170314611899578063750521f5146116ba578063780022a01461169d5780637defd5b01461160b57806380f5219914611659578063826cddf61461162f57806386e3772e146116105780638e6350e21461160b57806391796aa7146115a257806395326e2d1461158757806395e80c50146115695780639bc64285146114e15780639ec5a894146114ba5780639f450b5a14611441578063a2be4fbb14611417578063a763cf5b146113f4578063aa5480cf146113d6578063ac00ff2614611375578063aced16611461134e578063b252720b14611324578063b3d7f6b9146112b3578063b6042b341461126f578063b8fe8d5f14611232578063c21fa571146111d1578063c7b9d53014611159578063ca1dd22e146110b4578063cccaf72c14611070578063ce5494bb14610cfc578063d4b1b76f14610cb8578063dd224c1a14610bd0578063e463b9ac14610947578063ec38a86214610786578063ed882c2b1461075d578063eef09bad1461073f578063ef8b30f71461068a578063efbb5cb014610667578063f017c92f14610619578063fbfa77cf146105f2578063fcf2d0ad146103985763fe2508a614610348575f80fd5b34610395576020366003190112610395577fe5ef7832c564a10cbe7b4f1e01ac33a406cb63fcf430a97a9af8616d150af5f3602060043561038761394c565b80600a55604051908152a180f35b80fd5b50346103955780600319360112610395576001600160a01b0360035416331480156105d9575b8015610556575b80156104cf575b1561039557600160ff196009541617600955806001600160a01b03600254166040516339ebf82360e01b815230600482015261012081602481855afa80156104c4576040918491610495575b500151610448575b50807f97e963041e952738788b9d4871d854d282065b8f90a464928d6528f2e9a4fd0b91a180f35b803b156104925781809160046040518094819363507257cd60e11b83525af180156104875715610420578161047c91612e75565b61039557805f610420565b6040513d84823e3d90fd5b50fd5b6104b791506101203d81116104bd575b6104af8183612e75565b8101906130e1565b5f610418565b503d6104a5565b6040513d85823e3d90fd5b50600460206001600160a01b0360025416604051928380926344546b0160e11b82525afa8015610487578290610512575b6001600160a01b0391501633146103cc565b506020813d60201161054e575b8161052c60209383612e75565b8101031261054a576105456001600160a01b03916133f6565b610500565b5080fd5b3d915061051f565b50600460206001600160a01b036002541660405192838092630229549960e51b82525afa8015610487578290610599575b6001600160a01b0391501633146103c5565b506020813d6020116105d1575b816105b360209383612e75565b8101031261054a576105cc6001600160a01b03916133f6565b610587565b3d91506105a6565b506001600160a01b036105ea6138a1565b1633146103be565b503461039557806003193601126103955760206001600160a01b0360025416604051908152f35b5034610395576020366003190112610395577f5430e11864ad7aa9775b07d12657fe52df9aa2ba734355bd8ef8747be2c800c56020600435610659613b06565b80600855604051908152a180f35b5034610395578060031936011261039557602061068261364d565b604051908152f35b50346103955760203660031901126103955760405163ef8b30f760e01b81526004803590820152906020826024817f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03165afa90811561073357906106fc575b602090604051908152f35b506020813d60201161072b575b8161071660209383612e75565b8101031261072757602090516106f1565b5f80fd5b3d9150610709565b604051903d90823e3d90fd5b50346103955780600319360112610395576020601354604051908152f35b503461039557602036600319011261039557602061077c6004356134ae565b6040519015158152f35b5034610395576020366003190112610395576107a0612dda565b6001600160a01b036107b06138a1565b1633148015610933575b1561054a576001600160a01b0316801561054a578160206001600160a01b036002541660446001600160a01b0360045416604051948593849263095ea7b360e01b845260048401528160248401525af180156104c4576108fc575b50806bffffffffffffffffffffffff60a01b60045416176004558160206001600160a01b036002541660446040518094819363095ea7b360e01b83528760048401525f1960248401525af180156104c45761089a575b5060207fafbb66abf8f3b719799940473a4052a3717cdd8e40fb6c8a3faadab316b1a06991604051908152a180f35b6020813d6020116108f4575b816108b360209383612e75565b810103126108f0577fafbb66abf8f3b719799940473a4052a3717cdd8e40fb6c8a3faadab316b1a069916108e86020926131e8565b50915061086b565b8280fd5b3d91506108a6565b6020813d60201161092b575b8161091560209383612e75565b810103126108f057610926906131e8565b610815565b3d9150610908565b506001600160a01b036003541633146107ba565b5034610395578060031936011261039557806040805161096681612e28565b6060815260606020820152015260405161097f81612e28565b60405180816020600f549283815201600f86527f8d1108e10bcb7c27dddfc02ed9d693a074039d026cf4ea4240b40f7d581ac80292865b818110610bae5750506109cb92500382612e75565b81526010549067ffffffffffffffff8211610b9a5760208260051b01916109f56040519384612e75565b808352601084527f1b6847dc741a1b0cd08d278845f9d819d87b734759afb55fe2de5cb82a9ae6729084602085015b828210610af85750505050602081019182526011549260408201938452604051926020845260808401925192606060208601528351809152602060a08601940190835b818110610ad9575050505191601f19848203016040850152825190818152602081016020808460051b840101950193915b838310610aac578751606088015286860387f35b9091929394602080610aca600193601f198682030187528951612e97565b97019301930191939290610a98565b82516001600160a01b0316865260209586019590920191600101610a67565b604051878554610b0781612df0565b8084529060018116908115610b775750600114610b41575b5060019282610b3385946020940382612e75565b815201940191019092610a24565b868a5260208a208a92505b818310610b6157505081016020016001610b1f565b6001816020925483868801015201920191610b4c565b60ff191660208581019190915291151560051b8401909101915060019050610b1f565b634e487b7160e01b83526041600452602483fd5b84546001600160a01b03168352600194850194869450602090930192016109b6565b5034610395578060031936011261039557610be9613888565b60405163de75b13760e01b8152600f6004820152818160248173ecb5d39de95345ae62c9bd8c87ad1297a32bcfa05af49182156107335780918193610c4d575b5050610c4960405192839215158352604060208401526040830190612e97565b0390f35b915091503d8083833e610c608183612e75565b81016040828203126108f057610c75826131e8565b9160208101519067ffffffffffffffff8211610cb4570181601f82011215610cb057610ca8929350602081519101612f41565b905f80610c29565b8380fd5b8480fd5b503461039557806003193601126103955760206040516001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000168152f35b503461039557602036600319011261039557610d16612dda565b6001600160a01b03600254168033036108f05760405163fbfa77cf60e01b81526020816004816001600160a01b0387165afa8015610f2a578490611027575b6001600160a01b039150160361054a576040516326e0a6bd60e01b81523060048201527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031690602081602481855afa908115610f2a578491610ff5575b50610fe6576020602491604051928380926356f03f5160e01b82523060048301525afa9081156104c4578391610fb4575b50610fa5576040516370a0823160e01b81523060048201527f00000000000000000000000000000000000000000000000000000000000000006020826024816001600160a01b0385165afa908115610f2a5783908592610f6d575b610e509350613909565b6040516370a0823160e01b81523060048201527f00000000000000000000000000000000000000000000000000000000000000006020826024816001600160a01b0385165afa908115610f2a5783908592610f35575b610eb09350613909565b6001600160a01b0360065416604051916370a0823160e01b8352306004840152602083602481855afa918215610f2a578492610ef4575b610ef19350613909565b80f35b91506020833d602011610f22575b81610f0f60209383612e75565b8101031261072757610ef1925191610ee7565b3d9150610f02565b6040513d86823e3d90fd5b9150506020823d602011610f65575b81610f5160209383612e75565b810103126107275782610eb0925191610ea6565b3d9150610f44565b9150506020823d602011610f9d575b81610f8960209383612e75565b810103126107275782610e50925191610e46565b3d9150610f7c565b6306def89560e51b8252600482fd5b90506020813d602011610fde575b81610fcf60209383612e75565b8101031261072757515f610deb565b3d9150610fc2565b6306def89560e51b8352600483fd5b90506020813d60201161101f575b8161101060209383612e75565b8101031261072757515f610dba565b3d9150611003565b506020813d602011611068575b8161104160209383612e75565b81010312610cb057516001600160a01b0381168103610cb0576001600160a01b0390610d55565b3d9150611034565b503461039557806003193601126103955760206040516001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000168152f35b503461039557604036600319011261039557806110cf612dda565b60243590811515809203611155576110e5613888565b73ecb5d39de95345ae62c9bd8c87ad1297a32bcfa090813b156111535760646001600160a01b039185936040519586948593632f2a069760e21b8552600f600486015216602484015260448301525af48015610487576111425750f35b8161114c91612e75565b6103955780f35b505b5050fd5b5034610395576020366003190112610395576001600160a01b0361117b612dda565b611183613b06565b16801561054a576020817f352ececae6d7d1e6d26bcf2c549dfd55be1637e9b22dc0cf3b71ddb36097a6b4926bffffffffffffffffffffffff60a01b6003541617600355604051908152a180f35b50346103955780600319360112610395576111ea613b06565b8073ecb5d39de95345ae62c9bd8c87ad1297a32bcfa0803b15610492578160249160405192838092636b8197e560e01b8252600f60048301525af48015610487576111425750f35b50346103955760203660031901126103955760ff60406020926001600160a01b0361125b612dda565b168152601284522054166040519015158152f35b503461039557806003193601126103955760206040516001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000168152f35b50346103955760203660031901126103955760405163b3d7f6b960e01b81526004803590820152906020826024817f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03165afa90811561073357906106fc57602090604051908152f35b503461039557806003193601126103955760206001600160a01b0360015460081c16604051908152f35b503461039557806003193601126103955760206001600160a01b0360055416604051908152f35b50346103955760203660031901126103955761138f612eec565b61139761394c565b7ff769f6bf659bbbdabf212d830720ce893eedc57f25ebb8e44edf5b300618a35b602060405192151592838152a160ff80196001541691161760015580f35b50346103955780600319360112610395576020600a54604051908152f35b5034610395578060031936011261039557602060ff600b54166040519015158152f35b50346103955760203660031901126103955760406020916004358152601683522054604051908152f35b5034610395576020366003190112610395577f711be97287cb9ec921887b9be36e148e1a27c6b158547b22b9704ffc54447a0f602061147e612dda565b61148661394c565b600954610100600160a81b038260081b1690610100600160a81b031916176009556001600160a01b0360405191168152a180f35b503461039557806003193601126103955760206001600160a01b0360045416604051908152f35b5034610395576020366003190112610395576004353033036115375760407f6355917f85a4e327b3edb1bd6daabf31a3e1f69e4d8878b376ac256a613ff34b91600e549080600e5582519182526020820152a180f35b604051632774044360e11b815260206004820152600960248201526827a7262cafa9a2a62360b91b6044820152606490fd5b50346103955780600319360112610395576020600754604051908152f35b5034610395578060031936011261039557602061077c613437565b503461039557602036600319011261039557303303611537578073ecb5d39de95345ae62c9bd8c87ad1297a32bcfa0803b15610492578160449160405192838092638f077d3d60e01b8252600f600483015260043560248301525af48015610487576111425750f35b612efb565b503461039557602036600319011261039557602061077c600435613417565b503461039557806003193601126103955760206001600160a01b0360095460081c16604051908152f35b503461039557806003193601126103955760206040516001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000168152f35b503461039557602036600319011261039557602090604051908152f35b5034610395576020366003190112610395576004359067ffffffffffffffff821161039557366023830112156103955781600401359167ffffffffffffffff831161054a57366024848301011161054a57611713613b06565b61171d8254612df0565b601f8111611832575b508192601f81116001146117a0578083947f300e67d5a415b6d015a471d9c7b95dd58f3e8290af965e84e0f845de2996dda69491611792575b508160011b905f198360031b1c19161784555b61178c604051928392602084526024602085019201612f87565b0390a180f35b60249150830101355f61175f565b828052601f198116935f516020614c605f395f51905f5290845b8681106118175750827f300e67d5a415b6d015a471d9c7b95dd58f3e8290af965e84e0f845de2996dda69596106117fb575b5050600181811b018455611772565b8301602401355f19600384901b60f8161c191690555f806117ec565b909160206001819260248689010135815501930191016117ba565b828052601f840160051c5f516020614c605f395f51905f52019060208510611884575b601f0160051c5f516020614c605f395f51905f5201905b8181106118795750611726565b83815560010161186c565b5f516020614c605f395f51905f529150611855565b5034610395576020366003190112610395576004356001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016803303611ada579060c06024926118ed61438a565b60405193848092634e1f0f1960e01b82528560048301525afa80156104c45783928491611a8d575b50611a7e57808352600d6020526119436040842054905f90823091815f52601660205260405f205491151590565b92509250505f14611a6a576040516370a0823160e01b8152306004820152936020856024817f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03165afa9081156107335790611a37575b6020945081808210911802188281105f14611a31576119c4816119cc92946131c7565b60145461340a565b6014555b6119fb82337f0000000000000000000000000000000000000000000000000000000000000000613909565b7fb7cc6375d00bb7c09ac9b49f423f777e510a13d6fa2694da2ca3c850742fc8e883604051848152a26001600c55604051908152f35b506119d0565b506020843d602011611a62575b81611a5160209383612e75565b8101031261072757602093516119a1565b3d9150611a44565b634e487b7160e01b84526001600452602484fd5b6332edbb0f60e21b8352600483fd5b92505060c0823d60c011611ad2575b81611aa960c09383612e75565b810103126108f057611aba826133f6565b50611acc60a0604084015193016131e8565b5f611915565b3d9150611a9c565b632f220d7560e21b8352600483fd5b5034610395576020366003190112610395576001600160a01b03611b0b612dda565b611b13613b06565b16801561054a576020817f2f202ddb4a2e345f6323ed90f8fc8559d770a7abbbeee84dde8aca3351fe7154926bffffffffffffffffffffffff60a01b6005541617600555604051908152a180f35b50346103955780600319360112610395576020600e54604051908152f35b5034610395578060031936011261039557602060ff600154166040519015158152f35b503461039557602036600319011261039557602061077c6132e0565b50346103955760203660031901126103955760a06001600160a01b03611bf9600435905f90823091815f52601660205260405f205491151590565b93949190926040519586526020860152166040840152606083015215156080820152f35b5034610395578060031936011261039557602060ff600954166040519015158152f35b503461039557602036600319011261039557602061068260043561328a565b503461039557806003193601126103955760206106826131f5565b50346103955780600319360112610395576020601454604051908152f35b5034610395578060031936011261039557611cb1613d8e565b8060048160206001600160a01b03600254166040519384809263bf3759b560e01b82525afa9182156104c457839261217e575b5060095460ff161561209257602490611cfb61438a565b611d35611d30611d0961364d565b7f00000000000000000000000000000000000000000000000000000000000000009061340a565b614589565b50505060206001600160a01b0360065416604051938480926370a0823160e01b82523060048301525afa918215610f2a57849261205e575b506001600c55828210156120305750611d89611d9091836131c7565b80926131c7565b925b60ff19600b5416600b557f6ad28df1b554fa6cacd46ae82fa811748d53798feeb437ddf234bf30839533196020604051858152a16001600160a01b0360025416936040516339ebf82360e01b815230600482015261012081602481895afa908115611f5a579060c0918691612011575b5001516040516328766ebf60e21b8152600481018490526024810185905260448101839052956020908790606490829089905af1958615611f5a578596611fdd575b50611e4d61438a565b611e5686613ef1565b6001600c5560015460ff811680611fc8575b15611f65575060206001600160a01b0360015460081c169160a46040518094819363c70fa00b60e01b83528860048401528960248401528760448401528b606484015260848301525afa908115611f5a578591611f20575b5015611f11577f4c0f499ffe6befa0ca7c826b0916cf87bea98de658013e76938489368d60d509939461178c915b604051948594859094939260609260808301968352602083015260408201520152565b63796b712960e01b8452600484fd5b90506020813d602011611f52575b81611f3b60209383612e75565b81010312610cb457611f4c906131e8565b5f611ec0565b3d9150611f2e565b6040513d87823e3d90fd5b7f4c0f499ffe6befa0ca7c826b0916cf87bea98de658013e76938489368d60d5099596915091600161178c937ff769f6bf659bbbdabf212d830720ce893eedc57f25ebb8e44edf5b300618a35b6020604051848152a160ff191617600155611eee565b506001600160a01b038160081c161515611e68565b9095506020813d602011612009575b81611ff960209383612e75565b810103126107275751945f611e44565b3d9150611fec565b61202a91506101203d81116104bd576104af8183612e75565b5f611e02565b9190818111612045575b5081611d90916131c7565b819450612056611d909284926131c7565b94915061203a565b9091506020813d60201161208a575b8161207a60209383612e75565b810103126107275751905f611d6d565b3d915061206d565b5061212b92506120fa6120ed6120cf92846120ab61364d565b916120d560c06120b96144a2565b01519184808210911802841880978180966131c7565b926131c7565b908181111561216f576120e892506131c7565b61340a565b6120f561438a565b6139f0565b506001600c5561210861364d565b61213160c06121156144a2565b01519383808210911802831880968180946131c7565b946131c7565b918284111561215b5761214892916120cf916131c7565b90818082109118021881925b9290611d92565b5050906121699083926131c7565b92612154565b612178916131c7565b5061340a565b9091506020813d6020116121aa575b8161219a60209383612e75565b810103126107275751905f611ce4565b3d915061218d565b50346103955780600319360112610395576121cb613d8e565b600460206001600160a01b03600254166040519283809263bf3759b560e01b82525afa8015610487578290612215575b61220d915061220861438a565b613ef1565b6001600c5580f35b506020813d602011612241575b8161222f60209383612e75565b810103126107275761220d90516121fb565b3d9150612222565b5034610395578060031936011261039557602060405162093a808152f35b503461039557602036600319011261039557612281613888565b608061228e600435613b21565b916040519315158452602084015260408301526060820152f35b5034610395576020366003190112610395577fbb2c369a0355a34b02ab5fce0643150c87e1c8dfe7c918d465591879f57948b160206004356122e8613b06565b80600755604051908152a180f35b5034610395576020366003190112610395576001600160a01b0360025416330361234b5761232261438a565b60206106826123326004356139f0565b91906001600c55336001600160a01b0360065416613909565b6362df054560e01b8152600490fd5b5034610395578060031936011261039557602060405162278d008152f35b50346103955780600319360112610395576020600854604051908152f35b503461039557806003193601126103955750610c496040516123b9604082612e75565b600581526418171a171b60d91b6020820152604051918291602083526020830190612e97565b5034610395578060031936011261039557602061077c613152565b503461039557602036600319011261039557600435612417613888565b6040516370a0823160e01b81523060048201527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03811691602081602481865afa908115611f5a5785916125f9575b5083156125ea576014548085116125d357508084116125bc5750908391604460206001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000169360405192838092636eb1769f60e11b82523060048301528760248301525afa8015610f2a5785918591612580575b506124f76124fd9392859261340a565b91614b9e565b803b1561054a578180916024604051809481936323afca2560e11b83528860048401525af180156104875761256b575b507f3ad20ff068059496cdbf0353dff5d6db4591bce40b815e454cf29d4a933c5c8a60208361255e816014546131c7565b601455604051908152a180f35b8161257591612e75565b61054a57815f61252d565b92945050506020813d6020116125b4575b8161259e60209383612e75565b81010312610727575184929084906124f76124e7565b3d9150612591565b630c9f8b8560e01b85526004849052602452604484fd5b630ef9f73160e31b86526004859052602452604485fd5b630cb65c7760e21b8552600485fd5b90506020813d602011612623575b8161261460209383612e75565b8101031261072757515f61246d565b3d9150612607565b5034610395578060031936011261039557604061264661302c565b82519182526020820152f35b503461039557806003193601126103955760206001600160a01b0360035416604051908152f35b503461039557806003193601126103955760206001600160a01b0360065416604051908152f35b5034610395578060031936011261039557602060405161a8c08152f35b5034610395576020366003190112610395576126d7612dda565b6126df61394c565b7fc8db9c35f716b87af1fbb83f03c78646061931269301fd7ba6dcf189b4cdc2fc60206040516001600160a01b0384168152a1610100600160a81b036001549160081b1690610100600160a81b0319161760015580f35b50346103955760203660031901126103955760406020916004358152600d83522054604051908152f35b5034610395576020366003190112610395577f6ad28df1b554fa6cacd46ae82fa811748d53798feeb437ddf234bf3083953319602061279d612eec565b6127a561394c565b151560ff19600b541660ff821617600b55604051908152a180f35b5034610395576020366003190112610395576020610682600435612fa7565b50346107275760403660031901126107275760043567ffffffffffffffff811161072757612811903690600401612ebb565b9060243567ffffffffffffffff811161072757612832903690600401612ebb565b9061283b613888565b604051612849606082612e75565b6002815260208101926040368537639bc6428560e01b61286883612f22565b526391796aa760e01b61287a83612f15565b5273ecb5d39de95345ae62c9bd8c87ad1297a32bcfa094853b1561072757604051639481232960e01b8152600f600482015260806024820152608481018890529687959460a487019493909290915f905b8082106129e657505050600319868503016044870152808452602084019060208160051b86010194835f91601e1982360301905b848410612983575050505050505060209060031985840301606486015251918281520191905f5b818110612960575050509181805f9403915af4801561295557612947575080f35b61295391505f90612e75565b005b6040513d5f823e3d90fd5b82516001600160e01b031916845286945060209384019390920191600101612926565b91939598995091939596601f19828203018752883583811215610727578401906020823592019167ffffffffffffffff8111610727578036038313610727576129d26020928392600195612f87565b9a0197019401918b999897969593916128ff565b91939697509193948335906001600160a01b03821680920361072757602081600193829352019401920189979693959492916128cb565b34610727575f366003190112610727576040516306fdde0360e01b81525f816004817f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03165afa908115612955575f91612ae3575b610c496020612acf6036856040519384917f53747261746567794d6f7270686f56315661756c742000000000000000000000828401528051918291018484015e81015f838201520301601f198101835282612e75565b604051918291602083526020830190612e97565b90503d805f833e612af48183612e75565b8101906020818303126107275780519067ffffffffffffffff821161072757019080601f8301121561072757612acf6036612b396020938585610c4997519101612f41565b93505050612a79565b34610727575f366003190112610727576040515f5f54612b6181612df0565b8084529060018116908115612bd25750600114612b89575b610c4983612acf81850382612e75565b5f8080525f516020614c605f395f51905f52939250905b808210612bb857509091508101602001612acf612b79565b919260018160209254838588010152019101909291612ba0565b60ff191660208086019190915291151560051b84019091019150612acf9050612b79565b3461072757602036600319011261072757612c0f612dda565b612c17613888565b6001600160a01b0380600654169116908114612d9e576001600160a01b03600254168114612d8f5760405190612c4e606083612e75565b6002825260403660208401376001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016612c8d83612f15565b526001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016612cc183612f22565b525f5b8251811015612d1a578251811015612d06576001600160a01b0360208260051b85010151168214612cf757600101612cc4565b633502ad7560e11b5f5260045ffd5b634e487b7160e01b5f52603260045260245ffd5b50612d236138a1565b6040516370a0823160e01b81523060048201529190602083602481855afa918215612955575f92612d59575b6129539350613909565b91506020833d602011612d87575b81612d7460209383612e75565b8101031261072757612953925191612d4f565b3d9150612d67565b63578dc2d360e01b5f5260045ffd5b63371188b760e01b5f5260045ffd5b3461072757602036600319011261072757612dc6613888565b634e487b7160e01b5f52600160045260245ffd5b600435906001600160a01b038216820361072757565b90600182811c92168015612e1e575b6020831014612e0a57565b634e487b7160e01b5f52602260045260245ffd5b91607f1691612dff565b6060810190811067ffffffffffffffff821117612e4457604052565b634e487b7160e01b5f52604160045260245ffd5b610120810190811067ffffffffffffffff821117612e4457604052565b90601f8019910116810190811067ffffffffffffffff821117612e4457604052565b805180835260209291819084018484015e5f828201840152601f01601f1916010190565b9181601f840112156107275782359167ffffffffffffffff8311610727576020808501948460051b01011161072757565b60043590811515820361072757565b34610727575f3660031901126107275760206040515f8152f35b805115612d065760200190565b805160011015612d065760400190565b90816020910312610727575190565b92919267ffffffffffffffff8211612e445760405191612f6b601f8201601f191660200184612e75565b829481845281830111610727578281602093845f96015e010152565b908060209392818452848401375f828201840152601f01601f1916010190565b60405190630a28a47760e01b825260048201526020816024816001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000165afa908115612955575f91612ffd575090565b90506020813d602011613024575b8161301860209383612e75565b81010312610727575190565b3d915061300b565b60405163402d267d60e01b8152306004820152906020826024817f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03165afa918215612955575f926130ad575b507f0000000000000000000000000000000000000000000000000000000000000000808318818410021890565b9091506020813d6020116130d9575b816130c960209383612e75565b810103126107275751905f613080565b3d91506130bc565b908161012091031261072757610100604051916130fd83612e58565b805183526020810151602084015260408101516040840152606081015160608401526080810151608084015260a081015160a084015260c081015160c084015260e081015160e0840152015161010082015290565b60246101206001600160a01b0360025416604051928380926339ebf82360e01b82523060048301525afa8015612955576040915f916131a8575b5001511580159061319a5790565b506131a361364d565b151590565b6131c191506101203d81116104bd576104af8183612e75565b5f61318c565b919082039182116131d457565b634e487b7160e01b5f52601160045260245ffd5b5190811515820361072757565b60246131ff61364d565b60206001600160a01b0360065416604051938480926370a0823160e01b82523060048301525afa918215612955575f92613256575b5080156132505761324861324d92826131c7565b614249565b90565b50505f90565b9091506020813d602011613282575b8161327260209383612e75565b810103126107275751905f613234565b3d9150613265565b6040519063266d6a8360e11b825260048201526020816024816001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000165afa908115612955575f91612ffd575090565b60ff600954166133f257602460206001600160a01b0360065416604051928380926370a0823160e01b82523060048301525afa908115612955575f916133c0575b5080156133bb57604051630b1bbb3160e41b8152906020826004817f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03165afa8015612955575f90613387575b61324d925081808210911802186142ff565b506020823d6020116133b3575b816133a160209383612e75565b810103126107275761324d9151613375565b3d9150613394565b505f90565b90506020813d6020116133ea575b816133db60209383612e75565b8101031261072757515f613321565b3d91506133ce565b5f90565b51906001600160a01b038216820361072757565b919082018092116131d457565b5f52600d60205260405f205480156133bb575f8190526016602052151590565b60095460081c6001600160a01b0316806134515750600190565b602060049160405192838092630d2a79d760e21b82525afa908115612955575f9161347a575090565b90506020813d6020116134a6575b8161349560209383612e75565b810103126107275761324d906131e8565b3d9150613488565b506134b76143aa565b60a06134c16144a2565b015190156133bb5760ff600b5416613647576134dd81426131c7565b60085411156136475742146133f2576134f461364d565b60c06134fe6144a2565b015160025460405163112c1f9b60e01b81529192906001600160a01b0316602083600481845afa928315612955575f93613612575b5060206004916040519283809263bf3759b560e01b82525afa908115612955575f916135dc575b509261357982856120cf9661359195115f146135d157958680926131c7565b5f918181116135c0575b5050600a5460011c9361340a565b90808210156135a957906135a4916131c7565b101590565b8082116135b7575050505f90565b6135a4916131c7565b6135ca92506131c7565b5f80613583565b5080958680926131c7565b9390506020843d60201161360a575b816135f860209383612e75565b8101031261072757925161357961355a565b3d91506135eb565b9092506020813d60201161363f575b8161362e60209383612e75565b810103126107275751916020613533565b3d9150613621565b50600190565b602460206001600160a01b0360065416604051928380926370a0823160e01b82523060048301525afa908115612955575f91613856575b506040516370a0823160e01b81523060048201526020816024817f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03165afa908115612955575f91613824575b506040516370a0823160e01b8152306004820152906020826024817f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03165afa918215612955575f926137ee575b5061373a6137409261328a565b9061340a565b906040519163ef8b30f760e01b835260048301526020826024816001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000165afa908115612955575f916137b8575b61379e925061340a565b601454818111156137af5750505f90565b61324d916131c7565b90506020823d6020116137e6575b816137d360209383612e75565b810103126107275761379e915190613794565b3d91506137c6565b91506020823d60201161381c575b8161380960209383612e75565b810103126107275790519061373a61372d565b3d91506137fc565b90506020813d60201161384e575b8161383f60209383612e75565b8101031261072757515f6136d8565b3d9150613832565b90506020813d602011613880575b8161387160209383612e75565b8101031261072757515f613684565b3d9150613864565b6001600160a01b036138986138a1565b16330361072757565b600460206001600160a01b036002541660405192838092635aa6e67560e01b82525afa908115612955575f916138d5575090565b90506020813d602011613901575b816138f060209383612e75565b810103126107275761324d906133f6565b3d91506138e3565b60405163a9059cbb60e01b60208201526001600160a01b0392909216602483015260448083019390935291815261394a91613945606483612e75565b614535565b565b600460206001600160a01b0360025416604051928380926344546b0160e11b82525afa8015612955575f906139b0575b6001600160a01b0391501633148015613997575b1561072757565b506001600160a01b036139a86138a1565b163314613990565b506020813d6020116139e8575b816139ca60209383612e75565b81010312610727576139e36001600160a01b03916133f6565b61397c565b3d91506139bd565b905f602460206001600160a01b0360065416604051928380926370a0823160e01b82523060048301525afa908115612955575f91613ad4575b508381101561325057613a41611d30602492866131c7565b9391505060206001600160a01b0360065416604051938480926370a0823160e01b82523060048301525afa918215612955575f92613aa0575b5081851115613a9a575080613a8f91946131c7565b818082109118021890565b92915050565b9091506020813d602011613acc575b81613abc60209383612e75565b810103126107275751905f613a7a565b3d9150613aaf565b90506020813d602011613afe575b81613aef60209383612e75565b8101031261072757515f613a29565b3d9150613ae2565b6001600160a01b036003541633148015613997571561072757565b5f905f906001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000169060405163266d6a8360e11b8152816004820152602081602481865afa908115612955575f91613d5c575b5015613d4e576040516370a0823160e01b81523060048201527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031691602082602481865afa918215612955575f92613d1a575b505f9360646020926040519687938492635d043b2960e11b845260048401523060248401523060448401525af15f9381613ce6575b50613c18575050505f925f90565b92509250926001936015545f1981146131d457602093600160249201806015555f52601685528260405f2055613c63601554905f90823091815f52601660205260405f205491151590565b50915050959096604051938480926370a0823160e01b82523060048301525afa8015612955575f90613cb2575b613c9a92506131c7565b03613ca3575f90565b63015392e960e01b5f5260045ffd5b506020823d602011613cde575b81613ccc60209383612e75565b8101031261072757613c9a9151613c90565b3d9150613cbf565b9093506020813d602011613d12575b81613d0260209383612e75565b810103126107275751925f613c0a565b3d9150613cf5565b9091506020813d602011613d46575b81613d3660209383612e75565b810103126107275751905f613bd5565b3d9150613d29565b505050505f905f905f905f90565b90506020813d602011613d86575b81613d7760209383612e75565b8101031261072757515f613b7a565b3d9150613d6a565b6001600160a01b036005541633148015613edd575b8015613ec4575b8015613e41575b8015613dbe571561072757565b50600460206001600160a01b0360025416604051928380926344546b0160e11b82525afa8015612955575f90613e01575b6001600160a01b039150163314613990565b506020813d602011613e39575b81613e1b60209383612e75565b8101031261072757613e346001600160a01b03916133f6565b613def565b3d9150613e0e565b50600460206001600160a01b036002541660405192838092630229549960e51b82525afa8015612955575f90613e84575b6001600160a01b039150163314613db1565b506020813d602011613ebc575b81613e9e60209383612e75565b8101031261072757613eb76001600160a01b03916133f6565b613e72565b3d9150613e91565b506001600160a01b03613ed56138a1565b163314613daa565b506001600160a01b03600354163314613da3565b60ff6009541661424657602460206001600160a01b0360065416604051928380926370a0823160e01b82523060048301525afa908115612955575f91614214575b50808210156142105760405162aa589360e21b81523060048201527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316929091602083602481875afa928315612955575f936141dc575b5003908180821091180218613fa461302c565b90506040519063ef8b30f760e01b82526004820152602081602481865afa908115612955575f916141aa575b50604051916308cf664760e21b8352818082109118021860048201526020816024815f865af1908115612955575f91614178575b50601454600e5410614171575b61402861401d826148d6565b9390508391506131c7565b91826140e3575b50604051634a417a5360e01b815291602083600481305afa928315612955575f936140ad575b50916140a87fa1fc548c152f6a7fcc2f4524311cecb1c98943d46e477f75a2950ab22f9688cb9361408461364d565b90604051948594859094939260609260808301968352602083015260408201520152565b0390a1565b92506020833d6020116140db575b816140c860209383612e75565b81010312610727579151916140a8614055565b3d91506140bb565b61410e83827f0000000000000000000000000000000000000000000000000000000000000000614b9e565b604051636e553f6560e01b81526004810184905230602482015290602090829060449082905f905af18015612955571561402f576141639060203d60201161416a575b61415b8183612e75565b810190612f32565b505f61402f565b503d614151565b505f614011565b90506020813d6020116141a2575b8161419360209383612e75565b8101031261072757515f614004565b3d9150614186565b90506020813d6020116141d4575b816141c560209383612e75565b8101031261072757515f613fd0565b3d91506141b8565b9092506020813d602011614208575b816141f860209383612e75565b810103126107275751915f613f91565b3d91506141eb565b5050565b90506020813d60201161423e575b8161422f60209383612e75565b8101031261072757515f613f32565b3d9150614222565b50565b5f196127108209916127108202918280851094039380850394146142de57838211156142c657612710829109815f0382168092046002816003021880820260020302808202600203028082026002030280820260020302808202600203028091026002030293600183805f03040190848311900302920304170290565b50634e487b715f52156003026011186020526024601cfd5b50809250156142eb570490565b634e487b7160e01b5f52601260045260245ffd5b6040519063ef8b30f760e01b825260048201526020816024816001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000165afa908115612955575f91614358575b50151590565b90506020813d602011614382575b8161437360209383612e75565b8101031261072757515f614352565b3d9150614366565b6002600c541461439b576002600c55565b633ee5aeb560e01b5f5260045ffd5b6143b2613152565b156133f2576143bf613437565b156133f25760ff600b541661449d576001600160a01b03600254166040516339ebf82360e01b815230600482015261012081602481855afa908115612955576144159160a0915f9161447e575b500151426131c7565b60085411156136475760206004916040519283809263112c1f9b60e01b82525afa908115612955575f9161444c575b50600a541090565b90506020813d602011614476575b8161446760209383612e75565b8101031261072757515f614444565b3d915061445a565b61449791506101203d81116104bd576104af8183612e75565b5f61440c565b600190565b5f6101006040516144b281612e58565b8281528260208201528260408201528260608201528260808201528260a08201528260c08201528260e0820152015260246101206001600160a01b0360025416604051928380926339ebf82360e01b82523060048301525afa908115612955575f9161451c575090565b61324d91506101203d81116104bd576104af8183612e75565b905f602091828151910182855af115612955575f513d61458057506001600160a01b0381163b155b6145645750565b6001600160a01b0390635274afe760e01b5f521660045260245ffd5b6001141561455d565b6001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001660405163b3d7f6b960e01b8152826004820152602081602481855afa908115612955575f916148a2575b5061460c614611917f00000000000000000000000000000000000000000000000000000000000000009061340a565b612fa7565b6040516370a0823160e01b8152306004820152906020826024817f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03165afa908115612955575f9161486c575b61467792508180821091180218613b21565b959293909193156147465760ff600954161580614864575b614855575f951561478c57604090602482518098819363f2a89c3560e01b83528660048401525af18015612955575f955f91614753575b5093851561474657848692817f39e708ab7fe5d98ea225937bff798a3024cd2e51f4c64e5ab441760702091149938181115f1461473b576147369161470a916131c7565b96855f52600d6020528060405f2055604051938493846040919493926060820195825260208201520152565b0390a2565b50506147365f61470a565b505f945084935083925050565b9550506040853d604011614784575b8161476f60409383612e75565b8101031261072757602085519501515f6146c6565b3d9150614762565b602092949350816147c16147eb93837f0000000000000000000000000000000000000000000000000000000000000000614b9e565b604051636e553f6560e01b8152600481019190915230602482015295869283919082906044820190565b03925af1928315612955575f93614821575b50828082111561481857614810916131c7565b905b5f929190565b50505f90614812565b9092506020813d60201161484d575b8161483d60209383612e75565b810103126107275751915f6147fd565b3d9150614830565b6303ea775760e61b5f5260045ffd5b50831561468f565b90506020823d60201161489a575b8161488760209383612e75565b8101031261072757614677915190614665565b3d915061487a565b90506020813d6020116148ce575b816148bd60209383612e75565b81010312610727575161460c6145dd565b3d91506148b0565b5f905f906148e3816142ff565b15614b93576040516370a0823160e01b81523060048201527f0000000000000000000000000000000000000000000000000000000000000000916001600160a01b0383169190602082602481865afa918215612955575f92614b5f575b506040516370a0823160e01b81523060048201527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031694602082602481895afa918215612955575f92614b29575b506149ff602091846149d55f95966001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000168094614b9e565b604051636e553f6560e01b8152600481019190915230602482015293849283919082906044820190565b03925af19081614afa575b50614a1757505050505f92565b909194506024929593506020600196604051948580926370a0823160e01b82523060048301525afa928315612955575f93614ac4575b50614a5c6020916024946131c7565b94604051938480926370a0823160e01b82523060048301525afa908115612955575f91614a8e575b61324d92506131c7565b90506020823d602011614abc575b81614aa960209383612e75565b810103126107275761324d915190614a84565b3d9150614a9c565b92506020833d602011614af2575b81614adf60209383612e75565b8101031261072757915191614a5c614a4d565b3d9150614ad2565b6020813d602011614b21575b81614b1360209383612e75565b810103126107275751614a0a565b3d9150614b06565b91506020823d602011614b57575b81614b4460209383612e75565b81010312610727579051906149ff614996565b3d9150614b37565b9091506020813d602011614b8b575b81614b7b60209383612e75565b810103126107275751905f614940565b3d9150614b6e565b5050505f905f905f90565b60405163095ea7b360e01b60208083019182526001600160a01b0385166024840152604480840196909652948252929390925f90614bdd606486612e75565b84519082855af15f513d82614c3a575b505015614bf957505050565b61394561394a936001600160a01b036040519163095ea7b360e01b60208401521660248201525f604482015260448152614c34606482612e75565b82614535565b909150614c5757506001600160a01b0381163b15155b5f80614bed565b600114614c5056fe290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e563a164736f6c634300081d000a0000000000000000000000009e3e70f4d09bcfe08f456c426eb90f7aa6f70df2000000000000000000000000dd0f28e19c1780eb6396170735d45153d261490d

Deployed Bytecode

0x60806040526004361015610011575f80fd5b5f5f3560e01c806301504adf14612dad57806301681a6214612bf657806303ee438c14612b4257806306fdde0314612a1d578063083f01fa146127df5780630a28a477146127c05780630ada4dab146127605780630d674c731461273657806311bc8245146126bd578063169070eb146126a05780631f1fcd51146126795780631fe4a68614612652578063203618141461262b578063217b5042146123fa57806322f3e2d4146123df578063258294101461239657806328b7ccf7146123785780632a083ca31461235a5780632e1a7d4d146122f657806339a172a8146122a85780633d61b286146122675780633e65164a14612249578063440368a3146121b25780634641257d14611c985780634883a19c14611c7a5780634a417a5314611c5f5780634cdad50614611c405780635641ec0314611c1d57806361bc2e0814611bbe578063650d188014611ba25780636718835f14611b7f5780636c610dcc1461169d5780637086f5f214611b61578063748747e614611ae957806374f1170314611899578063750521f5146116ba578063780022a01461169d5780637defd5b01461160b57806380f5219914611659578063826cddf61461162f57806386e3772e146116105780638e6350e21461160b57806391796aa7146115a257806395326e2d1461158757806395e80c50146115695780639bc64285146114e15780639ec5a894146114ba5780639f450b5a14611441578063a2be4fbb14611417578063a763cf5b146113f4578063aa5480cf146113d6578063ac00ff2614611375578063aced16611461134e578063b252720b14611324578063b3d7f6b9146112b3578063b6042b341461126f578063b8fe8d5f14611232578063c21fa571146111d1578063c7b9d53014611159578063ca1dd22e146110b4578063cccaf72c14611070578063ce5494bb14610cfc578063d4b1b76f14610cb8578063dd224c1a14610bd0578063e463b9ac14610947578063ec38a86214610786578063ed882c2b1461075d578063eef09bad1461073f578063ef8b30f71461068a578063efbb5cb014610667578063f017c92f14610619578063fbfa77cf146105f2578063fcf2d0ad146103985763fe2508a614610348575f80fd5b34610395576020366003190112610395577fe5ef7832c564a10cbe7b4f1e01ac33a406cb63fcf430a97a9af8616d150af5f3602060043561038761394c565b80600a55604051908152a180f35b80fd5b50346103955780600319360112610395576001600160a01b0360035416331480156105d9575b8015610556575b80156104cf575b1561039557600160ff196009541617600955806001600160a01b03600254166040516339ebf82360e01b815230600482015261012081602481855afa80156104c4576040918491610495575b500151610448575b50807f97e963041e952738788b9d4871d854d282065b8f90a464928d6528f2e9a4fd0b91a180f35b803b156104925781809160046040518094819363507257cd60e11b83525af180156104875715610420578161047c91612e75565b61039557805f610420565b6040513d84823e3d90fd5b50fd5b6104b791506101203d81116104bd575b6104af8183612e75565b8101906130e1565b5f610418565b503d6104a5565b6040513d85823e3d90fd5b50600460206001600160a01b0360025416604051928380926344546b0160e11b82525afa8015610487578290610512575b6001600160a01b0391501633146103cc565b506020813d60201161054e575b8161052c60209383612e75565b8101031261054a576105456001600160a01b03916133f6565b610500565b5080fd5b3d915061051f565b50600460206001600160a01b036002541660405192838092630229549960e51b82525afa8015610487578290610599575b6001600160a01b0391501633146103c5565b506020813d6020116105d1575b816105b360209383612e75565b8101031261054a576105cc6001600160a01b03916133f6565b610587565b3d91506105a6565b506001600160a01b036105ea6138a1565b1633146103be565b503461039557806003193601126103955760206001600160a01b0360025416604051908152f35b5034610395576020366003190112610395577f5430e11864ad7aa9775b07d12657fe52df9aa2ba734355bd8ef8747be2c800c56020600435610659613b06565b80600855604051908152a180f35b5034610395578060031936011261039557602061068261364d565b604051908152f35b50346103955760203660031901126103955760405163ef8b30f760e01b81526004803590820152906020826024817f000000000000000000000000dd0f28e19c1780eb6396170735d45153d261490d6001600160a01b03165afa90811561073357906106fc575b602090604051908152f35b506020813d60201161072b575b8161071660209383612e75565b8101031261072757602090516106f1565b5f80fd5b3d9150610709565b604051903d90823e3d90fd5b50346103955780600319360112610395576020601354604051908152f35b503461039557602036600319011261039557602061077c6004356134ae565b6040519015158152f35b5034610395576020366003190112610395576107a0612dda565b6001600160a01b036107b06138a1565b1633148015610933575b1561054a576001600160a01b0316801561054a578160206001600160a01b036002541660446001600160a01b0360045416604051948593849263095ea7b360e01b845260048401528160248401525af180156104c4576108fc575b50806bffffffffffffffffffffffff60a01b60045416176004558160206001600160a01b036002541660446040518094819363095ea7b360e01b83528760048401525f1960248401525af180156104c45761089a575b5060207fafbb66abf8f3b719799940473a4052a3717cdd8e40fb6c8a3faadab316b1a06991604051908152a180f35b6020813d6020116108f4575b816108b360209383612e75565b810103126108f0577fafbb66abf8f3b719799940473a4052a3717cdd8e40fb6c8a3faadab316b1a069916108e86020926131e8565b50915061086b565b8280fd5b3d91506108a6565b6020813d60201161092b575b8161091560209383612e75565b810103126108f057610926906131e8565b610815565b3d9150610908565b506001600160a01b036003541633146107ba565b5034610395578060031936011261039557806040805161096681612e28565b6060815260606020820152015260405161097f81612e28565b60405180816020600f549283815201600f86527f8d1108e10bcb7c27dddfc02ed9d693a074039d026cf4ea4240b40f7d581ac80292865b818110610bae5750506109cb92500382612e75565b81526010549067ffffffffffffffff8211610b9a5760208260051b01916109f56040519384612e75565b808352601084527f1b6847dc741a1b0cd08d278845f9d819d87b734759afb55fe2de5cb82a9ae6729084602085015b828210610af85750505050602081019182526011549260408201938452604051926020845260808401925192606060208601528351809152602060a08601940190835b818110610ad9575050505191601f19848203016040850152825190818152602081016020808460051b840101950193915b838310610aac578751606088015286860387f35b9091929394602080610aca600193601f198682030187528951612e97565b97019301930191939290610a98565b82516001600160a01b0316865260209586019590920191600101610a67565b604051878554610b0781612df0565b8084529060018116908115610b775750600114610b41575b5060019282610b3385946020940382612e75565b815201940191019092610a24565b868a5260208a208a92505b818310610b6157505081016020016001610b1f565b6001816020925483868801015201920191610b4c565b60ff191660208581019190915291151560051b8401909101915060019050610b1f565b634e487b7160e01b83526041600452602483fd5b84546001600160a01b03168352600194850194869450602090930192016109b6565b5034610395578060031936011261039557610be9613888565b60405163de75b13760e01b8152600f6004820152818160248173ecb5d39de95345ae62c9bd8c87ad1297a32bcfa05af49182156107335780918193610c4d575b5050610c4960405192839215158352604060208401526040830190612e97565b0390f35b915091503d8083833e610c608183612e75565b81016040828203126108f057610c75826131e8565b9160208101519067ffffffffffffffff8211610cb4570181601f82011215610cb057610ca8929350602081519101612f41565b905f80610c29565b8380fd5b8480fd5b503461039557806003193601126103955760206040516001600160a01b037f0000000000000000000000008e53cdaa89381c203a074fb3388f65936358f200168152f35b503461039557602036600319011261039557610d16612dda565b6001600160a01b03600254168033036108f05760405163fbfa77cf60e01b81526020816004816001600160a01b0387165afa8015610f2a578490611027575b6001600160a01b039150160361054a576040516326e0a6bd60e01b81523060048201527f0000000000000000000000008e53cdaa89381c203a074fb3388f65936358f2006001600160a01b031690602081602481855afa908115610f2a578491610ff5575b50610fe6576020602491604051928380926356f03f5160e01b82523060048301525afa9081156104c4578391610fb4575b50610fa5576040516370a0823160e01b81523060048201527f000000000000000000000000dd0f28e19c1780eb6396170735d45153d261490d6020826024816001600160a01b0385165afa908115610f2a5783908592610f6d575b610e509350613909565b6040516370a0823160e01b81523060048201527f000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb486020826024816001600160a01b0385165afa908115610f2a5783908592610f35575b610eb09350613909565b6001600160a01b0360065416604051916370a0823160e01b8352306004840152602083602481855afa918215610f2a578492610ef4575b610ef19350613909565b80f35b91506020833d602011610f22575b81610f0f60209383612e75565b8101031261072757610ef1925191610ee7565b3d9150610f02565b6040513d86823e3d90fd5b9150506020823d602011610f65575b81610f5160209383612e75565b810103126107275782610eb0925191610ea6565b3d9150610f44565b9150506020823d602011610f9d575b81610f8960209383612e75565b810103126107275782610e50925191610e46565b3d9150610f7c565b6306def89560e51b8252600482fd5b90506020813d602011610fde575b81610fcf60209383612e75565b8101031261072757515f610deb565b3d9150610fc2565b6306def89560e51b8352600483fd5b90506020813d60201161101f575b8161101060209383612e75565b8101031261072757515f610dba565b3d9150611003565b506020813d602011611068575b8161104160209383612e75565b81010312610cb057516001600160a01b0381168103610cb0576001600160a01b0390610d55565b3d9150611034565b503461039557806003193601126103955760206040516001600160a01b037f000000000000000000000000dd0f28e19c1780eb6396170735d45153d261490d168152f35b503461039557604036600319011261039557806110cf612dda565b60243590811515809203611155576110e5613888565b73ecb5d39de95345ae62c9bd8c87ad1297a32bcfa090813b156111535760646001600160a01b039185936040519586948593632f2a069760e21b8552600f600486015216602484015260448301525af48015610487576111425750f35b8161114c91612e75565b6103955780f35b505b5050fd5b5034610395576020366003190112610395576001600160a01b0361117b612dda565b611183613b06565b16801561054a576020817f352ececae6d7d1e6d26bcf2c549dfd55be1637e9b22dc0cf3b71ddb36097a6b4926bffffffffffffffffffffffff60a01b6003541617600355604051908152a180f35b50346103955780600319360112610395576111ea613b06565b8073ecb5d39de95345ae62c9bd8c87ad1297a32bcfa0803b15610492578160249160405192838092636b8197e560e01b8252600f60048301525af48015610487576111425750f35b50346103955760203660031901126103955760ff60406020926001600160a01b0361125b612dda565b168152601284522054166040519015158152f35b503461039557806003193601126103955760206040516001600160a01b037f000000000000000000000000dd0f28e19c1780eb6396170735d45153d261490d168152f35b50346103955760203660031901126103955760405163b3d7f6b960e01b81526004803590820152906020826024817f000000000000000000000000dd0f28e19c1780eb6396170735d45153d261490d6001600160a01b03165afa90811561073357906106fc57602090604051908152f35b503461039557806003193601126103955760206001600160a01b0360015460081c16604051908152f35b503461039557806003193601126103955760206001600160a01b0360055416604051908152f35b50346103955760203660031901126103955761138f612eec565b61139761394c565b7ff769f6bf659bbbdabf212d830720ce893eedc57f25ebb8e44edf5b300618a35b602060405192151592838152a160ff80196001541691161760015580f35b50346103955780600319360112610395576020600a54604051908152f35b5034610395578060031936011261039557602060ff600b54166040519015158152f35b50346103955760203660031901126103955760406020916004358152601683522054604051908152f35b5034610395576020366003190112610395577f711be97287cb9ec921887b9be36e148e1a27c6b158547b22b9704ffc54447a0f602061147e612dda565b61148661394c565b600954610100600160a81b038260081b1690610100600160a81b031916176009556001600160a01b0360405191168152a180f35b503461039557806003193601126103955760206001600160a01b0360045416604051908152f35b5034610395576020366003190112610395576004353033036115375760407f6355917f85a4e327b3edb1bd6daabf31a3e1f69e4d8878b376ac256a613ff34b91600e549080600e5582519182526020820152a180f35b604051632774044360e11b815260206004820152600960248201526827a7262cafa9a2a62360b91b6044820152606490fd5b50346103955780600319360112610395576020600754604051908152f35b5034610395578060031936011261039557602061077c613437565b503461039557602036600319011261039557303303611537578073ecb5d39de95345ae62c9bd8c87ad1297a32bcfa0803b15610492578160449160405192838092638f077d3d60e01b8252600f600483015260043560248301525af48015610487576111425750f35b612efb565b503461039557602036600319011261039557602061077c600435613417565b503461039557806003193601126103955760206001600160a01b0360095460081c16604051908152f35b503461039557806003193601126103955760206040516001600160a01b037f000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48168152f35b503461039557602036600319011261039557602090604051908152f35b5034610395576020366003190112610395576004359067ffffffffffffffff821161039557366023830112156103955781600401359167ffffffffffffffff831161054a57366024848301011161054a57611713613b06565b61171d8254612df0565b601f8111611832575b508192601f81116001146117a0578083947f300e67d5a415b6d015a471d9c7b95dd58f3e8290af965e84e0f845de2996dda69491611792575b508160011b905f198360031b1c19161784555b61178c604051928392602084526024602085019201612f87565b0390a180f35b60249150830101355f61175f565b828052601f198116935f516020614c605f395f51905f5290845b8681106118175750827f300e67d5a415b6d015a471d9c7b95dd58f3e8290af965e84e0f845de2996dda69596106117fb575b5050600181811b018455611772565b8301602401355f19600384901b60f8161c191690555f806117ec565b909160206001819260248689010135815501930191016117ba565b828052601f840160051c5f516020614c605f395f51905f52019060208510611884575b601f0160051c5f516020614c605f395f51905f5201905b8181106118795750611726565b83815560010161186c565b5f516020614c605f395f51905f529150611855565b5034610395576020366003190112610395576004356001600160a01b037f0000000000000000000000008e53cdaa89381c203a074fb3388f65936358f20016803303611ada579060c06024926118ed61438a565b60405193848092634e1f0f1960e01b82528560048301525afa80156104c45783928491611a8d575b50611a7e57808352600d6020526119436040842054905f90823091815f52601660205260405f205491151590565b92509250505f14611a6a576040516370a0823160e01b8152306004820152936020856024817f000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb486001600160a01b03165afa9081156107335790611a37575b6020945081808210911802188281105f14611a31576119c4816119cc92946131c7565b60145461340a565b6014555b6119fb82337f000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48613909565b7fb7cc6375d00bb7c09ac9b49f423f777e510a13d6fa2694da2ca3c850742fc8e883604051848152a26001600c55604051908152f35b506119d0565b506020843d602011611a62575b81611a5160209383612e75565b8101031261072757602093516119a1565b3d9150611a44565b634e487b7160e01b84526001600452602484fd5b6332edbb0f60e21b8352600483fd5b92505060c0823d60c011611ad2575b81611aa960c09383612e75565b810103126108f057611aba826133f6565b50611acc60a0604084015193016131e8565b5f611915565b3d9150611a9c565b632f220d7560e21b8352600483fd5b5034610395576020366003190112610395576001600160a01b03611b0b612dda565b611b13613b06565b16801561054a576020817f2f202ddb4a2e345f6323ed90f8fc8559d770a7abbbeee84dde8aca3351fe7154926bffffffffffffffffffffffff60a01b6005541617600555604051908152a180f35b50346103955780600319360112610395576020600e54604051908152f35b5034610395578060031936011261039557602060ff600154166040519015158152f35b503461039557602036600319011261039557602061077c6132e0565b50346103955760203660031901126103955760a06001600160a01b03611bf9600435905f90823091815f52601660205260405f205491151590565b93949190926040519586526020860152166040840152606083015215156080820152f35b5034610395578060031936011261039557602060ff600954166040519015158152f35b503461039557602036600319011261039557602061068260043561328a565b503461039557806003193601126103955760206106826131f5565b50346103955780600319360112610395576020601454604051908152f35b5034610395578060031936011261039557611cb1613d8e565b8060048160206001600160a01b03600254166040519384809263bf3759b560e01b82525afa9182156104c457839261217e575b5060095460ff161561209257602490611cfb61438a565b611d35611d30611d0961364d565b7f00000000000000000000000000000000000000000000000000000000000f42409061340a565b614589565b50505060206001600160a01b0360065416604051938480926370a0823160e01b82523060048301525afa918215610f2a57849261205e575b506001600c55828210156120305750611d89611d9091836131c7565b80926131c7565b925b60ff19600b5416600b557f6ad28df1b554fa6cacd46ae82fa811748d53798feeb437ddf234bf30839533196020604051858152a16001600160a01b0360025416936040516339ebf82360e01b815230600482015261012081602481895afa908115611f5a579060c0918691612011575b5001516040516328766ebf60e21b8152600481018490526024810185905260448101839052956020908790606490829089905af1958615611f5a578596611fdd575b50611e4d61438a565b611e5686613ef1565b6001600c5560015460ff811680611fc8575b15611f65575060206001600160a01b0360015460081c169160a46040518094819363c70fa00b60e01b83528860048401528960248401528760448401528b606484015260848301525afa908115611f5a578591611f20575b5015611f11577f4c0f499ffe6befa0ca7c826b0916cf87bea98de658013e76938489368d60d509939461178c915b604051948594859094939260609260808301968352602083015260408201520152565b63796b712960e01b8452600484fd5b90506020813d602011611f52575b81611f3b60209383612e75565b81010312610cb457611f4c906131e8565b5f611ec0565b3d9150611f2e565b6040513d87823e3d90fd5b7f4c0f499ffe6befa0ca7c826b0916cf87bea98de658013e76938489368d60d5099596915091600161178c937ff769f6bf659bbbdabf212d830720ce893eedc57f25ebb8e44edf5b300618a35b6020604051848152a160ff191617600155611eee565b506001600160a01b038160081c161515611e68565b9095506020813d602011612009575b81611ff960209383612e75565b810103126107275751945f611e44565b3d9150611fec565b61202a91506101203d81116104bd576104af8183612e75565b5f611e02565b9190818111612045575b5081611d90916131c7565b819450612056611d909284926131c7565b94915061203a565b9091506020813d60201161208a575b8161207a60209383612e75565b810103126107275751905f611d6d565b3d915061206d565b5061212b92506120fa6120ed6120cf92846120ab61364d565b916120d560c06120b96144a2565b01519184808210911802841880978180966131c7565b926131c7565b908181111561216f576120e892506131c7565b61340a565b6120f561438a565b6139f0565b506001600c5561210861364d565b61213160c06121156144a2565b01519383808210911802831880968180946131c7565b946131c7565b918284111561215b5761214892916120cf916131c7565b90818082109118021881925b9290611d92565b5050906121699083926131c7565b92612154565b612178916131c7565b5061340a565b9091506020813d6020116121aa575b8161219a60209383612e75565b810103126107275751905f611ce4565b3d915061218d565b50346103955780600319360112610395576121cb613d8e565b600460206001600160a01b03600254166040519283809263bf3759b560e01b82525afa8015610487578290612215575b61220d915061220861438a565b613ef1565b6001600c5580f35b506020813d602011612241575b8161222f60209383612e75565b810103126107275761220d90516121fb565b3d9150612222565b5034610395578060031936011261039557602060405162093a808152f35b503461039557602036600319011261039557612281613888565b608061228e600435613b21565b916040519315158452602084015260408301526060820152f35b5034610395576020366003190112610395577fbb2c369a0355a34b02ab5fce0643150c87e1c8dfe7c918d465591879f57948b160206004356122e8613b06565b80600755604051908152a180f35b5034610395576020366003190112610395576001600160a01b0360025416330361234b5761232261438a565b60206106826123326004356139f0565b91906001600c55336001600160a01b0360065416613909565b6362df054560e01b8152600490fd5b5034610395578060031936011261039557602060405162278d008152f35b50346103955780600319360112610395576020600854604051908152f35b503461039557806003193601126103955750610c496040516123b9604082612e75565b600581526418171a171b60d91b6020820152604051918291602083526020830190612e97565b5034610395578060031936011261039557602061077c613152565b503461039557602036600319011261039557600435612417613888565b6040516370a0823160e01b81523060048201527f000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb486001600160a01b03811691602081602481865afa908115611f5a5785916125f9575b5083156125ea576014548085116125d357508084116125bc5750908391604460206001600160a01b037f0000000000000000000000008e53cdaa89381c203a074fb3388f65936358f200169360405192838092636eb1769f60e11b82523060048301528760248301525afa8015610f2a5785918591612580575b506124f76124fd9392859261340a565b91614b9e565b803b1561054a578180916024604051809481936323afca2560e11b83528860048401525af180156104875761256b575b507f3ad20ff068059496cdbf0353dff5d6db4591bce40b815e454cf29d4a933c5c8a60208361255e816014546131c7565b601455604051908152a180f35b8161257591612e75565b61054a57815f61252d565b92945050506020813d6020116125b4575b8161259e60209383612e75565b81010312610727575184929084906124f76124e7565b3d9150612591565b630c9f8b8560e01b85526004849052602452604484fd5b630ef9f73160e31b86526004859052602452604485fd5b630cb65c7760e21b8552600485fd5b90506020813d602011612623575b8161261460209383612e75565b8101031261072757515f61246d565b3d9150612607565b5034610395578060031936011261039557604061264661302c565b82519182526020820152f35b503461039557806003193601126103955760206001600160a01b0360035416604051908152f35b503461039557806003193601126103955760206001600160a01b0360065416604051908152f35b5034610395578060031936011261039557602060405161a8c08152f35b5034610395576020366003190112610395576126d7612dda565b6126df61394c565b7fc8db9c35f716b87af1fbb83f03c78646061931269301fd7ba6dcf189b4cdc2fc60206040516001600160a01b0384168152a1610100600160a81b036001549160081b1690610100600160a81b0319161760015580f35b50346103955760203660031901126103955760406020916004358152600d83522054604051908152f35b5034610395576020366003190112610395577f6ad28df1b554fa6cacd46ae82fa811748d53798feeb437ddf234bf3083953319602061279d612eec565b6127a561394c565b151560ff19600b541660ff821617600b55604051908152a180f35b5034610395576020366003190112610395576020610682600435612fa7565b50346107275760403660031901126107275760043567ffffffffffffffff811161072757612811903690600401612ebb565b9060243567ffffffffffffffff811161072757612832903690600401612ebb565b9061283b613888565b604051612849606082612e75565b6002815260208101926040368537639bc6428560e01b61286883612f22565b526391796aa760e01b61287a83612f15565b5273ecb5d39de95345ae62c9bd8c87ad1297a32bcfa094853b1561072757604051639481232960e01b8152600f600482015260806024820152608481018890529687959460a487019493909290915f905b8082106129e657505050600319868503016044870152808452602084019060208160051b86010194835f91601e1982360301905b848410612983575050505050505060209060031985840301606486015251918281520191905f5b818110612960575050509181805f9403915af4801561295557612947575080f35b61295391505f90612e75565b005b6040513d5f823e3d90fd5b82516001600160e01b031916845286945060209384019390920191600101612926565b91939598995091939596601f19828203018752883583811215610727578401906020823592019167ffffffffffffffff8111610727578036038313610727576129d26020928392600195612f87565b9a0197019401918b999897969593916128ff565b91939697509193948335906001600160a01b03821680920361072757602081600193829352019401920189979693959492916128cb565b34610727575f366003190112610727576040516306fdde0360e01b81525f816004817f000000000000000000000000dd0f28e19c1780eb6396170735d45153d261490d6001600160a01b03165afa908115612955575f91612ae3575b610c496020612acf6036856040519384917f53747261746567794d6f7270686f56315661756c742000000000000000000000828401528051918291018484015e81015f838201520301601f198101835282612e75565b604051918291602083526020830190612e97565b90503d805f833e612af48183612e75565b8101906020818303126107275780519067ffffffffffffffff821161072757019080601f8301121561072757612acf6036612b396020938585610c4997519101612f41565b93505050612a79565b34610727575f366003190112610727576040515f5f54612b6181612df0565b8084529060018116908115612bd25750600114612b89575b610c4983612acf81850382612e75565b5f8080525f516020614c605f395f51905f52939250905b808210612bb857509091508101602001612acf612b79565b919260018160209254838588010152019101909291612ba0565b60ff191660208086019190915291151560051b84019091019150612acf9050612b79565b3461072757602036600319011261072757612c0f612dda565b612c17613888565b6001600160a01b0380600654169116908114612d9e576001600160a01b03600254168114612d8f5760405190612c4e606083612e75565b6002825260403660208401376001600160a01b037f000000000000000000000000dd0f28e19c1780eb6396170735d45153d261490d16612c8d83612f15565b526001600160a01b037f000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4816612cc183612f22565b525f5b8251811015612d1a578251811015612d06576001600160a01b0360208260051b85010151168214612cf757600101612cc4565b633502ad7560e11b5f5260045ffd5b634e487b7160e01b5f52603260045260245ffd5b50612d236138a1565b6040516370a0823160e01b81523060048201529190602083602481855afa918215612955575f92612d59575b6129539350613909565b91506020833d602011612d87575b81612d7460209383612e75565b8101031261072757612953925191612d4f565b3d9150612d67565b63578dc2d360e01b5f5260045ffd5b63371188b760e01b5f5260045ffd5b3461072757602036600319011261072757612dc6613888565b634e487b7160e01b5f52600160045260245ffd5b600435906001600160a01b038216820361072757565b90600182811c92168015612e1e575b6020831014612e0a57565b634e487b7160e01b5f52602260045260245ffd5b91607f1691612dff565b6060810190811067ffffffffffffffff821117612e4457604052565b634e487b7160e01b5f52604160045260245ffd5b610120810190811067ffffffffffffffff821117612e4457604052565b90601f8019910116810190811067ffffffffffffffff821117612e4457604052565b805180835260209291819084018484015e5f828201840152601f01601f1916010190565b9181601f840112156107275782359167ffffffffffffffff8311610727576020808501948460051b01011161072757565b60043590811515820361072757565b34610727575f3660031901126107275760206040515f8152f35b805115612d065760200190565b805160011015612d065760400190565b90816020910312610727575190565b92919267ffffffffffffffff8211612e445760405191612f6b601f8201601f191660200184612e75565b829481845281830111610727578281602093845f96015e010152565b908060209392818452848401375f828201840152601f01601f1916010190565b60405190630a28a47760e01b825260048201526020816024816001600160a01b037f000000000000000000000000dd0f28e19c1780eb6396170735d45153d261490d165afa908115612955575f91612ffd575090565b90506020813d602011613024575b8161301860209383612e75565b81010312610727575190565b3d915061300b565b60405163402d267d60e01b8152306004820152906020826024817f000000000000000000000000dd0f28e19c1780eb6396170735d45153d261490d6001600160a01b03165afa918215612955575f926130ad575b507f00000000000000000000000000000000000000000000000000038d7ea4c68000808318818410021890565b9091506020813d6020116130d9575b816130c960209383612e75565b810103126107275751905f613080565b3d91506130bc565b908161012091031261072757610100604051916130fd83612e58565b805183526020810151602084015260408101516040840152606081015160608401526080810151608084015260a081015160a084015260c081015160c084015260e081015160e0840152015161010082015290565b60246101206001600160a01b0360025416604051928380926339ebf82360e01b82523060048301525afa8015612955576040915f916131a8575b5001511580159061319a5790565b506131a361364d565b151590565b6131c191506101203d81116104bd576104af8183612e75565b5f61318c565b919082039182116131d457565b634e487b7160e01b5f52601160045260245ffd5b5190811515820361072757565b60246131ff61364d565b60206001600160a01b0360065416604051938480926370a0823160e01b82523060048301525afa918215612955575f92613256575b5080156132505761324861324d92826131c7565b614249565b90565b50505f90565b9091506020813d602011613282575b8161327260209383612e75565b810103126107275751905f613234565b3d9150613265565b6040519063266d6a8360e11b825260048201526020816024816001600160a01b037f000000000000000000000000dd0f28e19c1780eb6396170735d45153d261490d165afa908115612955575f91612ffd575090565b60ff600954166133f257602460206001600160a01b0360065416604051928380926370a0823160e01b82523060048301525afa908115612955575f916133c0575b5080156133bb57604051630b1bbb3160e41b8152906020826004817f0000000000000000000000008e53cdaa89381c203a074fb3388f65936358f2006001600160a01b03165afa8015612955575f90613387575b61324d925081808210911802186142ff565b506020823d6020116133b3575b816133a160209383612e75565b810103126107275761324d9151613375565b3d9150613394565b505f90565b90506020813d6020116133ea575b816133db60209383612e75565b8101031261072757515f613321565b3d91506133ce565b5f90565b51906001600160a01b038216820361072757565b919082018092116131d457565b5f52600d60205260405f205480156133bb575f8190526016602052151590565b60095460081c6001600160a01b0316806134515750600190565b602060049160405192838092630d2a79d760e21b82525afa908115612955575f9161347a575090565b90506020813d6020116134a6575b8161349560209383612e75565b810103126107275761324d906131e8565b3d9150613488565b506134b76143aa565b60a06134c16144a2565b015190156133bb5760ff600b5416613647576134dd81426131c7565b60085411156136475742146133f2576134f461364d565b60c06134fe6144a2565b015160025460405163112c1f9b60e01b81529192906001600160a01b0316602083600481845afa928315612955575f93613612575b5060206004916040519283809263bf3759b560e01b82525afa908115612955575f916135dc575b509261357982856120cf9661359195115f146135d157958680926131c7565b5f918181116135c0575b5050600a5460011c9361340a565b90808210156135a957906135a4916131c7565b101590565b8082116135b7575050505f90565b6135a4916131c7565b6135ca92506131c7565b5f80613583565b5080958680926131c7565b9390506020843d60201161360a575b816135f860209383612e75565b8101031261072757925161357961355a565b3d91506135eb565b9092506020813d60201161363f575b8161362e60209383612e75565b810103126107275751916020613533565b3d9150613621565b50600190565b602460206001600160a01b0360065416604051928380926370a0823160e01b82523060048301525afa908115612955575f91613856575b506040516370a0823160e01b81523060048201526020816024817f000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb486001600160a01b03165afa908115612955575f91613824575b506040516370a0823160e01b8152306004820152906020826024817f000000000000000000000000dd0f28e19c1780eb6396170735d45153d261490d6001600160a01b03165afa918215612955575f926137ee575b5061373a6137409261328a565b9061340a565b906040519163ef8b30f760e01b835260048301526020826024816001600160a01b037f0000000000000000000000008e53cdaa89381c203a074fb3388f65936358f200165afa908115612955575f916137b8575b61379e925061340a565b601454818111156137af5750505f90565b61324d916131c7565b90506020823d6020116137e6575b816137d360209383612e75565b810103126107275761379e915190613794565b3d91506137c6565b91506020823d60201161381c575b8161380960209383612e75565b810103126107275790519061373a61372d565b3d91506137fc565b90506020813d60201161384e575b8161383f60209383612e75565b8101031261072757515f6136d8565b3d9150613832565b90506020813d602011613880575b8161387160209383612e75565b8101031261072757515f613684565b3d9150613864565b6001600160a01b036138986138a1565b16330361072757565b600460206001600160a01b036002541660405192838092635aa6e67560e01b82525afa908115612955575f916138d5575090565b90506020813d602011613901575b816138f060209383612e75565b810103126107275761324d906133f6565b3d91506138e3565b60405163a9059cbb60e01b60208201526001600160a01b0392909216602483015260448083019390935291815261394a91613945606483612e75565b614535565b565b600460206001600160a01b0360025416604051928380926344546b0160e11b82525afa8015612955575f906139b0575b6001600160a01b0391501633148015613997575b1561072757565b506001600160a01b036139a86138a1565b163314613990565b506020813d6020116139e8575b816139ca60209383612e75565b81010312610727576139e36001600160a01b03916133f6565b61397c565b3d91506139bd565b905f602460206001600160a01b0360065416604051928380926370a0823160e01b82523060048301525afa908115612955575f91613ad4575b508381101561325057613a41611d30602492866131c7565b9391505060206001600160a01b0360065416604051938480926370a0823160e01b82523060048301525afa918215612955575f92613aa0575b5081851115613a9a575080613a8f91946131c7565b818082109118021890565b92915050565b9091506020813d602011613acc575b81613abc60209383612e75565b810103126107275751905f613a7a565b3d9150613aaf565b90506020813d602011613afe575b81613aef60209383612e75565b8101031261072757515f613a29565b3d9150613ae2565b6001600160a01b036003541633148015613997571561072757565b5f905f906001600160a01b037f000000000000000000000000dd0f28e19c1780eb6396170735d45153d261490d169060405163266d6a8360e11b8152816004820152602081602481865afa908115612955575f91613d5c575b5015613d4e576040516370a0823160e01b81523060048201527f000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb486001600160a01b031691602082602481865afa918215612955575f92613d1a575b505f9360646020926040519687938492635d043b2960e11b845260048401523060248401523060448401525af15f9381613ce6575b50613c18575050505f925f90565b92509250926001936015545f1981146131d457602093600160249201806015555f52601685528260405f2055613c63601554905f90823091815f52601660205260405f205491151590565b50915050959096604051938480926370a0823160e01b82523060048301525afa8015612955575f90613cb2575b613c9a92506131c7565b03613ca3575f90565b63015392e960e01b5f5260045ffd5b506020823d602011613cde575b81613ccc60209383612e75565b8101031261072757613c9a9151613c90565b3d9150613cbf565b9093506020813d602011613d12575b81613d0260209383612e75565b810103126107275751925f613c0a565b3d9150613cf5565b9091506020813d602011613d46575b81613d3660209383612e75565b810103126107275751905f613bd5565b3d9150613d29565b505050505f905f905f905f90565b90506020813d602011613d86575b81613d7760209383612e75565b8101031261072757515f613b7a565b3d9150613d6a565b6001600160a01b036005541633148015613edd575b8015613ec4575b8015613e41575b8015613dbe571561072757565b50600460206001600160a01b0360025416604051928380926344546b0160e11b82525afa8015612955575f90613e01575b6001600160a01b039150163314613990565b506020813d602011613e39575b81613e1b60209383612e75565b8101031261072757613e346001600160a01b03916133f6565b613def565b3d9150613e0e565b50600460206001600160a01b036002541660405192838092630229549960e51b82525afa8015612955575f90613e84575b6001600160a01b039150163314613db1565b506020813d602011613ebc575b81613e9e60209383612e75565b8101031261072757613eb76001600160a01b03916133f6565b613e72565b3d9150613e91565b506001600160a01b03613ed56138a1565b163314613daa565b506001600160a01b03600354163314613da3565b60ff6009541661424657602460206001600160a01b0360065416604051928380926370a0823160e01b82523060048301525afa908115612955575f91614214575b50808210156142105760405162aa589360e21b81523060048201527f0000000000000000000000008e53cdaa89381c203a074fb3388f65936358f2006001600160a01b0316929091602083602481875afa928315612955575f936141dc575b5003908180821091180218613fa461302c565b90506040519063ef8b30f760e01b82526004820152602081602481865afa908115612955575f916141aa575b50604051916308cf664760e21b8352818082109118021860048201526020816024815f865af1908115612955575f91614178575b50601454600e5410614171575b61402861401d826148d6565b9390508391506131c7565b91826140e3575b50604051634a417a5360e01b815291602083600481305afa928315612955575f936140ad575b50916140a87fa1fc548c152f6a7fcc2f4524311cecb1c98943d46e477f75a2950ab22f9688cb9361408461364d565b90604051948594859094939260609260808301968352602083015260408201520152565b0390a1565b92506020833d6020116140db575b816140c860209383612e75565b81010312610727579151916140a8614055565b3d91506140bb565b61410e83827f000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48614b9e565b604051636e553f6560e01b81526004810184905230602482015290602090829060449082905f905af18015612955571561402f576141639060203d60201161416a575b61415b8183612e75565b810190612f32565b505f61402f565b503d614151565b505f614011565b90506020813d6020116141a2575b8161419360209383612e75565b8101031261072757515f614004565b3d9150614186565b90506020813d6020116141d4575b816141c560209383612e75565b8101031261072757515f613fd0565b3d91506141b8565b9092506020813d602011614208575b816141f860209383612e75565b810103126107275751915f613f91565b3d91506141eb565b5050565b90506020813d60201161423e575b8161422f60209383612e75565b8101031261072757515f613f32565b3d9150614222565b50565b5f196127108209916127108202918280851094039380850394146142de57838211156142c657612710829109815f0382168092046002816003021880820260020302808202600203028082026002030280820260020302808202600203028091026002030293600183805f03040190848311900302920304170290565b50634e487b715f52156003026011186020526024601cfd5b50809250156142eb570490565b634e487b7160e01b5f52601260045260245ffd5b6040519063ef8b30f760e01b825260048201526020816024816001600160a01b037f000000000000000000000000dd0f28e19c1780eb6396170735d45153d261490d165afa908115612955575f91614358575b50151590565b90506020813d602011614382575b8161437360209383612e75565b8101031261072757515f614352565b3d9150614366565b6002600c541461439b576002600c55565b633ee5aeb560e01b5f5260045ffd5b6143b2613152565b156133f2576143bf613437565b156133f25760ff600b541661449d576001600160a01b03600254166040516339ebf82360e01b815230600482015261012081602481855afa908115612955576144159160a0915f9161447e575b500151426131c7565b60085411156136475760206004916040519283809263112c1f9b60e01b82525afa908115612955575f9161444c575b50600a541090565b90506020813d602011614476575b8161446760209383612e75565b8101031261072757515f614444565b3d915061445a565b61449791506101203d81116104bd576104af8183612e75565b5f61440c565b600190565b5f6101006040516144b281612e58565b8281528260208201528260408201528260608201528260808201528260a08201528260c08201528260e0820152015260246101206001600160a01b0360025416604051928380926339ebf82360e01b82523060048301525afa908115612955575f9161451c575090565b61324d91506101203d81116104bd576104af8183612e75565b905f602091828151910182855af115612955575f513d61458057506001600160a01b0381163b155b6145645750565b6001600160a01b0390635274afe760e01b5f521660045260245ffd5b6001141561455d565b6001600160a01b037f0000000000000000000000008e53cdaa89381c203a074fb3388f65936358f2001660405163b3d7f6b960e01b8152826004820152602081602481855afa908115612955575f916148a2575b5061460c614611917f00000000000000000000000000000000000000000000000000000000000000009061340a565b612fa7565b6040516370a0823160e01b8152306004820152906020826024817f000000000000000000000000dd0f28e19c1780eb6396170735d45153d261490d6001600160a01b03165afa908115612955575f9161486c575b61467792508180821091180218613b21565b959293909193156147465760ff600954161580614864575b614855575f951561478c57604090602482518098819363f2a89c3560e01b83528660048401525af18015612955575f955f91614753575b5093851561474657848692817f39e708ab7fe5d98ea225937bff798a3024cd2e51f4c64e5ab441760702091149938181115f1461473b576147369161470a916131c7565b96855f52600d6020528060405f2055604051938493846040919493926060820195825260208201520152565b0390a2565b50506147365f61470a565b505f945084935083925050565b9550506040853d604011614784575b8161476f60409383612e75565b8101031261072757602085519501515f6146c6565b3d9150614762565b602092949350816147c16147eb93837f000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48614b9e565b604051636e553f6560e01b8152600481019190915230602482015295869283919082906044820190565b03925af1928315612955575f93614821575b50828082111561481857614810916131c7565b905b5f929190565b50505f90614812565b9092506020813d60201161484d575b8161483d60209383612e75565b810103126107275751915f6147fd565b3d9150614830565b6303ea775760e61b5f5260045ffd5b50831561468f565b90506020823d60201161489a575b8161488760209383612e75565b8101031261072757614677915190614665565b3d915061487a565b90506020813d6020116148ce575b816148bd60209383612e75565b81010312610727575161460c6145dd565b3d91506148b0565b5f905f906148e3816142ff565b15614b93576040516370a0823160e01b81523060048201527f000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48916001600160a01b0383169190602082602481865afa918215612955575f92614b5f575b506040516370a0823160e01b81523060048201527f000000000000000000000000dd0f28e19c1780eb6396170735d45153d261490d6001600160a01b031694602082602481895afa918215612955575f92614b29575b506149ff602091846149d55f95966001600160a01b037f000000000000000000000000dd0f28e19c1780eb6396170735d45153d261490d168094614b9e565b604051636e553f6560e01b8152600481019190915230602482015293849283919082906044820190565b03925af19081614afa575b50614a1757505050505f92565b909194506024929593506020600196604051948580926370a0823160e01b82523060048301525afa928315612955575f93614ac4575b50614a5c6020916024946131c7565b94604051938480926370a0823160e01b82523060048301525afa908115612955575f91614a8e575b61324d92506131c7565b90506020823d602011614abc575b81614aa960209383612e75565b810103126107275761324d915190614a84565b3d9150614a9c565b92506020833d602011614af2575b81614adf60209383612e75565b8101031261072757915191614a5c614a4d565b3d9150614ad2565b6020813d602011614b21575b81614b1360209383612e75565b810103126107275751614a0a565b3d9150614b06565b91506020823d602011614b57575b81614b4460209383612e75565b81010312610727579051906149ff614996565b3d9150614b37565b9091506020813d602011614b8b575b81614b7b60209383612e75565b810103126107275751905f614940565b3d9150614b6e565b5050505f905f905f90565b60405163095ea7b360e01b60208083019182526001600160a01b0385166024840152604480840196909652948252929390925f90614bdd606486612e75565b84519082855af15f513d82614c3a575b505015614bf957505050565b61394561394a936001600160a01b036040519163095ea7b360e01b60208401521660248201525f604482015260448152614c34606482612e75565b82614535565b909150614c5757506001600160a01b0381163b15155b5f80614bed565b600114614c5056fe290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e563a164736f6c634300081d000a

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.