ETH Price: $2,890.46 (-1.31%)
 

Overview

ETH Balance

0 ETH

Eth Value

$0.00

More Info

Private Name Tags

ContractCreator

Multichain Info

No addresses found
Transaction Hash
Method
Block
From
To

There are no matching entries

Please try again later

View more zero value Internal Transactions in Advanced View mode

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

Block Transaction Difficulty Gas Used Reward
View All Blocks Produced

Validator Index Block Amount
View All Withdrawals

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

Contract Source Code Verified (Exact Match)

Contract Name:
USD3

Compiler Version
v0.8.22+commit.4fc1097e

Optimization Enabled:
Yes with 999999 runs

Other Settings:
shanghai EvmVersion
// SPDX-License-Identifier: AGPL-3.0
pragma solidity 0.8.22;

import {BaseHooksUpgradeable} from "./base/BaseHooksUpgradeable.sol";
import {IERC20, SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import {Math} from "../../lib/openzeppelin/contracts/utils/math/Math.sol";
import {IMorpho, IMorphoCredit, MarketParams, Id} from "../interfaces/IMorpho.sol";
import {MorphoLib} from "../libraries/periphery/MorphoLib.sol";
import {MorphoBalancesLib} from "../libraries/periphery/MorphoBalancesLib.sol";
import {SharesMathLib} from "../libraries/SharesMathLib.sol";
import {IERC4626} from "../../lib/openzeppelin/contracts/interfaces/IERC4626.sol";
import {Pausable} from "../../lib/openzeppelin/contracts/utils/Pausable.sol";
import {TokenizedStrategyStorageLib, ERC20} from "@periphery/libraries/TokenizedStrategyStorageLib.sol";
import {IProtocolConfig} from "../interfaces/IProtocolConfig.sol";
import {ProtocolConfigLib} from "../libraries/ProtocolConfigLib.sol";

/**
 * @title USD3
 * @author 3Jane Protocol
 * @notice Senior tranche strategy for USDC-based lending on 3Jane's credit markets
 * @dev Implements Yearn V3 tokenized strategy pattern for unsecured lending via MorphoCredit.
 * Deploys USDC capital to 3Jane's modified Morpho Blue markets that use credit-based
 * underwriting instead of collateral. Features first-loss protection through sUSD3
 * subordinate tranche absorption.
 *
 * Key features:
 * - Senior tranche with first-loss protection from sUSD3 holders
 * - Configurable deployment ratio to credit markets (maxOnCredit)
 * - Automatic yield distribution to sUSD3 via performance fees
 * - Loss absorption through direct share burning of sUSD3 holdings
 * - Commitment period enforcement for deposits
 * - Optional whitelist for controlled access
 * - Dynamic fee adjustment via ProtocolConfig integration
 *
 * Yield Distribution Mechanism:
 * - Tranche share distributed to sUSD3 holders via TokenizedStrategy's performance fee
 * - Performance fee can be set from 0-100% through syncTrancheShare()
 * - Direct storage manipulation bypasses TokenizedStrategy's 50% fee limit
 * - Keeper-controlled updates ensure protocol-wide consistency
 *
 * Loss Absorption Mechanism:
 * - When losses occur, sUSD3 shares are burned first (subordination)
 * - Direct storage manipulation used to burn shares without asset transfers
 * - USD3 holders protected up to total sUSD3 holdings
 * - Losses exceeding sUSD3 balance shared proportionally among USD3 holders
 */
contract USD3 is BaseHooksUpgradeable {
    using SafeERC20 for IERC20;
    using MorphoLib for IMorpho;
    using MorphoBalancesLib for IMorpho;
    using SharesMathLib for uint256;
    using Math for uint256;

    /*//////////////////////////////////////////////////////////////
                        CONSTANTS
    //////////////////////////////////////////////////////////////*/
    IERC4626 public constant WAUSDC = IERC4626(0xD4fa2D31b7968E448877f69A96DE69f5de8cD23E);

    /*//////////////////////////////////////////////////////////////
                        STORAGE - MORPHO PARAMETERS
    //////////////////////////////////////////////////////////////*/
    /// @notice MorphoCredit contract for lending operations
    IMorpho public morphoCredit;

    /// @notice Market ID for the lending market this strategy uses
    Id public marketId;

    /// @notice Market parameters for the lending market
    MarketParams internal _marketParams;

    /*//////////////////////////////////////////////////////////////
                        UPGRADEABLE STORAGE
    //////////////////////////////////////////////////////////////*/
    /// @notice Address of the subordinate sUSD3 strategy
    /// @dev Used for loss absorption and yield distribution
    address public sUSD3;

    /// @notice Whether whitelist is enforced for deposits
    bool public whitelistEnabled;

    /// @notice Whitelist status for addresses
    mapping(address => bool) public whitelist;

    /// @notice Whitelist of depositors allowed to 3rd party deposit
    mapping(address => bool) public depositorWhitelist;

    /// @notice Minimum deposit amount required
    uint256 public minDeposit;

    /// @notice Timestamp of last deposit for each user
    /// @dev Used to enforce commitment periods
    mapping(address => uint256) public depositTimestamp;

    /*//////////////////////////////////////////////////////////////
                            EVENTS
    //////////////////////////////////////////////////////////////*/
    event SUSD3StrategyUpdated(address oldStrategy, address newStrategy);
    event WhitelistUpdated(address indexed user, bool allowed);
    event DepositorWhitelistUpdated(address indexed depositor, bool allowed);
    event MinDepositUpdated(uint256 newMinDeposit);
    event TrancheShareSynced(uint256 trancheShare);

    /// @custom:oz-upgrades-unsafe-allow constructor
    constructor() {
        _disableInitializers();
    }

    /**
     * @notice Initialize the USD3 strategy
     * @param _morphoCredit Address of the MorphoCredit lending contract
     * @param _marketId Market ID for the lending market
     * @param _management Management address for the strategy
     * @param _keeper Keeper address for automated operations
     */
    function initialize(address _morphoCredit, Id _marketId, address _management, address _keeper)
        external
        initializer
    {
        require(_morphoCredit != address(0), "!morpho");

        morphoCredit = IMorpho(_morphoCredit);
        marketId = _marketId;

        // Get and cache market params
        MarketParams memory params = morphoCredit.idToMarketParams(_marketId);
        require(params.loanToken != address(0), "Invalid market");
        _marketParams = params;

        // Initialize BaseStrategy with management as temporary performanceFeeRecipient
        // It will be updated to sUSD3 address after sUSD3 is deployed
        __BaseStrategy_init(params.loanToken, "USD3", _management, _management, _keeper);

        // Approve Morpho
        IERC20(asset).forceApprove(address(morphoCredit), type(uint256).max);
    }

    /**
     * @notice Reinitialize the USD3 strategy to switch asset from waUSDC to USDC
     * @dev This function is called during the upgrade from the previous USD3 implementation.
     *      The upgrade process MUST follow this sequence to prevent user losses:
     *      1. Set performance fee to 0 (via setPerformanceFee)
     *      2. Set profit unlock time to 0 (via setProfitMaxUnlockTime)
     *      3. Call report() on OLD implementation to finalize state before upgrade
     *      4. Upgrade proxy to new implementation
     *      5. Call reinitialize() to switch the underlying asset
     *      6. Call report() on NEW implementation to update totalAssets with new asset
     *      7. Call syncTrancheShare() to restore performance fees
     *      8. Restore profit unlock time to previous value
     *      This ensures totalAssets reflects the true USDC value before users can withdraw.
     *      Without both report() calls, users would lose value as totalAssets would not
     *      account for waUSDC appreciation or the asset switch.
     */
    function reinitialize() external reinitializer(2) {
        address usdc = 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48;
        asset = ERC20(usdc);
        TokenizedStrategyStorageLib.StrategyData storage strategyData = TokenizedStrategyStorageLib.getStrategyStorage();
        strategyData.asset = ERC20(usdc);
        IERC20(usdc).forceApprove(address(WAUSDC), type(uint256).max);
    }

    /*//////////////////////////////////////////////////////////////
                        EXTERNAL VIEW FUNCTIONS
    //////////////////////////////////////////////////////////////*/

    /**
     * @notice Get the symbol for the USD3 token
     * @return Symbol string "USD3"
     */
    function symbol() external pure returns (string memory) {
        return "USD3";
    }

    /**
     * @notice Get the market parameters for this strategy
     * @return MarketParams struct containing lending market configuration
     */
    function marketParams() external view returns (MarketParams memory) {
        return _marketParams;
    }

    /*//////////////////////////////////////////////////////////////
                        INTERNAL VIEW FUNCTIONS
    //////////////////////////////////////////////////////////////*/

    /**
     * @dev Get current market liquidity information
     * @return totalSupplyAssets Total assets supplied to the market
     * @return totalShares Total supply shares in the market
     * @return totalBorrowAssets Total assets borrowed from the market
     * @return waUSDCLiquidity Available liquidity in the market
     */
    function getMarketLiquidity()
        public
        view
        returns (uint256 totalSupplyAssets, uint256 totalShares, uint256 totalBorrowAssets, uint256 waUSDCLiquidity)
    {
        (totalSupplyAssets, totalShares, totalBorrowAssets,) = morphoCredit.expectedMarketBalances(_marketParams);
        waUSDCLiquidity = totalSupplyAssets > totalBorrowAssets ? totalSupplyAssets - totalBorrowAssets : 0;
    }

    /**
     * @dev Get strategy's position in the market
     * @return shares Number of supply shares held
     * @return waUSDCMax Maximum waUSDC that can be withdrawn
     * @return waUSDCLiquidity Available market liquidity in waUSDC
     */
    function getPosition() internal view returns (uint256 shares, uint256 waUSDCMax, uint256 waUSDCLiquidity) {
        shares = morphoCredit.position(marketId, address(this)).supplyShares;
        uint256 totalSupplyAssets;
        uint256 totalShares;
        (totalSupplyAssets, totalShares,, waUSDCLiquidity) = getMarketLiquidity();
        waUSDCMax = shares.toAssetsDown(totalSupplyAssets, totalShares);
    }

    /*//////////////////////////////////////////////////////////////
                    INTERNAL STRATEGY FUNCTIONS
    //////////////////////////////////////////////////////////////*/

    /// @dev Deploy funds to MorphoCredit market respecting maxOnCredit ratio
    /// @param _amount Amount of asset to deploy
    function _deployFunds(uint256 _amount) internal override {
        if (_amount == 0) return;

        // Wrap USDC to waUSDC
        _amount = WAUSDC.deposit(_amount, address(this));

        uint256 maxOnCreditRatio = maxOnCredit();
        if (maxOnCreditRatio == 0) {
            // Don't deploy anything when set to 0%, keep all waUSDC local
            return;
        }

        // Calculate total waUSDC (deployed + local)
        uint256 deployedWaUSDC = suppliedWaUSDC();
        uint256 localWaUSDC = balanceOfWaUSDC();
        uint256 totalWaUSDC = deployedWaUSDC + localWaUSDC;

        // Calculate max that should be deployed to MorphoCredit
        uint256 maxDeployableWaUSDC = (totalWaUSDC * maxOnCreditRatio) / 10_000;

        if (maxDeployableWaUSDC <= deployedWaUSDC) {
            // Already at or above max, keep all new waUSDC local
            return;
        }

        // Deploy only the amount needed to reach max
        uint256 waUSDCToSupply = Math.min(localWaUSDC, maxDeployableWaUSDC - deployedWaUSDC);

        _supplyToMorpho(waUSDCToSupply);
    }

    /// @dev Withdraw funds from MorphoCredit market
    /// @param _amount Amount of asset to free up
    function _freeFunds(uint256 _amount) internal override {
        if (_amount == 0) {
            return;
        }

        // Calculate how much waUSDC we need
        uint256 waUSDCNeeded = WAUSDC.previewWithdraw(_amount);

        // Check local waUSDC balance first
        uint256 localWaUSDC = balanceOfWaUSDC();

        if (localWaUSDC < waUSDCNeeded) {
            // Need to withdraw from MorphoCredit
            uint256 waUSDCToWithdraw = waUSDCNeeded - localWaUSDC;

            uint256 withdrawn = _withdrawFromMorpho(waUSDCToWithdraw);

            if (withdrawn > 0) {
                localWaUSDC = balanceOfWaUSDC();
            }
        }

        uint256 waUSDCToUnwrap = Math.min(localWaUSDC, waUSDCNeeded);

        if (waUSDCToUnwrap > 0) {
            WAUSDC.redeem(waUSDCToUnwrap, address(this), address(this));
        }
    }

    /// @dev Emergency withdraw function to free funds from MorphoCredit
    /// @param amount The amount to withdraw (use type(uint256).max for all)
    function _emergencyWithdraw(uint256 amount) internal override {
        // This is called during shutdown to free funds from Morpho
        // Use _freeFunds which already handles the withdrawal logic
        _freeFunds(amount);
    }

    /// @dev Harvest interest from MorphoCredit and report total assets
    /// @return Total assets held by the strategy
    function _harvestAndReport() internal override returns (uint256) {
        MarketParams memory params = _marketParams;

        morphoCredit.accrueInterest(params);

        _tend(asset.balanceOf(address(this)));

        uint256 totalWaUSDC = suppliedWaUSDC() + balanceOfWaUSDC();

        return WAUSDC.convertToAssets(totalWaUSDC) + asset.balanceOf(address(this));
    }

    /// @dev Rebalances between idle and deployed funds to maintain maxOnCredit ratio
    /// @param _totalIdle Current idle funds available
    function _tend(uint256 _totalIdle) internal virtual override {
        // First wrap any idle USDC to waUSDC
        if (_totalIdle > 0) {
            WAUSDC.deposit(_totalIdle, address(this));
        }

        // Calculate based on waUSDC amounts
        uint256 deployedWaUSDC = suppliedWaUSDC();
        uint256 localWaUSDC = balanceOfWaUSDC();
        uint256 totalWaUSDC = deployedWaUSDC + localWaUSDC;

        uint256 targetDeployedWaUSDC = (totalWaUSDC * maxOnCredit()) / 10_000;

        if (deployedWaUSDC > targetDeployedWaUSDC) {
            // Withdraw excess from MorphoCredit
            uint256 waUSDCToWithdraw = deployedWaUSDC - targetDeployedWaUSDC;
            _withdrawFromMorpho(waUSDCToWithdraw);
        } else if (targetDeployedWaUSDC > deployedWaUSDC && localWaUSDC > 0) {
            // Deploy more if we have local waUSDC
            uint256 waUSDCToDeploy = Math.min(localWaUSDC, targetDeployedWaUSDC - deployedWaUSDC);
            _supplyToMorpho(waUSDCToDeploy);
        }
    }

    /// @dev Helper function to supply waUSDC to MorphoCredit
    /// @param amount Amount of waUSDC to supply
    /// @return supplied Actual amount supplied (for consistency with withdraw helper)
    function _supplyToMorpho(uint256 amount) internal returns (uint256 supplied) {
        if (amount == 0) return 0;

        morphoCredit.supply(_marketParams, amount, 0, address(this), "");
        return amount;
    }

    /// @dev Helper function to withdraw waUSDC from MorphoCredit
    /// @param amountRequested Amount of waUSDC to withdraw
    /// @return amountWithdrawn Actual amount withdrawn (may be less than requested)
    function _withdrawFromMorpho(uint256 amountRequested) internal returns (uint256 amountWithdrawn) {
        if (amountRequested == 0) return 0;

        morphoCredit.accrueInterest(_marketParams);
        (uint256 shares, uint256 waUSDCMax, uint256 waUSDCLiquidity) = getPosition();

        uint256 availableWaUSDC = Math.min(waUSDCMax, waUSDCLiquidity);

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

        amountWithdrawn = Math.min(amountRequested, availableWaUSDC);

        if (amountWithdrawn > 0) {
            if (amountWithdrawn >= waUSDCMax) {
                morphoCredit.withdraw(_marketParams, 0, shares, address(this), address(this));
            } else {
                morphoCredit.withdraw(_marketParams, amountWithdrawn, 0, address(this), address(this));
            }
        }

        return amountWithdrawn;
    }

    /*//////////////////////////////////////////////////////////////
                    PUBLIC VIEW FUNCTIONS (OVERRIDES)
    //////////////////////////////////////////////////////////////*/

    /// @dev Returns available withdraw limit, enforcing commitment time
    /// @param _owner Address to check limit for
    /// @return Maximum amount that can be withdrawn
    function availableWithdrawLimit(address _owner) public view override returns (uint256) {
        // Get available liquidity first
        uint256 idleAsset = asset.balanceOf(address(this));

        (, uint256 waUSDCMax, uint256 waUSDCLiquidity) = getPosition();

        uint256 availableWaUSDC;

        if (Pausable(address(WAUSDC)).paused()) {
            availableWaUSDC = 0;
        } else {
            uint256 localWaUSDC = Math.min(balanceOfWaUSDC(), WAUSDC.maxRedeem(address(this)));
            uint256 morphoWaUSDC = Math.min(waUSDCMax, waUSDCLiquidity);
            morphoWaUSDC = Math.min(morphoWaUSDC, WAUSDC.maxRedeem(address(morphoCredit)));
            availableWaUSDC = localWaUSDC + morphoWaUSDC;
        }

        uint256 availableLiquidity = idleAsset + WAUSDC.convertToAssets(availableWaUSDC);

        // During shutdown, bypass all checks
        if (TokenizedStrategy.isShutdown()) {
            return availableLiquidity;
        }

        // Check commitment time
        uint256 commitTime = minCommitmentTime();
        if (commitTime > 0) {
            uint256 depositTime = depositTimestamp[_owner];
            if (depositTime > 0 && block.timestamp < depositTime + commitTime) {
                return 0; // Commitment period not met
            }
        }

        return availableLiquidity;
    }

    /// @dev Returns available deposit limit, enforcing whitelist and supply cap
    /// @param _owner Address to check limit for
    /// @return Maximum amount that can be deposited
    function availableDepositLimit(address _owner) public view override returns (uint256) {
        // Check whitelist if enabled
        if (whitelistEnabled && !whitelist[_owner]) {
            return 0;
        }

        uint256 maxDeposit = WAUSDC.maxDeposit(address(this));

        if (Pausable(address(WAUSDC)).paused() || maxDeposit == 0) {
            return 0;
        }

        // Block deposits from borrowers
        if (morphoCredit.borrowShares(marketId, _owner) > 0) {
            return 0;
        }

        uint256 cap = supplyCap();
        if (cap == 0 || cap == type(uint256).max) {
            return type(uint256).max;
        }

        uint256 currentTotalAssets = TokenizedStrategy.totalAssets();
        if (cap <= currentTotalAssets) {
            return 0;
        }
        return Math.min(cap - currentTotalAssets, maxDeposit);
    }

    /*//////////////////////////////////////////////////////////////
                        HOOKS IMPLEMENTATION
    //////////////////////////////////////////////////////////////*/

    /// @dev Pre-deposit hook to enforce minimum deposit and track commitment time
    function _preDepositHook(uint256 assets, uint256 shares, address receiver) internal override {
        if (assets == 0 && shares > 0) {
            assets = TokenizedStrategy.previewMint(shares);
        }

        // Handle type(uint256).max case - resolve to actual balance
        if (assets == type(uint256).max) {
            assets = asset.balanceOf(msg.sender);
        }

        // Enforce minimum deposit only for first-time depositors
        uint256 currentBalance = TokenizedStrategy.balanceOf(receiver);
        if (currentBalance == 0) {
            require(assets >= minDeposit, "Below minimum deposit");
        }

        // Prevent commitment bypass and griefing attacks
        if (minCommitmentTime() > 0) {
            // Only allow self-deposits or whitelisted depositors
            require(
                msg.sender == receiver || depositorWhitelist[msg.sender],
                "USD3: Only self or whitelisted deposits allowed"
            );

            // Always extend commitment for valid deposits
            depositTimestamp[receiver] = block.timestamp;
        }
    }

    /// @dev Post-withdraw hook to clear commitment on full exit
    function _postWithdrawHook(uint256 assets, uint256 shares, address receiver, address owner, uint256 maxLoss)
        internal
        override
    {
        // Clear commitment timestamp if user fully exited
        if (TokenizedStrategy.balanceOf(owner) == 0) {
            delete depositTimestamp[owner];
        }
    }

    /// @dev Post-report hook to handle loss absorption by burning sUSD3's shares
    function _postReportHook(uint256 profit, uint256 loss) internal override {
        if (loss > 0 && sUSD3 != address(0)) {
            // Get sUSD3's current USD3 balance
            uint256 susd3Balance = TokenizedStrategy.balanceOf(sUSD3);

            if (susd3Balance > 0) {
                // Calculate how many shares are needed to cover the loss
                // IMPORTANT: We must use pre-report values to calculate the correct share amount
                // The report has already reduced totalAssets, so we add the loss back
                uint256 totalSupply = TokenizedStrategy.totalSupply();
                uint256 totalAssets = TokenizedStrategy.totalAssets();

                // Calculate shares to burn using pre-loss exchange rate
                uint256 sharesToBurn = loss.mulDiv(totalSupply, totalAssets + loss, Math.Rounding.Floor);

                // Cap at sUSD3's actual balance - they can't lose more than they have
                if (sharesToBurn > susd3Balance) {
                    sharesToBurn = susd3Balance;
                }

                if (sharesToBurn > 0) {
                    _burnSharesFromSusd3(sharesToBurn);
                }
            }
        }
    }

    /**
     * @notice Prevent transfers during commitment period
     * @dev Override from BaseHooksUpgradeable to enforce commitment
     * @param from Address transferring shares
     * @param to Address receiving shares
     * @param amount Amount of shares being transferred
     */
    function _preTransferHook(address from, address to, uint256 amount) internal override {
        // Allow minting (from == 0) and burning (to == 0)
        if (from == address(0) || to == address(0)) return;

        // Allow transfers to/from sUSD3 (staking and withdrawals)
        if (to == sUSD3 || from == sUSD3) return;

        // Check commitment period
        uint256 commitmentEnd = depositTimestamp[from] + minCommitmentTime();
        require(
            block.timestamp >= commitmentEnd || depositTimestamp[from] == 0,
            "USD3: Cannot transfer during commitment period"
        );
    }

    /*//////////////////////////////////////////////////////////////
                    INTERNAL HELPER FUNCTIONS
    //////////////////////////////////////////////////////////////*/

    /**
     * @dev Directly burn shares from sUSD3's balance using storage manipulation
     *
     * IMPORTANT: Direct storage manipulation is necessary here because TokenizedStrategy
     * does not expose a public burn function. The only ways to burn shares in
     * TokenizedStrategy are through withdraw/redeem (which require asset transfers)
     * or internal profit/loss accounting. Since we need to burn sUSD3's shares
     * without triggering asset transfers, direct storage manipulation is the only
     * viable approach.
     *
     * @param amount Number of shares to burn from sUSD3
     */
    function _burnSharesFromSusd3(uint256 amount) internal {
        // Calculate storage slots using the library
        bytes32 totalSupplySlot = TokenizedStrategyStorageLib.totalSupplySlot();
        bytes32 balanceSlot = TokenizedStrategyStorageLib.balancesSlot(sUSD3);

        // Read current values
        uint256 currentBalance;
        uint256 currentTotalSupply;
        assembly {
            currentBalance := sload(balanceSlot)
            currentTotalSupply := sload(totalSupplySlot)
        }

        // Ensure we don't burn more than available
        uint256 actualBurn = amount;
        if (actualBurn > currentBalance) {
            actualBurn = currentBalance;
        }

        // Update storage
        assembly {
            sstore(balanceSlot, sub(currentBalance, actualBurn))
            sstore(totalSupplySlot, sub(currentTotalSupply, actualBurn))
        }

        // Emit Transfer event to address(0) for transparency
        emit IERC20.Transfer(sUSD3, address(0), actualBurn);
    }

    /*//////////////////////////////////////////////////////////////
                        PUBLIC VIEW FUNCTIONS
    //////////////////////////////////////////////////////////////*/

    /**
     * @notice Get the balance of waUSDC held locally (not deployed to MorphoCredit)
     * @return Amount of waUSDC held in this contract
     */
    function balanceOfWaUSDC() public view returns (uint256) {
        return WAUSDC.balanceOf(address(this));
    }

    /**
     * @notice Get the amount of waUSDC supplied to MorphoCredit
     * @return Amount of waUSDC deployed to the lending market
     */
    function suppliedWaUSDC() public view returns (uint256) {
        return morphoCredit.expectedSupplyAssets(_marketParams, address(this));
    }

    /**
     * @notice Get the maximum percentage of funds to deploy to credit markets from ProtocolConfig
     * @return Maximum deployment ratio in basis points (10000 = 100%)
     * @dev Returns the value from ProtocolConfig directly. If not configured in ProtocolConfig,
     *      it returns 0, effectively preventing deployment until explicitly configured.
     */
    function maxOnCredit() public view returns (uint256) {
        IProtocolConfig config = IProtocolConfig(IMorphoCredit(address(morphoCredit)).protocolConfig());
        return config.getMaxOnCredit();
    }

    /**
     * @notice Get the minimum commitment time from ProtocolConfig
     * @return Minimum commitment time in seconds
     */
    function minCommitmentTime() public view returns (uint256) {
        IProtocolConfig config = IProtocolConfig(IMorphoCredit(address(morphoCredit)).protocolConfig());
        return config.getUsd3CommitmentTime();
    }

    /**
     * @notice Get the supply cap from ProtocolConfig
     * @return Supply cap in asset units (0 means no cap)
     */
    function supplyCap() public view returns (uint256) {
        IProtocolConfig config = IProtocolConfig(IMorphoCredit(address(morphoCredit)).protocolConfig());
        return config.config(ProtocolConfigLib.USD3_SUPPLY_CAP);
    }

    /*//////////////////////////////////////////////////////////////
                        MANAGEMENT FUNCTIONS
    //////////////////////////////////////////////////////////////*/

    /**
     * @notice Set the sUSD3 subordinate strategy address
     * @param _sUSD3 Address of the sUSD3 strategy
     * @dev Only callable by management. After calling, also set performance fee recipient.
     */
    function setSUSD3(address _sUSD3) external onlyManagement {
        require(sUSD3 == address(0), "sUSD3 already set");
        require(_sUSD3 != address(0), "Invalid address");

        sUSD3 = _sUSD3;
        emit SUSD3StrategyUpdated(address(0), _sUSD3);

        // NOTE: After calling this, management should also call:
        // ITokenizedStrategy(usd3Address).setPerformanceFeeRecipient(_sUSD3)
        // to ensure yield distribution goes to sUSD3
    }

    /**
     * @notice Enable or disable whitelist requirement
     * @param _enabled True to enable whitelist, false to disable
     */
    function setWhitelistEnabled(bool _enabled) external onlyManagement {
        whitelistEnabled = _enabled;
    }

    /**
     * @notice Update whitelist status for an address
     * @param _user Address to update
     * @param _allowed True to whitelist, false to remove from whitelist
     */
    function setWhitelist(address _user, bool _allowed) external onlyManagement {
        whitelist[_user] = _allowed;
        emit WhitelistUpdated(_user, _allowed);
    }

    /**
     * @notice Update depositor whitelist status for an address
     * @param _depositor Address to update
     * @param _allowed True to allow extending commitments, false to disallow
     */
    function setDepositorWhitelist(address _depositor, bool _allowed) external onlyManagement {
        depositorWhitelist[_depositor] = _allowed;
        emit DepositorWhitelistUpdated(_depositor, _allowed);
    }

    /**
     * @notice Set minimum deposit amount
     * @param _minDeposit Minimum amount required for deposits
     */
    function setMinDeposit(uint256 _minDeposit) external onlyManagement {
        minDeposit = _minDeposit;
        emit MinDepositUpdated(_minDeposit);
    }

    /*//////////////////////////////////////////////////////////////
                        KEEPER FUNCTIONS
    //////////////////////////////////////////////////////////////*/

    /**
     * @notice Sync the tranche share (performance fee) from ProtocolConfig
     * @dev Reads TRANCHE_SHARE_VARIANT from ProtocolConfig and updates local storage
     *
     * IMPORTANT: Direct storage manipulation is necessary here because TokenizedStrategy's
     * setPerformanceFee() function has a hardcoded MAX_FEE limit of 5000 (50%). Since we
     * need to support higher fee distributions to sUSD3 (potentially up to 100% for full
     * subordination scenarios), we must bypass this restriction by directly modifying the
     * storage slot.
     *
     * Storage layout in TokenizedStrategy (slot 9):
     * - Bits 0-31: profitMaxUnlockTime (uint32)
     * - Bits 32-47: performanceFee (uint16) <- We modify this
     * - Bits 48-207: performanceFeeRecipient (address)
     *
     * @dev Only callable by keepers to ensure controlled updates
     */
    function syncTrancheShare() external onlyKeepers {
        // Get the protocol config through MorphoCredit
        IProtocolConfig config = IProtocolConfig(IMorphoCredit(address(morphoCredit)).protocolConfig());

        // Read the tranche share variant (yield share to sUSD3 in basis points)
        uint256 trancheShare = config.getTrancheShareVariant();
        require(trancheShare <= 10_000, "Invalid tranche share");

        // Get the storage slot for performanceFee using the library
        bytes32 targetSlot = TokenizedStrategyStorageLib.profitConfigSlot();

        // Read current slot value
        uint256 currentSlotValue;
        assembly {
            currentSlotValue := sload(targetSlot)
        }

        // Clear the performanceFee bits (32-47) and set new value
        uint256 mask = ~(uint256(0xFFFF) << 32);
        uint256 newSlotValue = (currentSlotValue & mask) | (trancheShare << 32);

        // Write back to storage
        assembly {
            sstore(targetSlot, newSlotValue)
        }

        emit TrancheShareSynced(trancheShare);
    }

    /*//////////////////////////////////////////////////////////////
                        STORAGE GAP
    //////////////////////////////////////////////////////////////*/

    /**
     * @dev This empty reserved space is put in place to allow future versions to add new
     * variables without shifting down storage in the inheritance chain.
     * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
     */
    uint256[40] private __gap;
}

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

import {BaseStrategyUpgradeable} from "./BaseStrategyUpgradeable.sol";
import {Hooks} from "@periphery/Bases/Hooks/Hooks.sol";
import {ITokenizedStrategy} from "@tokenized-strategy/interfaces/ITokenizedStrategy.sol";

/**
 * @title BaseHooksUpgradeable
 * @author Yearn's BaseHooks adapted for upgradeable strategies
 * @notice This contract can be inherited by any strategy wishing to implement
 *         pre or post hooks for deposit, withdraw, transfer, or report functions.
 *
 *         This version:
 *         - Inherits from BaseStrategyUpgradeable instead of BaseHealthCheck
 *         - Uses Yearn's Hooks contract for standardized hook interfaces
 *         - Is compatible with upgradeable proxy patterns
 */
abstract contract BaseHooksUpgradeable is BaseStrategyUpgradeable, Hooks {
    /*//////////////////////////////////////////////////////////////
                            CONSTANTS
    //////////////////////////////////////////////////////////////*/

    uint256 internal constant MAX_BPS = 10_000;

    /*//////////////////////////////////////////////////////////////
                        OVERRIDDEN FUNCTIONS
    //////////////////////////////////////////////////////////////*/

    /**
     * @notice Deposit assets and receive shares
     * @param assets Amount of assets to deposit
     * @param receiver Address to receive the shares
     * @return shares Amount of shares minted
     */
    function deposit(uint256 assets, address receiver) external virtual returns (uint256 shares) {
        _preDepositHook(assets, shares, receiver);
        shares = abi.decode(
            _delegateCall(abi.encodeCall(ITokenizedStrategy(address(this)).deposit, (assets, receiver))), (uint256)
        );
        _postDepositHook(assets, shares, receiver);
    }

    /**
     * @notice Mint shares by depositing assets
     * @param shares Amount of shares to mint
     * @param receiver Address to receive the shares
     * @return assets Amount of assets deposited
     */
    function mint(uint256 shares, address receiver) external virtual returns (uint256 assets) {
        _preDepositHook(assets, shares, receiver);
        assets = abi.decode(
            _delegateCall(abi.encodeCall(ITokenizedStrategy(address(this)).mint, (shares, receiver))), (uint256)
        );
        _postDepositHook(assets, shares, receiver);
    }

    /**
     * @notice Withdraw assets by burning shares
     * @param assets Amount of assets to withdraw
     * @param receiver Address to receive the assets
     * @param owner Address whose shares are burned
     * @return shares Amount of shares burned
     */
    function withdraw(uint256 assets, address receiver, address owner) external virtual returns (uint256 shares) {
        return withdraw(assets, receiver, owner, 0);
    }

    /**
     * @notice Withdraw assets with custom max loss
     * @param assets Amount of assets to withdraw
     * @param receiver Address to receive the assets
     * @param owner Address whose shares are burned
     * @param maxLoss Maximum acceptable loss in basis points
     * @return shares Amount of shares burned
     */
    function withdraw(uint256 assets, address receiver, address owner, uint256 maxLoss)
        public
        virtual
        returns (uint256 shares)
    {
        _preWithdrawHook(assets, shares, receiver, owner, maxLoss);
        shares = abi.decode(
            _delegateCall(
                abi.encodeWithSelector(ITokenizedStrategy.withdraw.selector, assets, receiver, owner, maxLoss)
            ),
            (uint256)
        );
        _postWithdrawHook(assets, shares, receiver, owner, maxLoss);
    }

    /**
     * @notice Redeem shares for assets
     * @param shares Amount of shares to redeem
     * @param receiver Address to receive the assets
     * @param owner Address whose shares are burned
     * @return assets Amount of assets withdrawn
     */
    function redeem(uint256 shares, address receiver, address owner) external virtual returns (uint256 assets) {
        return redeem(shares, receiver, owner, MAX_BPS);
    }

    /**
     * @notice Redeem shares with custom max loss
     * @param shares Amount of shares to redeem
     * @param receiver Address to receive the assets
     * @param owner Address whose shares are burned
     * @param maxLoss Maximum acceptable loss in basis points
     * @return assets Amount of assets withdrawn
     */
    function redeem(uint256 shares, address receiver, address owner, uint256 maxLoss)
        public
        virtual
        returns (uint256 assets)
    {
        _preWithdrawHook(assets, shares, receiver, owner, maxLoss);
        assets = abi.decode(
            _delegateCall(abi.encodeWithSelector(ITokenizedStrategy.redeem.selector, shares, receiver, owner, maxLoss)),
            (uint256)
        );
        _postWithdrawHook(assets, shares, receiver, owner, maxLoss);
    }

    /**
     * @notice Transfer shares to another address
     * @param to Address to receive the shares
     * @param amount Amount of shares to transfer
     * @return success Whether the transfer succeeded
     */
    function transfer(address to, uint256 amount) external virtual returns (bool) {
        _preTransferHook(msg.sender, to, amount);
        bool success =
            abi.decode(_delegateCall(abi.encodeCall(ITokenizedStrategy(address(this)).transfer, (to, amount))), (bool));
        _postTransferHook(msg.sender, to, amount, success);
        return success;
    }

    /**
     * @notice Transfer shares from one address to another
     * @param from Address to transfer from
     * @param to Address to transfer to
     * @param amount Amount of shares to transfer
     * @return success Whether the transfer succeeded
     */
    function transferFrom(address from, address to, uint256 amount) external virtual returns (bool) {
        _preTransferHook(from, to, amount);
        bool success = abi.decode(
            _delegateCall(abi.encodeCall(ITokenizedStrategy(address(this)).transferFrom, (from, to, amount))), (bool)
        );
        _postTransferHook(from, to, amount, success);
        return success;
    }

    /**
     * @notice Report profit and loss
     * @return profit Amount of profit generated
     * @return loss Amount of loss incurred
     */
    function report() external virtual returns (uint256 profit, uint256 loss) {
        _preReportHook();
        (profit, loss) =
            abi.decode(_delegateCall(abi.encodeCall(ITokenizedStrategy(address(this)).report, ())), (uint256, uint256));
        _postReportHook(profit, loss);
    }
}

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

pragma solidity ^0.8.0;

import "../IERC20.sol";
import "../extensions/IERC20Permit.sol";
import "../../../utils/Address.sol";

/**
 * @title SafeERC20
 * @dev Wrappers around ERC20 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 {
    using Address for address;

    /**
     * @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.encodeWithSelector(token.transfer.selector, 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.encodeWithSelector(token.transferFrom.selector, from, to, value));
    }

    /**
     * @dev Deprecated. This function has issues similar to the ones found in
     * {IERC20-approve}, and its usage is discouraged.
     *
     * Whenever possible, use {safeIncreaseAllowance} and
     * {safeDecreaseAllowance} instead.
     */
    function safeApprove(IERC20 token, address spender, uint256 value) internal {
        // safeApprove should only be called when setting an initial allowance,
        // or when resetting it to zero. To increase and decrease it, use
        // 'safeIncreaseAllowance' and 'safeDecreaseAllowance'
        require(
            (value == 0) || (token.allowance(address(this), spender) == 0),
            "SafeERC20: approve from non-zero to non-zero allowance"
        );
        _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, 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.
     */
    function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal {
        uint256 oldAllowance = token.allowance(address(this), spender);
        _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, oldAllowance + value));
    }

    /**
     * @dev Decrease the calling contract's allowance toward `spender` by `value`. If `token` returns no value,
     * non-reverting calls are assumed to be successful.
     */
    function safeDecreaseAllowance(IERC20 token, address spender, uint256 value) internal {
        unchecked {
            uint256 oldAllowance = token.allowance(address(this), spender);
            require(oldAllowance >= value, "SafeERC20: decreased allowance below zero");
            _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, oldAllowance - value));
        }
    }

    /**
     * @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.
     */
    function forceApprove(IERC20 token, address spender, uint256 value) internal {
        bytes memory approvalCall = abi.encodeWithSelector(token.approve.selector, spender, value);

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

    /**
     * @dev Use a ERC-2612 signature to set the `owner` approval toward `spender` on `token`.
     * Revert on invalid signature.
     */
    function safePermit(
        IERC20Permit token,
        address owner,
        address spender,
        uint256 value,
        uint256 deadline,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) internal {
        uint256 nonceBefore = token.nonces(owner);
        token.permit(owner, spender, value, deadline, v, r, s);
        uint256 nonceAfter = token.nonces(owner);
        require(nonceAfter == nonceBefore + 1, "SafeERC20: permit did not succeed");
    }

    /**
     * @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).
     */
    function _callOptionalReturn(IERC20 token, bytes memory data) private {
        // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
        // we're implementing it ourselves. We use {Address-functionCall} to perform this call, which verifies that
        // the target address contains contract code and also asserts for success in the low-level call.

        bytes memory returndata = address(token).functionCall(data, "SafeERC20: low-level call failed");
        require(returndata.length == 0 || abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed");
    }

    /**
     * @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 silents catches all reverts and returns a bool instead.
     */
    function _callOptionalReturnBool(IERC20 token, bytes memory data) private returns (bool) {
        // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
        // we're implementing it ourselves. We cannot use {Address-functionCall} here since this should return false
        // and not revert is the subcall reverts.

        (bool success, bytes memory returndata) = address(token).call(data);
        return
            success && (returndata.length == 0 || abi.decode(returndata, (bool))) && Address.isContract(address(token));
    }
}

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

pragma solidity ^0.8.20;

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

        uint256 mLen = m.length;

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

File 5 of 35 : IMorpho.sol
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.8.18;

type Id is bytes32;

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

/// @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.
/// @dev Warning: `totalMarkdownAmount` may be stale as markdowns are only updated when borrowers are touched.
struct Market {
    uint128 totalSupplyAssets;
    uint128 totalSupplyShares;
    uint128 totalBorrowAssets;
    uint128 totalBorrowShares;
    uint128 lastUpdate;
    uint128 fee;
    uint128 totalMarkdownAmount; // Running tally of all borrower markdowns
}

/// @notice Per-borrower premium tracking
/// @param lastAccrualTime Timestamp of the last premium accrual for this borrower
/// @param rate Current risk premium rate per second (scaled by WAD)
/// @param borrowAssetsAtLastAccrual Snapshot of borrow position at last premium accrual
struct BorrowerPremium {
    uint128 lastAccrualTime;
    uint128 rate;
    uint128 borrowAssetsAtLastAccrual;
}

/// @notice Repayment tracking structures
enum RepaymentStatus {
    Current,
    GracePeriod,
    Delinquent,
    Default
}

struct PaymentCycle {
    uint256 endDate;
}

struct RepaymentObligation {
    uint128 paymentCycleId;
    uint128 amountDue;
    uint128 endingBalance;
}

/// @notice Markdown state for tracking defaulted debt value reduction
/// @param lastCalculatedMarkdown Last calculated markdown amount
struct MarkdownState {
    uint128 lastCalculatedMarkdown;
}

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 chains sharing the
    /// same chain id and on forks 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 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 assumptions on the market's dependencies which, if broken, could break Morpho's liveness
    /// properties (funds could get stuck):
    /// - The token should not revert on `transfer` and `transferFrom` if balances and approvals are right.
    /// - The amount of assets supplied and borrowed should not go above ~1e35 (otherwise the computation of
    /// `toSharesUp` and `toSharesDown` can overflow).
    /// - The IRM should not revert on `borrowRate`.
    /// - The IRM should not return a very high borrow rate (otherwise the computation of `interest` in
    /// `_accrueInterest` can overflow).
    /// - The oracle should not revert `price`.
    /// - The oracle should not return a very high price (otherwise the computation of `maxBorrow` in `_isHealthy` or of
    /// `assetsRepaid` in `liquidate` can 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
    /// `onMorphoRepay` 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 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,
            uint128 totalMarkdownAmount
        );

    /// @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,
            address creditLine
        );
}

/// @title IMorpho
/// @author Morpho Labs
/// @custom:contact [email protected]
/// @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);
}

/// @title IMorphoCredit
/// @author Morpho Labs
/// @custom:contact [email protected]
/// @dev Use this interface for Morpho to have access to all the functions with the appropriate function signatures.
interface IMorphoCredit {
    /// @notice The helper of the contract.
    function helper() external view returns (address);

    /// @notice The usd3 contract
    function usd3() external view returns (address);

    /// @notice The protocol config of the contract.
    function protocolConfig() external view returns (address);

    /// @notice Sets `helper` as `helper` of the contract.
    /// @param newHelper The new helper address
    function setHelper(address newHelper) external;

    /// @notice Sets `usd3` as `usd3` of the contract.
    /// @param newUsd3 The new usd3 address
    function setUsd3(address newUsd3) external;

    /// @notice Sets the credit line and premium rate for a borrower
    /// @param id The market ID
    /// @param borrower The borrower address
    /// @param credit The credit line amount
    /// @param drp The drp per second in WAD
    function setCreditLine(Id id, address borrower, uint256 credit, uint128 drp) external;

    /// @notice Returns the premium data for a specific borrower in a market
    /// @param id The market ID
    /// @param borrower The borrower address
    /// @return lastAccrualTime Timestamp of the last premium accrual
    /// @return rate Current risk premium rate per second (scaled by WAD)
    /// @return borrowAssetsAtLastAccrual Snapshot of borrow position at last premium accrual
    function borrowerPremium(Id id, address borrower)
        external
        view
        returns (uint128 lastAccrualTime, uint128 rate, uint128 borrowAssetsAtLastAccrual);

    /// @notice Batch accrue premiums for multiple borrowers
    /// @param id Market ID
    /// @param borrowers Array of borrower addresses
    /// @dev Gas usage scales linearly with array size. Callers should manage batch sizes based on block gas limits.
    function accruePremiumsForBorrowers(Id id, address[] calldata borrowers) external;

    /// @notice Close a payment cycle and post obligations for multiple borrowers
    /// @param id Market ID
    /// @param endDate Cycle end date
    /// @param borrowers Array of borrower addresses
    /// @param repaymentBps Array of repayment basis points (e.g., 500 = 5%)
    /// @param endingBalances Array of ending balances for penalty calculations
    function closeCycleAndPostObligations(
        Id id,
        uint256 endDate,
        address[] calldata borrowers,
        uint256[] calldata repaymentBps,
        uint256[] calldata endingBalances
    ) external;

    /// @notice Add obligations to the latest payment cycle
    /// @param id Market ID
    /// @param borrowers Array of borrower addresses
    /// @param repaymentBps Array of repayment basis points (e.g., 500 = 5%)
    /// @param endingBalances Array of ending balances
    function addObligationsToLatestCycle(
        Id id,
        address[] calldata borrowers,
        uint256[] calldata repaymentBps,
        uint256[] calldata endingBalances
    ) external;

    /// @notice Get repayment obligation for a borrower
    /// @param id Market ID
    /// @param borrower Borrower address
    /// @return cycleId The payment cycle ID
    /// @return amountDue The amount due
    /// @return endingBalance The ending balance for penalty calculations
    function repaymentObligation(Id id, address borrower)
        external
        view
        returns (uint128 cycleId, uint128 amountDue, uint128 endingBalance);

    /// @notice Get payment cycle end date
    /// @param id Market ID
    /// @param cycleId Cycle ID
    /// @return endDate The cycle end date
    function paymentCycle(Id id, uint256 cycleId) external view returns (uint256 endDate);

    /// @notice Settle a borrower's account by writing off all remaining debt
    /// @dev Only callable by credit line contract
    /// @dev Should be called after any partial repayments have been made
    /// @param marketParams The market parameters
    /// @param borrower The borrower whose account to settle
    /// @return writtenOffAssets Amount of assets written off
    /// @return writtenOffShares Amount of shares written off
    function settleAccount(MarketParams memory marketParams, address borrower)
        external
        returns (uint256 writtenOffAssets, uint256 writtenOffShares);

    /// @notice Get markdown state for a borrower
    /// @param id Market ID
    /// @param borrower Borrower address
    /// @return lastCalculatedMarkdown Last calculated markdown amount
    function markdownState(Id id, address borrower) external view returns (uint128 lastCalculatedMarkdown);
}

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

import {IMorpho, Id} from "../../interfaces/IMorpho.sol";
import {MorphoStorageLib} from "./MorphoStorageLib.sol";

/// @title MorphoLib
/// @author Morpho Labs
/// @custom:contact [email protected]
/// @notice Helper library to access Morpho storage variables.
/// @dev Warning: Supply and borrow getters may return outdated values that do not include accrued interest.
library MorphoLib {
    function supplyShares(IMorpho morpho, Id id, address user) internal view returns (uint256) {
        bytes32[] memory slot = _array(MorphoStorageLib.positionSupplySharesSlot(id, user));
        return uint256(morpho.extSloads(slot)[0]);
    }

    function borrowShares(IMorpho morpho, Id id, address user) internal view returns (uint256) {
        bytes32[] memory slot = _array(MorphoStorageLib.positionBorrowSharesAndCollateralSlot(id, user));
        return uint128(uint256(morpho.extSloads(slot)[0]));
    }

    function collateral(IMorpho morpho, Id id, address user) internal view returns (uint256) {
        bytes32[] memory slot = _array(MorphoStorageLib.positionBorrowSharesAndCollateralSlot(id, user));
        return uint256(morpho.extSloads(slot)[0] >> 128);
    }

    function totalSupplyAssets(IMorpho morpho, Id id) internal view returns (uint256) {
        bytes32[] memory slot = _array(MorphoStorageLib.marketTotalSupplyAssetsAndSharesSlot(id));
        return uint128(uint256(morpho.extSloads(slot)[0]));
    }

    function totalSupplyShares(IMorpho morpho, Id id) internal view returns (uint256) {
        bytes32[] memory slot = _array(MorphoStorageLib.marketTotalSupplyAssetsAndSharesSlot(id));
        return uint256(morpho.extSloads(slot)[0] >> 128);
    }

    function totalBorrowAssets(IMorpho morpho, Id id) internal view returns (uint256) {
        bytes32[] memory slot = _array(MorphoStorageLib.marketTotalBorrowAssetsAndSharesSlot(id));
        return uint128(uint256(morpho.extSloads(slot)[0]));
    }

    function totalBorrowShares(IMorpho morpho, Id id) internal view returns (uint256) {
        bytes32[] memory slot = _array(MorphoStorageLib.marketTotalBorrowAssetsAndSharesSlot(id));
        return uint256(morpho.extSloads(slot)[0] >> 128);
    }

    function lastUpdate(IMorpho morpho, Id id) internal view returns (uint256) {
        bytes32[] memory slot = _array(MorphoStorageLib.marketLastUpdateAndFeeSlot(id));
        return uint128(uint256(morpho.extSloads(slot)[0]));
    }

    function fee(IMorpho morpho, Id id) internal view returns (uint256) {
        bytes32[] memory slot = _array(MorphoStorageLib.marketLastUpdateAndFeeSlot(id));
        return uint256(morpho.extSloads(slot)[0] >> 128);
    }

    function _array(bytes32 x) private pure returns (bytes32[] memory) {
        bytes32[] memory res = new bytes32[](1);
        res[0] = x;
        return res;
    }
}

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

import {Id, MarketParams, Market, IMorpho} from "../../interfaces/IMorpho.sol";
import {IIrm} from "../../interfaces/IIrm.sol";

import {MathLib} from "../MathLib.sol";
import {UtilsLib} from "../UtilsLib.sol";
import {MorphoLib} from "./MorphoLib.sol";
import {SharesMathLib} from "../SharesMathLib.sol";
import {MarketParamsLib} from "../MarketParamsLib.sol";

/// @title MorphoBalancesLib
/// @author Morpho Labs
/// @custom:contact [email protected]
/// @notice Helper library exposing getters with the expected value after interest accrual.
/// @dev This library is not used in Morpho itself and is intended to be used by integrators.
/// @dev The getter to retrieve the expected total borrow shares is not exposed because interest accrual does not apply
/// to it. The value can be queried directly on Morpho using `totalBorrowShares`.
library MorphoBalancesLib {
    using MathLib for uint256;
    using MathLib for uint128;
    using UtilsLib for uint256;
    using MorphoLib for IMorpho;
    using SharesMathLib for uint256;
    using MarketParamsLib for MarketParams;

    /// @notice Returns the expected market balances of a market after having accrued interest.
    /// @return The expected total supply assets.
    /// @return The expected total supply shares.
    /// @return The expected total borrow assets.
    /// @return The expected total borrow shares.
    function expectedMarketBalances(IMorpho morpho, MarketParams memory marketParams)
        internal
        view
        returns (uint256, uint256, uint256, uint256)
    {
        Id id = marketParams.id();
        Market memory market = morpho.market(id);

        uint256 elapsed = block.timestamp - market.lastUpdate;

        // Skipped if elapsed == 0 or totalBorrowAssets == 0 because interest would be null, or if irm == address(0).
        if (elapsed != 0 && market.totalBorrowAssets != 0 && marketParams.irm != address(0)) {
            uint256 borrowRate = IIrm(marketParams.irm).borrowRateView(marketParams, market);
            uint256 interest = market.totalBorrowAssets.wMulDown(borrowRate.wTaylorCompounded(elapsed));
            market.totalBorrowAssets += interest.toUint128();
            market.totalSupplyAssets += interest.toUint128();

            if (market.fee != 0) {
                uint256 feeAmount = interest.wMulDown(market.fee);
                // The fee amount is subtracted from the total supply in this calculation to compensate for the fact
                // that total supply is already updated.
                uint256 feeShares =
                    feeAmount.toSharesDown(market.totalSupplyAssets - feeAmount, market.totalSupplyShares);
                market.totalSupplyShares += feeShares.toUint128();
            }
        }

        return (market.totalSupplyAssets, market.totalSupplyShares, market.totalBorrowAssets, market.totalBorrowShares);
    }

    /// @notice Returns the expected total supply assets of a market after having accrued interest.
    function expectedTotalSupplyAssets(IMorpho morpho, MarketParams memory marketParams)
        internal
        view
        returns (uint256 totalSupplyAssets)
    {
        (totalSupplyAssets,,,) = expectedMarketBalances(morpho, marketParams);
    }

    /// @notice Returns the expected total borrow assets of a market after having accrued interest.
    function expectedTotalBorrowAssets(IMorpho morpho, MarketParams memory marketParams)
        internal
        view
        returns (uint256 totalBorrowAssets)
    {
        (,, totalBorrowAssets,) = expectedMarketBalances(morpho, marketParams);
    }

    /// @notice Returns the expected total supply shares of a market after having accrued interest.
    function expectedTotalSupplyShares(IMorpho morpho, MarketParams memory marketParams)
        internal
        view
        returns (uint256 totalSupplyShares)
    {
        (, totalSupplyShares,,) = expectedMarketBalances(morpho, marketParams);
    }

    /// @notice Returns the expected supply assets balance of `user` on a market after having accrued interest.
    /// @dev Warning: Wrong for `feeRecipient` because their supply shares increase is not taken into account.
    /// @dev Warning: Withdrawing using the expected supply assets can lead to a revert due to conversion roundings from
    /// assets to shares.
    function expectedSupplyAssets(IMorpho morpho, MarketParams memory marketParams, address user)
        internal
        view
        returns (uint256)
    {
        Id id = marketParams.id();
        uint256 supplyShares = morpho.supplyShares(id, user);
        (uint256 totalSupplyAssets, uint256 totalSupplyShares,,) = expectedMarketBalances(morpho, marketParams);

        return supplyShares.toAssetsDown(totalSupplyAssets, totalSupplyShares);
    }

    /// @notice Returns the expected borrow assets balance of `user` on a market after having accrued interest.
    /// @dev Warning: The expected balance is rounded up, so it may be greater than the market's expected total borrow
    /// assets.
    function expectedBorrowAssets(IMorpho morpho, MarketParams memory marketParams, address user)
        internal
        view
        returns (uint256)
    {
        Id id = marketParams.id();
        uint256 borrowShares = morpho.borrowShares(id, user);
        (,, uint256 totalBorrowAssets, uint256 totalBorrowShares) = expectedMarketBalances(morpho, marketParams);

        return borrowShares.toAssetsUp(totalBorrowAssets, totalBorrowShares);
    }
}

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

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

/// @title SharesMathLib
/// @author Morpho Labs
/// @custom:contact [email protected]
/// @notice Shares management library.
/// @dev This implementation mitigates share price manipulations, using OpenZeppelin's method of virtual shares:
/// https://docs.openzeppelin.com/contracts/4.x/erc4626#inflation-attack.
library SharesMathLib {
    using MathLib for uint256;

    /// @dev The number of virtual shares has been chosen low enough to prevent overflows, and high enough to ensure
    /// high precision computations.
    /// @dev Virtual shares can never be redeemed for the assets they are entitled to, but it is assumed the share price
    /// stays low enough not to inflate these assets to a significant value.
    /// @dev Warning: The assets to which virtual borrow shares are entitled behave like unrealizable bad debt.
    uint256 internal constant VIRTUAL_SHARES = 1e6;

    /// @dev A number of virtual assets of 1 enforces a conversion rate between shares and assets when a market is
    /// empty.
    uint256 internal constant VIRTUAL_ASSETS = 1;

    /// @dev Calculates the value of `assets` quoted in shares, rounding down.
    function toSharesDown(uint256 assets, uint256 totalAssets, uint256 totalShares) internal pure returns (uint256) {
        return assets.mulDivDown(totalShares + VIRTUAL_SHARES, totalAssets + VIRTUAL_ASSETS);
    }

    /// @dev Calculates the value of `shares` quoted in assets, rounding down.
    function toAssetsDown(uint256 shares, uint256 totalAssets, uint256 totalShares) internal pure returns (uint256) {
        return shares.mulDivDown(totalAssets + VIRTUAL_ASSETS, totalShares + VIRTUAL_SHARES);
    }

    /// @dev Calculates the value of `assets` quoted in shares, rounding up.
    function toSharesUp(uint256 assets, uint256 totalAssets, uint256 totalShares) internal pure returns (uint256) {
        return assets.mulDivUp(totalShares + VIRTUAL_SHARES, totalAssets + VIRTUAL_ASSETS);
    }

    /// @dev Calculates the value of `shares` quoted in assets, rounding up.
    function toAssetsUp(uint256 shares, uint256 totalAssets, uint256 totalShares) internal pure returns (uint256) {
        return shares.mulDivUp(totalAssets + VIRTUAL_ASSETS, totalShares + VIRTUAL_SHARES);
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.3.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 ERC-4626 "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 redemption 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.3.0) (utils/Pausable.sol)

pragma solidity ^0.8.20;

import {Context} from "../utils/Context.sol";

/**
 * @dev Contract module which allows children to implement an emergency stop
 * mechanism that can be triggered by an authorized account.
 *
 * This module is used through inheritance. It will make available the
 * modifiers `whenNotPaused` and `whenPaused`, which can be applied to
 * the functions of your contract. Note that they will not be pausable by
 * simply including this module, only once the modifiers are put in place.
 */
abstract contract Pausable is Context {
    bool private _paused;

    /**
     * @dev Emitted when the pause is triggered by `account`.
     */
    event Paused(address account);

    /**
     * @dev Emitted when the pause is lifted by `account`.
     */
    event Unpaused(address account);

    /**
     * @dev The operation failed because the contract is paused.
     */
    error EnforcedPause();

    /**
     * @dev The operation failed because the contract is not paused.
     */
    error ExpectedPause();

    /**
     * @dev Modifier to make a function callable only when the contract is not paused.
     *
     * Requirements:
     *
     * - The contract must not be paused.
     */
    modifier whenNotPaused() {
        _requireNotPaused();
        _;
    }

    /**
     * @dev Modifier to make a function callable only when the contract is paused.
     *
     * Requirements:
     *
     * - The contract must be paused.
     */
    modifier whenPaused() {
        _requirePaused();
        _;
    }

    /**
     * @dev Returns true if the contract is paused, and false otherwise.
     */
    function paused() public view virtual returns (bool) {
        return _paused;
    }

    /**
     * @dev Throws if the contract is paused.
     */
    function _requireNotPaused() internal view virtual {
        if (paused()) {
            revert EnforcedPause();
        }
    }

    /**
     * @dev Throws if the contract is not paused.
     */
    function _requirePaused() internal view virtual {
        if (!paused()) {
            revert ExpectedPause();
        }
    }

    /**
     * @dev Triggers stopped state.
     *
     * Requirements:
     *
     * - The contract must not be paused.
     */
    function _pause() internal virtual whenNotPaused {
        _paused = true;
        emit Paused(_msgSender());
    }

    /**
     * @dev Returns to normal state.
     *
     * Requirements:
     *
     * - The contract must be paused.
     */
    function _unpause() internal virtual whenPaused {
        _paused = false;
        emit Unpaused(_msgSender());
    }
}

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

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

/**
 * @title TokenizedStrategy Storage Library
 * @author yearn.finance
 * @notice Library for accessing storage slots of TokenizedStrategy contracts
 * @dev This library provides helper functions to compute storage slot locations
 * for TokenizedStrategy state variables. This is useful for off-chain tools,
 * monitoring systems, and contracts that need direct storage access.
 *
 * Based on the pattern used by Morpho's MorphoStorageLib.
 */
library TokenizedStrategyStorageLib {
    /**
     * @dev The main storage slot for the StrategyData struct.
     * This matches the BASE_STRATEGY_STORAGE constant in TokenizedStrategy.sol
     */
    bytes32 internal constant BASE_STRATEGY_STORAGE =
        bytes32(uint256(keccak256("yearn.base.strategy.storage")) - 1);

    /**
     * @dev The StrategyData struct that holds all storage for TokenizedStrategy v3.0.4.
     * This must match the exact layout in TokenizedStrategy.sol to ensure compatibility.
     */
    struct StrategyData {
        // Slot 0: ERC20 asset (160 bits) + decimals (8 bits) + 88 bits unused
        ERC20 asset;
        uint8 decimals;
        // Slot 1: string name (dynamic storage)
        string name;
        // Slot 2: uint256 totalSupply
        uint256 totalSupply;
        // Slot 3: mapping nonces
        mapping(address => uint256) nonces;
        // Slot 4: mapping balances
        mapping(address => uint256) balances;
        // Slot 5: mapping allowances
        mapping(address => mapping(address => uint256)) allowances;
        // Slot 6: uint256 totalAssets
        uint256 totalAssets;
        // Slot 7: uint256 profitUnlockingRate
        uint256 profitUnlockingRate;
        // Slot 8: uint96 fullProfitUnlockDate (96 bits) + address keeper (160 bits)
        uint96 fullProfitUnlockDate;
        address keeper;
        // Slot 9: uint32 profitMaxUnlockTime + uint16 performanceFee + address performanceFeeRecipient (208 bits total)
        uint32 profitMaxUnlockTime;
        uint16 performanceFee;
        address performanceFeeRecipient;
        // Slot 10: uint96 lastReport + address management
        uint96 lastReport;
        address management;
        // Slot 11: address pendingManagement
        address pendingManagement;
        // Slot 12: address emergencyAdmin
        address emergencyAdmin;
        // Slot 13: uint8 entered + bool shutdown
        uint8 entered;
        bool shutdown;
    }

    /**
     * @notice Get the main storage slot for the StrategyData struct
     * @return slot The storage slot where StrategyData is stored
     */
    function strategyStorageSlot() internal pure returns (bytes32 slot) {
        return BASE_STRATEGY_STORAGE;
    }

    /**
     * @notice Get the storage slot for asset and decimals (packed)
     * @return slot The storage slot containing asset (20 bytes) and decimals (1 byte)
     */
    function assetSlot() internal pure returns (bytes32 slot) {
        return BASE_STRATEGY_STORAGE;
    }

    /**
     * @notice Get the storage slot for the strategy name
     * @return slot The storage slot for the name string
     */
    function nameSlot() internal pure returns (bytes32 slot) {
        return bytes32(uint256(BASE_STRATEGY_STORAGE) + 1);
    }

    /**
     * @notice Get the storage slot for totalSupply
     * @return slot The storage slot for totalSupply
     */
    function totalSupplySlot() internal pure returns (bytes32 slot) {
        return bytes32(uint256(BASE_STRATEGY_STORAGE) + 2);
    }

    /**
     * @notice Get the storage slot for totalAssets
     * @return slot The storage slot for totalAssets
     */
    function totalAssetsSlot() internal pure returns (bytes32 slot) {
        return bytes32(uint256(BASE_STRATEGY_STORAGE) + 6);
    }

    /**
     * @notice Get the storage slot for profitUnlockingRate
     * @return slot The storage slot for profitUnlockingRate
     */
    function profitUnlockingRateSlot() internal pure returns (bytes32 slot) {
        return bytes32(uint256(BASE_STRATEGY_STORAGE) + 7);
    }

    /**
     * @notice Get the storage slot for fullProfitUnlockDate and keeper (packed)
     * @return slot The storage slot containing fullProfitUnlockDate (uint96) and keeper (address)
     */
    function fullProfitUnlockDateAndKeeperSlot()
        internal
        pure
        returns (bytes32 slot)
    {
        return bytes32(uint256(BASE_STRATEGY_STORAGE) + 8);
    }

    /**
     * @notice Get the storage slot for profitMaxUnlockTime, performanceFee, and performanceFeeRecipient (packed)
     * @return slot The storage slot containing profitMaxUnlockTime (uint32), performanceFee (uint16), and performanceFeeRecipient (address)
     */
    function profitConfigSlot() internal pure returns (bytes32 slot) {
        return bytes32(uint256(BASE_STRATEGY_STORAGE) + 9);
    }

    /**
     * @notice Get the storage slot for lastReport and management (packed)
     * @return slot The storage slot containing lastReport (uint96) and management (address)
     */
    function lastReportAndManagementSlot()
        internal
        pure
        returns (bytes32 slot)
    {
        return bytes32(uint256(BASE_STRATEGY_STORAGE) + 10);
    }

    /**
     * @notice Get the storage slot for pendingManagement
     * @return slot The storage slot for pendingManagement address
     */
    function pendingManagementSlot() internal pure returns (bytes32 slot) {
        return bytes32(uint256(BASE_STRATEGY_STORAGE) + 11);
    }

    /**
     * @notice Get the storage slot for emergencyAdmin
     * @return slot The storage slot for emergencyAdmin address
     */
    function emergencyAdminSlot() internal pure returns (bytes32 slot) {
        return bytes32(uint256(BASE_STRATEGY_STORAGE) + 12);
    }

    /**
     * @notice Get the storage slot for entered and shutdown (packed)
     * @return slot The storage slot containing entered (uint8) and shutdown (bool)
     */
    function statusSlot() internal pure returns (bytes32 slot) {
        return bytes32(uint256(BASE_STRATEGY_STORAGE) + 13);
    }

    /**
     * @notice Calculate the storage slot for a specific nonce
     * @param owner The address to get the nonce slot for
     * @return slot The storage slot for the owner's nonce
     */
    function noncesSlot(address owner) internal pure returns (bytes32 slot) {
        // nonces mapping is at slot position 3 from BASE_STRATEGY_STORAGE
        bytes32 noncesPosition = bytes32(uint256(BASE_STRATEGY_STORAGE) + 3);
        return keccak256(abi.encode(owner, noncesPosition));
    }

    /**
     * @notice Calculate the storage slot for a specific balance
     * @param account The address to get the balance slot for
     * @return slot The storage slot for the account's balance
     */
    function balancesSlot(
        address account
    ) internal pure returns (bytes32 slot) {
        // balances mapping is at slot position 4 from BASE_STRATEGY_STORAGE
        bytes32 balancesPosition = bytes32(uint256(BASE_STRATEGY_STORAGE) + 4);
        return keccak256(abi.encode(account, balancesPosition));
    }

    /**
     * @notice Calculate the storage slot for a specific allowance
     * @param owner The address that owns the tokens
     * @param spender The address that can spend the tokens
     * @return slot The storage slot for the allowance
     */
    function allowancesSlot(
        address owner,
        address spender
    ) internal pure returns (bytes32 slot) {
        // allowances mapping is at slot position 5 from BASE_STRATEGY_STORAGE
        bytes32 allowancesPosition = bytes32(
            uint256(BASE_STRATEGY_STORAGE) + 5
        );
        // For nested mappings: keccak256(spender . keccak256(owner . slot))
        bytes32 ownerSlot = keccak256(abi.encode(owner, allowancesPosition));
        return keccak256(abi.encode(spender, ownerSlot));
    }

    /**
     * @notice Helper to load the StrategyData struct from storage
     * @dev This can be used in external contracts to load the full struct
     * @return S The StrategyData struct from storage
     */
    function getStrategyStorage()
        internal
        pure
        returns (StrategyData storage S)
    {
        bytes32 slot = BASE_STRATEGY_STORAGE;
        assembly {
            S.slot := slot
        }
    }
}

// SPDX-License-Identifier: GPL-20later
pragma solidity ^0.8.18;

/// @notice Interface for the ProtocolConfig contract

// Struct to hold market parameters
struct MarketConfig {
    uint256 gracePeriod; // Duration of grace period after cycle end
    uint256 delinquencyPeriod; // Duration of delinquency period before default
    uint256 minBorrow; // Minimum outstanding loan balance to prevent dust
    uint256 irp; // Penalty rate per second for delinquent borrowers
}

// Struct to hold credit line parameters
struct CreditLineConfig {
    uint256 maxLTV;
    uint256 maxVV;
    uint256 maxCreditLine;
    uint256 minCreditLine;
    uint256 maxDRP;
}

// Struct to hold IRM parameters
struct IRMConfig {
    uint256 curveSteepness;
    uint256 adjustmentSpeed;
    uint256 targetUtilization;
    uint256 initialRateAtTarget;
    uint256 minRateAtTarget;
    uint256 maxRateAtTarget;
}

/// @notice Struct to hold IRM parameters with int256 types for internal calculations
struct IRMConfigTyped {
    int256 curveSteepness;
    int256 adjustmentSpeed;
    int256 targetUtilization;
    int256 initialRateAtTarget;
    int256 minRateAtTarget;
    int256 maxRateAtTarget;
}

interface IProtocolConfig {
    /// @dev Initialize the contract with the owner
    /// @param newOwner The address of the new owner
    function initialize(address newOwner) external;

    /// @dev Set a configuration value
    /// @param key The configuration key
    /// @param value The configuration value
    function setConfig(bytes32 key, uint256 value) external;

    // Credit Line getters
    /// @dev Get the credit line parameters
    /// @return The credit line parameters
    function getCreditLineConfig() external view returns (CreditLineConfig memory);

    // Market getters
    /// @dev Get the pause status
    /// @return The pause status value
    function getIsPaused() external view returns (uint256);

    /// @dev Get the maximum on credit
    /// @return The max on credit value
    function getMaxOnCredit() external view returns (uint256);

    /// @dev Get the market parameters
    /// @return The market parameters
    function getMarketConfig() external view returns (MarketConfig memory);

    /// @dev Get the cycle duration for payment cycles
    /// @return The cycle duration in seconds
    function getCycleDuration() external view returns (uint256);

    // IRM getters
    /// @dev Get the IRM parameters
    /// @return The IRM parameters
    function getIRMConfig() external view returns (IRMConfig memory);

    // USD3 & sUSD3 getters
    /// @dev Get the tranche ratio
    /// @return The tranche ratio value
    function getTrancheRatio() external view returns (uint256);

    /// @dev Get the tranche share variant
    /// @return The tranche share variant value
    function getTrancheShareVariant() external view returns (uint256);

    /// @dev Get the SUSD3 lock duration
    /// @return The SUSD3 lock duration value
    function getSusd3LockDuration() external view returns (uint256);

    /// @dev Get the SUSD3 cooldown period
    /// @return The SUSD3 cooldown period value
    function getSusd3CooldownPeriod() external view returns (uint256);

    /// @dev Get the USD3 commitment time
    /// @return The lock period in seconds
    function getUsd3CommitmentTime() external view returns (uint256);

    /// @dev Get the sUSD3 withdrawal window
    /// @return The withdrawal window duration in seconds after cooldown
    function getSusd3WithdrawalWindow() external view returns (uint256);

    /// @dev Get the USD3 supply cap
    /// @return The supply cap in asset units (0 means no cap)
    function getUsd3SupplyCap() external view returns (uint256);

    /// @dev Get configuration value by key
    /// @param key The configuration key
    /// @return The configuration value
    function config(bytes32 key) external view returns (uint256);
}

File 13 of 35 : ProtocolConfigLib.sol
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.8.0;

/// @title ProtocolConfigLib
/// @notice Library containing all configuration keys for ProtocolConfig
/// @dev Centralizes all configuration keys to avoid magic strings and improve maintainability
library ProtocolConfigLib {
    // Market Control Keys
    bytes32 internal constant IS_PAUSED = keccak256("IS_PAUSED");
    bytes32 internal constant MAX_ON_CREDIT = keccak256("MAX_ON_CREDIT");
    bytes32 internal constant DEBT_CAP = keccak256("DEBT_CAP");

    // Credit Line Keys
    bytes32 internal constant MIN_LOAN_DURATION = keccak256("MIN_LOAN_DURATION");
    bytes32 internal constant LATE_REPAYMENT_THRESHOLD = keccak256("LATE_REPAYMENT_THRESHOLD");
    bytes32 internal constant DEFAULT_THRESHOLD = keccak256("DEFAULT_THRESHOLD");
    bytes32 internal constant GRACE_PERIOD = keccak256("GRACE_PERIOD");

    // Interest Rate Keys
    bytes32 internal constant MIN_RATE_AT_TARGET = keccak256("MIN_RATE_AT_TARGET");
    bytes32 internal constant MAX_RATE_AT_TARGET = keccak256("MAX_RATE_AT_TARGET");

    // Tranche Keys (USD3 & sUSD3)
    bytes32 internal constant TRANCHE_RATIO = keccak256("TRANCHE_RATIO");
    bytes32 internal constant TRANCHE_SHARE_VARIANT = keccak256("TRANCHE_SHARE_VARIANT");
    bytes32 internal constant MIN_SUSD3_BACKING_RATIO = keccak256("MIN_SUSD3_BACKING_RATIO");

    // Timing Keys
    bytes32 internal constant SUSD3_LOCK_DURATION = keccak256("SUSD3_LOCK_DURATION");
    bytes32 internal constant SUSD3_COOLDOWN_PERIOD = keccak256("SUSD3_COOLDOWN_PERIOD");
    bytes32 internal constant USD3_COMMITMENT_TIME = keccak256("USD3_COMMITMENT_TIME");
    bytes32 internal constant SUSD3_WITHDRAWAL_WINDOW = keccak256("SUSD3_WITHDRAWAL_WINDOW");

    // Supply Cap Keys
    bytes32 internal constant USD3_SUPPLY_CAP = keccak256("USD3_SUPPLY_CAP");

    // Markdown Keys
    bytes32 internal constant FULL_MARKDOWN_DURATION = keccak256("FULL_MARKDOWN_DURATION");
}

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

import {Initializable} from "../../../lib/openzeppelin/contracts/proxy/utils/Initializable.sol";

// TokenizedStrategy interface used for internal view delegateCalls.
import {ITokenizedStrategy, ERC20} from "@tokenized-strategy/interfaces/ITokenizedStrategy.sol";

/**
 * @title BaseStrategyUpgradeable
 * @author Modified from yearn.finance BaseStrategy for upgradeability
 * @notice
 *  BaseStrategyUpgradeable is a fork of Yearn V3's BaseStrategy that works
 *  with upgradeable proxy patterns. The main change is replacing immutable
 *  variables with storage variables and constructor with initializer.
 *
 *  It maintains full compatibility with TokenizedStrategy singleton pattern
 *  while allowing the strategy to be deployed behind a proxy.
 */
abstract contract BaseStrategyUpgradeable is Initializable {
    /*//////////////////////////////////////////////////////////////
                            MODIFIERS
    //////////////////////////////////////////////////////////////*/
    /**
     * @dev Used on TokenizedStrategy callback functions to make sure it is post
     * a delegateCall from this address to the TokenizedStrategy.
     */
    modifier onlySelf() {
        _onlySelf();
        _;
    }

    /**
     * @dev Use to assure that the call is coming from the strategies management.
     */
    modifier onlyManagement() {
        TokenizedStrategy.requireManagement(msg.sender);
        _;
    }

    /**
     * @dev Use to assure that the call is coming from either the strategies
     * management or the keeper.
     */
    modifier onlyKeepers() {
        TokenizedStrategy.requireKeeperOrManagement(msg.sender);
        _;
    }

    /**
     * @dev Use to assure that the call is coming from either the strategies
     * management or the emergency admin.
     */
    modifier onlyEmergencyAuthorized() {
        TokenizedStrategy.requireEmergencyAuthorized(msg.sender);
        _;
    }

    /**
     * @dev Require that the msg.sender is this address.
     */
    function _onlySelf() internal view {
        require(msg.sender == address(this), "!self");
    }

    /*//////////////////////////////////////////////////////////////
                            CONSTANTS
    //////////////////////////////////////////////////////////////*/

    /**
     * @dev This is the address of the TokenizedStrategy implementation
     * contract that will be used by all strategies to handle the
     * accounting, logic, storage etc.
     *
     * Any external calls to the that don't hit one of the functions
     * defined in this base or the strategy will end up being forwarded
     * through the fallback function, which will delegateCall this address.
     *
     * This address should be the same for every strategy, never be adjusted
     * and always be checked before any integration with the Strategy.
     */
    address public constant tokenizedStrategyAddress = 0xD377919FA87120584B21279a491F82D5265A139c;

    /*//////////////////////////////////////////////////////////////
                            STORAGE
    //////////////////////////////////////////////////////////////*/

    /**
     * @dev Underlying asset the Strategy is earning yield on.
     * Stored here for cheap retrievals within the strategy.
     */
    ERC20 internal asset;

    /**
     * @dev This variable is set to ITokenizedStrategy(address(this)) during initialization.
     *
     * This provides a convenient way to call TokenizedStrategy functions
     * using clean syntax like TokenizedStrategy.totalAssets()
     */
    ITokenizedStrategy internal TokenizedStrategy;

    /**
     * @dev Storage gap for future upgrades
     */
    uint256[48] private __gap;

    /*//////////////////////////////////////////////////////////////
                            INITIALIZATION
    //////////////////////////////////////////////////////////////*/

    /**
     * @notice Initialize the BaseStrategy.
     *
     * @param _asset Address of the underlying asset.
     * @param _name Name the strategy will use.
     * @param _management Address to set as management.
     * @param _performanceFeeRecipient Address to receive performance fees.
     * @param _keeper Address allowed to call tend() and report().
     */
    function __BaseStrategy_init(
        address _asset,
        string memory _name,
        address _management,
        address _performanceFeeRecipient,
        address _keeper
    ) internal onlyInitializing {
        asset = ERC20(_asset);
        TokenizedStrategy = ITokenizedStrategy(address(this));

        // Initialize the strategy's storage variables via delegatecall.
        _delegateCall(
            abi.encodeCall(
                ITokenizedStrategy.initialize, (_asset, _name, _management, _performanceFeeRecipient, _keeper)
            )
        );
    }

    /*//////////////////////////////////////////////////////////////
                NEEDED TO BE OVERRIDDEN BY STRATEGIST
    //////////////////////////////////////////////////////////////*/

    /**
     * @dev Can deploy up to '_amount' of 'asset' in the yield source.
     *
     * This function is called at the end of a {deposit} or {mint}
     * call. Meaning that unless a whitelist is implemented it will
     * be entirely permissionless and thus can be sandwiched or otherwise
     * manipulated.
     *
     * @param _amount The amount of 'asset' that the strategy can attempt
     * to deposit in the yield source.
     */
    function _deployFunds(uint256 _amount) internal virtual;

    /**
     * @dev Should attempt to free the '_amount' of 'asset'.
     *
     * NOTE: The amount of 'asset' that is already loose has already
     * been accounted for.
     *
     * This function is called during {withdraw} and {redeem} calls.
     * Meaning that unless a whitelist is implemented it will be
     * entirely permissionless and thus can be sandwiched or otherwise
     * manipulated.
     *
     * Should not rely on asset.balanceOf(address(this)) calls other than
     * for diff accounting purposes.
     *
     * Any difference between `_amount` and what is actually freed will be
     * counted as a loss and passed on to the withdrawer. This means
     * care should be taken in times of illiquidity. It may be better to revert
     * if withdraws are simply illiquid so not to realize incorrect losses.
     *
     * @param _amount, The amount of 'asset' to be freed.
     */
    function _freeFunds(uint256 _amount) internal virtual;

    /**
     * @dev Internal function to harvest all rewards, redeploy any idle
     * funds and return an accurate accounting of all funds currently
     * held by the Strategy.
     *
     * This should do any needed harvesting, rewards selling, accrual,
     * redepositing etc. to get the most accurate view of current assets.
     *
     * NOTE: All applicable assets including loose assets should be
     * accounted for in this function.
     *
     * Care should be taken when relying on oracles or swap values rather
     * than actual amounts as all Strategy profit/loss accounting will
     * be done based on this returned value.
     *
     * This can still be called post a shutdown, a strategist can check
     * `TokenizedStrategy.isShutdown()` to decide if funds should be
     * redeployed or simply realize any profits/losses.
     *
     * @return _totalAssets A trusted and accurate account for the total
     * amount of 'asset' the strategy currently holds including idle funds.
     */
    function _harvestAndReport() internal virtual returns (uint256 _totalAssets);

    /*//////////////////////////////////////////////////////////////
                    OPTIONAL TO OVERRIDE BY STRATEGIST
    //////////////////////////////////////////////////////////////*/

    /**
     * @dev Optional function for strategist to override that can
     *  be called in between reports.
     *
     * If '_tend' is used tendTrigger() will also need to be overridden.
     *
     * This call can only be called by a permissioned role so may be
     * through protected relays.
     *
     * This can be used to harvest and compound rewards, deposit idle funds,
     * perform needed position maintenance or anything else that doesn't need
     * a full report for.
     *
     *   EX: A strategy that can not deposit funds without getting
     *       sandwiched can use the tend when a certain threshold
     *       of idle to totalAssets has been reached.
     *
     * This will have no effect on PPS of the strategy till report() is called.
     *
     * @param _totalIdle The current amount of idle funds that are available to deploy.
     */
    function _tend(uint256 _totalIdle) internal virtual {}

    /**
     * @dev Optional trigger to override if tend() will be used by the strategy.
     * This must be implemented if the strategy hopes to invoke _tend().
     *
     * @return . Should return true if tend() should be called by keeper or false if not.
     */
    function _tendTrigger() internal view virtual returns (bool) {
        return false;
    }

    /**
     * @notice Returns if tend() should be called by a keeper.
     *
     * @return . Should return true if tend() should be called by keeper or false if not.
     * @return . Calldata for the tend call.
     */
    function tendTrigger() external view virtual returns (bool, bytes memory) {
        return (
            // Return the status of the tend trigger.
            _tendTrigger(),
            // And the needed calldata either way.
            abi.encodeWithSelector(ITokenizedStrategy.tend.selector)
        );
    }

    /**
     * @notice Gets the max amount of `asset` that an address can deposit.
     * @dev Defaults to an unlimited amount for any address. But can
     * be overridden by strategists.
     *
     * This function will be called before any deposit or mints to enforce
     * any limits desired by the strategist. This can be used for either a
     * traditional deposit limit or for implementing a whitelist etc.
     *
     *   EX:
     *      if(isAllowed[_owner]) return super.availableDepositLimit(_owner);
     *
     * This does not need to take into account any conversion rates
     * from shares to assets. But should know that any non max uint256
     * amounts may be converted to shares. So it is recommended to keep
     * custom amounts low enough as not to cause overflow when multiplied
     * by `totalSupply`.
     *
     * @param . The address that is depositing into the strategy.
     * @return . The available amount the `_owner` can deposit in terms of `asset`
     */
    function availableDepositLimit(
        address /*_owner*/
    )
        public
        view
        virtual
        returns (uint256)
    {
        return type(uint256).max;
    }

    /**
     * @notice Gets the max amount of `asset` that can be withdrawn.
     * @dev Defaults to an unlimited amount for any address. But can
     * be overridden by strategists.
     *
     * This function will be called before any withdraw or redeem to enforce
     * any limits desired by the strategist. This can be used for illiquid
     * or sandwichable strategies. It should never be lower than `totalIdle`.
     *
     *   EX:
     *       return TokenIzedStrategy.totalIdle();
     *
     * This does not need to take into account the `_owner`'s share balance
     * or conversion rates from shares to assets.
     *
     * @param . The address that is withdrawing from the strategy.
     * @return . The available amount that can be withdrawn in terms of `asset`
     */
    function availableWithdrawLimit(
        address /*_owner*/
    )
        public
        view
        virtual
        returns (uint256)
    {
        return type(uint256).max;
    }

    /**
     * @dev Optional function for a strategist to override that will
     * allow management to manually withdraw deployed funds from the
     * yield source if a strategy is shutdown.
     *
     * This should attempt to free `_amount`, noting that `_amount` may
     * be more than is currently deployed.
     *
     * NOTE: This will not realize any profits or losses. A separate
     * {report} will be needed in order to record any profit/loss. If
     * a report may need to be called after a shutdown it is important
     * to check if the strategy is shutdown during {_harvestAndReport}
     * so that it does not simply re-deploy all funds that had been freed.
     *
     * EX:
     *   if(freeAsset > 0 && !TokenizedStrategy.isShutdown()) {
     *       depositFunds...
     *    }
     *
     * @param _amount The amount of asset to attempt to free.
     */
    function _emergencyWithdraw(uint256 _amount) internal virtual {}

    /*//////////////////////////////////////////////////////////////
                        TokenizedStrategy HOOKS
    //////////////////////////////////////////////////////////////*/

    /**
     * @notice Can deploy up to '_amount' of 'asset' in yield source.
     * @dev Callback for the TokenizedStrategy to call during a {deposit}
     * or {mint} to tell the strategy it can deploy funds.
     *
     * Since this can only be called after a {deposit} or {mint}
     * delegateCall to the TokenizedStrategy msg.sender == address(this).
     *
     * Unless a whitelist is implemented this will be entirely permissionless
     * and thus can be sandwiched or otherwise manipulated.
     *
     * @param _amount The amount of 'asset' that the strategy can
     * attempt to deposit in the yield source.
     */
    function deployFunds(uint256 _amount) external virtual onlySelf {
        _deployFunds(_amount);
    }

    /**
     * @notice Should attempt to free the '_amount' of 'asset'.
     * @dev Callback for the TokenizedStrategy to call during a withdraw
     * or redeem to free the needed funds to service the withdraw.
     *
     * This can only be called after a 'withdraw' or 'redeem' delegateCall
     * to the TokenizedStrategy so msg.sender == address(this).
     *
     * @param _amount The amount of 'asset' that the strategy should attempt to free up.
     */
    function freeFunds(uint256 _amount) external virtual onlySelf {
        _freeFunds(_amount);
    }

    /**
     * @notice Returns the accurate amount of all funds currently
     * held by the Strategy.
     * @dev Callback for the TokenizedStrategy to call during a report to
     * get an accurate accounting of assets the strategy controls.
     *
     * This can only be called after a report() delegateCall to the
     * TokenizedStrategy so msg.sender == address(this).
     *
     * @return . A trusted and accurate account for the total amount
     * of 'asset' the strategy currently holds including idle funds.
     */
    function harvestAndReport() external virtual onlySelf returns (uint256) {
        return _harvestAndReport();
    }

    /**
     * @notice Will call the internal '_tend' when a keeper tends the strategy.
     * @dev Callback for the TokenizedStrategy to initiate a _tend call in the strategy.
     *
     * This can only be called after a tend() delegateCall to the TokenizedStrategy
     * so msg.sender == address(this).
     *
     * We name the function `tendThis` so that `tend` calls are forwarded to
     * the TokenizedStrategy.
     *
     * @param _totalIdle The amount of current idle funds that can be
     * deployed during the tend
     */
    function tendThis(uint256 _totalIdle) external virtual onlySelf {
        _tend(_totalIdle);
    }

    /**
     * @notice Will call the internal '_emergencyWithdraw' function.
     * @dev Callback for the TokenizedStrategy during an emergency withdraw.
     *
     * This can only be called after a emergencyWithdraw() delegateCall to
     * the TokenizedStrategy so msg.sender == address(this).
     *
     * We name the function `shutdownWithdraw` so that `emergencyWithdraw`
     * calls are forwarded to the TokenizedStrategy.
     *
     * @param _amount The amount of asset to attempt to free.
     */
    function shutdownWithdraw(uint256 _amount) external virtual onlySelf {
        _emergencyWithdraw(_amount);
    }

    /**
     * @dev Function used to delegate call the TokenizedStrategy with
     * certain `_calldata` and return any return values.
     *
     * This is used to setup the initial storage of the strategy, and
     * can be used by strategist to forward any other call to the
     * TokenizedStrategy implementation.
     *
     * @param _calldata The abi encoded calldata to use in delegatecall.
     * @return . The return value if the call was successful in bytes.
     */
    function _delegateCall(bytes memory _calldata) internal returns (bytes memory) {
        // Delegate call the tokenized strategy with provided calldata.
        (bool success, bytes memory result) = tokenizedStrategyAddress.delegatecall(_calldata);

        // If the call reverted. Return the error.
        if (!success) {
            assembly {
                let ptr := mload(0x40)
                let size := returndatasize()
                returndatacopy(ptr, 0, size)
                revert(ptr, size)
            }
        }

        // Return the result.
        return result;
    }

    /**
     * @dev Execute a function on the TokenizedStrategy and return any value.
     *
     * This fallback function will be executed when any of the standard functions
     * defined in the TokenizedStrategy are called since they wont be defined in
     * this contract.
     *
     * It will delegatecall the TokenizedStrategy implementation with the exact
     * calldata and return any relevant values.
     *
     */
    fallback() external {
        // load our target address
        address _tokenizedStrategyAddress = tokenizedStrategyAddress;
        // Execute external function using delegatecall and return any value.
        assembly {
            // Copy function selector and any arguments.
            calldatacopy(0, 0, calldatasize())
            // Execute function delegatecall.
            let result := delegatecall(gas(), _tokenizedStrategyAddress, 0, calldatasize(), 0, 0)
            // Get any return value
            returndatacopy(0, 0, returndatasize())
            // Return any return value or error back to the caller
            switch result
            case 0 { revert(0, returndatasize()) }
            default { return(0, returndatasize()) }
        }
    }
}

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

contract DepositHooks {
    function _preDepositHook(
        uint256 assets,
        uint256 shares,
        address receiver
    ) internal virtual {}

    function _postDepositHook(
        uint256 assets,
        uint256 shares,
        address receiver
    ) internal virtual {}
}

contract WithdrawHooks {
    function _preWithdrawHook(
        uint256 assets,
        uint256 shares,
        address receiver,
        address owner,
        uint256 maxLoss
    ) internal virtual {}

    function _postWithdrawHook(
        uint256 assets,
        uint256 shares,
        address receiver,
        address owner,
        uint256 maxLoss
    ) internal virtual {}
}

contract TransferHooks {
    function _preTransferHook(
        address from,
        address to,
        uint256 amount
    ) internal virtual {}

    function _postTransferHook(
        address from,
        address to,
        uint256 amount,
        bool success
    ) internal virtual {}
}

contract ReportHooks {
    function _preReportHook() internal virtual {}

    function _postReportHook(uint256 profit, uint256 loss) internal virtual {}
}

contract Hooks is DepositHooks, WithdrawHooks, TransferHooks, ReportHooks {}

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

import {ERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import {IERC4626} from "@openzeppelin/contracts/interfaces/IERC4626.sol";
import {IERC20Permit} from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Permit.sol";

// Interface that implements the 4626 standard and the implementation functions
interface ITokenizedStrategy is IERC4626, IERC20Permit {
    /*//////////////////////////////////////////////////////////////
                                 EVENTS
    //////////////////////////////////////////////////////////////*/

    event StrategyShutdown();

    event NewTokenizedStrategy(
        address indexed strategy,
        address indexed asset,
        string apiVersion
    );

    event Reported(
        uint256 profit,
        uint256 loss,
        uint256 protocolFees,
        uint256 performanceFees
    );

    event UpdatePerformanceFeeRecipient(
        address indexed newPerformanceFeeRecipient
    );

    event UpdateKeeper(address indexed newKeeper);

    event UpdatePerformanceFee(uint16 newPerformanceFee);

    event UpdateManagement(address indexed newManagement);

    event UpdateEmergencyAdmin(address indexed newEmergencyAdmin);

    event UpdateProfitMaxUnlockTime(uint256 newProfitMaxUnlockTime);

    event UpdatePendingManagement(address indexed newPendingManagement);

    /*//////////////////////////////////////////////////////////////
                           INITIALIZATION
    //////////////////////////////////////////////////////////////*/

    function initialize(
        address _asset,
        string memory _name,
        address _management,
        address _performanceFeeRecipient,
        address _keeper
    ) external;

    /*//////////////////////////////////////////////////////////////
                    NON-STANDARD 4626 OPTIONS
    //////////////////////////////////////////////////////////////*/

    function withdraw(
        uint256 assets,
        address receiver,
        address owner,
        uint256 maxLoss
    ) external returns (uint256);

    function redeem(
        uint256 shares,
        address receiver,
        address owner,
        uint256 maxLoss
    ) external returns (uint256);

    function maxWithdraw(
        address owner,
        uint256 /*maxLoss*/
    ) external view returns (uint256);

    function maxRedeem(
        address owner,
        uint256 /*maxLoss*/
    ) external view returns (uint256);

    /*//////////////////////////////////////////////////////////////
                        MODIFIER HELPERS
    //////////////////////////////////////////////////////////////*/

    function requireManagement(address _sender) external view;

    function requireKeeperOrManagement(address _sender) external view;

    function requireEmergencyAuthorized(address _sender) external view;

    /*//////////////////////////////////////////////////////////////
                        KEEPERS FUNCTIONS
    //////////////////////////////////////////////////////////////*/

    function tend() external;

    function report() external returns (uint256 _profit, uint256 _loss);

    /*//////////////////////////////////////////////////////////////
                        CONSTANTS
    //////////////////////////////////////////////////////////////*/

    function MAX_FEE() external view returns (uint16);

    function FACTORY() external view returns (address);

    /*//////////////////////////////////////////////////////////////
                            GETTERS
    //////////////////////////////////////////////////////////////*/

    function apiVersion() external view returns (string memory);

    function pricePerShare() external view returns (uint256);

    function management() external view returns (address);

    function pendingManagement() external view returns (address);

    function keeper() external view returns (address);

    function emergencyAdmin() external view returns (address);

    function performanceFee() external view returns (uint16);

    function performanceFeeRecipient() external view returns (address);

    function fullProfitUnlockDate() external view returns (uint256);

    function profitUnlockingRate() external view returns (uint256);

    function profitMaxUnlockTime() external view returns (uint256);

    function lastReport() external view returns (uint256);

    function isShutdown() external view returns (bool);

    function unlockedShares() external view returns (uint256);

    /*//////////////////////////////////////////////////////////////
                            SETTERS
    //////////////////////////////////////////////////////////////*/

    function setPendingManagement(address) external;

    function acceptManagement() external;

    function setKeeper(address _keeper) external;

    function setEmergencyAdmin(address _emergencyAdmin) external;

    function setPerformanceFee(uint16 _performanceFee) external;

    function setPerformanceFeeRecipient(
        address _performanceFeeRecipient
    ) external;

    function setProfitMaxUnlockTime(uint256 _profitMaxUnlockTime) external;

    function setName(string calldata _newName) external;

    function shutdownStrategy() external;

    function emergencyWithdraw(uint256 _amount) external;
}

// 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 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 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 (last updated v4.9.4) (token/ERC20/extensions/IERC20Permit.sol)

pragma solidity ^0.8.0;

/**
 * @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: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (utils/Address.sol)

pragma solidity ^0.8.1;

/**
 * @dev Collection of functions related to the address type
 */
library Address {
    /**
     * @dev Returns true if `account` is a contract.
     *
     * [IMPORTANT]
     * ====
     * It is unsafe to assume that an address for which this function returns
     * false is an externally-owned account (EOA) and not a contract.
     *
     * Among others, `isContract` will return false for the following
     * types of addresses:
     *
     *  - an externally-owned account
     *  - a contract in construction
     *  - an address where a contract will be created
     *  - an address where a contract lived, but was destroyed
     *
     * Furthermore, `isContract` will also return true if the target contract within
     * the same transaction is already scheduled for destruction by `SELFDESTRUCT`,
     * which only has an effect at the end of a transaction.
     * ====
     *
     * [IMPORTANT]
     * ====
     * You shouldn't rely on `isContract` to protect against flash loan attacks!
     *
     * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets
     * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract
     * constructor.
     * ====
     */
    function isContract(address account) internal view returns (bool) {
        // This method relies on extcodesize/address.code.length, which returns 0
        // for contracts in construction, since the code is only stored at the end
        // of the constructor execution.

        return account.code.length > 0;
    }

    /**
     * @dev Replacement for Solidity's `transfer`: sends `amount` wei to
     * `recipient`, forwarding all available gas and reverting on errors.
     *
     * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
     * of certain opcodes, possibly making contracts go over the 2300 gas limit
     * imposed by `transfer`, making them unable to receive funds via
     * `transfer`. {sendValue} removes this limitation.
     *
     * https://consensys.net/diligence/blog/2019/09/stop-using-soliditys-transfer-now/[Learn more].
     *
     * IMPORTANT: because control is transferred to `recipient`, care must be
     * taken to not create reentrancy vulnerabilities. Consider using
     * {ReentrancyGuard} or the
     * https://solidity.readthedocs.io/en/v0.8.0/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
     */
    function sendValue(address payable recipient, uint256 amount) internal {
        require(address(this).balance >= amount, "Address: insufficient balance");

        (bool success, ) = recipient.call{value: amount}("");
        require(success, "Address: unable to send value, recipient may have reverted");
    }

    /**
     * @dev Performs a Solidity function call using a low level `call`. A
     * plain `call` is an unsafe replacement for a function call: use this
     * function instead.
     *
     * If `target` reverts with a revert reason, it is bubbled up by this
     * function (like regular Solidity function calls).
     *
     * Returns the raw returned data. To convert to the expected return value,
     * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
     *
     * Requirements:
     *
     * - `target` must be a contract.
     * - calling `target` with `data` must not revert.
     *
     * _Available since v3.1._
     */
    function functionCall(address target, bytes memory data) internal returns (bytes memory) {
        return functionCallWithValue(target, data, 0, "Address: low-level call failed");
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
     * `errorMessage` as a fallback revert reason when `target` reverts.
     *
     * _Available since v3.1._
     */
    function functionCall(
        address target,
        bytes memory data,
        string memory errorMessage
    ) internal returns (bytes memory) {
        return functionCallWithValue(target, data, 0, errorMessage);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but also transferring `value` wei to `target`.
     *
     * Requirements:
     *
     * - the calling contract must have an ETH balance of at least `value`.
     * - the called Solidity function must be `payable`.
     *
     * _Available since v3.1._
     */
    function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
        return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
    }

    /**
     * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
     * with `errorMessage` as a fallback revert reason when `target` reverts.
     *
     * _Available since v3.1._
     */
    function functionCallWithValue(
        address target,
        bytes memory data,
        uint256 value,
        string memory errorMessage
    ) internal returns (bytes memory) {
        require(address(this).balance >= value, "Address: insufficient balance for call");
        (bool success, bytes memory returndata) = target.call{value: value}(data);
        return verifyCallResultFromTarget(target, success, returndata, errorMessage);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but performing a static call.
     *
     * _Available since v3.3._
     */
    function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
        return functionStaticCall(target, data, "Address: low-level static call failed");
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
     * but performing a static call.
     *
     * _Available since v3.3._
     */
    function functionStaticCall(
        address target,
        bytes memory data,
        string memory errorMessage
    ) internal view returns (bytes memory) {
        (bool success, bytes memory returndata) = target.staticcall(data);
        return verifyCallResultFromTarget(target, success, returndata, errorMessage);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but performing a delegate call.
     *
     * _Available since v3.4._
     */
    function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
        return functionDelegateCall(target, data, "Address: low-level delegate call failed");
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
     * but performing a delegate call.
     *
     * _Available since v3.4._
     */
    function functionDelegateCall(
        address target,
        bytes memory data,
        string memory errorMessage
    ) internal returns (bytes memory) {
        (bool success, bytes memory returndata) = target.delegatecall(data);
        return verifyCallResultFromTarget(target, success, returndata, errorMessage);
    }

    /**
     * @dev Tool to verify that a low level call to smart-contract was successful, and revert (either by bubbling
     * the revert reason or using the provided one) in case of unsuccessful call or if target was not a contract.
     *
     * _Available since v4.8._
     */
    function verifyCallResultFromTarget(
        address target,
        bool success,
        bytes memory returndata,
        string memory errorMessage
    ) internal view returns (bytes memory) {
        if (success) {
            if (returndata.length == 0) {
                // only check isContract if the call was successful and the return data is empty
                // otherwise we already know that it was a contract
                require(isContract(target), "Address: call to non-contract");
            }
            return returndata;
        } else {
            _revert(returndata, errorMessage);
        }
    }

    /**
     * @dev Tool to verify that a low level call was successful, and revert if it wasn't, either by bubbling the
     * revert reason or using the provided one.
     *
     * _Available since v4.3._
     */
    function verifyCallResult(
        bool success,
        bytes memory returndata,
        string memory errorMessage
    ) internal pure returns (bytes memory) {
        if (success) {
            return returndata;
        } else {
            _revert(returndata, errorMessage);
        }
    }

    function _revert(bytes memory returndata, string memory errorMessage) private pure {
        // Look for revert reason and bubble it up if present
        if (returndata.length > 0) {
            // The easiest way to bubble the revert reason is using memory via assembly
            /// @solidity memory-safe-assembly
            assembly {
                let returndata_size := mload(returndata)
                revert(add(32, returndata), returndata_size)
            }
        } else {
            revert(errorMessage);
        }
    }
}

// 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 21 of 35 : SafeCast.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (utils/math/SafeCast.sol)
// This file was procedurally generated from scripts/generate/templates/SafeCast.js.

pragma solidity ^0.8.20;

/**
 * @dev Wrappers over Solidity's uintXX/intXX/bool casting operators with added overflow
 * checks.
 *
 * Downcasting from uint256/int256 in Solidity does not revert on overflow. This can
 * easily result in undesired exploitation or bugs, since developers usually
 * assume that overflows raise errors. `SafeCast` restores this intuition by
 * reverting the transaction when such an operation overflows.
 *
 * Using this library instead of the unchecked operations eliminates an entire
 * class of bugs, so it's recommended to use it always.
 */
library SafeCast {
    /**
     * @dev Value doesn't fit in an uint of `bits` size.
     */
    error SafeCastOverflowedUintDowncast(uint8 bits, uint256 value);

    /**
     * @dev An int value doesn't fit in an uint of `bits` size.
     */
    error SafeCastOverflowedIntToUint(int256 value);

    /**
     * @dev Value doesn't fit in an int of `bits` size.
     */
    error SafeCastOverflowedIntDowncast(uint8 bits, int256 value);

    /**
     * @dev An uint value doesn't fit in an int of `bits` size.
     */
    error SafeCastOverflowedUintToInt(uint256 value);

    /**
     * @dev Returns the downcasted uint248 from uint256, reverting on
     * overflow (when the input is greater than largest uint248).
     *
     * Counterpart to Solidity's `uint248` operator.
     *
     * Requirements:
     *
     * - input must fit into 248 bits
     */
    function toUint248(uint256 value) internal pure returns (uint248) {
        if (value > type(uint248).max) {
            revert SafeCastOverflowedUintDowncast(248, value);
        }
        return uint248(value);
    }

    /**
     * @dev Returns the downcasted uint240 from uint256, reverting on
     * overflow (when the input is greater than largest uint240).
     *
     * Counterpart to Solidity's `uint240` operator.
     *
     * Requirements:
     *
     * - input must fit into 240 bits
     */
    function toUint240(uint256 value) internal pure returns (uint240) {
        if (value > type(uint240).max) {
            revert SafeCastOverflowedUintDowncast(240, value);
        }
        return uint240(value);
    }

    /**
     * @dev Returns the downcasted uint232 from uint256, reverting on
     * overflow (when the input is greater than largest uint232).
     *
     * Counterpart to Solidity's `uint232` operator.
     *
     * Requirements:
     *
     * - input must fit into 232 bits
     */
    function toUint232(uint256 value) internal pure returns (uint232) {
        if (value > type(uint232).max) {
            revert SafeCastOverflowedUintDowncast(232, value);
        }
        return uint232(value);
    }

    /**
     * @dev Returns the downcasted uint224 from uint256, reverting on
     * overflow (when the input is greater than largest uint224).
     *
     * Counterpart to Solidity's `uint224` operator.
     *
     * Requirements:
     *
     * - input must fit into 224 bits
     */
    function toUint224(uint256 value) internal pure returns (uint224) {
        if (value > type(uint224).max) {
            revert SafeCastOverflowedUintDowncast(224, value);
        }
        return uint224(value);
    }

    /**
     * @dev Returns the downcasted uint216 from uint256, reverting on
     * overflow (when the input is greater than largest uint216).
     *
     * Counterpart to Solidity's `uint216` operator.
     *
     * Requirements:
     *
     * - input must fit into 216 bits
     */
    function toUint216(uint256 value) internal pure returns (uint216) {
        if (value > type(uint216).max) {
            revert SafeCastOverflowedUintDowncast(216, value);
        }
        return uint216(value);
    }

    /**
     * @dev Returns the downcasted uint208 from uint256, reverting on
     * overflow (when the input is greater than largest uint208).
     *
     * Counterpart to Solidity's `uint208` operator.
     *
     * Requirements:
     *
     * - input must fit into 208 bits
     */
    function toUint208(uint256 value) internal pure returns (uint208) {
        if (value > type(uint208).max) {
            revert SafeCastOverflowedUintDowncast(208, value);
        }
        return uint208(value);
    }

    /**
     * @dev Returns the downcasted uint200 from uint256, reverting on
     * overflow (when the input is greater than largest uint200).
     *
     * Counterpart to Solidity's `uint200` operator.
     *
     * Requirements:
     *
     * - input must fit into 200 bits
     */
    function toUint200(uint256 value) internal pure returns (uint200) {
        if (value > type(uint200).max) {
            revert SafeCastOverflowedUintDowncast(200, value);
        }
        return uint200(value);
    }

    /**
     * @dev Returns the downcasted uint192 from uint256, reverting on
     * overflow (when the input is greater than largest uint192).
     *
     * Counterpart to Solidity's `uint192` operator.
     *
     * Requirements:
     *
     * - input must fit into 192 bits
     */
    function toUint192(uint256 value) internal pure returns (uint192) {
        if (value > type(uint192).max) {
            revert SafeCastOverflowedUintDowncast(192, value);
        }
        return uint192(value);
    }

    /**
     * @dev Returns the downcasted uint184 from uint256, reverting on
     * overflow (when the input is greater than largest uint184).
     *
     * Counterpart to Solidity's `uint184` operator.
     *
     * Requirements:
     *
     * - input must fit into 184 bits
     */
    function toUint184(uint256 value) internal pure returns (uint184) {
        if (value > type(uint184).max) {
            revert SafeCastOverflowedUintDowncast(184, value);
        }
        return uint184(value);
    }

    /**
     * @dev Returns the downcasted uint176 from uint256, reverting on
     * overflow (when the input is greater than largest uint176).
     *
     * Counterpart to Solidity's `uint176` operator.
     *
     * Requirements:
     *
     * - input must fit into 176 bits
     */
    function toUint176(uint256 value) internal pure returns (uint176) {
        if (value > type(uint176).max) {
            revert SafeCastOverflowedUintDowncast(176, value);
        }
        return uint176(value);
    }

    /**
     * @dev Returns the downcasted uint168 from uint256, reverting on
     * overflow (when the input is greater than largest uint168).
     *
     * Counterpart to Solidity's `uint168` operator.
     *
     * Requirements:
     *
     * - input must fit into 168 bits
     */
    function toUint168(uint256 value) internal pure returns (uint168) {
        if (value > type(uint168).max) {
            revert SafeCastOverflowedUintDowncast(168, value);
        }
        return uint168(value);
    }

    /**
     * @dev Returns the downcasted uint160 from uint256, reverting on
     * overflow (when the input is greater than largest uint160).
     *
     * Counterpart to Solidity's `uint160` operator.
     *
     * Requirements:
     *
     * - input must fit into 160 bits
     */
    function toUint160(uint256 value) internal pure returns (uint160) {
        if (value > type(uint160).max) {
            revert SafeCastOverflowedUintDowncast(160, value);
        }
        return uint160(value);
    }

    /**
     * @dev Returns the downcasted uint152 from uint256, reverting on
     * overflow (when the input is greater than largest uint152).
     *
     * Counterpart to Solidity's `uint152` operator.
     *
     * Requirements:
     *
     * - input must fit into 152 bits
     */
    function toUint152(uint256 value) internal pure returns (uint152) {
        if (value > type(uint152).max) {
            revert SafeCastOverflowedUintDowncast(152, value);
        }
        return uint152(value);
    }

    /**
     * @dev Returns the downcasted uint144 from uint256, reverting on
     * overflow (when the input is greater than largest uint144).
     *
     * Counterpart to Solidity's `uint144` operator.
     *
     * Requirements:
     *
     * - input must fit into 144 bits
     */
    function toUint144(uint256 value) internal pure returns (uint144) {
        if (value > type(uint144).max) {
            revert SafeCastOverflowedUintDowncast(144, value);
        }
        return uint144(value);
    }

    /**
     * @dev Returns the downcasted uint136 from uint256, reverting on
     * overflow (when the input is greater than largest uint136).
     *
     * Counterpart to Solidity's `uint136` operator.
     *
     * Requirements:
     *
     * - input must fit into 136 bits
     */
    function toUint136(uint256 value) internal pure returns (uint136) {
        if (value > type(uint136).max) {
            revert SafeCastOverflowedUintDowncast(136, value);
        }
        return uint136(value);
    }

    /**
     * @dev Returns the downcasted uint128 from uint256, reverting on
     * overflow (when the input is greater than largest uint128).
     *
     * Counterpart to Solidity's `uint128` operator.
     *
     * Requirements:
     *
     * - input must fit into 128 bits
     */
    function toUint128(uint256 value) internal pure returns (uint128) {
        if (value > type(uint128).max) {
            revert SafeCastOverflowedUintDowncast(128, value);
        }
        return uint128(value);
    }

    /**
     * @dev Returns the downcasted uint120 from uint256, reverting on
     * overflow (when the input is greater than largest uint120).
     *
     * Counterpart to Solidity's `uint120` operator.
     *
     * Requirements:
     *
     * - input must fit into 120 bits
     */
    function toUint120(uint256 value) internal pure returns (uint120) {
        if (value > type(uint120).max) {
            revert SafeCastOverflowedUintDowncast(120, value);
        }
        return uint120(value);
    }

    /**
     * @dev Returns the downcasted uint112 from uint256, reverting on
     * overflow (when the input is greater than largest uint112).
     *
     * Counterpart to Solidity's `uint112` operator.
     *
     * Requirements:
     *
     * - input must fit into 112 bits
     */
    function toUint112(uint256 value) internal pure returns (uint112) {
        if (value > type(uint112).max) {
            revert SafeCastOverflowedUintDowncast(112, value);
        }
        return uint112(value);
    }

    /**
     * @dev Returns the downcasted uint104 from uint256, reverting on
     * overflow (when the input is greater than largest uint104).
     *
     * Counterpart to Solidity's `uint104` operator.
     *
     * Requirements:
     *
     * - input must fit into 104 bits
     */
    function toUint104(uint256 value) internal pure returns (uint104) {
        if (value > type(uint104).max) {
            revert SafeCastOverflowedUintDowncast(104, value);
        }
        return uint104(value);
    }

    /**
     * @dev Returns the downcasted uint96 from uint256, reverting on
     * overflow (when the input is greater than largest uint96).
     *
     * Counterpart to Solidity's `uint96` operator.
     *
     * Requirements:
     *
     * - input must fit into 96 bits
     */
    function toUint96(uint256 value) internal pure returns (uint96) {
        if (value > type(uint96).max) {
            revert SafeCastOverflowedUintDowncast(96, value);
        }
        return uint96(value);
    }

    /**
     * @dev Returns the downcasted uint88 from uint256, reverting on
     * overflow (when the input is greater than largest uint88).
     *
     * Counterpart to Solidity's `uint88` operator.
     *
     * Requirements:
     *
     * - input must fit into 88 bits
     */
    function toUint88(uint256 value) internal pure returns (uint88) {
        if (value > type(uint88).max) {
            revert SafeCastOverflowedUintDowncast(88, value);
        }
        return uint88(value);
    }

    /**
     * @dev Returns the downcasted uint80 from uint256, reverting on
     * overflow (when the input is greater than largest uint80).
     *
     * Counterpart to Solidity's `uint80` operator.
     *
     * Requirements:
     *
     * - input must fit into 80 bits
     */
    function toUint80(uint256 value) internal pure returns (uint80) {
        if (value > type(uint80).max) {
            revert SafeCastOverflowedUintDowncast(80, value);
        }
        return uint80(value);
    }

    /**
     * @dev Returns the downcasted uint72 from uint256, reverting on
     * overflow (when the input is greater than largest uint72).
     *
     * Counterpart to Solidity's `uint72` operator.
     *
     * Requirements:
     *
     * - input must fit into 72 bits
     */
    function toUint72(uint256 value) internal pure returns (uint72) {
        if (value > type(uint72).max) {
            revert SafeCastOverflowedUintDowncast(72, value);
        }
        return uint72(value);
    }

    /**
     * @dev Returns the downcasted uint64 from uint256, reverting on
     * overflow (when the input is greater than largest uint64).
     *
     * Counterpart to Solidity's `uint64` operator.
     *
     * Requirements:
     *
     * - input must fit into 64 bits
     */
    function toUint64(uint256 value) internal pure returns (uint64) {
        if (value > type(uint64).max) {
            revert SafeCastOverflowedUintDowncast(64, value);
        }
        return uint64(value);
    }

    /**
     * @dev Returns the downcasted uint56 from uint256, reverting on
     * overflow (when the input is greater than largest uint56).
     *
     * Counterpart to Solidity's `uint56` operator.
     *
     * Requirements:
     *
     * - input must fit into 56 bits
     */
    function toUint56(uint256 value) internal pure returns (uint56) {
        if (value > type(uint56).max) {
            revert SafeCastOverflowedUintDowncast(56, value);
        }
        return uint56(value);
    }

    /**
     * @dev Returns the downcasted uint48 from uint256, reverting on
     * overflow (when the input is greater than largest uint48).
     *
     * Counterpart to Solidity's `uint48` operator.
     *
     * Requirements:
     *
     * - input must fit into 48 bits
     */
    function toUint48(uint256 value) internal pure returns (uint48) {
        if (value > type(uint48).max) {
            revert SafeCastOverflowedUintDowncast(48, value);
        }
        return uint48(value);
    }

    /**
     * @dev Returns the downcasted uint40 from uint256, reverting on
     * overflow (when the input is greater than largest uint40).
     *
     * Counterpart to Solidity's `uint40` operator.
     *
     * Requirements:
     *
     * - input must fit into 40 bits
     */
    function toUint40(uint256 value) internal pure returns (uint40) {
        if (value > type(uint40).max) {
            revert SafeCastOverflowedUintDowncast(40, value);
        }
        return uint40(value);
    }

    /**
     * @dev Returns the downcasted uint32 from uint256, reverting on
     * overflow (when the input is greater than largest uint32).
     *
     * Counterpart to Solidity's `uint32` operator.
     *
     * Requirements:
     *
     * - input must fit into 32 bits
     */
    function toUint32(uint256 value) internal pure returns (uint32) {
        if (value > type(uint32).max) {
            revert SafeCastOverflowedUintDowncast(32, value);
        }
        return uint32(value);
    }

    /**
     * @dev Returns the downcasted uint24 from uint256, reverting on
     * overflow (when the input is greater than largest uint24).
     *
     * Counterpart to Solidity's `uint24` operator.
     *
     * Requirements:
     *
     * - input must fit into 24 bits
     */
    function toUint24(uint256 value) internal pure returns (uint24) {
        if (value > type(uint24).max) {
            revert SafeCastOverflowedUintDowncast(24, value);
        }
        return uint24(value);
    }

    /**
     * @dev Returns the downcasted uint16 from uint256, reverting on
     * overflow (when the input is greater than largest uint16).
     *
     * Counterpart to Solidity's `uint16` operator.
     *
     * Requirements:
     *
     * - input must fit into 16 bits
     */
    function toUint16(uint256 value) internal pure returns (uint16) {
        if (value > type(uint16).max) {
            revert SafeCastOverflowedUintDowncast(16, value);
        }
        return uint16(value);
    }

    /**
     * @dev Returns the downcasted uint8 from uint256, reverting on
     * overflow (when the input is greater than largest uint8).
     *
     * Counterpart to Solidity's `uint8` operator.
     *
     * Requirements:
     *
     * - input must fit into 8 bits
     */
    function toUint8(uint256 value) internal pure returns (uint8) {
        if (value > type(uint8).max) {
            revert SafeCastOverflowedUintDowncast(8, value);
        }
        return uint8(value);
    }

    /**
     * @dev Converts a signed int256 into an unsigned uint256.
     *
     * Requirements:
     *
     * - input must be greater than or equal to 0.
     */
    function toUint256(int256 value) internal pure returns (uint256) {
        if (value < 0) {
            revert SafeCastOverflowedIntToUint(value);
        }
        return uint256(value);
    }

    /**
     * @dev Returns the downcasted int248 from int256, reverting on
     * overflow (when the input is less than smallest int248 or
     * greater than largest int248).
     *
     * Counterpart to Solidity's `int248` operator.
     *
     * Requirements:
     *
     * - input must fit into 248 bits
     */
    function toInt248(int256 value) internal pure returns (int248 downcasted) {
        downcasted = int248(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(248, value);
        }
    }

    /**
     * @dev Returns the downcasted int240 from int256, reverting on
     * overflow (when the input is less than smallest int240 or
     * greater than largest int240).
     *
     * Counterpart to Solidity's `int240` operator.
     *
     * Requirements:
     *
     * - input must fit into 240 bits
     */
    function toInt240(int256 value) internal pure returns (int240 downcasted) {
        downcasted = int240(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(240, value);
        }
    }

    /**
     * @dev Returns the downcasted int232 from int256, reverting on
     * overflow (when the input is less than smallest int232 or
     * greater than largest int232).
     *
     * Counterpart to Solidity's `int232` operator.
     *
     * Requirements:
     *
     * - input must fit into 232 bits
     */
    function toInt232(int256 value) internal pure returns (int232 downcasted) {
        downcasted = int232(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(232, value);
        }
    }

    /**
     * @dev Returns the downcasted int224 from int256, reverting on
     * overflow (when the input is less than smallest int224 or
     * greater than largest int224).
     *
     * Counterpart to Solidity's `int224` operator.
     *
     * Requirements:
     *
     * - input must fit into 224 bits
     */
    function toInt224(int256 value) internal pure returns (int224 downcasted) {
        downcasted = int224(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(224, value);
        }
    }

    /**
     * @dev Returns the downcasted int216 from int256, reverting on
     * overflow (when the input is less than smallest int216 or
     * greater than largest int216).
     *
     * Counterpart to Solidity's `int216` operator.
     *
     * Requirements:
     *
     * - input must fit into 216 bits
     */
    function toInt216(int256 value) internal pure returns (int216 downcasted) {
        downcasted = int216(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(216, value);
        }
    }

    /**
     * @dev Returns the downcasted int208 from int256, reverting on
     * overflow (when the input is less than smallest int208 or
     * greater than largest int208).
     *
     * Counterpart to Solidity's `int208` operator.
     *
     * Requirements:
     *
     * - input must fit into 208 bits
     */
    function toInt208(int256 value) internal pure returns (int208 downcasted) {
        downcasted = int208(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(208, value);
        }
    }

    /**
     * @dev Returns the downcasted int200 from int256, reverting on
     * overflow (when the input is less than smallest int200 or
     * greater than largest int200).
     *
     * Counterpart to Solidity's `int200` operator.
     *
     * Requirements:
     *
     * - input must fit into 200 bits
     */
    function toInt200(int256 value) internal pure returns (int200 downcasted) {
        downcasted = int200(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(200, value);
        }
    }

    /**
     * @dev Returns the downcasted int192 from int256, reverting on
     * overflow (when the input is less than smallest int192 or
     * greater than largest int192).
     *
     * Counterpart to Solidity's `int192` operator.
     *
     * Requirements:
     *
     * - input must fit into 192 bits
     */
    function toInt192(int256 value) internal pure returns (int192 downcasted) {
        downcasted = int192(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(192, value);
        }
    }

    /**
     * @dev Returns the downcasted int184 from int256, reverting on
     * overflow (when the input is less than smallest int184 or
     * greater than largest int184).
     *
     * Counterpart to Solidity's `int184` operator.
     *
     * Requirements:
     *
     * - input must fit into 184 bits
     */
    function toInt184(int256 value) internal pure returns (int184 downcasted) {
        downcasted = int184(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(184, value);
        }
    }

    /**
     * @dev Returns the downcasted int176 from int256, reverting on
     * overflow (when the input is less than smallest int176 or
     * greater than largest int176).
     *
     * Counterpart to Solidity's `int176` operator.
     *
     * Requirements:
     *
     * - input must fit into 176 bits
     */
    function toInt176(int256 value) internal pure returns (int176 downcasted) {
        downcasted = int176(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(176, value);
        }
    }

    /**
     * @dev Returns the downcasted int168 from int256, reverting on
     * overflow (when the input is less than smallest int168 or
     * greater than largest int168).
     *
     * Counterpart to Solidity's `int168` operator.
     *
     * Requirements:
     *
     * - input must fit into 168 bits
     */
    function toInt168(int256 value) internal pure returns (int168 downcasted) {
        downcasted = int168(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(168, value);
        }
    }

    /**
     * @dev Returns the downcasted int160 from int256, reverting on
     * overflow (when the input is less than smallest int160 or
     * greater than largest int160).
     *
     * Counterpart to Solidity's `int160` operator.
     *
     * Requirements:
     *
     * - input must fit into 160 bits
     */
    function toInt160(int256 value) internal pure returns (int160 downcasted) {
        downcasted = int160(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(160, value);
        }
    }

    /**
     * @dev Returns the downcasted int152 from int256, reverting on
     * overflow (when the input is less than smallest int152 or
     * greater than largest int152).
     *
     * Counterpart to Solidity's `int152` operator.
     *
     * Requirements:
     *
     * - input must fit into 152 bits
     */
    function toInt152(int256 value) internal pure returns (int152 downcasted) {
        downcasted = int152(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(152, value);
        }
    }

    /**
     * @dev Returns the downcasted int144 from int256, reverting on
     * overflow (when the input is less than smallest int144 or
     * greater than largest int144).
     *
     * Counterpart to Solidity's `int144` operator.
     *
     * Requirements:
     *
     * - input must fit into 144 bits
     */
    function toInt144(int256 value) internal pure returns (int144 downcasted) {
        downcasted = int144(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(144, value);
        }
    }

    /**
     * @dev Returns the downcasted int136 from int256, reverting on
     * overflow (when the input is less than smallest int136 or
     * greater than largest int136).
     *
     * Counterpart to Solidity's `int136` operator.
     *
     * Requirements:
     *
     * - input must fit into 136 bits
     */
    function toInt136(int256 value) internal pure returns (int136 downcasted) {
        downcasted = int136(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(136, value);
        }
    }

    /**
     * @dev Returns the downcasted int128 from int256, reverting on
     * overflow (when the input is less than smallest int128 or
     * greater than largest int128).
     *
     * Counterpart to Solidity's `int128` operator.
     *
     * Requirements:
     *
     * - input must fit into 128 bits
     */
    function toInt128(int256 value) internal pure returns (int128 downcasted) {
        downcasted = int128(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(128, value);
        }
    }

    /**
     * @dev Returns the downcasted int120 from int256, reverting on
     * overflow (when the input is less than smallest int120 or
     * greater than largest int120).
     *
     * Counterpart to Solidity's `int120` operator.
     *
     * Requirements:
     *
     * - input must fit into 120 bits
     */
    function toInt120(int256 value) internal pure returns (int120 downcasted) {
        downcasted = int120(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(120, value);
        }
    }

    /**
     * @dev Returns the downcasted int112 from int256, reverting on
     * overflow (when the input is less than smallest int112 or
     * greater than largest int112).
     *
     * Counterpart to Solidity's `int112` operator.
     *
     * Requirements:
     *
     * - input must fit into 112 bits
     */
    function toInt112(int256 value) internal pure returns (int112 downcasted) {
        downcasted = int112(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(112, value);
        }
    }

    /**
     * @dev Returns the downcasted int104 from int256, reverting on
     * overflow (when the input is less than smallest int104 or
     * greater than largest int104).
     *
     * Counterpart to Solidity's `int104` operator.
     *
     * Requirements:
     *
     * - input must fit into 104 bits
     */
    function toInt104(int256 value) internal pure returns (int104 downcasted) {
        downcasted = int104(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(104, value);
        }
    }

    /**
     * @dev Returns the downcasted int96 from int256, reverting on
     * overflow (when the input is less than smallest int96 or
     * greater than largest int96).
     *
     * Counterpart to Solidity's `int96` operator.
     *
     * Requirements:
     *
     * - input must fit into 96 bits
     */
    function toInt96(int256 value) internal pure returns (int96 downcasted) {
        downcasted = int96(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(96, value);
        }
    }

    /**
     * @dev Returns the downcasted int88 from int256, reverting on
     * overflow (when the input is less than smallest int88 or
     * greater than largest int88).
     *
     * Counterpart to Solidity's `int88` operator.
     *
     * Requirements:
     *
     * - input must fit into 88 bits
     */
    function toInt88(int256 value) internal pure returns (int88 downcasted) {
        downcasted = int88(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(88, value);
        }
    }

    /**
     * @dev Returns the downcasted int80 from int256, reverting on
     * overflow (when the input is less than smallest int80 or
     * greater than largest int80).
     *
     * Counterpart to Solidity's `int80` operator.
     *
     * Requirements:
     *
     * - input must fit into 80 bits
     */
    function toInt80(int256 value) internal pure returns (int80 downcasted) {
        downcasted = int80(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(80, value);
        }
    }

    /**
     * @dev Returns the downcasted int72 from int256, reverting on
     * overflow (when the input is less than smallest int72 or
     * greater than largest int72).
     *
     * Counterpart to Solidity's `int72` operator.
     *
     * Requirements:
     *
     * - input must fit into 72 bits
     */
    function toInt72(int256 value) internal pure returns (int72 downcasted) {
        downcasted = int72(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(72, value);
        }
    }

    /**
     * @dev Returns the downcasted int64 from int256, reverting on
     * overflow (when the input is less than smallest int64 or
     * greater than largest int64).
     *
     * Counterpart to Solidity's `int64` operator.
     *
     * Requirements:
     *
     * - input must fit into 64 bits
     */
    function toInt64(int256 value) internal pure returns (int64 downcasted) {
        downcasted = int64(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(64, value);
        }
    }

    /**
     * @dev Returns the downcasted int56 from int256, reverting on
     * overflow (when the input is less than smallest int56 or
     * greater than largest int56).
     *
     * Counterpart to Solidity's `int56` operator.
     *
     * Requirements:
     *
     * - input must fit into 56 bits
     */
    function toInt56(int256 value) internal pure returns (int56 downcasted) {
        downcasted = int56(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(56, value);
        }
    }

    /**
     * @dev Returns the downcasted int48 from int256, reverting on
     * overflow (when the input is less than smallest int48 or
     * greater than largest int48).
     *
     * Counterpart to Solidity's `int48` operator.
     *
     * Requirements:
     *
     * - input must fit into 48 bits
     */
    function toInt48(int256 value) internal pure returns (int48 downcasted) {
        downcasted = int48(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(48, value);
        }
    }

    /**
     * @dev Returns the downcasted int40 from int256, reverting on
     * overflow (when the input is less than smallest int40 or
     * greater than largest int40).
     *
     * Counterpart to Solidity's `int40` operator.
     *
     * Requirements:
     *
     * - input must fit into 40 bits
     */
    function toInt40(int256 value) internal pure returns (int40 downcasted) {
        downcasted = int40(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(40, value);
        }
    }

    /**
     * @dev Returns the downcasted int32 from int256, reverting on
     * overflow (when the input is less than smallest int32 or
     * greater than largest int32).
     *
     * Counterpart to Solidity's `int32` operator.
     *
     * Requirements:
     *
     * - input must fit into 32 bits
     */
    function toInt32(int256 value) internal pure returns (int32 downcasted) {
        downcasted = int32(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(32, value);
        }
    }

    /**
     * @dev Returns the downcasted int24 from int256, reverting on
     * overflow (when the input is less than smallest int24 or
     * greater than largest int24).
     *
     * Counterpart to Solidity's `int24` operator.
     *
     * Requirements:
     *
     * - input must fit into 24 bits
     */
    function toInt24(int256 value) internal pure returns (int24 downcasted) {
        downcasted = int24(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(24, value);
        }
    }

    /**
     * @dev Returns the downcasted int16 from int256, reverting on
     * overflow (when the input is less than smallest int16 or
     * greater than largest int16).
     *
     * Counterpart to Solidity's `int16` operator.
     *
     * Requirements:
     *
     * - input must fit into 16 bits
     */
    function toInt16(int256 value) internal pure returns (int16 downcasted) {
        downcasted = int16(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(16, value);
        }
    }

    /**
     * @dev Returns the downcasted int8 from int256, reverting on
     * overflow (when the input is less than smallest int8 or
     * greater than largest int8).
     *
     * Counterpart to Solidity's `int8` operator.
     *
     * Requirements:
     *
     * - input must fit into 8 bits
     */
    function toInt8(int256 value) internal pure returns (int8 downcasted) {
        downcasted = int8(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(8, value);
        }
    }

    /**
     * @dev Converts an unsigned uint256 into a signed int256.
     *
     * Requirements:
     *
     * - input must be less than or equal to maxInt256.
     */
    function toInt256(uint256 value) internal pure returns (int256) {
        // Note: Unsafe cast below is okay because `type(int256).max` is guaranteed to be positive
        if (value > uint256(type(int256).max)) {
            revert SafeCastOverflowedUintToInt(value);
        }
        return int256(value);
    }

    /**
     * @dev Cast a boolean (false or true) to a uint256 (0 or 1) with no jump.
     */
    function toUint(bool b) internal pure returns (uint256 u) {
        assembly ("memory-safe") {
            u := iszero(iszero(b))
        }
    }
}

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

import {Id} from "../../interfaces/IMorpho.sol";

/// @title MorphoStorageLib
/// @author Morpho Labs
/// @custom:contact [email protected]
/// @notice Helper library exposing getters to access Morpho storage variables' slot.
/// @dev This library is not used in Morpho itself and is intended to be used by integrators.
library MorphoStorageLib {
    /* SLOTS */

    uint256 internal constant OWNER_SLOT = 0;
    uint256 internal constant FEE_RECIPIENT_SLOT = 1;
    uint256 internal constant POSITION_SLOT = 2;
    uint256 internal constant MARKET_SLOT = 3;
    uint256 internal constant IS_IRM_ENABLED_SLOT = 4;
    uint256 internal constant IS_LLTV_ENABLED_SLOT = 5;
    uint256 internal constant NONCE_SLOT = 6;
    uint256 internal constant ID_TO_MARKET_PARAMS_SLOT = 7;

    /* SLOT OFFSETS */

    uint256 internal constant LOAN_TOKEN_OFFSET = 0;
    uint256 internal constant COLLATERAL_TOKEN_OFFSET = 1;
    uint256 internal constant ORACLE_OFFSET = 2;
    uint256 internal constant IRM_OFFSET = 3;
    uint256 internal constant LLTV_OFFSET = 4;
    uint256 internal constant CREDIT_LINE_OFFSET = 5;

    uint256 internal constant SUPPLY_SHARES_OFFSET = 0;
    uint256 internal constant BORROW_SHARES_AND_COLLATERAL_OFFSET = 1;

    uint256 internal constant TOTAL_SUPPLY_ASSETS_AND_SHARES_OFFSET = 0;
    uint256 internal constant TOTAL_BORROW_ASSETS_AND_SHARES_OFFSET = 1;
    uint256 internal constant LAST_UPDATE_AND_FEE_OFFSET = 2;

    /* GETTERS */

    function ownerSlot() internal pure returns (bytes32) {
        return bytes32(OWNER_SLOT);
    }

    function feeRecipientSlot() internal pure returns (bytes32) {
        return bytes32(FEE_RECIPIENT_SLOT);
    }

    function positionSupplySharesSlot(Id id, address user) internal pure returns (bytes32) {
        return
            bytes32(
                uint256(keccak256(abi.encode(user, keccak256(abi.encode(id, POSITION_SLOT))))) + SUPPLY_SHARES_OFFSET
            );
    }

    function positionBorrowSharesAndCollateralSlot(Id id, address user) internal pure returns (bytes32) {
        return bytes32(
            uint256(keccak256(abi.encode(user, keccak256(abi.encode(id, POSITION_SLOT)))))
                + BORROW_SHARES_AND_COLLATERAL_OFFSET
        );
    }

    function marketTotalSupplyAssetsAndSharesSlot(Id id) internal pure returns (bytes32) {
        return bytes32(uint256(keccak256(abi.encode(id, MARKET_SLOT))) + TOTAL_SUPPLY_ASSETS_AND_SHARES_OFFSET);
    }

    function marketTotalBorrowAssetsAndSharesSlot(Id id) internal pure returns (bytes32) {
        return bytes32(uint256(keccak256(abi.encode(id, MARKET_SLOT))) + TOTAL_BORROW_ASSETS_AND_SHARES_OFFSET);
    }

    function marketLastUpdateAndFeeSlot(Id id) internal pure returns (bytes32) {
        return bytes32(uint256(keccak256(abi.encode(id, MARKET_SLOT))) + LAST_UPDATE_AND_FEE_OFFSET);
    }

    function isIrmEnabledSlot(address irm) internal pure returns (bytes32) {
        return keccak256(abi.encode(irm, IS_IRM_ENABLED_SLOT));
    }

    function isLltvEnabledSlot(uint256 lltv) internal pure returns (bytes32) {
        return keccak256(abi.encode(lltv, IS_LLTV_ENABLED_SLOT));
    }

    function nonceSlot(address authorizer) internal pure returns (bytes32) {
        return keccak256(abi.encode(authorizer, NONCE_SLOT));
    }

    function idToLoanTokenSlot(Id id) internal pure returns (bytes32) {
        return bytes32(uint256(keccak256(abi.encode(id, ID_TO_MARKET_PARAMS_SLOT))) + LOAN_TOKEN_OFFSET);
    }

    function idToCollateralTokenSlot(Id id) internal pure returns (bytes32) {
        return bytes32(uint256(keccak256(abi.encode(id, ID_TO_MARKET_PARAMS_SLOT))) + COLLATERAL_TOKEN_OFFSET);
    }

    function idToOracleSlot(Id id) internal pure returns (bytes32) {
        return bytes32(uint256(keccak256(abi.encode(id, ID_TO_MARKET_PARAMS_SLOT))) + ORACLE_OFFSET);
    }

    function idToIrmSlot(Id id) internal pure returns (bytes32) {
        return bytes32(uint256(keccak256(abi.encode(id, ID_TO_MARKET_PARAMS_SLOT))) + IRM_OFFSET);
    }

    function idToLltvSlot(Id id) internal pure returns (bytes32) {
        return bytes32(uint256(keccak256(abi.encode(id, ID_TO_MARKET_PARAMS_SLOT))) + LLTV_OFFSET);
    }

    function idToCreditLineSlot(Id id) internal pure returns (bytes32) {
        return bytes32(uint256(keccak256(abi.encode(id, ID_TO_MARKET_PARAMS_SLOT))) + CREDIT_LINE_OFFSET);
    }
}

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

import {MarketParams, Market} from "./IMorpho.sol";

/// @title IIrm
/// @author Morpho Labs
/// @custom:contact [email protected]
/// @notice Interface that Interest Rate Models (IRMs) used by Morpho must implement.
interface IIrm {
    /// @notice Returns the borrow rate per second (scaled by WAD) of the market `marketParams`.
    /// @dev Assumes that `market` corresponds to `marketParams`.
    function borrowRate(MarketParams memory marketParams, Market memory market) external returns (uint256);

    /// @notice Returns the borrow rate per second (scaled by WAD) of the market `marketParams` without modifying any
    /// storage.
    /// @dev Assumes that `market` corresponds to `marketParams`.
    function borrowRateView(MarketParams memory marketParams, Market memory market) external view returns (uint256);
}

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

uint256 constant WAD = 1e18;

/// @title MathLib
/// @author Morpho Labs
/// @custom:contact [email protected]
/// @notice Library to manage fixed-point arithmetic.
library MathLib {
    /// @dev Returns (`x` * `y`) / `WAD` rounded down.
    function wMulDown(uint256 x, uint256 y) internal pure returns (uint256) {
        return mulDivDown(x, y, WAD);
    }

    /// @dev Returns (`x` * `WAD`) / `y` rounded down.
    function wDivDown(uint256 x, uint256 y) internal pure returns (uint256) {
        return mulDivDown(x, WAD, y);
    }

    /// @dev Returns (`x` * `WAD`) / `y` rounded up.
    function wDivUp(uint256 x, uint256 y) internal pure returns (uint256) {
        return mulDivUp(x, WAD, y);
    }

    /// @dev Returns (`x` * `y`) / `d` rounded down.
    function mulDivDown(uint256 x, uint256 y, uint256 d) internal pure returns (uint256) {
        return (x * y) / d;
    }

    /// @dev Returns (`x` * `y`) / `d` rounded up.
    function mulDivUp(uint256 x, uint256 y, uint256 d) internal pure returns (uint256) {
        return (x * y + (d - 1)) / d;
    }

    /// @dev Returns the sum of the first three non-zero terms of a Taylor expansion of e^(nx) - 1, to approximate a
    /// continuous compound interest rate.
    function wTaylorCompounded(uint256 x, uint256 n) internal pure returns (uint256) {
        uint256 firstTerm = x * n;
        uint256 secondTerm = mulDivDown(firstTerm, firstTerm, 2 * WAD);
        uint256 thirdTerm = mulDivDown(secondTerm, firstTerm, 3 * WAD);

        return firstTerm + secondTerm + thirdTerm;
    }

    /// @dev Computes the inverse of wTaylorCompounded, finding the rate that produces the given growth factor.
    /// Uses a 3-term Taylor series approximation of ln(x) to solve for rate in the compound interest formula.
    /// Formula: rate = ln(x) / n ≈ [(x-1) - (x-1)²/2 + (x-1)³/3] / n
    ///
    /// Accuracy notes:
    /// - The Taylor approximation of ln(x) is most accurate for x close to 1
    /// - At growth factor x = 1.69*WAD (69% growth), approximation error < 2%
    /// - At growth factor x = 2*WAD (100% growth, where ln(2) ≈ 0.69), approximation error < 5%
    /// - Accuracy decreases for larger growth factors; not recommended for x > 2.5*WAD (150% growth)
    ///
    /// Example: If debt grew from 1000 to 1105 over 1 year (10.5% growth):
    /// - x = 1.105*WAD (growth factor)
    /// - n = 365 days (time period)
    /// - Returns ~10% APR as rate per second
    ///
    /// @param x The growth factor scaled by WAD (e.g., 1.1*WAD for 10% growth). Must be >= WAD.
    /// @param n The time period over which the growth occurred (in seconds)
    /// @return The continuously compounded rate per second that would produce this growth (scaled by WAD)
    function wInverseTaylorCompounded(uint256 x, uint256 n) internal pure returns (uint256) {
        require(x >= WAD, "ln undefined");

        uint256 firstTerm = x - WAD;
        uint256 secondTerm = wMulDown(firstTerm, firstTerm);
        uint256 thirdTerm = wMulDown(secondTerm, firstTerm);

        uint256 series = firstTerm - secondTerm / 2 + thirdTerm / 3;

        return series / n;
    }
}

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

import {ErrorsLib} from "../libraries/ErrorsLib.sol";

/// @title UtilsLib
/// @author Morpho Labs
/// @custom:contact [email protected]
/// @notice Library exposing helpers.
/// @dev Inspired by https://github.com/morpho-org/morpho-utils.
library UtilsLib {
    /// @dev Returns true if there is exactly one zero among `x` and `y`.
    function exactlyOneZero(uint256 x, uint256 y) internal pure returns (bool z) {
        assembly {
            z := xor(iszero(x), iszero(y))
        }
    }

    /// @dev Returns the min of `x` and `y`.
    function min(uint256 x, uint256 y) internal pure returns (uint256 z) {
        assembly {
            z := xor(x, mul(xor(x, y), lt(y, x)))
        }
    }

    /// @dev Returns `x` safely cast to uint128.
    function toUint128(uint256 x) internal pure returns (uint128) {
        if (x > type(uint128).max) revert ErrorsLib.MaxUint128Exceeded();
        return uint128(x);
    }

    /// @dev Returns max(0, x - y).
    function zeroFloorSub(uint256 x, uint256 y) internal pure returns (uint256 z) {
        assembly {
            z := mul(gt(x, y), sub(x, y))
        }
    }
}

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

import {Id, MarketParams} from "../interfaces/IMorpho.sol";

/// @title MarketParamsLib
/// @author Morpho Labs
/// @custom:contact [email protected]
/// @notice Library to convert a market to its id.
library MarketParamsLib {
    /// @notice The length of the data used to compute the id of a market.
    /// @dev The length is 6 * 32 because `MarketParams` has 6 variables of 32 bytes each.
    uint256 internal constant MARKET_PARAMS_BYTES_LENGTH = 6 * 32;

    /// @notice Returns the id of the market `marketParams`.
    function id(MarketParams memory marketParams) internal pure returns (Id marketParamsId) {
        assembly ("memory-safe") {
            marketParamsId := keccak256(marketParams, MARKET_PARAMS_BYTES_LENGTH)
        }
    }
}

// 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.0.1) (utils/Context.sol)

pragma solidity ^0.8.20;

/**
 * @dev Provides information about the current execution context, including the
 * sender of the transaction and its data. While these are generally available
 * via msg.sender and msg.data, they should not be accessed in such a direct
 * manner, since when dealing with meta-transactions the account sending and
 * paying for execution may not be the actual sender (as far as an application
 * is concerned).
 *
 * This contract is only required for intermediate, library-like contracts.
 */
abstract contract Context {
    function _msgSender() internal view virtual returns (address) {
        return msg.sender;
    }

    function _msgData() internal view virtual returns (bytes calldata) {
        return msg.data;
    }

    function _contextSuffixLength() internal view virtual returns (uint256) {
        return 0;
    }
}

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

pragma solidity ^0.8.0;

import "./IERC20.sol";
import "./extensions/IERC20Metadata.sol";
import "../../utils/Context.sol";

/**
 * @dev Implementation of the {IERC20} interface.
 *
 * This implementation is agnostic to the way tokens are created. This means
 * that a supply mechanism has to be added in a derived contract using {_mint}.
 * For a generic mechanism see {ERC20PresetMinterPauser}.
 *
 * TIP: For a detailed writeup see our guide
 * https://forum.openzeppelin.com/t/how-to-implement-erc20-supply-mechanisms/226[How
 * to implement supply mechanisms].
 *
 * The default value of {decimals} is 18. To change this, you should override
 * this function so it returns a different value.
 *
 * We have followed general OpenZeppelin Contracts guidelines: functions revert
 * instead returning `false` on failure. This behavior is nonetheless
 * conventional and does not conflict with the expectations of ERC20
 * applications.
 *
 * Additionally, an {Approval} event is emitted on calls to {transferFrom}.
 * This allows applications to reconstruct the allowance for all accounts just
 * by listening to said events. Other implementations of the EIP may not emit
 * these events, as it isn't required by the specification.
 *
 * Finally, the non-standard {decreaseAllowance} and {increaseAllowance}
 * functions have been added to mitigate the well-known issues around setting
 * allowances. See {IERC20-approve}.
 */
contract ERC20 is Context, IERC20, IERC20Metadata {
    mapping(address => uint256) private _balances;

    mapping(address => mapping(address => uint256)) private _allowances;

    uint256 private _totalSupply;

    string private _name;
    string private _symbol;

    /**
     * @dev Sets the values for {name} and {symbol}.
     *
     * All two of these values are immutable: they can only be set once during
     * construction.
     */
    constructor(string memory name_, string memory symbol_) {
        _name = name_;
        _symbol = symbol_;
    }

    /**
     * @dev Returns the name of the token.
     */
    function name() public view virtual override returns (string memory) {
        return _name;
    }

    /**
     * @dev Returns the symbol of the token, usually a shorter version of the
     * name.
     */
    function symbol() public view virtual override returns (string memory) {
        return _symbol;
    }

    /**
     * @dev Returns the number of decimals used to get its user representation.
     * For example, if `decimals` equals `2`, a balance of `505` tokens should
     * be displayed to a user as `5.05` (`505 / 10 ** 2`).
     *
     * Tokens usually opt for a value of 18, imitating the relationship between
     * Ether and Wei. This is the default value returned by this function, unless
     * it's overridden.
     *
     * NOTE: This information is only used for _display_ purposes: it in
     * no way affects any of the arithmetic of the contract, including
     * {IERC20-balanceOf} and {IERC20-transfer}.
     */
    function decimals() public view virtual override returns (uint8) {
        return 18;
    }

    /**
     * @dev See {IERC20-totalSupply}.
     */
    function totalSupply() public view virtual override returns (uint256) {
        return _totalSupply;
    }

    /**
     * @dev See {IERC20-balanceOf}.
     */
    function balanceOf(address account) public view virtual override returns (uint256) {
        return _balances[account];
    }

    /**
     * @dev See {IERC20-transfer}.
     *
     * Requirements:
     *
     * - `to` cannot be the zero address.
     * - the caller must have a balance of at least `amount`.
     */
    function transfer(address to, uint256 amount) public virtual override returns (bool) {
        address owner = _msgSender();
        _transfer(owner, to, amount);
        return true;
    }

    /**
     * @dev See {IERC20-allowance}.
     */
    function allowance(address owner, address spender) public view virtual override returns (uint256) {
        return _allowances[owner][spender];
    }

    /**
     * @dev See {IERC20-approve}.
     *
     * NOTE: If `amount` is the maximum `uint256`, the allowance is not updated on
     * `transferFrom`. This is semantically equivalent to an infinite approval.
     *
     * Requirements:
     *
     * - `spender` cannot be the zero address.
     */
    function approve(address spender, uint256 amount) public virtual override returns (bool) {
        address owner = _msgSender();
        _approve(owner, spender, amount);
        return true;
    }

    /**
     * @dev See {IERC20-transferFrom}.
     *
     * Emits an {Approval} event indicating the updated allowance. This is not
     * required by the EIP. See the note at the beginning of {ERC20}.
     *
     * NOTE: Does not update the allowance if the current allowance
     * is the maximum `uint256`.
     *
     * Requirements:
     *
     * - `from` and `to` cannot be the zero address.
     * - `from` must have a balance of at least `amount`.
     * - the caller must have allowance for ``from``'s tokens of at least
     * `amount`.
     */
    function transferFrom(address from, address to, uint256 amount) public virtual override returns (bool) {
        address spender = _msgSender();
        _spendAllowance(from, spender, amount);
        _transfer(from, to, amount);
        return true;
    }

    /**
     * @dev Atomically increases the allowance granted to `spender` by the caller.
     *
     * This is an alternative to {approve} that can be used as a mitigation for
     * problems described in {IERC20-approve}.
     *
     * Emits an {Approval} event indicating the updated allowance.
     *
     * Requirements:
     *
     * - `spender` cannot be the zero address.
     */
    function increaseAllowance(address spender, uint256 addedValue) public virtual returns (bool) {
        address owner = _msgSender();
        _approve(owner, spender, allowance(owner, spender) + addedValue);
        return true;
    }

    /**
     * @dev Atomically decreases the allowance granted to `spender` by the caller.
     *
     * This is an alternative to {approve} that can be used as a mitigation for
     * problems described in {IERC20-approve}.
     *
     * Emits an {Approval} event indicating the updated allowance.
     *
     * Requirements:
     *
     * - `spender` cannot be the zero address.
     * - `spender` must have allowance for the caller of at least
     * `subtractedValue`.
     */
    function decreaseAllowance(address spender, uint256 subtractedValue) public virtual returns (bool) {
        address owner = _msgSender();
        uint256 currentAllowance = allowance(owner, spender);
        require(currentAllowance >= subtractedValue, "ERC20: decreased allowance below zero");
        unchecked {
            _approve(owner, spender, currentAllowance - subtractedValue);
        }

        return true;
    }

    /**
     * @dev Moves `amount` of tokens from `from` to `to`.
     *
     * This internal function is equivalent to {transfer}, and can be used to
     * e.g. implement automatic token fees, slashing mechanisms, etc.
     *
     * Emits a {Transfer} event.
     *
     * Requirements:
     *
     * - `from` cannot be the zero address.
     * - `to` cannot be the zero address.
     * - `from` must have a balance of at least `amount`.
     */
    function _transfer(address from, address to, uint256 amount) internal virtual {
        require(from != address(0), "ERC20: transfer from the zero address");
        require(to != address(0), "ERC20: transfer to the zero address");

        _beforeTokenTransfer(from, to, amount);

        uint256 fromBalance = _balances[from];
        require(fromBalance >= amount, "ERC20: transfer amount exceeds balance");
        unchecked {
            _balances[from] = fromBalance - amount;
            // Overflow not possible: the sum of all balances is capped by totalSupply, and the sum is preserved by
            // decrementing then incrementing.
            _balances[to] += amount;
        }

        emit Transfer(from, to, amount);

        _afterTokenTransfer(from, to, amount);
    }

    /** @dev Creates `amount` tokens and assigns them to `account`, increasing
     * the total supply.
     *
     * Emits a {Transfer} event with `from` set to the zero address.
     *
     * Requirements:
     *
     * - `account` cannot be the zero address.
     */
    function _mint(address account, uint256 amount) internal virtual {
        require(account != address(0), "ERC20: mint to the zero address");

        _beforeTokenTransfer(address(0), account, amount);

        _totalSupply += amount;
        unchecked {
            // Overflow not possible: balance + amount is at most totalSupply + amount, which is checked above.
            _balances[account] += amount;
        }
        emit Transfer(address(0), account, amount);

        _afterTokenTransfer(address(0), account, amount);
    }

    /**
     * @dev Destroys `amount` tokens from `account`, reducing the
     * total supply.
     *
     * Emits a {Transfer} event with `to` set to the zero address.
     *
     * Requirements:
     *
     * - `account` cannot be the zero address.
     * - `account` must have at least `amount` tokens.
     */
    function _burn(address account, uint256 amount) internal virtual {
        require(account != address(0), "ERC20: burn from the zero address");

        _beforeTokenTransfer(account, address(0), amount);

        uint256 accountBalance = _balances[account];
        require(accountBalance >= amount, "ERC20: burn amount exceeds balance");
        unchecked {
            _balances[account] = accountBalance - amount;
            // Overflow not possible: amount <= accountBalance <= totalSupply.
            _totalSupply -= amount;
        }

        emit Transfer(account, address(0), amount);

        _afterTokenTransfer(account, address(0), amount);
    }

    /**
     * @dev Sets `amount` as the allowance of `spender` over the `owner` s tokens.
     *
     * This internal function is equivalent to `approve`, and can be used to
     * e.g. set automatic allowances for certain subsystems, etc.
     *
     * Emits an {Approval} event.
     *
     * Requirements:
     *
     * - `owner` cannot be the zero address.
     * - `spender` cannot be the zero address.
     */
    function _approve(address owner, address spender, uint256 amount) internal virtual {
        require(owner != address(0), "ERC20: approve from the zero address");
        require(spender != address(0), "ERC20: approve to the zero address");

        _allowances[owner][spender] = amount;
        emit Approval(owner, spender, amount);
    }

    /**
     * @dev Updates `owner` s allowance for `spender` based on spent `amount`.
     *
     * Does not update the allowance amount in case of infinite allowance.
     * Revert if not enough allowance is available.
     *
     * Might emit an {Approval} event.
     */
    function _spendAllowance(address owner, address spender, uint256 amount) internal virtual {
        uint256 currentAllowance = allowance(owner, spender);
        if (currentAllowance != type(uint256).max) {
            require(currentAllowance >= amount, "ERC20: insufficient allowance");
            unchecked {
                _approve(owner, spender, currentAllowance - amount);
            }
        }
    }

    /**
     * @dev Hook that is called before any transfer of tokens. This includes
     * minting and burning.
     *
     * Calling conditions:
     *
     * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens
     * will be transferred to `to`.
     * - when `from` is zero, `amount` tokens will be minted for `to`.
     * - when `to` is zero, `amount` of ``from``'s tokens will be burned.
     * - `from` and `to` are never both zero.
     *
     * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
     */
    function _beforeTokenTransfer(address from, address to, uint256 amount) internal virtual {}

    /**
     * @dev Hook that is called after any transfer of tokens. This includes
     * minting and burning.
     *
     * Calling conditions:
     *
     * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens
     * has been transferred to `to`.
     * - when `from` is zero, `amount` tokens have been minted for `to`.
     * - when `to` is zero, `amount` of ``from``'s tokens have been burned.
     * - `from` and `to` are never both zero.
     *
     * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
     */
    function _afterTokenTransfer(address from, address to, uint256 amount) internal virtual {}
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.3.0) (proxy/utils/Initializable.sol)

pragma solidity ^0.8.20;

/**
 * @dev This is a base contract to aid in writing upgradeable contracts, or any kind of contract that will be deployed
 * behind a proxy. Since proxied contracts do not make use of a constructor, it's common to move constructor logic to an
 * external initializer function, usually called `initialize`. It then becomes necessary to protect this initializer
 * function so it can only be called once. The {initializer} modifier provided by this contract will have this effect.
 *
 * The initialization functions use a version number. Once a version number is used, it is consumed and cannot be
 * reused. This mechanism prevents re-execution of each "step" but allows the creation of new initialization steps in
 * case an upgrade adds a module that needs to be initialized.
 *
 * For example:
 *
 * [.hljs-theme-light.nopadding]
 * ```solidity
 * contract MyToken is ERC20Upgradeable {
 *     function initialize() initializer public {
 *         __ERC20_init("MyToken", "MTK");
 *     }
 * }
 *
 * contract MyTokenV2 is MyToken, ERC20PermitUpgradeable {
 *     function initializeV2() reinitializer(2) public {
 *         __ERC20Permit_init("MyToken");
 *     }
 * }
 * ```
 *
 * TIP: To avoid leaving the proxy in an uninitialized state, the initializer function should be called as early as
 * possible by providing the encoded function call as the `_data` argument to {ERC1967Proxy-constructor}.
 *
 * CAUTION: When used with inheritance, manual care must be taken to not invoke a parent initializer twice, or to ensure
 * that all initializers are idempotent. This is not verified automatically as constructors are by Solidity.
 *
 * [CAUTION]
 * ====
 * Avoid leaving a contract uninitialized.
 *
 * An uninitialized contract can be taken over by an attacker. This applies to both a proxy and its implementation
 * contract, which may impact the proxy. To prevent the implementation contract from being used, you should invoke
 * the {_disableInitializers} function in the constructor to automatically lock it when it is deployed:
 *
 * [.hljs-theme-light.nopadding]
 * ```
 * /// @custom:oz-upgrades-unsafe-allow constructor
 * constructor() {
 *     _disableInitializers();
 * }
 * ```
 * ====
 */
abstract contract Initializable {
    /**
     * @dev Storage of the initializable contract.
     *
     * It's implemented on a custom ERC-7201 namespace to reduce the risk of storage collisions
     * when using with upgradeable contracts.
     *
     * @custom:storage-location erc7201:openzeppelin.storage.Initializable
     */
    struct InitializableStorage {
        /**
         * @dev Indicates that the contract has been initialized.
         */
        uint64 _initialized;
        /**
         * @dev Indicates that the contract is in the process of being initialized.
         */
        bool _initializing;
    }

    // keccak256(abi.encode(uint256(keccak256("openzeppelin.storage.Initializable")) - 1)) & ~bytes32(uint256(0xff))
    bytes32 private constant INITIALIZABLE_STORAGE = 0xf0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00;

    /**
     * @dev The contract is already initialized.
     */
    error InvalidInitialization();

    /**
     * @dev The contract is not initializing.
     */
    error NotInitializing();

    /**
     * @dev Triggered when the contract has been initialized or reinitialized.
     */
    event Initialized(uint64 version);

    /**
     * @dev A modifier that defines a protected initializer function that can be invoked at most once. In its scope,
     * `onlyInitializing` functions can be used to initialize parent contracts.
     *
     * Similar to `reinitializer(1)`, except that in the context of a constructor an `initializer` may be invoked any
     * number of times. This behavior in the constructor can be useful during testing and is not expected to be used in
     * production.
     *
     * Emits an {Initialized} event.
     */
    modifier initializer() {
        // solhint-disable-next-line var-name-mixedcase
        InitializableStorage storage $ = _getInitializableStorage();

        // Cache values to avoid duplicated sloads
        bool isTopLevelCall = !$._initializing;
        uint64 initialized = $._initialized;

        // Allowed calls:
        // - initialSetup: the contract is not in the initializing state and no previous version was
        //                 initialized
        // - construction: the contract is initialized at version 1 (no reinitialization) and the
        //                 current contract is just being deployed
        bool initialSetup = initialized == 0 && isTopLevelCall;
        bool construction = initialized == 1 && address(this).code.length == 0;

        if (!initialSetup && !construction) {
            revert InvalidInitialization();
        }
        $._initialized = 1;
        if (isTopLevelCall) {
            $._initializing = true;
        }
        _;
        if (isTopLevelCall) {
            $._initializing = false;
            emit Initialized(1);
        }
    }

    /**
     * @dev A modifier that defines a protected reinitializer function that can be invoked at most once, and only if the
     * contract hasn't been initialized to a greater version before. In its scope, `onlyInitializing` functions can be
     * used to initialize parent contracts.
     *
     * A reinitializer may be used after the original initialization step. This is essential to configure modules that
     * are added through upgrades and that require initialization.
     *
     * When `version` is 1, this modifier is similar to `initializer`, except that functions marked with `reinitializer`
     * cannot be nested. If one is invoked in the context of another, execution will revert.
     *
     * Note that versions can jump in increments greater than 1; this implies that if multiple reinitializers coexist in
     * a contract, executing them in the right order is up to the developer or operator.
     *
     * WARNING: Setting the version to 2**64 - 1 will prevent any future reinitialization.
     *
     * Emits an {Initialized} event.
     */
    modifier reinitializer(uint64 version) {
        // solhint-disable-next-line var-name-mixedcase
        InitializableStorage storage $ = _getInitializableStorage();

        if ($._initializing || $._initialized >= version) {
            revert InvalidInitialization();
        }
        $._initialized = version;
        $._initializing = true;
        _;
        $._initializing = false;
        emit Initialized(version);
    }

    /**
     * @dev Modifier to protect an initialization function so that it can only be invoked by functions with the
     * {initializer} and {reinitializer} modifiers, directly or indirectly.
     */
    modifier onlyInitializing() {
        _checkInitializing();
        _;
    }

    /**
     * @dev Reverts if the contract is not in an initializing state. See {onlyInitializing}.
     */
    function _checkInitializing() internal view virtual {
        if (!_isInitializing()) {
            revert NotInitializing();
        }
    }

    /**
     * @dev Locks the contract, preventing any future reinitialization. This cannot be part of an initializer call.
     * Calling this in the constructor of a contract will prevent that contract from being initialized or reinitialized
     * to any version. It is recommended to use this to lock implementation contracts that are designed to be called
     * through proxies.
     *
     * Emits an {Initialized} event the first time it is successfully executed.
     */
    function _disableInitializers() internal virtual {
        // solhint-disable-next-line var-name-mixedcase
        InitializableStorage storage $ = _getInitializableStorage();

        if ($._initializing) {
            revert InvalidInitialization();
        }
        if ($._initialized != type(uint64).max) {
            $._initialized = type(uint64).max;
            emit Initialized(type(uint64).max);
        }
    }

    /**
     * @dev Returns the highest version that has been initialized. See {reinitializer}.
     */
    function _getInitializedVersion() internal view returns (uint64) {
        return _getInitializableStorage()._initialized;
    }

    /**
     * @dev Returns `true` if the contract is currently initializing. See {onlyInitializing}.
     */
    function _isInitializing() internal view returns (bool) {
        return _getInitializableStorage()._initializing;
    }

    /**
     * @dev Pointer to storage slot. Allows integrators to override it with a custom storage location.
     *
     * NOTE: Consider following the ERC-7201 formula to derive storage locations.
     */
    function _initializableStorageSlot() internal pure virtual returns (bytes32) {
        return INITIALIZABLE_STORAGE;
    }

    /**
     * @dev Returns a pointer to the storage namespace.
     */
    // solhint-disable-next-line var-name-mixedcase
    function _getInitializableStorage() private pure returns (InitializableStorage storage $) {
        bytes32 slot = _initializableStorageSlot();
        assembly {
            $.slot := slot
        }
    }
}

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

pragma solidity ^0.8.0;

import "../token/ERC20/IERC20.sol";
import "../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].
 *
 * _Available since v4.7._
 */
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);
}

File 33 of 35 : ErrorsLib.sol
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.8.0;

/// @title ErrorsLib
/// @author Morpho Labs
/// @custom:contact [email protected]
/// @notice Library exposing custom errors.
library ErrorsLib {
    /// @notice Thrown when the caller is not the owner.
    error NotOwner();

    /// @notice Thrown when the caller is not the market's credit line.
    error NotCreditLine();

    /// @notice Thrown when the caller is not the market's helper.
    error NotHelper();

    /// @notice Thrown when the caller is not the market's usd3.
    error NotUsd3();

    /// @notice Thrown when the caller is not the owner or ozd.
    error NotOwnerOrOzd();

    /// @notice Thrown when the user is unverified.
    error Unverified();

    /// @notice Thrown when the LLTV to enable exceeds the maximum LLTV.
    error MaxLltvExceeded();

    /// @notice Thrown when the LTV to enable exceeds the maximum LTV.
    error MaxLtvExceeded();

    /// @notice Thrown when the VV to enable exceeds the maximum VV.
    error MaxVvExceeded();

    /// @notice Thrown when the credit to enable exceeds the maximum credit.
    error MaxCreditLineExceeded();

    /// @notice Thrown when the credit to enable is below the minimum credit.
    error MinCreditLineExceeded();

    /// @notice Thrown when the fee to set exceeds the maximum fee.
    error MaxFeeExceeded();

    /// @notice Thrown when the value is already set.
    error AlreadySet();

    /// @notice Thrown when the IRM is not enabled at market creation.
    error IrmNotEnabled();

    /// @notice Thrown when the LLTV is not enabled at market creation.
    error LltvNotEnabled();

    /// @notice Thrown when the market is already created.
    error MarketAlreadyCreated();

    /// @notice Thrown when a token to transfer doesn't have code.
    error NoCode();

    /// @notice Thrown when the market is not created.
    error MarketNotCreated();

    /// @notice Thrown when not exactly one of the input amount is zero.
    error InconsistentInput();

    /// @notice Thrown when zero assets is passed as input.
    error ZeroAssets();

    /// @notice Thrown when a zero address is passed as input.
    error ZeroAddress();

    /// @notice Thrown when an array has an invalid length.
    error InvalidArrayLength();

    /// @notice Thrown when the caller is not authorized to conduct an action.
    error Unauthorized();

    /// @notice Thrown when the collateral is insufficient to `borrow` or `withdrawCollateral`.
    error InsufficientCollateral();

    /// @notice Thrown when the liquidity is insufficient to `withdraw` or `borrow`.
    error InsufficientLiquidity();

    /// @notice Thrown when borrowing shares would result in borrowing zero assets.
    error InsufficientBorrowAmount();

    /// @notice Thrown when a token transfer reverted.
    error TransferReverted();

    /// @notice Thrown when a token transfer returned false.
    error TransferReturnedFalse();

    /// @notice Thrown when a token transferFrom reverted.
    error TransferFromReverted();

    /// @notice Thrown when a token transferFrom returned false
    error TransferFromReturnedFalse();

    /// @notice Thrown when the maximum uint128 is exceeded.
    error MaxUint128Exceeded();

    /// @notice Thrown when the premium rate exceeds the maximum allowed.
    error MaxDrpExceeded();

    /// @notice Thrown when the borrower has outstanding repayment obligations.
    error OutstandingRepayment();

    /// @notice Thrown when the protocol is paused.
    error Paused();

    /// @notice Thrown when trying to close a future cycle.
    error CannotCloseFutureCycle();

    /// @notice Thrown when cycle duration is invalid.
    error InvalidCycleDuration();

    /// @notice Thrown when no payment cycles exist.
    error NoCyclesExist();

    /// @notice Thrown when cycle ID is invalid.
    error InvalidCycleId();

    /// @notice Thrown when partial payment is attempted but full obligation payment is required.
    error MustPayFullObligation();

    /// @notice Thrown when repayment basis points exceed 100%.
    error RepaymentExceedsHundredPercent();

    /// @notice Thrown when an invalid markdown manager is set.
    error InvalidMarkdownManager();

    /// @notice Thrown when trying to settle non-existent debt.
    error NoAccountToSettle();

    /// @notice Thrown when the cover amount exceeds the assets amount.
    error InvalidCoverAmount();

    /// @notice Thrown when attempting operations on a frozen market.
    error MarketFrozen();

    /// @notice Thrown when a borrow would exceed the protocol debt cap.
    error DebtCapExceeded();

    /// @notice Thrown when borrow or repay would result in debt below minimum borrow amount.
    error BelowMinimumBorrow();
}

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

pragma solidity ^0.8.0;

import "../IERC20.sol";

/**
 * @dev Interface for the optional metadata functions from the ERC20 standard.
 *
 * _Available since v4.1._
 */
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 v4.9.4) (utils/Context.sol)

pragma solidity ^0.8.0;

/**
 * @dev Provides information about the current execution context, including the
 * sender of the transaction and its data. While these are generally available
 * via msg.sender and msg.data, they should not be accessed in such a direct
 * manner, since when dealing with meta-transactions the account sending and
 * paying for execution may not be the actual sender (as far as an application
 * is concerned).
 *
 * This contract is only required for intermediate, library-like contracts.
 */
abstract contract Context {
    function _msgSender() internal view virtual returns (address) {
        return msg.sender;
    }

    function _msgData() internal view virtual returns (bytes calldata) {
        return msg.data;
    }

    function _contextSuffixLength() internal view virtual returns (uint256) {
        return 0;
    }
}

Settings
{
  "remappings": [
    "forge-std/=lib/forge-std/src/",
    "halmos-cheatcodes/=lib/halmos-cheatcodes/src/",
    "@tokenized-strategy/=lib/tokenized-strategy/src/",
    "@periphery/=lib/tokenized-strategy-periphery/src/",
    "openzeppelin-foundry-upgrades/=lib/openzeppelin-foundry-upgrades/src/",
    "@openzeppelin/=lib/tokenized-strategy-periphery/lib/openzeppelin-contracts/",
    "@yearn-vaults/=lib/tokenized-strategy-periphery/lib/yearn-vaults-v3/contracts/",
    "ds-test/=lib/forge-std/lib/ds-test/src/",
    "erc4626-tests/=lib/tokenized-strategy/lib/erc4626-tests/",
    "openzeppelin-contracts/=lib/tokenized-strategy/lib/openzeppelin-contracts/",
    "openzeppelin/=lib/openzeppelin/",
    "tokenized-strategy-periphery/=lib/tokenized-strategy-periphery/",
    "tokenized-strategy/=lib/tokenized-strategy/",
    "yearn-vaults-v3/=lib/tokenized-strategy-periphery/lib/yearn-vaults-v3/"
  ],
  "optimizer": {
    "enabled": true,
    "runs": 999999
  },
  "metadata": {
    "useLiteralContent": false,
    "bytecodeHash": "none",
    "appendCBOR": false
  },
  "outputSelection": {
    "*": {
      "*": [
        "evm.bytecode",
        "evm.deployedBytecode",
        "devdoc",
        "userdoc",
        "metadata",
        "abi"
      ]
    }
  },
  "evmVersion": "shanghai",
  "viaIR": true
}

Contract Security Audit

Contract ABI

API
[{"inputs":[],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"InvalidInitialization","type":"error"},{"inputs":[],"name":"MaxUint128Exceeded","type":"error"},{"inputs":[],"name":"NotInitializing","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"depositor","type":"address"},{"indexed":false,"internalType":"bool","name":"allowed","type":"bool"}],"name":"DepositorWhitelistUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint64","name":"version","type":"uint64"}],"name":"Initialized","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"newMinDeposit","type":"uint256"}],"name":"MinDepositUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"oldStrategy","type":"address"},{"indexed":false,"internalType":"address","name":"newStrategy","type":"address"}],"name":"SUSD3StrategyUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"trancheShare","type":"uint256"}],"name":"TrancheShareSynced","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"Transfer","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":false,"internalType":"bool","name":"allowed","type":"bool"}],"name":"WhitelistUpdated","type":"event"},{"stateMutability":"nonpayable","type":"fallback"},{"inputs":[],"name":"WAUSDC","outputs":[{"internalType":"contract IERC4626","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_owner","type":"address"}],"name":"availableDepositLimit","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_owner","type":"address"}],"name":"availableWithdrawLimit","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"balanceOfWaUSDC","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"deployFunds","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"assets","type":"uint256"},{"internalType":"address","name":"receiver","type":"address"}],"name":"deposit","outputs":[{"internalType":"uint256","name":"shares","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"depositTimestamp","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"depositorWhitelist","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"freeFunds","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"getMarketLiquidity","outputs":[{"internalType":"uint256","name":"totalSupplyAssets","type":"uint256"},{"internalType":"uint256","name":"totalShares","type":"uint256"},{"internalType":"uint256","name":"totalBorrowAssets","type":"uint256"},{"internalType":"uint256","name":"waUSDCLiquidity","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"harvestAndReport","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_morphoCredit","type":"address"},{"internalType":"Id","name":"_marketId","type":"bytes32"},{"internalType":"address","name":"_management","type":"address"},{"internalType":"address","name":"_keeper","type":"address"}],"name":"initialize","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"marketId","outputs":[{"internalType":"Id","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"marketParams","outputs":[{"components":[{"internalType":"address","name":"loanToken","type":"address"},{"internalType":"address","name":"collateralToken","type":"address"},{"internalType":"address","name":"oracle","type":"address"},{"internalType":"address","name":"irm","type":"address"},{"internalType":"uint256","name":"lltv","type":"uint256"},{"internalType":"address","name":"creditLine","type":"address"}],"internalType":"struct MarketParams","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"maxOnCredit","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"minCommitmentTime","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"minDeposit","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"shares","type":"uint256"},{"internalType":"address","name":"receiver","type":"address"}],"name":"mint","outputs":[{"internalType":"uint256","name":"assets","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"morphoCredit","outputs":[{"internalType":"contract IMorpho","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"shares","type":"uint256"},{"internalType":"address","name":"receiver","type":"address"},{"internalType":"address","name":"owner","type":"address"},{"internalType":"uint256","name":"maxLoss","type":"uint256"}],"name":"redeem","outputs":[{"internalType":"uint256","name":"assets","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"shares","type":"uint256"},{"internalType":"address","name":"receiver","type":"address"},{"internalType":"address","name":"owner","type":"address"}],"name":"redeem","outputs":[{"internalType":"uint256","name":"assets","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"reinitialize","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"report","outputs":[{"internalType":"uint256","name":"profit","type":"uint256"},{"internalType":"uint256","name":"loss","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"sUSD3","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_depositor","type":"address"},{"internalType":"bool","name":"_allowed","type":"bool"}],"name":"setDepositorWhitelist","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_minDeposit","type":"uint256"}],"name":"setMinDeposit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_sUSD3","type":"address"}],"name":"setSUSD3","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_user","type":"address"},{"internalType":"bool","name":"_allowed","type":"bool"}],"name":"setWhitelist","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bool","name":"_enabled","type":"bool"}],"name":"setWhitelistEnabled","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"shutdownWithdraw","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"suppliedWaUSDC","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"supplyCap","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"syncTrancheShare","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_totalIdle","type":"uint256"}],"name":"tendThis","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"tendTrigger","outputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"bytes","name":"","type":"bytes"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"tokenizedStrategyAddress","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"transfer","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"transferFrom","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"whitelist","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"whitelistEnabled","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"assets","type":"uint256"},{"internalType":"address","name":"receiver","type":"address"},{"internalType":"address","name":"owner","type":"address"},{"internalType":"uint256","name":"maxLoss","type":"uint256"}],"name":"withdraw","outputs":[{"internalType":"uint256","name":"shares","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"assets","type":"uint256"},{"internalType":"address","name":"receiver","type":"address"},{"internalType":"address","name":"owner","type":"address"}],"name":"withdraw","outputs":[{"internalType":"uint256","name":"shares","type":"uint256"}],"stateMutability":"nonpayable","type":"function"}]

60808060405234620000bd577ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a009081549060ff8260401c16620000ae57506001600160401b036002600160401b03198282160162000068575b6040516158249081620000c28239f35b6001600160401b031990911681179091556040519081527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d290602090a15f808062000058565b63f92ee8a960e01b8152600490fd5b5f80fdfe60806040526004361015610019575b3415613c48575b5f80fd5b5f3560e01c806304bd4629146102c45780630517bbab146102bf578063052d9e7e146102ba57806323b872dd146102b55780632606a10b146102b05780632b832dbc146102ab5780632f6c1f53146102a65780633d6cb575146101f757806341b3d185146102a15780634251c3541461029c57806346aa2f121461029757806346b00d041461029257806349317f1d1461028d578063503160d91461028857806351fb012d1461028357806353d6fd591461027e57806359ddbab2146102795780635d265d3f146102745780636c2eb3501461026f5780636e553f651461026a5780636ed71ede146102655780637b9e68f2146102605780638f770ad01461025b5780638fcc9cfb1461025657806394bf804d1461025157806395d89b411461024c57806396c9f17e146102475780639b19251a146102425780639d7fb70c1461023d5780639f40a7b314610238578063a318c1a414610233578063a46537571461022e578063a9059cbb14610229578063a9b89c0714610224578063b460af941461021f578063ba0876521461021a578063cb6d71bc14610215578063d00330ab14610210578063d19a3bb81461020b578063e61582b514610206578063efb5f91114610201578063f7f6ba53146101fc5763fde813a80361000e575b610bbe565b6128be565b61286d565b612835565b6127e9565b61277d565b612731565b6126aa565b612625565b6125a6565b6124e2565b6122b8565b612230565b6120e3565b612053565b611fe7565b611ddf565b611d92565b611a2e565b611953565b61191b565b611857565b6117b8565b61140d565b611260565b6111df565b611133565b61101f565b610fdc565b610f9a565b610d21565b610cba565b610c75565b610c3d565b610c02565b610a9e565b61083d565b610567565b610480565b610380565b61033e565b6102e7565b73ffffffffffffffffffffffffffffffffffffffff81160361001557565b346100155760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261001557602061032c600435610327816102c9565b612a74565b604051908152f35b5f91031261001557565b34610015575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261001557602061032c612e60565b8015150361001557565b34610015575f60207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610015576004356103bc81610376565b73ffffffffffffffffffffffffffffffffffffffff60015416803b15610015575f602491604051928380927f48e4a6490000000000000000000000000000000000000000000000000000000082523360048301525afa801561047b57610468575b507fffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffff74ff0000000000000000000000000000000000000000603a5492151560a01b16911617603a5580f35b61047391925061293c565b5f905f61041d565b612a15565b346100155760607ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610015576105636105516105426004356104c4816102c9565b602435906104d1826102c9565b6104db8282613d8a565b604051917f23b872dd00000000000000000000000000000000000000000000000000000000602084015273ffffffffffffffffffffffffffffffffffffffff809216602484015216604482015260443560648201526064815261053d81612971565b613f45565b60208082518301019101612a20565b60405190151581529081906020820190565b0390f35b34610015575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610015576040516105e16105d36020927f2606a10b00000000000000000000000000000000000000000000000000000000848201526004815261053d8161298d565b828082518301019101612f55565b9182151580610800575b610602575b50604080519182526020820192909252f35b6106b09061064161062860015473ffffffffffffffffffffffffffffffffffffffff1690565b73ffffffffffffffffffffffffffffffffffffffff1690565b81610661603a5473ffffffffffffffffffffffffffffffffffffffff1690565b6040517f70a0823100000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff909116600482015293849081906024820190565b0381845afa92831561047b575f936107e1575b50826106d1575b50506105f0565b6040517f18160ddd0000000000000000000000000000000000000000000000000000000081528281600481855afa90811561047b5783905f926107c1575b5060049192604051928380927f01e1d1140000000000000000000000000000000000000000000000000000000082525afa92831561047b576107669361075f9288925f92610794575b5050612a62565b9085615653565b9080821161078c575b508061077d575b80806106ca565b61078690614ea2565b5f610776565b90505f61076f565b6107b39250803d106107ba575b6107ab81836129c5565b810190612a06565b5f80610758565b503d6107a1565b600492506107db90823d84116107ba576107ab81836129c5565b9161070f565b6107f9919350823d84116107ba576107ab81836129c5565b915f6106c3565b5073ffffffffffffffffffffffffffffffffffffffff610835603a5473ffffffffffffffffffffffffffffffffffffffff1690565b1615156105eb565b34610015575f60207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261001557600435610879816102c9565b73ffffffffffffffffffffffffffffffffffffffff8060015416803b15610015575f602491604051928380927f48e4a6490000000000000000000000000000000000000000000000000000000082523360048301525afa801561047b57610a45575b5080603a54166109e75781161561098957610983816109567fc3212a26ea3792ed00e630d63eb8ec2a9295974fe90e94ff25a8f70987fcdeeb9373ffffffffffffffffffffffffffffffffffffffff167fffffffffffffffffffffffff0000000000000000000000000000000000000000603a541617603a55565b604080515f815273ffffffffffffffffffffffffffffffffffffffff909216602083015290918291820190565b0390a180f35b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600f60248201527f496e76616c6964206164647265737300000000000000000000000000000000006044820152fd5b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601160248201527f735553443320616c7265616479207365740000000000000000000000000000006044820152fd5b610a5091935061293c565b5f915f6108db565b7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc604091011261001557600435610a8e816102c9565b90602435610a9b81610376565b90565b3461001557610aac36610a58565b6001545f9273ffffffffffffffffffffffffffffffffffffffff809216803b15610015575f602491604051928380927f48e4a6490000000000000000000000000000000000000000000000000000000082523360048301525afa801561047b57610b84575b507f8bb3c9e62c5e823d77565af4d29339378715313d3cb898327e576392ffa79b00916020911692835f52603c8252610b788160405f209060ff7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0083541691151516179055565b6040519015158152a280f35b602091945091610bb47f8bb3c9e62c5e823d77565af4d29339378715313d3cb898327e576392ffa79b009361293c565b5f94915091610b11565b346100155760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261001557610bf5613f81565b610c00600435613fe8565b005b34610015575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610015576020603d54604051908152f35b34610015575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261001557602061032c612f6b565b346100155760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261001557602061032c600435610cb5816102c9565b612feb565b346100155760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126100155773ffffffffffffffffffffffffffffffffffffffff600435610d0a816102c9565b165f52603e602052602060405f2054604051908152f35b34610015575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261001557610d57613f81565b610d5f613269565b610d8161062860325473ffffffffffffffffffffffffffffffffffffffff1690565b803b15610015576040517f1e4711cf000000000000000000000000000000000000000000000000000000008152905f908290818381610dc388600483016117f3565b03925af1801561047b57610f81575b50610df46106285f5473ffffffffffffffffffffffffffffffffffffffff1690565b6040517f70a082310000000000000000000000000000000000000000000000000000000080825230600483015291602091908290829060249082905afa801561047b57610e48915f91610f64575b50614d8b565b610e9d81610e65610e57613a6b565b610e5f612f6b565b90612a62565b604051809381927f07a2d13a000000000000000000000000000000000000000000000000000000008352600483019190602083019252565b038173d4fa2d31b7968e448877f69a96de69f5de8cd23e5afa90811561047b575f91610f47575b5081610ee76106285f5473ffffffffffffffffffffffffffffffffffffffff1690565b604051948552306004860152849060249082905afa90811561047b5761056393610f18935f93610f28575050612a62565b6040519081529081906020820190565b610f3f929350803d106107ba576107ab81836129c5565b908480610758565b610f5e9150823d84116107ba576107ab81836129c5565b83610ec4565b610f7b9150833d85116107ba576107ab81836129c5565b84610e42565b80610f8e610f949261293c565b80610334565b80610dd2565b346100155760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261001557610fd1613f81565b610c00600435614395565b34610015575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261001557602060ff603a5460a01c166040519015158152f35b346100155761102d36610a58565b6001545f9273ffffffffffffffffffffffffffffffffffffffff809216803b15610015575f602491604051928380927f48e4a6490000000000000000000000000000000000000000000000000000000082523360048301525afa801561047b576110f9575b507ff93f9a76c1bf3444d22400a00cb9fe990e6abe9dbb333fda48859cfee864543d916020911692835f52603b8252610b788160405f209060ff7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0083541691151516179055565b6020919450916111297ff93f9a76c1bf3444d22400a00cb9fe990e6abe9dbb333fda48859cfee864543d9361293c565b5f94915091611092565b34610015575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261001557608061116b6132c4565b91604051938452602084015260408301526060820152f35b91908251928382525f5b8481106111cb5750507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f845f6020809697860101520116010190565b60208183018101518483018201520161118d565b34610015575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610015576105636040517f440368a3000000000000000000000000000000000000000000000000000000006020820152600481526112468161298d565b6040519182915f8352604060208401526040830190611183565b34610015575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610015577ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00805460ff8160401c169081156113f7575b506113cd576113147ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a0060027fffffffffffffffffffffffffffffffffffffffffffffffff0000000000000000825416179055565b680100000000000000007fffffffffffffffffffffffffffffffffffffffffffffff00ffffffffffffffff82541617905561134d61330a565b6113997ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a007fffffffffffffffffffffffffffffffffffffffffffffff00ffffffffffffffff8154169055565b604051600281527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d29080602081015b0390a1005b60046040517ff92ee8a9000000000000000000000000000000000000000000000000000000008152fd5b6002915067ffffffffffffffff1610155f6112c0565b34610015576040807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610015576004359060243561144d816102c9565b82908315806117b1575b611719575b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff821461168a575b6114a661062860015473ffffffffffffffffffffffffffffffffffffffff1690565b83517f70a0823100000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8316600482015260209290918390839060249082905afa91821561047b57610563966115b5956115a89461053d935f9161166d575b5015611659575b50611525612e60565b6115c4575b86517f6e553f650000000000000000000000000000000000000000000000000000000086820152602481019190915273ffffffffffffffffffffffffffffffffffffffff909216604483015281606481015b037fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe081018352826129c5565b8051810182019101612a06565b90519081529081906020820190565b73ffffffffffffffffffffffffffffffffffffffff83163314801561161c575b6115ed90614bca565b426116168473ffffffffffffffffffffffffffffffffffffffff165f52603e60205260405f2090565b5561152a565b506115ed61165261164b3373ffffffffffffffffffffffffffffffffffffffff165f52603c60205260405f2090565b5460ff1690565b90506115e4565b61166790603d541115614b65565b5f61151c565b6116849150873d89116107ba576107ab81836129c5565b5f611515565b90506116ad6106285f5473ffffffffffffffffffffffffffffffffffffffff1690565b82517f70a0823100000000000000000000000000000000000000000000000000000000815233600482015290602090829060249082905afa90811561047b575f916116fa575b5090611484565b611713915060203d6020116107ba576107ab81836129c5565b5f6116f3565b905061173d61062860015473ffffffffffffffffffffffffffffffffffffffff1690565b6020835180927fb3d7f6b9000000000000000000000000000000000000000000000000000000008252818061177960048201905f602083019252565b03915afa90811561047b575f91611792575b509061145c565b6117ab915060203d6020116107ba576107ab81836129c5565b5f61178b565b505f611457565b34610015575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610015576020603354604051908152f35b6118559092919260c081019360a0908173ffffffffffffffffffffffffffffffffffffffff9182815116855282602082015116602086015282604082015116604086015282606082015116606086015260808101516080860152015116910152565b565b34610015575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610015575f60a060405161189481612955565b828152826020820152826040820152826060820152826080820152015260c06118bb613269565b611919604051809260a0908173ffffffffffffffffffffffffffffffffffffffff9182815116855282602082015116602086015282604082015116604086015282606082015116606086015260808101516080860152015116910152565bf35b34610015575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261001557602061032c6134d3565b34610015575f60207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126100155760043573ffffffffffffffffffffffffffffffffffffffff60015416803b15610015575f602491604051928380927f48e4a6490000000000000000000000000000000000000000000000000000000082523360048301525afa801561047b57611a1a575b506020817f96a008f96f1c0ab9fa3d9ddd43cdfc614848c4d054d51f43662ed900e9d094c892603d55604051908152a180f35b611a2591925061293c565b5f9060206119e7565b34610015576040807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126100155760043590602435611a6e816102c9565b5f9083611ce8575b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8214611c59575b611ac061062860015473ffffffffffffffffffffffffffffffffffffffff1690565b83517f70a0823100000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8316600482015260209290918390839060249082905afa91821561047b57610563966115b5956115a89461053d935f91611c3c575b5015611c28575b50611b3f612e60565b611b9a575b86517f94bf804d0000000000000000000000000000000000000000000000000000000086820152602481019190915273ffffffffffffffffffffffffffffffffffffffff9092166044830152816064810161157c565b73ffffffffffffffffffffffffffffffffffffffff831633148015611bf2575b611bc390614bca565b42611bec8473ffffffffffffffffffffffffffffffffffffffff165f52603e60205260405f2090565b55611b44565b50611bc3611c2161164b3373ffffffffffffffffffffffffffffffffffffffff165f52603c60205260405f2090565b9050611bba565b611c3690603d541115614b65565b5f611b36565b611c539150873d89116107ba576107ab81836129c5565b5f611b2f565b9050611c7c6106285f5473ffffffffffffffffffffffffffffffffffffffff1690565b82517f70a0823100000000000000000000000000000000000000000000000000000000815233600482015290602090829060249082905afa90811561047b575f91611cc9575b5090611a9e565b611ce2915060203d6020116107ba576107ab81836129c5565b5f611cc2565b9050611d0c61062860015473ffffffffffffffffffffffffffffffffffffffff1690565b6020835180927fb3d7f6b90000000000000000000000000000000000000000000000000000000082528180611d4989600483019190602083019252565b03915afa90811561047b575f91611d62575b5090611a76565b611d7b915060203d6020116107ba576107ab81836129c5565b5f611d5b565b906020610a9b928181520190611183565b34610015575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261001557610563611dcb6135e1565b604051918291602083526020830190611183565b346100155760807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261001557600435611e1a816102c9565b60443590611e27826102c9565b606435611e33816102c9565b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00549267ffffffffffffffff60ff8560401c1615941680159081611fdf575b6001149081611fd5575b159081611fcc575b506113cd57611eeb9284611edd7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a0060017fffffffffffffffffffffffffffffffffffffffffffffffff0000000000000000825416179055565b611f70575b60243590613842565b611ef157005b611f3d7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a007fffffffffffffffffffffffffffffffffffffffffffffff00ffffffffffffffff8154169055565b604051600181527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d29080602081016113c8565b611fc77ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00680100000000000000007fffffffffffffffffffffffffffffffffffffffffffffff00ffffffffffffffff825416179055565b611ee2565b9050155f611e84565b303b159150611e7c565b859150611e72565b346100155760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126100155773ffffffffffffffffffffffffffffffffffffffff600435612037816102c9565b165f52603b602052602060ff60405f2054166040519015158152f35b346100155760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126100155761208a613f81565b610c00600435614d8b565b7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc608091011261001557600435906024356120cf816102c9565b906044356120dc816102c9565b9060643590565b346100155761157c61053d61217961216b6120fd36612095565b6040517f9f40a7b300000000000000000000000000000000000000000000000000000000602080830191909152602482019590955273ffffffffffffffffffffffffffffffffffffffff938416604482015292821660648401526084830152919591949091829060a4820190565b838082518301019101612a06565b9073ffffffffffffffffffffffffffffffffffffffff838160015416916024604051809481937f70a08231000000000000000000000000000000000000000000000000000000008352871660048301525afa90811561047b575f91612213575b50156121e9575b50604051908152f35b73ffffffffffffffffffffffffffffffffffffffff165f908152603e60205260408120555f6121e0565b61222a9150843d86116107ba576107ab81836129c5565b5f6121d9565b346100155761157c61053d61217961216b61224a36612095565b6040517fa318c1a400000000000000000000000000000000000000000000000000000000602080830191909152602482019590955273ffffffffffffffffffffffffffffffffffffffff938416604482015292821660648401526084830152919591949091829060a4820190565b34610015575f807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126100155773ffffffffffffffffffffffffffffffffffffffff8060015416803b15610015575f602491604051928380927fd43fdcf70000000000000000000000000000000000000000000000000000000082523360048301525afa801561047b576124cf575b5061237261062861062861062860325473ffffffffffffffffffffffffffffffffffffffff1690565b9060405180927ff5efbb4f00000000000000000000000000000000000000000000000000000000825281600460209586935afa801561047b5783915f916124a2575b506004604051809481937f76e66a1c000000000000000000000000000000000000000000000000000000008352165afa801561047b577f617d69aa6f8f8f7158e168208ffd102c878214b34e8510746322af83f775aab792610983925f92612483575b50612426612710831115613a06565b7fd2841a5d2692465040bd5e06a6f3b37483952c866e0f304dc0e03f76a1f8a0b990827fffffffffffffffffffffffffffffffffffffffffffffffffffff0000ffffffff835416911b179055604051918291829190602083019252565b8161249b9293503d84116107ba576107ab81836129c5565b905f612417565b6124c29150823d84116124c8575b6124ba81836129c5565b810190612e4b565b5f6123b4565b503d6124b0565b6124da91925061293c565b5f905f612349565b346100155760407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261001557602061259c61258e61157c61053d60043561252b816102c9565b6125358133613d8a565b6040517fa9059cbb000000000000000000000000000000000000000000000000000000008782015273ffffffffffffffffffffffffffffffffffffffff9091166024808301919091523560448201529182906064820190565b828082518301019101612a20565b6040519015158152f35b34610015575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261001557602061032c613a6b565b7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc60609101126100155760043590602435612618816102c9565b90604435610a9b816102c9565b346100155761216b61053d61217961263c366125de565b6040517fa318c1a400000000000000000000000000000000000000000000000000000000602080830191909152602482019490945273ffffffffffffffffffffffffffffffffffffffff928316604482015291811660648301525f6084830152919491938160a4810161157c565b346100155761216b61053d6121796126c1366125de565b6040517f9f40a7b300000000000000000000000000000000000000000000000000000000602080830191909152602482019490945273ffffffffffffffffffffffffffffffffffffffff928316604482015291811660648301526127106084830152919491938160a4810161157c565b34610015575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261001557602060405173d4fa2d31b7968e448877f69a96de69f5de8cd23e8152f35b346100155760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126100155773ffffffffffffffffffffffffffffffffffffffff6004356127cd816102c9565b165f52603c602052602060ff60405f2054166040519015158152f35b34610015575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261001557602060405173d377919fa87120584b21279a491f82d5265a139c8152f35b34610015575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261001557602061032c613b69565b34610015575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261001557602073ffffffffffffffffffffffffffffffffffffffff60325416604051908152f35b34610015575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261001557602073ffffffffffffffffffffffffffffffffffffffff603a5416604051908152f35b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b67ffffffffffffffff811161295057604052565b61290f565b60c0810190811067ffffffffffffffff82111761295057604052565b60a0810190811067ffffffffffffffff82111761295057604052565b6040810190811067ffffffffffffffff82111761295057604052565b6060810190811067ffffffffffffffff82111761295057604052565b90601f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0910116810190811067ffffffffffffffff82111761295057604052565b90816020910312610015575190565b6040513d5f823e3d90fd5b908160209103126100155751610a9b81610376565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b91908201809211612a6f57565b612a35565b612a956106285f5473ffffffffffffffffffffffffffffffffffffffff1690565b604080517f70a082310000000000000000000000000000000000000000000000000000000081523060048083019190915291939260209291908390829060249082905afa90811561047b575f91612e2e575b50612af0613c95565b91508651907f5c975abb00000000000000000000000000000000000000000000000000000000825273d4fa2d31b7968e448877f69a96de69f5de8cd23e9086838781855afa801561047b5787935f91612e11575b5015612cc15750612b8a92505f905b885180809581947f07a2d13a0000000000000000000000000000000000000000000000000000000083528983019190602083019252565b03915afa801561047b578492612ba7925f92612ca2575b50612a62565b94612bca61062860015473ffffffffffffffffffffffffffffffffffffffff1690565b9051928380927fbf86d6900000000000000000000000000000000000000000000000000000000082525afa91821561047b575f92612c75575b5050612c7157612c11612e60565b9081612c1c57505090565b612c449073ffffffffffffffffffffffffffffffffffffffff165f52603e60205260405f2090565b54908115159182612c5e575b5050612c595790565b505f90565b612c689250612a62565b42105f80612c50565b5090565b612c949250803d10612c9b575b612c8c81836129c5565b810190612a20565b5f80612c03565b503d612c82565b612cba919250843d86116107ba576107ab81836129c5565b905f612ba1565b9150612ccb612f6b565b8851937fd905777e0000000000000000000000000000000000000000000000000000000091828652888680612d1f308c830191909173ffffffffffffffffffffffffffffffffffffffff6020820193169052565b0381875afa91821561047b57612d4b8a93612d5793612da9995f92612df9575b50808218908211021890565b95818118908211021890565b91612d7a61062860325473ffffffffffffffffffffffffffffffffffffffff1690565b908b51968792839283528a830191909173ffffffffffffffffffffffffffffffffffffffff6020820193169052565b0381855afa92831561047b57610e5f8894612dd493612b8a975f92612dda5750808218908211021890565b90612b53565b612df2919250873d89116107ba576107ab81836129c5565b905f612d3f565b612df2919250863d88116107ba576107ab81836129c5565b612e289150843d8611612c9b57612c8c81836129c5565b5f612b44565b612e459150833d85116107ba576107ab81836129c5565b5f612ae7565b908160209103126100155751610a9b816102c9565b612e8861062861062861062860325473ffffffffffffffffffffffffffffffffffffffff1690565b60405180917ff5efbb4f00000000000000000000000000000000000000000000000000000000825281600460209485935afa90811561047b5773ffffffffffffffffffffffffffffffffffffffff9183915f91612f38575b506004604051809481937fe4c3b0da000000000000000000000000000000000000000000000000000000008352165afa91821561047b575f92612f2257505090565b610a9b9250803d106107ba576107ab81836129c5565b612f4f9150823d84116124c8576124ba81836129c5565b5f612ee0565b9190826040910312610015576020825192015190565b6040517f70a0823100000000000000000000000000000000000000000000000000000000815230600482015260208160248173d4fa2d31b7968e448877f69a96de69f5de8cd23e5afa90811561047b575f91612fc5575090565b610a9b915060203d6020116107ba576107ab81836129c5565b91908203918211612a6f57565b603a5460a01c60ff1680613231575b612c59576040517f402d267d00000000000000000000000000000000000000000000000000000000815230600482015260209173d4fa2d31b7968e448877f69a96de69f5de8cd23e918381602481865afa92831561047b5784915f9461320d575b5090600491604051928380927f5c975abb0000000000000000000000000000000000000000000000000000000082525afa90811561047b575f916131f0575b5080156131e8575b61316c576130d2906130c960325473ffffffffffffffffffffffffffffffffffffffff1690565b60335490614234565b6131e2576130de6134d3565b801580156131b9575b6131925760048361311061062860015473ffffffffffffffffffffffffffffffffffffffff1690565b604051928380927f01e1d1140000000000000000000000000000000000000000000000000000000082525afa93841561047b575f94613173575b50508281111561316c57610a9b9261316191612fde565b818082109118021890565b5050505f90565b61318a929450803d106107ba576107ab81836129c5565b915f8061314a565b5050507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff90565b507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff81146130e7565b50505f90565b5081156130a2565b6132079150843d8611612c9b57612c8c81836129c5565b5f61309a565b82919450613229906004933d84116107ba576107ab81836129c5565b93909161305b565b5061326461326061164b8373ffffffffffffffffffffffffffffffffffffffff165f52603b60205260405f2090565b1590565b612ffa565b6040519061327682612955565b8160a073ffffffffffffffffffffffffffffffffffffffff80603454168352806035541660208401528060365416604084015280603754166060840152603854608084015260395416910152565b6132ee73ffffffffffffffffffffffffffffffffffffffff603254166132e8613269565b90614622565b509283919280948082115f146131e2578103908111612a6f5790565b73a0b86991c6218b36c1d19d4a2e9eb0ce3606eb487fffffffffffffffffffffffff000000000000000000000000000000000000000081815f5416175f55817fd2841a5d2692465040bd5e06a6f3b37483952c866e0f304dc0e03f76a1f8a0b091825416179055604051602081017f095ea7b300000000000000000000000000000000000000000000000000000000928382525f80846133eb60248201907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff6020604084019373d4fa2d31b7968e448877f69a96de69f5de8cd23e81520152565b039361341d7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0958681018852876129c5565b85519082855af19061342d613ee8565b826134a1575b5081613496575b501561344557505050565b61185592613491613491926040519260208401528261348560248201905f6020604084019373d4fa2d31b7968e448877f69a96de69f5de8cd23e81520152565b039081018352826129c5565b6154c3565b90503b15155f61343a565b805191925081159182156134b9575b5050905f613433565b6134cc9250602080918301019101612a20565b5f806134b0565b6134fb61062861062861062860325473ffffffffffffffffffffffffffffffffffffffff1690565b60405180917ff5efbb4f00000000000000000000000000000000000000000000000000000000825281600460209485935afa90811561047b575f916135c4575b508160405180927fcc718f760000000000000000000000000000000000000000000000000000000082528173ffffffffffffffffffffffffffffffffffffffff816135ad60048201907f4bba860c0c28b1a4ae0214c01f08e53b00bfe2e087690d7a04d73a15360ec6a7602083019252565b0392165afa91821561047b575f92612f2257505090565b6135db9150823d84116124c8576124ba81836129c5565b5f61353b565b604051906135ee8261298d565b600482527f55534433000000000000000000000000000000000000000000000000000000006020830152565b1561362157565b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600760248201527f216d6f7270686f000000000000000000000000000000000000000000000000006044820152fd5b908160c09103126100155760a06040519161369983612955565b80516136a4816102c9565b835260208101516136b4816102c9565b602084015260408101516136c7816102c9565b604084015260608101516136da816102c9565b60608401526080810151608084015201516136f4816102c9565b60a082015290565b1561370357565b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600e60248201527f496e76616c6964206d61726b65740000000000000000000000000000000000006044820152fd5b61380160a06118559273ffffffffffffffffffffffffffffffffffffffff80825116907fffffffffffffffffffffffff00000000000000000000000000000000000000009182603454161760345580602084015116826035541617603555806040840151168260365416176036556060830151169060375416176037556080810151603855015173ffffffffffffffffffffffffffffffffffffffff1690565b73ffffffffffffffffffffffffffffffffffffffff167fffffffffffffffffffffffff00000000000000000000000000000000000000006039541617603955565b6139149392916138b073ffffffffffffffffffffffffffffffffffffffff60c0931661386f81151561361a565b73ffffffffffffffffffffffffffffffffffffffff167fffffffffffffffffffffffff00000000000000000000000000000000000000006032541617603255565b6138b981603355565b6138db61062860325473ffffffffffffffffffffffffffffffffffffffff1690565b60405180809781947f2c3c9157000000000000000000000000000000000000000000000000000000008352600483019190602083019252565b03915afa801561047b5761397d829161398b955f916139d7575b5061395a613953610628835173ffffffffffffffffffffffffffffffffffffffff1690565b15156136fc565b61396381613761565b5173ffffffffffffffffffffffffffffffffffffffff1690565b6139856135e1565b90614c55565b6118556139af6106285f5473ffffffffffffffffffffffffffffffffffffffff1690565b6139d161062860325473ffffffffffffffffffffffffffffffffffffffff1690565b906149e7565b6139f9915060c03d60c0116139ff575b6139f181836129c5565b81019061367f565b5f61392e565b503d6139e7565b15613a0d57565b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601560248201527f496e76616c6964207472616e63686520736861726500000000000000000000006044820152fd5b613b1473ffffffffffffffffffffffffffffffffffffffff60325416613a8f613269565b5f613ae160c08320604051602081019182526002604082015260408152613ab5816129a9565b5190206040805130602082019081529181019290925290613ad9816060810161157c565b5190206152e4565b604051809581927f7784c685000000000000000000000000000000000000000000000000000000008352600483016141bf565b0381855afa90811561047b57613b38613b3f92610a9b955f91613b47575b506141fa565b5192614622565b505091614e7f565b613b6391503d805f833e613b5b81836129c5565b810190614145565b5f613b32565b613b9161062861062861062860325473ffffffffffffffffffffffffffffffffffffffff1690565b60405180917ff5efbb4f00000000000000000000000000000000000000000000000000000000825281600460209485935afa90811561047b5773ffffffffffffffffffffffffffffffffffffffff9183915f91613c2b575b506004604051809481937f111e3c08000000000000000000000000000000000000000000000000000000008352165afa91821561047b575f92612f2257505090565b613c429150823d84116124c8576124ba81836129c5565b5f613be9565b365f80375f80368173d377919fa87120584b21279a491f82d5265a139c5af43d5f803e15613c74573d5ff35b3d5ffd5b51906fffffffffffffffffffffffffffffffff8216820361001557565b73ffffffffffffffffffffffffffffffffffffffff6032541660606033546044604051809481937f93c5206200000000000000000000000000000000000000000000000000000000835260048301523060248301525afa90811561047b575f91613d15575b505190613d12613d086132c4565b9050929185614e7f565b91565b90506060813d606011613d82575b81613d30606093836129c5565b8101031261001557604051906060820182811067ffffffffffffffff82111761295057613d7791604091825280518452613d6c60208201613c78565b602085015201613c78565b60408201525f613cfa565b3d9150613d23565b9073ffffffffffffffffffffffffffffffffffffffff8083169182158015613ede575b613ed85781603a54169182911614918215613ece575b5050613ecb57613df18173ffffffffffffffffffffffffffffffffffffffff165f52603e60205260405f2090565b54613dfa612e60565b8101809111612a6f57421090811591613e9a575b5015613e1657565b60846040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602e60248201527f555344333a2043616e6e6f74207472616e7366657220647572696e6720636f6d60448201527f6d69746d656e7420706572696f640000000000000000000000000000000000006064820152fd5b613ec3915073ffffffffffffffffffffffffffffffffffffffff165f52603e60205260405f2090565b54155f613e0e565b50565b1490505f80613dc3565b50505050565b5081811615613dad565b3d15613f40573d9067ffffffffffffffff82116129505760405191613f3560207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f84011601846129c5565b82523d5f602084013e565b606090565b5f809160208151910173d377919fa87120584b21279a491f82d5265a139c5af4613f6d613ee8565b9015613f765790565b6040513d90815f823efd5b303303613f8a57565b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600560248201527f2173656c660000000000000000000000000000000000000000000000000000006044820152fd5b8015613ecb576040517f0a28a47700000000000000000000000000000000000000000000000000000000815280614029602093600483019190602083019252565b0390828173d4fa2d31b7968e448877f69a96de69f5de8cd23e9381855afa801561047b57614074915f91614110575b50614061612f6b565b8181106140eb575b808218908211021890565b908161407f57505050565b6040517fba0876520000000000000000000000000000000000000000000000000000000081526004810192909252306024830181905260448301528290829060649082905f905af1801561047b576140d5575050565b81613ecb92903d106107ba576107ab81836129c5565b6140fd6140f88284612fde565b61508c565b15614069575061410b612f6b565b614069565b6141279150843d86116107ba576107ab81836129c5565b5f614058565b67ffffffffffffffff81116129505760051b60200190565b60209081818403126100155780519067ffffffffffffffff821161001557019180601f8401121561001557825161417b8161412d565b9361418960405195866129c5565b818552838086019260051b820101928311610015578301905b8282106141b0575050505090565b815181529083019083016141a2565b60209060206040818301928281528551809452019301915f5b8281106141e6575050505090565b8351855293810193928101926001016141d8565b8051156142075760200190565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52603260045260245ffd5b919061429261157c9160405160208101918252600260408201526040815261425b816129a9565b5190206040519283916020830195866020909392919373ffffffffffffffffffffffffffffffffffffffff60408201951681520152565b5190209060018201809211612a6f576142f95f916142c473ffffffffffffffffffffffffffffffffffffffff946152e4565b6040519485809481937f7784c685000000000000000000000000000000000000000000000000000000008352600483016141bf565b0392165afa801561047b5761431b61433191610a9b935f91613b4757506141fa565b516fffffffffffffffffffffffffffffffff1690565b6fffffffffffffffffffffffffffffffff1690565b81810292918115918404141715612a6f57565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601260045260245ffd5b8115614390570490565b614359565b8015613ecb576040517f6e553f6500000000000000000000000000000000000000000000000000000000815260048101919091523060248201526020816044815f73d4fa2d31b7968e448877f69a96de69f5de8cd23e5af1801561047b57614464575b50614401613b69565b8015613ecb5761440f613a6b565b61443561442d61441d612f6b565b936144288585612a62565b614346565b612710900490565b918183111561445f5761444e61445a92613ecb94612fde565b90818082109118021890565b615309565b505050565b61447c9060203d6020116107ba576107ab81836129c5565b505f6143f8565b908160e0910312610015576040519060e0820182811067ffffffffffffffff821117612950576145199160c0916040526144bc81613c78565b84526144ca60208201613c78565b60208501526144db60408201613c78565b60408501526144ec60608201613c78565b60608501526144fd60808201613c78565b608085015261450e60a08201613c78565b60a085015201613c78565b60c082015290565b61018060c061185593959461458c846101a081019860a0908173ffffffffffffffffffffffffffffffffffffffff9182815116855282602082015116602086015282604082015116604086015282606082015116606086015260808101516080860152015116910152565b80516fffffffffffffffffffffffffffffffff908116858401526020820151811660e086015260408201518116610100860152606082015181166101208601526080820151811661014086015260a08201511661016085015201516fffffffffffffffffffffffffffffffff16910152565b9190916fffffffffffffffffffffffffffffffff80809416911601918211612a6f57565b60c082206040517f5c60e39a00000000000000000000000000000000000000000000000000000000815260048101919091529173ffffffffffffffffffffffffffffffffffffffff9160e0908490602490829086165afa92831561047b575f936149b6575b5082906146b36146ad61433160808501516fffffffffffffffffffffffffffffffff1690565b42612fde565b92831515908161497d575b81614951575b50614751575b50516fffffffffffffffffffffffffffffffff16905060208201516fffffffffffffffffffffffffffffffff1692614732606061471a60408601516fffffffffffffffffffffffffffffffff1690565b9401516fffffffffffffffffffffffffffffffff1690565b6fffffffffffffffffffffffffffffffff928316948316938316921690565b6147b5918161477f6106286106286060602096015173ffffffffffffffffffffffffffffffffffffffff1690565b906040518095819482937f2a944b5500000000000000000000000000000000000000000000000000000000845260048401614521565b03915afa90811561047b5761483d91614801915f91614932575b506147fb60408601946147f561433187516fffffffffffffffffffffffffffffffff1690565b926153fb565b90615443565b9161482761480e8461545a565b82516fffffffffffffffffffffffffffffffff166145fe565b6fffffffffffffffffffffffffffffffff169052565b61487b61486561484c8361545a565b84516fffffffffffffffffffffffffffffffff166145fe565b6fffffffffffffffffffffffffffffffff168352565b6fffffffffffffffffffffffffffffffff6148a960a08401516fffffffffffffffffffffffffffffffff1690565b16806148b7575b82906146ca565b6148cd670de0b6b3a76400009161492b93614346565b0461482761480e6149266148fd846148f861433189516fffffffffffffffffffffffffffffffff1690565b612fde565b93602087019461492061433187516fffffffffffffffffffffffffffffffff1690565b916154a1565b61545a565b5f806148b0565b61494b915060203d6020116107ba576107ab81836129c5565b5f6147cf565b9050614974606083015173ffffffffffffffffffffffffffffffffffffffff1690565b1615155f6146c4565b90506fffffffffffffffffffffffffffffffff6149ad60408501516fffffffffffffffffffffffffffffffff1690565b161515906146be565b6149d991935060e03d60e0116149e0575b6149d181836129c5565b810190614483565b915f614687565b503d6149c7565b6040517f095ea7b3000000000000000000000000000000000000000000000000000000006020820181815273ffffffffffffffffffffffffffffffffffffffff851660248401527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff604480850191909152835290939192917fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe091614a8c6064866129c5565b5f8073ffffffffffffffffffffffffffffffffffffffff86169287519082855af190614ab6613ee8565b82614b33575b5081614b28575b5015614ad1575b5050505050565b604051602081019590955273ffffffffffffffffffffffffffffffffffffffff1660248501525f6044850152614b1e93614b1991614b13908260648101613485565b826155df565b6155df565b5f80808080614aca565b90503b15155f614ac3565b80519192508115918215614b4b575b5050905f614abc565b614b5e9250602080918301019101612a20565b5f80614b42565b15614b6c57565b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601560248201527f42656c6f77206d696e696d756d206465706f73697400000000000000000000006044820152fd5b15614bd157565b60846040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602f60248201527f555344333a204f6e6c792073656c66206f722077686974656c6973746564206460448201527f65706f7369747320616c6c6f77656400000000000000000000000000000000006064820152fd5b939192909260ff7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a005460401c1615614d6157613ecb9461053d9373ffffffffffffffffffffffffffffffffffffffff80809316947fffffffffffffffffffffffff000000000000000000000000000000000000000086815f5416175f553090600154161760015581614d22604051998a987f97073ae60000000000000000000000000000000000000000000000000000000060208b015260248a015260a060448a015260c4890190611183565b951660648701521660848501521660a4830152037fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe081018352826129c5565b60046040517fd7e6bcf8000000000000000000000000000000000000000000000000000000008152fd5b80614e03575b50614d9a613a6b565b614da2612f6b565b90614dc161442d614db38484612a62565b614dbb613b69565b90614346565b9182821115614dd85750613ecb916140f891612fde565b81831180614dfa575b614dea57505050565b61444e61445a92613ecb94612fde565b50801515614de1565b6040517f6e553f6500000000000000000000000000000000000000000000000000000000815260048101919091523060248201526020816044815f73d4fa2d31b7968e448877f69a96de69f5de8cd23e5af1801561047b5715614d9157614e789060203d6020116107ba576107ab81836129c5565b505f614d91565b919060018101809111612a6f57620f42408201809211612a6f57610a9b92615649565b5f9073ffffffffffffffffffffffffffffffffffffffff80603a5416604051602081019182527fd2841a5d2692465040bd5e06a6f3b37483952c866e0f304dc0e03f76a1f8a0b4604082015260408152614efb816129a9565b51902080547fd2841a5d2692465040bd5e06a6f3b37483952c866e0f304dc0e03f76a1f8a0b291825491808611614f6a575b85900390558390039055603a5460405192835216907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef90602090a3565b945084614f2d565b91610120919493610140840195614fd08560a073ffffffffffffffffffffffffffffffffffffffff80603454168352806035541660208401528060365416604084015280603754166060840152603854608084015260395416910152565b60c08501525f60e085015273ffffffffffffffffffffffffffffffffffffffff80921661010085015216910152565b9161012091949361014084019561505d8560a073ffffffffffffffffffffffffffffffffffffffff80603454168352806035541660208401528060365416604084015280603754166060840152603854608084015260395416910152565b5f60c086015260e085015273ffffffffffffffffffffffffffffffffffffffff80921661010085015216910152565b8015612c59576150b461062860325473ffffffffffffffffffffffffffffffffffffffff1690565b803b15610015576040905f825180927f1e4711cf0000000000000000000000000000000000000000000000000000000082528183816151426004820160345473ffffffffffffffffffffffffffffffffffffffff908116825260355481166020830152603654811660408301526037548116606083015260385460808301526039541660a082015260c00190565b03925af1801561047b576152d1575b5061516e61515d613c95565b909491928186188287100290911890565b80156152c8578082189181119190910218928361518c575b50505090565b82908410615239576151f45f926151bb61062860325473ffffffffffffffffffffffffffffffffffffffff1690565b9083519485809481937fa0b16cc70000000000000000000000000000000000000000000000000000000083523090309060048501614fff565b03925af1801561047b5761520e575b50505b5f8080615186565b8161522d92903d10615232575b61522581836129c5565b810190612f55565b615203565b503d61521b565b505061525d61062860325473ffffffffffffffffffffffffffffffffffffffff1690565b81805180927fa0b16cc7000000000000000000000000000000000000000000000000000000008252815f8161529730308b60048501614f72565b03925af1801561047b576152ad575b5050615206565b816152c392903d106152325761522581836129c5565b6152a6565b50505050505f90565b80610f8e6152de9261293c565b5f615151565b604051906152f18261298d565b6001825260203681840137615305826141fa565b5290565b8015612c59575f604073ffffffffffffffffffffffffffffffffffffffff603254166101648251809481937f370160a00000000000000000000000000000000000000000000000000000000083526153ab6004840160a073ffffffffffffffffffffffffffffffffffffffff80603454168352806035541660208401528060365416604084015280603754166060840152603854608084015260395416910152565b8760c48401528160e484015230610104840152610140610124840152816101448401525af1801561047b576153de575090565b6153f69060403d6040116152325761522581836129c5565b505090565b9061540591614346565b671bc16d674ec800006154188280614346565b046729a2241af62c000061542c8383614346565b04908201809211612a6f578101809111612a6f5790565b670de0b6b3a76400009161545691614346565b0490565b6fffffffffffffffffffffffffffffffff90818111615477571690565b60046040517f6acc9c8f000000000000000000000000000000000000000000000000000000008152fd5b91620f42408101809111612a6f5760018201809211612a6f57610a9b92615649565b61552a6040516154d28161298d565b5f806020948584527f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c65648685015285815191018273a0b86991c6218b36c1d19d4a2e9eb0ce3606eb485af1615524613ee8565b9061571b565b8051908282159283156155c7575b505050156155435750565b608490604051907f08c379a00000000000000000000000000000000000000000000000000000000082526004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e60448201527f6f742073756363656564000000000000000000000000000000000000000000006064820152fd5b6155d79350820181019101612a20565b5f8281615538565b60405161552a9173ffffffffffffffffffffffffffffffffffffffff166156058261298d565b5f806020958685527f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c656487860152868151910182855af1615643613ee8565b916157f4565b9061438691614346565b9161565e82846156d9565b92909384156156cc57848311156156bf5790829109815f038216809204600280826003021880830282030280830282030280830282030280830282030280830282030280920290030293600183805f03040190848311900302920304170290565b821560030260111861570b565b505090610a9b9250614386565b907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8183099102908180821091030391565b634e487b715f526020526024601cfd5b909190156157aa575080511561572e5790565b73a0b86991c6218b36c1d19d4a2e9eb0ce3606eb483b1561574c5790565b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152fd5b8151156157ba5750805190602001fd5b6157f0906040519182917f08c379a000000000000000000000000000000000000000000000000000000000835260048301611d81565b0390fd5b919290156158115750815115615808575090565b3b1561574c5790565b8251909150156157ba5750805190602001fd

Deployed Bytecode

0x60806040526004361015610019575b3415613c48575b5f80fd5b5f3560e01c806304bd4629146102c45780630517bbab146102bf578063052d9e7e146102ba57806323b872dd146102b55780632606a10b146102b05780632b832dbc146102ab5780632f6c1f53146102a65780633d6cb575146101f757806341b3d185146102a15780634251c3541461029c57806346aa2f121461029757806346b00d041461029257806349317f1d1461028d578063503160d91461028857806351fb012d1461028357806353d6fd591461027e57806359ddbab2146102795780635d265d3f146102745780636c2eb3501461026f5780636e553f651461026a5780636ed71ede146102655780637b9e68f2146102605780638f770ad01461025b5780638fcc9cfb1461025657806394bf804d1461025157806395d89b411461024c57806396c9f17e146102475780639b19251a146102425780639d7fb70c1461023d5780639f40a7b314610238578063a318c1a414610233578063a46537571461022e578063a9059cbb14610229578063a9b89c0714610224578063b460af941461021f578063ba0876521461021a578063cb6d71bc14610215578063d00330ab14610210578063d19a3bb81461020b578063e61582b514610206578063efb5f91114610201578063f7f6ba53146101fc5763fde813a80361000e575b610bbe565b6128be565b61286d565b612835565b6127e9565b61277d565b612731565b6126aa565b612625565b6125a6565b6124e2565b6122b8565b612230565b6120e3565b612053565b611fe7565b611ddf565b611d92565b611a2e565b611953565b61191b565b611857565b6117b8565b61140d565b611260565b6111df565b611133565b61101f565b610fdc565b610f9a565b610d21565b610cba565b610c75565b610c3d565b610c02565b610a9e565b61083d565b610567565b610480565b610380565b61033e565b6102e7565b73ffffffffffffffffffffffffffffffffffffffff81160361001557565b346100155760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261001557602061032c600435610327816102c9565b612a74565b604051908152f35b5f91031261001557565b34610015575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261001557602061032c612e60565b8015150361001557565b34610015575f60207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610015576004356103bc81610376565b73ffffffffffffffffffffffffffffffffffffffff60015416803b15610015575f602491604051928380927f48e4a6490000000000000000000000000000000000000000000000000000000082523360048301525afa801561047b57610468575b507fffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffff74ff0000000000000000000000000000000000000000603a5492151560a01b16911617603a5580f35b61047391925061293c565b5f905f61041d565b612a15565b346100155760607ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610015576105636105516105426004356104c4816102c9565b602435906104d1826102c9565b6104db8282613d8a565b604051917f23b872dd00000000000000000000000000000000000000000000000000000000602084015273ffffffffffffffffffffffffffffffffffffffff809216602484015216604482015260443560648201526064815261053d81612971565b613f45565b60208082518301019101612a20565b60405190151581529081906020820190565b0390f35b34610015575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610015576040516105e16105d36020927f2606a10b00000000000000000000000000000000000000000000000000000000848201526004815261053d8161298d565b828082518301019101612f55565b9182151580610800575b610602575b50604080519182526020820192909252f35b6106b09061064161062860015473ffffffffffffffffffffffffffffffffffffffff1690565b73ffffffffffffffffffffffffffffffffffffffff1690565b81610661603a5473ffffffffffffffffffffffffffffffffffffffff1690565b6040517f70a0823100000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff909116600482015293849081906024820190565b0381845afa92831561047b575f936107e1575b50826106d1575b50506105f0565b6040517f18160ddd0000000000000000000000000000000000000000000000000000000081528281600481855afa90811561047b5783905f926107c1575b5060049192604051928380927f01e1d1140000000000000000000000000000000000000000000000000000000082525afa92831561047b576107669361075f9288925f92610794575b5050612a62565b9085615653565b9080821161078c575b508061077d575b80806106ca565b61078690614ea2565b5f610776565b90505f61076f565b6107b39250803d106107ba575b6107ab81836129c5565b810190612a06565b5f80610758565b503d6107a1565b600492506107db90823d84116107ba576107ab81836129c5565b9161070f565b6107f9919350823d84116107ba576107ab81836129c5565b915f6106c3565b5073ffffffffffffffffffffffffffffffffffffffff610835603a5473ffffffffffffffffffffffffffffffffffffffff1690565b1615156105eb565b34610015575f60207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261001557600435610879816102c9565b73ffffffffffffffffffffffffffffffffffffffff8060015416803b15610015575f602491604051928380927f48e4a6490000000000000000000000000000000000000000000000000000000082523360048301525afa801561047b57610a45575b5080603a54166109e75781161561098957610983816109567fc3212a26ea3792ed00e630d63eb8ec2a9295974fe90e94ff25a8f70987fcdeeb9373ffffffffffffffffffffffffffffffffffffffff167fffffffffffffffffffffffff0000000000000000000000000000000000000000603a541617603a55565b604080515f815273ffffffffffffffffffffffffffffffffffffffff909216602083015290918291820190565b0390a180f35b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600f60248201527f496e76616c6964206164647265737300000000000000000000000000000000006044820152fd5b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601160248201527f735553443320616c7265616479207365740000000000000000000000000000006044820152fd5b610a5091935061293c565b5f915f6108db565b7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc604091011261001557600435610a8e816102c9565b90602435610a9b81610376565b90565b3461001557610aac36610a58565b6001545f9273ffffffffffffffffffffffffffffffffffffffff809216803b15610015575f602491604051928380927f48e4a6490000000000000000000000000000000000000000000000000000000082523360048301525afa801561047b57610b84575b507f8bb3c9e62c5e823d77565af4d29339378715313d3cb898327e576392ffa79b00916020911692835f52603c8252610b788160405f209060ff7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0083541691151516179055565b6040519015158152a280f35b602091945091610bb47f8bb3c9e62c5e823d77565af4d29339378715313d3cb898327e576392ffa79b009361293c565b5f94915091610b11565b346100155760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261001557610bf5613f81565b610c00600435613fe8565b005b34610015575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610015576020603d54604051908152f35b34610015575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261001557602061032c612f6b565b346100155760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261001557602061032c600435610cb5816102c9565b612feb565b346100155760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126100155773ffffffffffffffffffffffffffffffffffffffff600435610d0a816102c9565b165f52603e602052602060405f2054604051908152f35b34610015575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261001557610d57613f81565b610d5f613269565b610d8161062860325473ffffffffffffffffffffffffffffffffffffffff1690565b803b15610015576040517f1e4711cf000000000000000000000000000000000000000000000000000000008152905f908290818381610dc388600483016117f3565b03925af1801561047b57610f81575b50610df46106285f5473ffffffffffffffffffffffffffffffffffffffff1690565b6040517f70a082310000000000000000000000000000000000000000000000000000000080825230600483015291602091908290829060249082905afa801561047b57610e48915f91610f64575b50614d8b565b610e9d81610e65610e57613a6b565b610e5f612f6b565b90612a62565b604051809381927f07a2d13a000000000000000000000000000000000000000000000000000000008352600483019190602083019252565b038173d4fa2d31b7968e448877f69a96de69f5de8cd23e5afa90811561047b575f91610f47575b5081610ee76106285f5473ffffffffffffffffffffffffffffffffffffffff1690565b604051948552306004860152849060249082905afa90811561047b5761056393610f18935f93610f28575050612a62565b6040519081529081906020820190565b610f3f929350803d106107ba576107ab81836129c5565b908480610758565b610f5e9150823d84116107ba576107ab81836129c5565b83610ec4565b610f7b9150833d85116107ba576107ab81836129c5565b84610e42565b80610f8e610f949261293c565b80610334565b80610dd2565b346100155760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261001557610fd1613f81565b610c00600435614395565b34610015575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261001557602060ff603a5460a01c166040519015158152f35b346100155761102d36610a58565b6001545f9273ffffffffffffffffffffffffffffffffffffffff809216803b15610015575f602491604051928380927f48e4a6490000000000000000000000000000000000000000000000000000000082523360048301525afa801561047b576110f9575b507ff93f9a76c1bf3444d22400a00cb9fe990e6abe9dbb333fda48859cfee864543d916020911692835f52603b8252610b788160405f209060ff7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0083541691151516179055565b6020919450916111297ff93f9a76c1bf3444d22400a00cb9fe990e6abe9dbb333fda48859cfee864543d9361293c565b5f94915091611092565b34610015575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261001557608061116b6132c4565b91604051938452602084015260408301526060820152f35b91908251928382525f5b8481106111cb5750507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f845f6020809697860101520116010190565b60208183018101518483018201520161118d565b34610015575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610015576105636040517f440368a3000000000000000000000000000000000000000000000000000000006020820152600481526112468161298d565b6040519182915f8352604060208401526040830190611183565b34610015575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610015577ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00805460ff8160401c169081156113f7575b506113cd576113147ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a0060027fffffffffffffffffffffffffffffffffffffffffffffffff0000000000000000825416179055565b680100000000000000007fffffffffffffffffffffffffffffffffffffffffffffff00ffffffffffffffff82541617905561134d61330a565b6113997ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a007fffffffffffffffffffffffffffffffffffffffffffffff00ffffffffffffffff8154169055565b604051600281527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d29080602081015b0390a1005b60046040517ff92ee8a9000000000000000000000000000000000000000000000000000000008152fd5b6002915067ffffffffffffffff1610155f6112c0565b34610015576040807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610015576004359060243561144d816102c9565b82908315806117b1575b611719575b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff821461168a575b6114a661062860015473ffffffffffffffffffffffffffffffffffffffff1690565b83517f70a0823100000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8316600482015260209290918390839060249082905afa91821561047b57610563966115b5956115a89461053d935f9161166d575b5015611659575b50611525612e60565b6115c4575b86517f6e553f650000000000000000000000000000000000000000000000000000000086820152602481019190915273ffffffffffffffffffffffffffffffffffffffff909216604483015281606481015b037fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe081018352826129c5565b8051810182019101612a06565b90519081529081906020820190565b73ffffffffffffffffffffffffffffffffffffffff83163314801561161c575b6115ed90614bca565b426116168473ffffffffffffffffffffffffffffffffffffffff165f52603e60205260405f2090565b5561152a565b506115ed61165261164b3373ffffffffffffffffffffffffffffffffffffffff165f52603c60205260405f2090565b5460ff1690565b90506115e4565b61166790603d541115614b65565b5f61151c565b6116849150873d89116107ba576107ab81836129c5565b5f611515565b90506116ad6106285f5473ffffffffffffffffffffffffffffffffffffffff1690565b82517f70a0823100000000000000000000000000000000000000000000000000000000815233600482015290602090829060249082905afa90811561047b575f916116fa575b5090611484565b611713915060203d6020116107ba576107ab81836129c5565b5f6116f3565b905061173d61062860015473ffffffffffffffffffffffffffffffffffffffff1690565b6020835180927fb3d7f6b9000000000000000000000000000000000000000000000000000000008252818061177960048201905f602083019252565b03915afa90811561047b575f91611792575b509061145c565b6117ab915060203d6020116107ba576107ab81836129c5565b5f61178b565b505f611457565b34610015575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610015576020603354604051908152f35b6118559092919260c081019360a0908173ffffffffffffffffffffffffffffffffffffffff9182815116855282602082015116602086015282604082015116604086015282606082015116606086015260808101516080860152015116910152565b565b34610015575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610015575f60a060405161189481612955565b828152826020820152826040820152826060820152826080820152015260c06118bb613269565b611919604051809260a0908173ffffffffffffffffffffffffffffffffffffffff9182815116855282602082015116602086015282604082015116604086015282606082015116606086015260808101516080860152015116910152565bf35b34610015575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261001557602061032c6134d3565b34610015575f60207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126100155760043573ffffffffffffffffffffffffffffffffffffffff60015416803b15610015575f602491604051928380927f48e4a6490000000000000000000000000000000000000000000000000000000082523360048301525afa801561047b57611a1a575b506020817f96a008f96f1c0ab9fa3d9ddd43cdfc614848c4d054d51f43662ed900e9d094c892603d55604051908152a180f35b611a2591925061293c565b5f9060206119e7565b34610015576040807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126100155760043590602435611a6e816102c9565b5f9083611ce8575b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8214611c59575b611ac061062860015473ffffffffffffffffffffffffffffffffffffffff1690565b83517f70a0823100000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8316600482015260209290918390839060249082905afa91821561047b57610563966115b5956115a89461053d935f91611c3c575b5015611c28575b50611b3f612e60565b611b9a575b86517f94bf804d0000000000000000000000000000000000000000000000000000000086820152602481019190915273ffffffffffffffffffffffffffffffffffffffff9092166044830152816064810161157c565b73ffffffffffffffffffffffffffffffffffffffff831633148015611bf2575b611bc390614bca565b42611bec8473ffffffffffffffffffffffffffffffffffffffff165f52603e60205260405f2090565b55611b44565b50611bc3611c2161164b3373ffffffffffffffffffffffffffffffffffffffff165f52603c60205260405f2090565b9050611bba565b611c3690603d541115614b65565b5f611b36565b611c539150873d89116107ba576107ab81836129c5565b5f611b2f565b9050611c7c6106285f5473ffffffffffffffffffffffffffffffffffffffff1690565b82517f70a0823100000000000000000000000000000000000000000000000000000000815233600482015290602090829060249082905afa90811561047b575f91611cc9575b5090611a9e565b611ce2915060203d6020116107ba576107ab81836129c5565b5f611cc2565b9050611d0c61062860015473ffffffffffffffffffffffffffffffffffffffff1690565b6020835180927fb3d7f6b90000000000000000000000000000000000000000000000000000000082528180611d4989600483019190602083019252565b03915afa90811561047b575f91611d62575b5090611a76565b611d7b915060203d6020116107ba576107ab81836129c5565b5f611d5b565b906020610a9b928181520190611183565b34610015575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261001557610563611dcb6135e1565b604051918291602083526020830190611183565b346100155760807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261001557600435611e1a816102c9565b60443590611e27826102c9565b606435611e33816102c9565b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00549267ffffffffffffffff60ff8560401c1615941680159081611fdf575b6001149081611fd5575b159081611fcc575b506113cd57611eeb9284611edd7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a0060017fffffffffffffffffffffffffffffffffffffffffffffffff0000000000000000825416179055565b611f70575b60243590613842565b611ef157005b611f3d7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a007fffffffffffffffffffffffffffffffffffffffffffffff00ffffffffffffffff8154169055565b604051600181527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d29080602081016113c8565b611fc77ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00680100000000000000007fffffffffffffffffffffffffffffffffffffffffffffff00ffffffffffffffff825416179055565b611ee2565b9050155f611e84565b303b159150611e7c565b859150611e72565b346100155760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126100155773ffffffffffffffffffffffffffffffffffffffff600435612037816102c9565b165f52603b602052602060ff60405f2054166040519015158152f35b346100155760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126100155761208a613f81565b610c00600435614d8b565b7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc608091011261001557600435906024356120cf816102c9565b906044356120dc816102c9565b9060643590565b346100155761157c61053d61217961216b6120fd36612095565b6040517f9f40a7b300000000000000000000000000000000000000000000000000000000602080830191909152602482019590955273ffffffffffffffffffffffffffffffffffffffff938416604482015292821660648401526084830152919591949091829060a4820190565b838082518301019101612a06565b9073ffffffffffffffffffffffffffffffffffffffff838160015416916024604051809481937f70a08231000000000000000000000000000000000000000000000000000000008352871660048301525afa90811561047b575f91612213575b50156121e9575b50604051908152f35b73ffffffffffffffffffffffffffffffffffffffff165f908152603e60205260408120555f6121e0565b61222a9150843d86116107ba576107ab81836129c5565b5f6121d9565b346100155761157c61053d61217961216b61224a36612095565b6040517fa318c1a400000000000000000000000000000000000000000000000000000000602080830191909152602482019590955273ffffffffffffffffffffffffffffffffffffffff938416604482015292821660648401526084830152919591949091829060a4820190565b34610015575f807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126100155773ffffffffffffffffffffffffffffffffffffffff8060015416803b15610015575f602491604051928380927fd43fdcf70000000000000000000000000000000000000000000000000000000082523360048301525afa801561047b576124cf575b5061237261062861062861062860325473ffffffffffffffffffffffffffffffffffffffff1690565b9060405180927ff5efbb4f00000000000000000000000000000000000000000000000000000000825281600460209586935afa801561047b5783915f916124a2575b506004604051809481937f76e66a1c000000000000000000000000000000000000000000000000000000008352165afa801561047b577f617d69aa6f8f8f7158e168208ffd102c878214b34e8510746322af83f775aab792610983925f92612483575b50612426612710831115613a06565b7fd2841a5d2692465040bd5e06a6f3b37483952c866e0f304dc0e03f76a1f8a0b990827fffffffffffffffffffffffffffffffffffffffffffffffffffff0000ffffffff835416911b179055604051918291829190602083019252565b8161249b9293503d84116107ba576107ab81836129c5565b905f612417565b6124c29150823d84116124c8575b6124ba81836129c5565b810190612e4b565b5f6123b4565b503d6124b0565b6124da91925061293c565b5f905f612349565b346100155760407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261001557602061259c61258e61157c61053d60043561252b816102c9565b6125358133613d8a565b6040517fa9059cbb000000000000000000000000000000000000000000000000000000008782015273ffffffffffffffffffffffffffffffffffffffff9091166024808301919091523560448201529182906064820190565b828082518301019101612a20565b6040519015158152f35b34610015575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261001557602061032c613a6b565b7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc60609101126100155760043590602435612618816102c9565b90604435610a9b816102c9565b346100155761216b61053d61217961263c366125de565b6040517fa318c1a400000000000000000000000000000000000000000000000000000000602080830191909152602482019490945273ffffffffffffffffffffffffffffffffffffffff928316604482015291811660648301525f6084830152919491938160a4810161157c565b346100155761216b61053d6121796126c1366125de565b6040517f9f40a7b300000000000000000000000000000000000000000000000000000000602080830191909152602482019490945273ffffffffffffffffffffffffffffffffffffffff928316604482015291811660648301526127106084830152919491938160a4810161157c565b34610015575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261001557602060405173d4fa2d31b7968e448877f69a96de69f5de8cd23e8152f35b346100155760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126100155773ffffffffffffffffffffffffffffffffffffffff6004356127cd816102c9565b165f52603c602052602060ff60405f2054166040519015158152f35b34610015575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261001557602060405173d377919fa87120584b21279a491f82d5265a139c8152f35b34610015575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261001557602061032c613b69565b34610015575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261001557602073ffffffffffffffffffffffffffffffffffffffff60325416604051908152f35b34610015575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261001557602073ffffffffffffffffffffffffffffffffffffffff603a5416604051908152f35b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b67ffffffffffffffff811161295057604052565b61290f565b60c0810190811067ffffffffffffffff82111761295057604052565b60a0810190811067ffffffffffffffff82111761295057604052565b6040810190811067ffffffffffffffff82111761295057604052565b6060810190811067ffffffffffffffff82111761295057604052565b90601f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0910116810190811067ffffffffffffffff82111761295057604052565b90816020910312610015575190565b6040513d5f823e3d90fd5b908160209103126100155751610a9b81610376565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b91908201809211612a6f57565b612a35565b612a956106285f5473ffffffffffffffffffffffffffffffffffffffff1690565b604080517f70a082310000000000000000000000000000000000000000000000000000000081523060048083019190915291939260209291908390829060249082905afa90811561047b575f91612e2e575b50612af0613c95565b91508651907f5c975abb00000000000000000000000000000000000000000000000000000000825273d4fa2d31b7968e448877f69a96de69f5de8cd23e9086838781855afa801561047b5787935f91612e11575b5015612cc15750612b8a92505f905b885180809581947f07a2d13a0000000000000000000000000000000000000000000000000000000083528983019190602083019252565b03915afa801561047b578492612ba7925f92612ca2575b50612a62565b94612bca61062860015473ffffffffffffffffffffffffffffffffffffffff1690565b9051928380927fbf86d6900000000000000000000000000000000000000000000000000000000082525afa91821561047b575f92612c75575b5050612c7157612c11612e60565b9081612c1c57505090565b612c449073ffffffffffffffffffffffffffffffffffffffff165f52603e60205260405f2090565b54908115159182612c5e575b5050612c595790565b505f90565b612c689250612a62565b42105f80612c50565b5090565b612c949250803d10612c9b575b612c8c81836129c5565b810190612a20565b5f80612c03565b503d612c82565b612cba919250843d86116107ba576107ab81836129c5565b905f612ba1565b9150612ccb612f6b565b8851937fd905777e0000000000000000000000000000000000000000000000000000000091828652888680612d1f308c830191909173ffffffffffffffffffffffffffffffffffffffff6020820193169052565b0381875afa91821561047b57612d4b8a93612d5793612da9995f92612df9575b50808218908211021890565b95818118908211021890565b91612d7a61062860325473ffffffffffffffffffffffffffffffffffffffff1690565b908b51968792839283528a830191909173ffffffffffffffffffffffffffffffffffffffff6020820193169052565b0381855afa92831561047b57610e5f8894612dd493612b8a975f92612dda5750808218908211021890565b90612b53565b612df2919250873d89116107ba576107ab81836129c5565b905f612d3f565b612df2919250863d88116107ba576107ab81836129c5565b612e289150843d8611612c9b57612c8c81836129c5565b5f612b44565b612e459150833d85116107ba576107ab81836129c5565b5f612ae7565b908160209103126100155751610a9b816102c9565b612e8861062861062861062860325473ffffffffffffffffffffffffffffffffffffffff1690565b60405180917ff5efbb4f00000000000000000000000000000000000000000000000000000000825281600460209485935afa90811561047b5773ffffffffffffffffffffffffffffffffffffffff9183915f91612f38575b506004604051809481937fe4c3b0da000000000000000000000000000000000000000000000000000000008352165afa91821561047b575f92612f2257505090565b610a9b9250803d106107ba576107ab81836129c5565b612f4f9150823d84116124c8576124ba81836129c5565b5f612ee0565b9190826040910312610015576020825192015190565b6040517f70a0823100000000000000000000000000000000000000000000000000000000815230600482015260208160248173d4fa2d31b7968e448877f69a96de69f5de8cd23e5afa90811561047b575f91612fc5575090565b610a9b915060203d6020116107ba576107ab81836129c5565b91908203918211612a6f57565b603a5460a01c60ff1680613231575b612c59576040517f402d267d00000000000000000000000000000000000000000000000000000000815230600482015260209173d4fa2d31b7968e448877f69a96de69f5de8cd23e918381602481865afa92831561047b5784915f9461320d575b5090600491604051928380927f5c975abb0000000000000000000000000000000000000000000000000000000082525afa90811561047b575f916131f0575b5080156131e8575b61316c576130d2906130c960325473ffffffffffffffffffffffffffffffffffffffff1690565b60335490614234565b6131e2576130de6134d3565b801580156131b9575b6131925760048361311061062860015473ffffffffffffffffffffffffffffffffffffffff1690565b604051928380927f01e1d1140000000000000000000000000000000000000000000000000000000082525afa93841561047b575f94613173575b50508281111561316c57610a9b9261316191612fde565b818082109118021890565b5050505f90565b61318a929450803d106107ba576107ab81836129c5565b915f8061314a565b5050507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff90565b507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff81146130e7565b50505f90565b5081156130a2565b6132079150843d8611612c9b57612c8c81836129c5565b5f61309a565b82919450613229906004933d84116107ba576107ab81836129c5565b93909161305b565b5061326461326061164b8373ffffffffffffffffffffffffffffffffffffffff165f52603b60205260405f2090565b1590565b612ffa565b6040519061327682612955565b8160a073ffffffffffffffffffffffffffffffffffffffff80603454168352806035541660208401528060365416604084015280603754166060840152603854608084015260395416910152565b6132ee73ffffffffffffffffffffffffffffffffffffffff603254166132e8613269565b90614622565b509283919280948082115f146131e2578103908111612a6f5790565b73a0b86991c6218b36c1d19d4a2e9eb0ce3606eb487fffffffffffffffffffffffff000000000000000000000000000000000000000081815f5416175f55817fd2841a5d2692465040bd5e06a6f3b37483952c866e0f304dc0e03f76a1f8a0b091825416179055604051602081017f095ea7b300000000000000000000000000000000000000000000000000000000928382525f80846133eb60248201907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff6020604084019373d4fa2d31b7968e448877f69a96de69f5de8cd23e81520152565b039361341d7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0958681018852876129c5565b85519082855af19061342d613ee8565b826134a1575b5081613496575b501561344557505050565b61185592613491613491926040519260208401528261348560248201905f6020604084019373d4fa2d31b7968e448877f69a96de69f5de8cd23e81520152565b039081018352826129c5565b6154c3565b90503b15155f61343a565b805191925081159182156134b9575b5050905f613433565b6134cc9250602080918301019101612a20565b5f806134b0565b6134fb61062861062861062860325473ffffffffffffffffffffffffffffffffffffffff1690565b60405180917ff5efbb4f00000000000000000000000000000000000000000000000000000000825281600460209485935afa90811561047b575f916135c4575b508160405180927fcc718f760000000000000000000000000000000000000000000000000000000082528173ffffffffffffffffffffffffffffffffffffffff816135ad60048201907f4bba860c0c28b1a4ae0214c01f08e53b00bfe2e087690d7a04d73a15360ec6a7602083019252565b0392165afa91821561047b575f92612f2257505090565b6135db9150823d84116124c8576124ba81836129c5565b5f61353b565b604051906135ee8261298d565b600482527f55534433000000000000000000000000000000000000000000000000000000006020830152565b1561362157565b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600760248201527f216d6f7270686f000000000000000000000000000000000000000000000000006044820152fd5b908160c09103126100155760a06040519161369983612955565b80516136a4816102c9565b835260208101516136b4816102c9565b602084015260408101516136c7816102c9565b604084015260608101516136da816102c9565b60608401526080810151608084015201516136f4816102c9565b60a082015290565b1561370357565b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600e60248201527f496e76616c6964206d61726b65740000000000000000000000000000000000006044820152fd5b61380160a06118559273ffffffffffffffffffffffffffffffffffffffff80825116907fffffffffffffffffffffffff00000000000000000000000000000000000000009182603454161760345580602084015116826035541617603555806040840151168260365416176036556060830151169060375416176037556080810151603855015173ffffffffffffffffffffffffffffffffffffffff1690565b73ffffffffffffffffffffffffffffffffffffffff167fffffffffffffffffffffffff00000000000000000000000000000000000000006039541617603955565b6139149392916138b073ffffffffffffffffffffffffffffffffffffffff60c0931661386f81151561361a565b73ffffffffffffffffffffffffffffffffffffffff167fffffffffffffffffffffffff00000000000000000000000000000000000000006032541617603255565b6138b981603355565b6138db61062860325473ffffffffffffffffffffffffffffffffffffffff1690565b60405180809781947f2c3c9157000000000000000000000000000000000000000000000000000000008352600483019190602083019252565b03915afa801561047b5761397d829161398b955f916139d7575b5061395a613953610628835173ffffffffffffffffffffffffffffffffffffffff1690565b15156136fc565b61396381613761565b5173ffffffffffffffffffffffffffffffffffffffff1690565b6139856135e1565b90614c55565b6118556139af6106285f5473ffffffffffffffffffffffffffffffffffffffff1690565b6139d161062860325473ffffffffffffffffffffffffffffffffffffffff1690565b906149e7565b6139f9915060c03d60c0116139ff575b6139f181836129c5565b81019061367f565b5f61392e565b503d6139e7565b15613a0d57565b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601560248201527f496e76616c6964207472616e63686520736861726500000000000000000000006044820152fd5b613b1473ffffffffffffffffffffffffffffffffffffffff60325416613a8f613269565b5f613ae160c08320604051602081019182526002604082015260408152613ab5816129a9565b5190206040805130602082019081529181019290925290613ad9816060810161157c565b5190206152e4565b604051809581927f7784c685000000000000000000000000000000000000000000000000000000008352600483016141bf565b0381855afa90811561047b57613b38613b3f92610a9b955f91613b47575b506141fa565b5192614622565b505091614e7f565b613b6391503d805f833e613b5b81836129c5565b810190614145565b5f613b32565b613b9161062861062861062860325473ffffffffffffffffffffffffffffffffffffffff1690565b60405180917ff5efbb4f00000000000000000000000000000000000000000000000000000000825281600460209485935afa90811561047b5773ffffffffffffffffffffffffffffffffffffffff9183915f91613c2b575b506004604051809481937f111e3c08000000000000000000000000000000000000000000000000000000008352165afa91821561047b575f92612f2257505090565b613c429150823d84116124c8576124ba81836129c5565b5f613be9565b365f80375f80368173d377919fa87120584b21279a491f82d5265a139c5af43d5f803e15613c74573d5ff35b3d5ffd5b51906fffffffffffffffffffffffffffffffff8216820361001557565b73ffffffffffffffffffffffffffffffffffffffff6032541660606033546044604051809481937f93c5206200000000000000000000000000000000000000000000000000000000835260048301523060248301525afa90811561047b575f91613d15575b505190613d12613d086132c4565b9050929185614e7f565b91565b90506060813d606011613d82575b81613d30606093836129c5565b8101031261001557604051906060820182811067ffffffffffffffff82111761295057613d7791604091825280518452613d6c60208201613c78565b602085015201613c78565b60408201525f613cfa565b3d9150613d23565b9073ffffffffffffffffffffffffffffffffffffffff8083169182158015613ede575b613ed85781603a54169182911614918215613ece575b5050613ecb57613df18173ffffffffffffffffffffffffffffffffffffffff165f52603e60205260405f2090565b54613dfa612e60565b8101809111612a6f57421090811591613e9a575b5015613e1657565b60846040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602e60248201527f555344333a2043616e6e6f74207472616e7366657220647572696e6720636f6d60448201527f6d69746d656e7420706572696f640000000000000000000000000000000000006064820152fd5b613ec3915073ffffffffffffffffffffffffffffffffffffffff165f52603e60205260405f2090565b54155f613e0e565b50565b1490505f80613dc3565b50505050565b5081811615613dad565b3d15613f40573d9067ffffffffffffffff82116129505760405191613f3560207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f84011601846129c5565b82523d5f602084013e565b606090565b5f809160208151910173d377919fa87120584b21279a491f82d5265a139c5af4613f6d613ee8565b9015613f765790565b6040513d90815f823efd5b303303613f8a57565b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600560248201527f2173656c660000000000000000000000000000000000000000000000000000006044820152fd5b8015613ecb576040517f0a28a47700000000000000000000000000000000000000000000000000000000815280614029602093600483019190602083019252565b0390828173d4fa2d31b7968e448877f69a96de69f5de8cd23e9381855afa801561047b57614074915f91614110575b50614061612f6b565b8181106140eb575b808218908211021890565b908161407f57505050565b6040517fba0876520000000000000000000000000000000000000000000000000000000081526004810192909252306024830181905260448301528290829060649082905f905af1801561047b576140d5575050565b81613ecb92903d106107ba576107ab81836129c5565b6140fd6140f88284612fde565b61508c565b15614069575061410b612f6b565b614069565b6141279150843d86116107ba576107ab81836129c5565b5f614058565b67ffffffffffffffff81116129505760051b60200190565b60209081818403126100155780519067ffffffffffffffff821161001557019180601f8401121561001557825161417b8161412d565b9361418960405195866129c5565b818552838086019260051b820101928311610015578301905b8282106141b0575050505090565b815181529083019083016141a2565b60209060206040818301928281528551809452019301915f5b8281106141e6575050505090565b8351855293810193928101926001016141d8565b8051156142075760200190565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52603260045260245ffd5b919061429261157c9160405160208101918252600260408201526040815261425b816129a9565b5190206040519283916020830195866020909392919373ffffffffffffffffffffffffffffffffffffffff60408201951681520152565b5190209060018201809211612a6f576142f95f916142c473ffffffffffffffffffffffffffffffffffffffff946152e4565b6040519485809481937f7784c685000000000000000000000000000000000000000000000000000000008352600483016141bf565b0392165afa801561047b5761431b61433191610a9b935f91613b4757506141fa565b516fffffffffffffffffffffffffffffffff1690565b6fffffffffffffffffffffffffffffffff1690565b81810292918115918404141715612a6f57565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601260045260245ffd5b8115614390570490565b614359565b8015613ecb576040517f6e553f6500000000000000000000000000000000000000000000000000000000815260048101919091523060248201526020816044815f73d4fa2d31b7968e448877f69a96de69f5de8cd23e5af1801561047b57614464575b50614401613b69565b8015613ecb5761440f613a6b565b61443561442d61441d612f6b565b936144288585612a62565b614346565b612710900490565b918183111561445f5761444e61445a92613ecb94612fde565b90818082109118021890565b615309565b505050565b61447c9060203d6020116107ba576107ab81836129c5565b505f6143f8565b908160e0910312610015576040519060e0820182811067ffffffffffffffff821117612950576145199160c0916040526144bc81613c78565b84526144ca60208201613c78565b60208501526144db60408201613c78565b60408501526144ec60608201613c78565b60608501526144fd60808201613c78565b608085015261450e60a08201613c78565b60a085015201613c78565b60c082015290565b61018060c061185593959461458c846101a081019860a0908173ffffffffffffffffffffffffffffffffffffffff9182815116855282602082015116602086015282604082015116604086015282606082015116606086015260808101516080860152015116910152565b80516fffffffffffffffffffffffffffffffff908116858401526020820151811660e086015260408201518116610100860152606082015181166101208601526080820151811661014086015260a08201511661016085015201516fffffffffffffffffffffffffffffffff16910152565b9190916fffffffffffffffffffffffffffffffff80809416911601918211612a6f57565b60c082206040517f5c60e39a00000000000000000000000000000000000000000000000000000000815260048101919091529173ffffffffffffffffffffffffffffffffffffffff9160e0908490602490829086165afa92831561047b575f936149b6575b5082906146b36146ad61433160808501516fffffffffffffffffffffffffffffffff1690565b42612fde565b92831515908161497d575b81614951575b50614751575b50516fffffffffffffffffffffffffffffffff16905060208201516fffffffffffffffffffffffffffffffff1692614732606061471a60408601516fffffffffffffffffffffffffffffffff1690565b9401516fffffffffffffffffffffffffffffffff1690565b6fffffffffffffffffffffffffffffffff928316948316938316921690565b6147b5918161477f6106286106286060602096015173ffffffffffffffffffffffffffffffffffffffff1690565b906040518095819482937f2a944b5500000000000000000000000000000000000000000000000000000000845260048401614521565b03915afa90811561047b5761483d91614801915f91614932575b506147fb60408601946147f561433187516fffffffffffffffffffffffffffffffff1690565b926153fb565b90615443565b9161482761480e8461545a565b82516fffffffffffffffffffffffffffffffff166145fe565b6fffffffffffffffffffffffffffffffff169052565b61487b61486561484c8361545a565b84516fffffffffffffffffffffffffffffffff166145fe565b6fffffffffffffffffffffffffffffffff168352565b6fffffffffffffffffffffffffffffffff6148a960a08401516fffffffffffffffffffffffffffffffff1690565b16806148b7575b82906146ca565b6148cd670de0b6b3a76400009161492b93614346565b0461482761480e6149266148fd846148f861433189516fffffffffffffffffffffffffffffffff1690565b612fde565b93602087019461492061433187516fffffffffffffffffffffffffffffffff1690565b916154a1565b61545a565b5f806148b0565b61494b915060203d6020116107ba576107ab81836129c5565b5f6147cf565b9050614974606083015173ffffffffffffffffffffffffffffffffffffffff1690565b1615155f6146c4565b90506fffffffffffffffffffffffffffffffff6149ad60408501516fffffffffffffffffffffffffffffffff1690565b161515906146be565b6149d991935060e03d60e0116149e0575b6149d181836129c5565b810190614483565b915f614687565b503d6149c7565b6040517f095ea7b3000000000000000000000000000000000000000000000000000000006020820181815273ffffffffffffffffffffffffffffffffffffffff851660248401527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff604480850191909152835290939192917fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe091614a8c6064866129c5565b5f8073ffffffffffffffffffffffffffffffffffffffff86169287519082855af190614ab6613ee8565b82614b33575b5081614b28575b5015614ad1575b5050505050565b604051602081019590955273ffffffffffffffffffffffffffffffffffffffff1660248501525f6044850152614b1e93614b1991614b13908260648101613485565b826155df565b6155df565b5f80808080614aca565b90503b15155f614ac3565b80519192508115918215614b4b575b5050905f614abc565b614b5e9250602080918301019101612a20565b5f80614b42565b15614b6c57565b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601560248201527f42656c6f77206d696e696d756d206465706f73697400000000000000000000006044820152fd5b15614bd157565b60846040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602f60248201527f555344333a204f6e6c792073656c66206f722077686974656c6973746564206460448201527f65706f7369747320616c6c6f77656400000000000000000000000000000000006064820152fd5b939192909260ff7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a005460401c1615614d6157613ecb9461053d9373ffffffffffffffffffffffffffffffffffffffff80809316947fffffffffffffffffffffffff000000000000000000000000000000000000000086815f5416175f553090600154161760015581614d22604051998a987f97073ae60000000000000000000000000000000000000000000000000000000060208b015260248a015260a060448a015260c4890190611183565b951660648701521660848501521660a4830152037fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe081018352826129c5565b60046040517fd7e6bcf8000000000000000000000000000000000000000000000000000000008152fd5b80614e03575b50614d9a613a6b565b614da2612f6b565b90614dc161442d614db38484612a62565b614dbb613b69565b90614346565b9182821115614dd85750613ecb916140f891612fde565b81831180614dfa575b614dea57505050565b61444e61445a92613ecb94612fde565b50801515614de1565b6040517f6e553f6500000000000000000000000000000000000000000000000000000000815260048101919091523060248201526020816044815f73d4fa2d31b7968e448877f69a96de69f5de8cd23e5af1801561047b5715614d9157614e789060203d6020116107ba576107ab81836129c5565b505f614d91565b919060018101809111612a6f57620f42408201809211612a6f57610a9b92615649565b5f9073ffffffffffffffffffffffffffffffffffffffff80603a5416604051602081019182527fd2841a5d2692465040bd5e06a6f3b37483952c866e0f304dc0e03f76a1f8a0b4604082015260408152614efb816129a9565b51902080547fd2841a5d2692465040bd5e06a6f3b37483952c866e0f304dc0e03f76a1f8a0b291825491808611614f6a575b85900390558390039055603a5460405192835216907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef90602090a3565b945084614f2d565b91610120919493610140840195614fd08560a073ffffffffffffffffffffffffffffffffffffffff80603454168352806035541660208401528060365416604084015280603754166060840152603854608084015260395416910152565b60c08501525f60e085015273ffffffffffffffffffffffffffffffffffffffff80921661010085015216910152565b9161012091949361014084019561505d8560a073ffffffffffffffffffffffffffffffffffffffff80603454168352806035541660208401528060365416604084015280603754166060840152603854608084015260395416910152565b5f60c086015260e085015273ffffffffffffffffffffffffffffffffffffffff80921661010085015216910152565b8015612c59576150b461062860325473ffffffffffffffffffffffffffffffffffffffff1690565b803b15610015576040905f825180927f1e4711cf0000000000000000000000000000000000000000000000000000000082528183816151426004820160345473ffffffffffffffffffffffffffffffffffffffff908116825260355481166020830152603654811660408301526037548116606083015260385460808301526039541660a082015260c00190565b03925af1801561047b576152d1575b5061516e61515d613c95565b909491928186188287100290911890565b80156152c8578082189181119190910218928361518c575b50505090565b82908410615239576151f45f926151bb61062860325473ffffffffffffffffffffffffffffffffffffffff1690565b9083519485809481937fa0b16cc70000000000000000000000000000000000000000000000000000000083523090309060048501614fff565b03925af1801561047b5761520e575b50505b5f8080615186565b8161522d92903d10615232575b61522581836129c5565b810190612f55565b615203565b503d61521b565b505061525d61062860325473ffffffffffffffffffffffffffffffffffffffff1690565b81805180927fa0b16cc7000000000000000000000000000000000000000000000000000000008252815f8161529730308b60048501614f72565b03925af1801561047b576152ad575b5050615206565b816152c392903d106152325761522581836129c5565b6152a6565b50505050505f90565b80610f8e6152de9261293c565b5f615151565b604051906152f18261298d565b6001825260203681840137615305826141fa565b5290565b8015612c59575f604073ffffffffffffffffffffffffffffffffffffffff603254166101648251809481937f370160a00000000000000000000000000000000000000000000000000000000083526153ab6004840160a073ffffffffffffffffffffffffffffffffffffffff80603454168352806035541660208401528060365416604084015280603754166060840152603854608084015260395416910152565b8760c48401528160e484015230610104840152610140610124840152816101448401525af1801561047b576153de575090565b6153f69060403d6040116152325761522581836129c5565b505090565b9061540591614346565b671bc16d674ec800006154188280614346565b046729a2241af62c000061542c8383614346565b04908201809211612a6f578101809111612a6f5790565b670de0b6b3a76400009161545691614346565b0490565b6fffffffffffffffffffffffffffffffff90818111615477571690565b60046040517f6acc9c8f000000000000000000000000000000000000000000000000000000008152fd5b91620f42408101809111612a6f5760018201809211612a6f57610a9b92615649565b61552a6040516154d28161298d565b5f806020948584527f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c65648685015285815191018273a0b86991c6218b36c1d19d4a2e9eb0ce3606eb485af1615524613ee8565b9061571b565b8051908282159283156155c7575b505050156155435750565b608490604051907f08c379a00000000000000000000000000000000000000000000000000000000082526004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e60448201527f6f742073756363656564000000000000000000000000000000000000000000006064820152fd5b6155d79350820181019101612a20565b5f8281615538565b60405161552a9173ffffffffffffffffffffffffffffffffffffffff166156058261298d565b5f806020958685527f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c656487860152868151910182855af1615643613ee8565b916157f4565b9061438691614346565b9161565e82846156d9565b92909384156156cc57848311156156bf5790829109815f038216809204600280826003021880830282030280830282030280830282030280830282030280830282030280920290030293600183805f03040190848311900302920304170290565b821560030260111861570b565b505090610a9b9250614386565b907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8183099102908180821091030391565b634e487b715f526020526024601cfd5b909190156157aa575080511561572e5790565b73a0b86991c6218b36c1d19d4a2e9eb0ce3606eb483b1561574c5790565b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152fd5b8151156157ba5750805190602001fd5b6157f0906040519182917f08c379a000000000000000000000000000000000000000000000000000000000835260048301611d81565b0390fd5b919290156158115750815115615808575090565b3b1561574c5790565b8251909150156157ba5750805190602001fd

Block Uncle Number Difficulty Gas Used Reward
View All Uncles
Loading...
Loading
Loading...
Loading
Loading...
Loading

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.