S Price: $0.067388 (+2.43%)
Gas: 55 Gwei

Contract

0x85bBA6f69055e1Cd712745BD8566807FD875D543

Overview

S Balance

Sonic LogoSonic LogoSonic Logo0 S

S Value

$0.00

More Info

Private Name Tags

Multichain Info

No addresses found
Transaction Hash
Block
From
To
Report525745112025-10-30 7:27:4988 days ago1761809269IN
0x85bBA6f6...FD875D543
0 S0.016026850
Rebalance Strate...525741492025-10-30 7:25:0188 days ago1761809101IN
0x85bBA6f6...FD875D543
0 S0.1104859556.42857142
Set Target Ltv525741492025-10-30 7:25:0188 days ago1761809101IN
0x85bBA6f6...FD875D543
0 S0.0030648656.42857142
Rebalance Strate...525739022025-10-30 7:23:0188 days ago1761808981IN
0x85bBA6f6...FD875D543
0 S0.1546193550
Set Target Ltv525739022025-10-30 7:23:0188 days ago1761808981IN
0x85bBA6f6...FD875D543
0 S0.002715750
Rebalance Strate...525730712025-10-30 7:17:0688 days ago1761808626IN
0x85bBA6f6...FD875D543
0 S0.154502250
Set Target Ltv525730712025-10-30 7:17:0688 days ago1761808626IN
0x85bBA6f6...FD875D543
0 S0.002587150
Rebalance Strate...525716972025-10-30 7:07:0988 days ago1761808029IN
0x85bBA6f6...FD875D543
0 S0.145178650
Rebalance Strate...525716972025-10-30 7:07:0988 days ago1761808029IN
0x85bBA6f6...FD875D543
0 S0.1548443550
Rebalance Strate...525715552025-10-30 7:06:1188 days ago1761807971IN
0x85bBA6f6...FD875D543
0 S0.1439766550
Rebalance Strate...525715552025-10-30 7:06:1188 days ago1761807971IN
0x85bBA6f6...FD875D543
0 S0.154610350
Rebalance Strate...525714112025-10-30 7:05:0788 days ago1761807907IN
0x85bBA6f6...FD875D543
0 S0.144270750
Rebalance Strate...525714112025-10-30 7:05:0788 days ago1761807907IN
0x85bBA6f6...FD875D543
0 S0.1439520550
Rebalance Strate...525714112025-10-30 7:05:0788 days ago1761807907IN
0x85bBA6f6...FD875D543
0 S0.143990750
Rebalance Strate...525714112025-10-30 7:05:0788 days ago1761807907IN
0x85bBA6f6...FD875D543
0 S0.1545689550
Rebalance Strate...525710092025-10-30 7:01:5588 days ago1761807715IN
0x85bBA6f6...FD875D543
0 S0.1545729550
Rebalance Strate...525706892025-10-30 6:59:2388 days ago1761807563IN
0x85bBA6f6...FD875D543
0 S0.154867850
Rebalance Strate...525698182025-10-30 6:52:3188 days ago1761807151IN
0x85bBA6f6...FD875D543
0 S0.2029637651.5
Rebalance Strate...525693232025-10-30 6:48:4488 days ago1761806924IN
0x85bBA6f6...FD875D543
0 S0.2042450855
Rebalance Strate...525692332025-10-30 6:48:0388 days ago1761806883IN
0x85bBA6f6...FD875D543
0 S0.2046551155
Rebalance Strate...524212582025-10-29 12:36:4489 days ago1761741404IN
0x85bBA6f6...FD875D543
0 S0.1745315550
Rebalance Strate...524206032025-10-29 12:31:5289 days ago1761741112IN
0x85bBA6f6...FD875D543
0 S0.161333750
Deposit Idle Str...524027952025-10-29 10:22:0489 days ago1761733324IN
0x85bBA6f6...FD875D543
0 S0.141479150
Rebalance Strate...522092062025-10-28 10:47:0290 days ago1761648422IN
0x85bBA6f6...FD875D543
0 S0.086086850
Rebalance Strate...521020122025-10-27 21:26:1791 days ago1761600377IN
0x85bBA6f6...FD875D543
0 S0.0829424550
View all transactions

Latest 1 internal transaction

Advanced mode:
Parent Transaction Hash Block From To
464139722025-09-10 19:45:13138 days ago1757533513  Contract Creation0 S
Cross-Chain Transactions
Loading...
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
CeresLeveragedStrategy

Compiler Version
v0.8.28+commit.7893614a

Optimization Enabled:
Yes with 200 runs

Other Settings:
cancun EvmVersion
File 1 of 74 : CeresLeveragedStrategy.sol
// SPDX-License-Identifier: AGPL-3.0
pragma solidity 0.8.28;

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

import {Math} from "@openzeppelin/contracts/utils/math/Math.sol";
import {BaseStrategy} from "@tokenized-strategy/BaseStrategy.sol";
import {TokenizedStrategyStorageAccess} from "./TokenizedStrategyStorageAccess.sol";

// Pendle
import {IPMarket, IStandardizedYield, IPPrincipalToken} from "@pendle/core-v2/contracts/interfaces/IPMarket.sol";
import {PendlePYOracleLib} from "@pendle/core-v2/contracts/oracles/PtYtLpOracle/PendlePYOracleLib.sol";

// Silo
import {ISilo} from "silo-core/contracts/interfaces/ISilo.sol";
import {ISiloOracle} from "silo-core/contracts/interfaces/ISiloOracle.sol";

import {ISiloConfig} from "silo-core/contracts/interfaces/ISiloConfig.sol";
import {ISiloLens} from "silo-core/contracts/SiloLens.sol";
import {IERC3156FlashBorrower} from "silo-core/contracts/interfaces/IERC3156FlashBorrower.sol";

import {LibError} from "./libraries/LibError.sol";
import {ICeresSwapper} from "./interfaces/ICeresSwapper.sol";

contract CeresLeveragedStrategy is BaseStrategy, TokenizedStrategyStorageAccess, IERC3156FlashBorrower {
    using Math for uint256;
    using SafeERC20 for IERC20;

    // Pendle config
    address public immutable PENDLE_MARKET;
    address public immutable PT;

    // Silo
    address public immutable SILO_CONFIG;
    address public immutable DEPOSIT_SILO;
    address public immutable BORROW_SILO;
    address public immutable SILO_BORROW_TOKEN;
    uint8 public immutable BORROW_TOKEN_DECIMALS;
    uint8 public immutable PT_TOKEN_DECIMALS;
    ISilo.CollateralType public immutable SILO_COLLATERAL_TYPE;

    ICeresSwapper public ceresSwapper;

    // Constants
    uint256 private constant MAX_BPS = 10_000;
    address private constant SILO_LENS_SONIC = 0x925D5466d4D5b01995E20e1245924aDa6415126a;
    uint256 private constant WAD = 1e18;
    uint256 private constant MIN_TWAP_DURATION = 5 minutes;
    uint256 private constant MAX_SLIPPAGE_LIMIT = 500; // 5% max slippage limit
    uint256 private constant DEFAULT_LTV_BUFFER = 5 * 1e16; // 5% buffer (0.05), 1e18 = 1 (100%)

    // Strategy config with default values
    uint256 public configTargetLtv = 50 * 1e16; // 50% LTV by default, 1e18 = 1 (100%)
    uint256 public configMaxSlippageBps = 100; // Maximum slippage in basis points, 1% = 100 bps
    uint32 public configTwapDuration = 30 minutes; // Default to a TWAP duration of 30 mins
    uint256 public depositLimit = type(uint256).max; // Default value
    uint256 public withdrawLimit = type(uint256).max; // Default value

    //Events
    event LossOnWithdrawal(uint256 lossAmount);
    event FlashLoan(uint256 amountRepaid);
    event StrategyRebalance();
    event MaxSlippageSet(uint256 slippage);
    event TargetLtvSet(uint256 targetLtv);
    event SwapperSet(address updatedSwapper);
    event DepositLimitSet(uint256);
    event WithdrawLimitSet(uint256);
    event TwapDurationSet(uint32);
    event StrategyFundsDeallocated();
    event TokensRecovered(address indexed token, uint256 amount);
    event DepositIdleAssets(uint256 assetAmount, uint256 ptAmount, uint256 borrowTokenBalance);

    /// @notice Constructor for the Strategy contract.
    /// @param _asset The address of the underlying asset token.
    /// @param _name The name of the strategy.
    /// @param _pendleMarket The address of the Pendle market contract.
    /// @param _siloMarketConfig The address of the Silo market config contract.
    constructor(
        address _asset,
        string memory _name,
        address _ceresSwapper,
        address _pendleMarket,
        address _siloMarketConfig,
        bool isCollateralProtected
    ) BaseStrategy(_asset, _name) {
        if (_ceresSwapper == address(0) || _pendleMarket == address(0) || _siloMarketConfig == address(0)) {
            revert LibError.InvalidAddress();
        }

        // Pendle
        _validatePendleMarket(_pendleMarket);
        PENDLE_MARKET = _pendleMarket;
        (, IPPrincipalToken ptToken, ) = IPMarket(PENDLE_MARKET).readTokens();
        PT = address(ptToken);

        // Silo
        (DEPOSIT_SILO, BORROW_SILO) = _validateSiloMarket(_siloMarketConfig);
        SILO_CONFIG = _siloMarketConfig;
        SILO_BORROW_TOKEN = ISilo(BORROW_SILO).asset();
        BORROW_TOKEN_DECIMALS = IERC20Metadata(SILO_BORROW_TOKEN).decimals();
        PT_TOKEN_DECIMALS = IERC20Metadata(PT).decimals();

        // Asset and Pendle PT token decimals should be the same
        if (IERC20Metadata(_asset).decimals() != PT_TOKEN_DECIMALS) {
            revert LibError.InvalidPendleMarket();
        }

        if (isCollateralProtected) {
            SILO_COLLATERAL_TYPE = ISilo.CollateralType.Protected;
        } else {
            SILO_COLLATERAL_TYPE = ISilo.CollateralType.Collateral;
        }

        ceresSwapper = ICeresSwapper(_ceresSwapper);
        _setSwapperApprovals();
    }

    /*//////////////////////////////////////////////////////////////
                    External/Public Functions
    //////////////////////////////////////////////////////////////*/

    /// @notice Deposits idle asset tokens to get Pendle PT and supply to Silo
    function depositIdleStrategyAssets() external nonReentrant onlyKeepers {
        _depositIdleStrategyAssets();
    }

    /// @notice Rebalances the strategy using a flash loan with specified amount and direction.
    /// @param flashLoanAmount The amount for the flash loan.
    /// @param isLeverageUp True if leveraging up, false if leveraging down.
    /// @param shouldDepositIdleAssets If true, checks all token balances and deposits assets before rebalancing
    /// @param swapData External aggregator swap data
    function rebalanceStrategy(
        uint256 flashLoanAmount,
        bool isLeverageUp,
        bool shouldDepositIdleAssets,
        bytes calldata swapData
    ) external nonReentrant onlyKeepers {
        if (TokenizedStrategy.isShutdown()) {
            _deallocateAllFunds();
        } else {
            if (shouldDepositIdleAssets) {
                _depositIdleStrategyAssets();
            }
            _rebalanceStrategy(flashLoanAmount, isLeverageUp, swapData);
        }
    }

    /// @notice Rebalances the strategy by calculating the required flash loan amount for leveraging up or down.
    /// @dev This function is called by keepers to maintain the target LTV.
    /// @param ltvBuffer The buffer to apply when checking if rebalancing is needed.
    /// @param shouldDepositIdleAssets If true, checks all token balances and deposits assets before rebalancing
    function rebalanceStrategy(uint256 ltvBuffer, bool shouldDepositIdleAssets) external nonReentrant onlyKeepers {
        if (TokenizedStrategy.isShutdown()) {
            _deallocateAllFunds();
        } else {
            if (shouldDepositIdleAssets) {
                _depositIdleStrategyAssets();
            }
            _rebalanceStrategy(ltvBuffer);
        }
    }

    /// @notice Handles the flash loan callback from the Silo protocol.
    /// The function cannot be marked as nonReentrant, as during callback,
    /// the reentrant flag will already be set by `rebalanceStrategy` function
    /// Additional validations on the contract initiator to be the strategy contract,
    /// validations on caller address and the token are in place
    /// @param initiator The address that initiated the flash loan.
    /// @param token The address of the token that was flash loaned.
    /// @param amount The amount of the token that was flash loaned.
    /// @param fee The fee for the flash loan.
    /// @param data Additional data passed to the flash loan callback.
    /// @return bytes32 The keccak256 hash of "ERC3156FlashBorrower.onFlashLoan".
    function onFlashLoan(
        address initiator,
        address token,
        uint256 amount,
        uint256 fee,
        bytes calldata data
    ) external returns (bytes32) {
        if (msg.sender != BORROW_SILO) revert LibError.InvalidFlashLoanCaller();
        if (initiator != address(this)) revert LibError.InvalidFlashLoanInitiator();
        if (token != SILO_BORROW_TOKEN) revert LibError.InvalidFlashLoanToken();

        // Decode the data
        (uint256 flashLoanAmount, bool leverageUp, bytes memory swapData) = abi.decode(data, (uint256, bool, bytes));

        if (flashLoanAmount != amount) revert LibError.InvalidFlashLoanAmount();
        uint256 flashLoanAmountWithFee = amount + fee;

        if (leverageUp) {
            _leverageUpOnFlashLoan(amount, flashLoanAmountWithFee, swapData);
        } else {
            _leverageDownOnFlashLoan(amount, flashLoanAmountWithFee, swapData);
        }

        // Repay flash loan
        emit FlashLoan(flashLoanAmountWithFee);
        IERC20(SILO_BORROW_TOKEN).forceApprove(BORROW_SILO, flashLoanAmountWithFee);
        return keccak256("ERC3156FlashBorrower.onFlashLoan");
    }

    /// @notice Rescues tokens from the strategy contract.
    /// @dev This function allows the emergency authorized address to rescue tokens that are not part of the strategy.
    /// @param _token The address of the token to rescue.
    function rescueTokens(address _token) external onlyEmergencyAuthorized {
        // Rescuing tokens is allowed only for tokens that are not part of the strategy
        if (_token == address(asset) || _token == PT || _token == SILO_BORROW_TOKEN) {
            revert LibError.InvalidToken();
        }
        uint256 amount = IERC20(_token).balanceOf(address(this));
        IERC20(_token).safeTransfer(msg.sender, amount);
        emit TokensRecovered(_token, amount);
    }

    ///////////////////// Permissioned Setter Functions ///////////////////

    /// @notice Sets the deposit limit for the strategy.
    /// @param _depositLimit The new deposit limit.
    function setDepositLimit(uint256 _depositLimit) external onlyManagement {
        depositLimit = _depositLimit;
        emit DepositLimitSet(_depositLimit);
    }

    /// @notice Sets the withdrawal limit for the strategy.
    /// @param _withdrawLimit The new withdrawal limit.
    function setWithdrawLimit(uint256 _withdrawLimit) external onlyManagement {
        withdrawLimit = _withdrawLimit;
        emit WithdrawLimitSet(_withdrawLimit);
    }

    /// @notice Sets the maximum slippage in basis points for swaps.
    /// @param _maxSlippageBps The maximum slippage in basis points (e.g., 100 for 1%).
    function setMaxSlippageBps(uint256 _maxSlippageBps) external onlyManagement {
        if (_maxSlippageBps > MAX_SLIPPAGE_LIMIT) revert LibError.AboveMaxValue(MAX_SLIPPAGE_LIMIT);
        configMaxSlippageBps = _maxSlippageBps;
        emit MaxSlippageSet(_maxSlippageBps);
    }

    /// @notice Sets the target Loan-to-Value (LTV) for the strategy.
    /// @dev Keeper (and management) can call this function to update the LTV after rebalance
    /// @param _targetLtv The target LTV in 1e18 format (e.g., 50 * 1e16 for 50%).
    function setTargetLtv(uint256 _targetLtv) external onlyKeepers {
        // Target LTV should be less than WAD and maxLtv allowed
        if (_targetLtv >= WAD) revert LibError.AboveMaxValue(WAD);

        uint256 maxLtv = getMaxLtvSilo();
        if (_targetLtv + DEFAULT_LTV_BUFFER > maxLtv) revert LibError.AboveMaxValue(maxLtv);
        configTargetLtv = _targetLtv;
        emit TargetLtvSet(_targetLtv);
    }

    /// @notice Sets the address of the swapper contract used for token swaps.
    /// @param _swapper The address of the swapper contract.
    function setSwapper(address _swapper) external onlyManagement {
        if (_swapper == address(0)) revert LibError.InvalidAddress();
        // Revoke approvals to the previous swapper
        _revokeSwapperApprovals();

        // Update swapper address and set approvals
        ceresSwapper = ICeresSwapper(_swapper);
        _setSwapperApprovals();

        emit SwapperSet(_swapper);
    }

    /// @notice Sets approvals to the swapper contract for token swaps.
    function setSwapperApprovals() external onlyManagement {
        _setSwapperApprovals();
    }

    /// @notice Revoke approvals from the swapper contract
    function revokeSwapperApprovals() external onlyManagement {
        _revokeSwapperApprovals();
    }

    /// @notice Sets the TWAP duration for oracle price.
    /// @param _twapDuration Duration to use for the Time Weighted Average Price (TWAP) of oracle
    function setTwapDuration(uint32 _twapDuration) external onlyManagement {
        if (_twapDuration < MIN_TWAP_DURATION) revert LibError.InvalidValue();
        configTwapDuration = _twapDuration;
        emit TwapDurationSet(_twapDuration);
    }

    //////////////////////// Getter Functions //////////////////////

    /// @notice Returns the available deposit limit for the strategy.
    /// @return Deposit limit.
    function availableDepositLimit(address /* _owner */) public view override returns (uint256) {
        return depositLimit;
    }

    /// @notice Returns the available withdrawal limit for the strategy.
    /// @return Withdrawal limit.
    function availableWithdrawLimit(address /* _owner */) public view override returns (uint256) {
        return withdrawLimit;
    }

    /// @notice Gets the maximum allowed Loan-to-Value (LTV) for the deposit silo.
    /// @return maxLtv The maximum LTV in 1e18 format.
    function getMaxLtvSilo() public view returns (uint256 maxLtv) {
        // Max LTV is calculated using the depositSilo, it is 0 for borrowSilo
        maxLtv = ISiloLens(SILO_LENS_SONIC).getMaxLtv(ISilo(DEPOSIT_SILO));
    }

    /// @notice Gets the current Loan-to-Value (LTV) of the strategy's position in the borrow silo.
    /// @return positionLtv The current LTV in 1e18 format.
    function getPositionLtv() public view returns (uint256 positionLtv) {
        positionLtv = ISiloLens(SILO_LENS_SONIC).getLtv(ISilo(BORROW_SILO), address(this));
    }

    /// @notice Gets the collateral balance of the strategy in the deposit silo.
    /// @return collateralBalance The collateral balance in Pendle PT units.
    function getPositionCollateral() public view returns (uint256 collateralBalance) {
        return _getSiloCollateralBalance();
    }

    /// @notice Gets the debt balance of the strategy in the borrow silo.
    /// @return debtBalance The debt balance in Silo borrow token units.
    function getPositionDebt() public view returns (uint256 debtBalance) {
        return _getSiloDebtBalance();
    }

    /// @notice Gets the net real asset balance of the strategy without looping,
    // The returned value is the net value in assets of the strategy
    /// @return totalAssets Value of the strategy in asset token
    function getRealAssetBalance()
        external
        view
        returns (uint256 totalAssets, uint256 totalCollateral, uint256 totalDebt)
    {
        return _getRealAssetBalance();
    }

    /// @notice Gets the price of the collateral token (PT) in terms of the borrow token from the Silo oracle.
    /// @return price The price of the collateral token in borrow token units (1e18 format).
    function getSiloCollateralTokenPrice() external view returns (uint256 price) {
        return _getSiloCollateralTokenPrice();
    }

    /// @notice Converts an amount of the strategy's asset to Pendle PT tokens based on the current rate.
    /// @param assetAmount The amount of strategy asset tokens to convert.
    /// @return ptAmount The equivalent amount of Pendle PT tokens.
    function convertAssetToPt(uint256 assetAmount) external view returns (uint256 ptAmount) {
        return _convertAssetToPt(assetAmount);
    }

    /// @notice Converts an amount of Pendle PT tokens to the strategy's asset based on the current rate.
    /// @param ptAmount The amount of Pendle PT tokens to convert.
    /// @return assetAmount The equivalent amount of strategy asset tokens.
    function convertPtToAsset(uint256 ptAmount) external view returns (uint256 assetAmount) {
        return _convertPtToAsset(ptAmount);
    }

    /// @notice Converts an amount of Silo borrow tokens to Pendle PT tokens based on the current price.
    /// @param borrowTokenAmount The amount of Silo borrow tokens to convert.
    /// @return ptAmount The equivalent amount of Pendle PT tokens.
    function convertBorrowTokenToPt(uint256 borrowTokenAmount) external view returns (uint256 ptAmount) {
        return _convertBorrowTokenToPt(borrowTokenAmount);
    }

    /// @notice Converts an amount of Pendle PT tokens to Silo borrow tokens based on the current price.
    /// @param ptAmount The amount of Pendle PT tokens to convert.
    /// @return borrowTokenAmount The equivalent amount of Silo borrow tokens.
    function convertPtToBorrowToken(uint256 ptAmount) external view returns (uint256 borrowTokenAmount) {
        return _convertPtToBorrowToken(ptAmount);
    }

    /// @notice Converts an amount of Silo Borrow tokens to strategy asset tokens based on the current price.
    /// @param tokenAmount The amount of SiloBorrowToken to convert.
    /// @return assetAmount The equivalent amount of asset tokens.
    function convertBorrowTokenToAsset(uint256 tokenAmount) external view returns (uint256 assetAmount) {
        return _convertBorrowTokenToAsset(tokenAmount);
    }

    /// @notice Converts an amount of strategy Asset tokens to Silo borrow tokens based on the current price.
    /// @param assetAmount The amount of asset tokens to convert to SiloBorrowToken.
    /// @return borrowTokenAmount The equivalent amount of Silo borrow tokens (SILO_BORROW_TOKEN).
    function convertAssetToBorrowToken(uint256 assetAmount) external view returns (uint256 borrowTokenAmount) {
        return _convertAssetToBorrowToken(assetAmount);
    }

    /// @notice Calculates the required flash loan amount and direction (leverage up or down) for rebalancing.
    /// @param ltvBuffer The buffer to apply when calculating FL amount
    /// @return flashLoanAmount The calculated amount for the flash loan.
    /// @return isLeverageUp True if leveraging up, false if leveraging down.
    function calculateFlashLoanAmount(
        uint256 ltvBuffer
    ) external view returns (uint256 flashLoanAmount, bool isLeverageUp) {
        return _calculateFlashLoanAmount(ltvBuffer);
    }

    /*//////////////////////////////////////////////////////////////
                    Internal Yearn override functions
    //////////////////////////////////////////////////////////////*/

    /// @notice Funds deposited to the strategy are deployed during rebalance
    /// This approach is used to avoid swaps on user deposits
    function _deployFunds(uint256 /* _amount */) internal override {}

    /// @notice Attempts to free a specified amount of the strategy's asset from the yield source.
    /// @dev This function is an internal override from the BaseStrategy.
    /// @param _amount The amount of 'asset' to be freed.
    function _freeFunds(uint256 _amount) internal override {
        uint256 amountToWithdrawInPt = _convertAssetToPt(_amount);
        uint256 ptBalance = _getSelfBalance(PT);

        // If strategy already has enough PT tokens, no need to withdraw from Silo
        if (ptBalance >= amountToWithdrawInPt) {
            // Swap PT to asset
            _swapPendlePtForAsset(amountToWithdrawInPt);
            return;
        } else {
            amountToWithdrawInPt -= ptBalance;
        }

        (uint256 totalAssets, , uint256 totalDebt) = _getRealAssetBalance();

        // If amount to withdraw is more than totalAssets, set amount to totalAssets
        if (_amount > totalAssets) {
            _amount = totalAssets;
        }

        uint256 targetDebt = (_convertAssetToBorrowToken(totalAssets - _amount) * configTargetLtv) /
            (WAD - configTargetLtv);

        // If the updated debt is less than the current debt,
        // we need wind-down the position to repay debt to Silo
        if (targetDebt < totalDebt) {
            uint256 repayAmount = totalDebt - targetDebt;
            bytes memory flashLoanData = abi.encode(repayAmount, false, "");

            // The flashloan is used to repay the Silo debt and reduce the LTV of the position
            // This is needed to ensure that the position is healthy and can be unwound safely
            // After flashloan, the strategy needs to withdraw collateral from Silo and swap the
            // received PT tokens to asset
            ISilo(BORROW_SILO).flashLoan(this, SILO_BORROW_TOKEN, repayAmount, flashLoanData);
        }

        // If the max withdraw amount is less than the withdraw amount, withdraw maxWithdrawAmount and book a loss
        uint256 maxWithdraw = ISilo(DEPOSIT_SILO).maxWithdraw(address(this), SILO_COLLATERAL_TYPE);
        if (amountToWithdrawInPt > maxWithdraw) {
            amountToWithdrawInPt = maxWithdraw;
        }

        // Withdraw and swap the required PT tokens to asset
        _withdrawCollateralFromSilo(amountToWithdrawInPt);
        uint256 assetsReceived = _swapPendlePtForAsset(_getSelfBalance(PT));

        if (assetsReceived < _amount) {
            emit LossOnWithdrawal(_amount - assetsReceived);
        }
    }

    /**
     * @dev Internal function to harvest all rewards, redeploy any idle
     * funds and return an accurate accounting of all funds currently
     * held by the Strategy.
     * @return _totalAssets A trusted and accurate account for the total
     * amount of 'asset' the strategy currently holds including idle funds.
     */
    function _harvestAndReport() internal override returns (uint256 _totalAssets) {
        if (_isLtvOutOfRange()) {
            _depositIdleStrategyAssets();
            _rebalanceStrategy(DEFAULT_LTV_BUFFER);
        }

        (_totalAssets, , ) = _getRealAssetBalance();
    }

    /// @notice Rebalances the strategy
    function _tend(uint256 /* _totalIdle */) internal override {
        _rebalanceStrategy(DEFAULT_LTV_BUFFER);
    }

    /// @notice Checks if the strategy needs to be tended (rebalanced).
    /// @return True if tending is required, false otherwise.
    function _tendTrigger() internal view override returns (bool) {
        // _tend needs to be triggered if the strategy LTV is outside configured range
        return _isLtvOutOfRange();
    }

    /// @notice Handles emergency withdrawals of assets from the strategy during shutdown.
    /// @dev This function is an internal override from the BaseStrategy.
    /// @param _amount The amount of 'asset' to withdraw.
    function _emergencyWithdraw(uint256 _amount) internal override {
        if (_amount == type(uint256).max) {
            _deallocateAllFunds();
            return;
        }

        _freeFunds(_amount);
    }

    /*//////////////////////////////////////////////////////////////
            Internal Functions with external interactions
    //////////////////////////////////////////////////////////////*/

    //////////////////////// Silo Functions ////////////////////////

    /// @notice Supplies a specified amount of Pendle PT tokens as collateral to the deposit silo.
    /// @param amount The amount of Pendle PT tokens to supply.
    function _supplyCollateralToSilo(uint256 amount) internal {
        IERC20(PT).forceApprove(DEPOSIT_SILO, amount);
        ISilo(DEPOSIT_SILO).deposit(amount, address(this), SILO_COLLATERAL_TYPE);
    }

    /// @notice Borrows a specified amount of tokens from the borrow silo.
    /// @param amount The amount of tokens to borrow.
    function _borrowFromSilo(uint256 amount) internal {
        ISilo(BORROW_SILO).borrow(amount, address(this), address(this));
    }

    /// @notice Repays a specified amount of debt to the borrow silo.
    /// @param repayAmount The amount of debt to repay.
    /// @return amountRepaid The actual amount of debt repaid.
    function _repaySiloDebt(uint256 repayAmount) internal returns (uint256 amountRepaid) {
        // Check the max loan amount of Silo
        uint256 existingBorrow = ISiloLens(SILO_LENS_SONIC).getBorrowAmount(ISilo(BORROW_SILO), address(this));
        if (repayAmount > existingBorrow) {
            repayAmount = existingBorrow;
        }

        if (repayAmount == 0) {
            // No debt to repay, return
            return 0;
        }

        IERC20(SILO_BORROW_TOKEN).forceApprove(BORROW_SILO, repayAmount);
        ISilo(BORROW_SILO).repay(repayAmount, address(this));
        return repayAmount;
    }

    /// @notice Withdraws a specified amount of collateral from the deposit silo.
    /// The function reverts if the withdrawal results in the strategy position being insolvent
    /// @param collateralAmount The amount of collateral to withdraw in Pendle PT units.
    /// @return amountWithdrawn The actual amount of collateral withdrawn.
    function _withdrawCollateralFromSilo(uint256 collateralAmount) internal returns (uint256 amountWithdrawn) {
        uint256 collateralBalance = _getSiloCollateralBalance();
        if (collateralAmount > collateralBalance) {
            // If the requested amount is more than the available collateral, withdraw all available collateral
            collateralAmount = collateralBalance;
        }

        ISilo(DEPOSIT_SILO).withdraw(collateralAmount, address(this), address(this), SILO_COLLATERAL_TYPE);
        return collateralAmount;
    }

    //////////////////////// Swapper Functions //////////////////////

    /// @notice Swap Pendle Asset tokens for Pendle PT tokens
    /// @dev NOTE: The strategy asset token is not the same as the pendle asset token
    /// For Ceres strategy, Pendle yieldToken is the strategy asset token
    function _swapAssetForPendlePt(uint256 assetAmount) internal returns (uint256) {
        uint256 minPtOut = _getSlippageAdjustedAmount(_convertAssetToPt(assetAmount));

        uint256 tokensReceived = ceresSwapper.swapFrom(
            address(asset),
            PT,
            assetAmount,
            minPtOut,
            address(this),
            abi.encode(PENDLE_MARKET)
        );

        if (tokensReceived < minPtOut) revert LibError.SlippageLimitExceeded();
        return tokensReceived;
    }

    /// @notice Swap Pendle Asset tokens for Pendle PT tokens
    /// @dev NOTE: The strategy asset token is not the same as the pendle asset token
    /// For Ceres strategy, Pendle yieldToken is the strategy asset token
    function _swapPendlePtForAsset(uint256 ptAmount) internal returns (uint256) {
        uint256 minAssetOut = _getSlippageAdjustedAmount(_convertPtToAsset(ptAmount));

        uint256 tokensReceived = ceresSwapper.swapFrom(
            PT,
            address(asset),
            ptAmount,
            minAssetOut,
            address(this),
            abi.encode(PENDLE_MARKET)
        );

        if (tokensReceived < minAssetOut) revert LibError.SlippageLimitExceeded();
        return tokensReceived;
    }

    /// @notice Swaps borrowed tokens for the strategy's asset tokens.
    /// @param _amountIn The amount of borrowed tokens to swap.
    /// @return The amount of strategy asset tokens received.
    function _swapBorrowTokenToAsset(uint256 _amountIn) internal returns (uint256) {
        uint256 minAmountOut = _getSlippageAdjustedAmount(_convertBorrowTokenToAsset(_amountIn));

        uint256 tokensReceived = ceresSwapper.swapFrom(
            SILO_BORROW_TOKEN,
            address(asset),
            _amountIn,
            minAmountOut,
            address(this),
            ""
        );
        if (tokensReceived < minAmountOut) revert LibError.SlippageLimitExceeded();
        return tokensReceived;
    }

    /*//////////////////////////////////////////////////////////////
                    Internal functions for conversions
    //////////////////////////////////////////////////////////////*/

    /// @notice Converts an amount of the strategy's asset to Pendle PT tokens based on the current rate.
    /// @param assetAmount The amount of strategy asset tokens to convert.
    /// @return ptAmount The equivalent amount of Pendle PT tokens.
    function _convertAssetToPt(uint256 assetAmount) internal view returns (uint256 ptAmount) {
        if (assetAmount > 0) {
            // PT to SY(Strategy asset) rate is in 18 decimals.
            // For example, 1 PT-wstkscUSD => 0.98 wstkscUSD (formatted) or 0.98 * 10 ** 18 (raw)
            uint256 ptPriceInAsset = PendlePYOracleLib.getPtToSyRate(IPMarket(PENDLE_MARKET), configTwapDuration);
            if (ptPriceInAsset == 0) revert LibError.InvalidPrice();

            ptAmount = assetAmount.mulDiv(WAD, ptPriceInAsset, Math.Rounding.Floor);
        }
    }

    /// @notice Converts an amount of Pendle PT tokens to the strategy's asset based on the current rate.
    /// @param ptAmount The amount of Pendle PT tokens to convert.
    /// @return assetAmount The equivalent amount of strategy asset tokens.
    function _convertPtToAsset(uint256 ptAmount) internal view returns (uint256 assetAmount) {
        if (ptAmount > 0) {
            // PT to SY(Strategy asset) rate is in 18 decimals.
            // For example, 1 PT-wstkscUSD => 0.98 wstkscUSD (formatted) or 0.98 * 10 ** 18 (raw)
            uint256 ptPriceInAsset = PendlePYOracleLib.getPtToSyRate(IPMarket(PENDLE_MARKET), configTwapDuration);
            if (ptPriceInAsset == 0) revert LibError.InvalidPrice();

            assetAmount = ptAmount.mulDiv(ptPriceInAsset, WAD, Math.Rounding.Floor);
        }
    }

    /// @notice Converts an amount of Silo borrow tokens to Pendle PT tokens based on the current price.
    /// @param borrowTokenAmount The amount of Silo borrow tokens to convert.
    /// @return ptAmount The equivalent amount of Pendle PT tokens.
    function _convertBorrowTokenToPt(uint256 borrowTokenAmount) internal view returns (uint256 ptAmount) {
        if (borrowTokenAmount > 0) {
            uint256 collateralTokenPrice = _getSiloCollateralTokenPrice();
            ptAmount =
                (borrowTokenAmount * 10 ** PT_TOKEN_DECIMALS * WAD) /
                (collateralTokenPrice * 10 ** BORROW_TOKEN_DECIMALS);
        }
    }

    /// @notice Converts an amount of Pendle PT tokens to Silo borrow tokens based on the current price.
    /// @param ptAmount The amount of Pendle PT tokens to convert.
    /// @return borrowTokenAmount The equivalent amount of Silo borrow tokens.
    function _convertPtToBorrowToken(uint256 ptAmount) internal view returns (uint256 borrowTokenAmount) {
        if (ptAmount > 0) {
            uint256 collateralTokenPrice = _getSiloCollateralTokenPrice();
            borrowTokenAmount =
                (ptAmount * collateralTokenPrice * 10 ** BORROW_TOKEN_DECIMALS) /
                (WAD * 10 ** PT_TOKEN_DECIMALS);
        }
    }

    /// @notice Converts an amount of Silo Borrow tokens to strategy asset tokens based on the current price.
    /// @param tokenAmount The amount of SiloBorrowToken to convert.
    /// @return assetAmount The equivalent amount of asset tokens.
    function _convertBorrowTokenToAsset(uint256 tokenAmount) internal view returns (uint256 assetAmount) {
        if (tokenAmount > 0) {
            // As direct oracle for Silo Borrow token to strategy Asset is not available
            // convert the tokenAmount in 2 steps: Silo Borrow Token -> PT -> Asset
            uint256 tokenAmountInPt = _convertBorrowTokenToPt(tokenAmount);
            assetAmount = _convertPtToAsset(tokenAmountInPt);
        }
    }

    /// @notice Converts an amount of strategy Asset tokens to Silo borrow tokens based on the current price.
    /// @param assetAmount The amount of asset tokens to convert to SiloBorrowToken.
    /// @return borrowTokenAmount The equivalent amount of Silo borrow tokens (SILO_BORROW_TOKEN).
    function _convertAssetToBorrowToken(uint256 assetAmount) internal view returns (uint256 borrowTokenAmount) {
        if (assetAmount > 0) {
            // As direct oracle for strategy Asset to Silo Borrow token is not available
            // convert the assetAmount in 2 steps: Asset -> PT -> Silo Borrow Token
            uint256 assetAmountInPt = _convertAssetToPt(assetAmount);
            borrowTokenAmount = _convertPtToBorrowToken(assetAmountInPt);
        }
    }

    /// @notice Calculates the slippage-adjusted amount based on the configured maximum slippage.
    /// @param amount The original amount.
    /// @return adjustedAmount The amount adjusted for slippage.
    function _getSlippageAdjustedAmount(uint256 amount) internal view returns (uint256 adjustedAmount) {
        return amount.mulDiv(MAX_BPS - configMaxSlippageBps, MAX_BPS, Math.Rounding.Ceil);
    }

    /*//////////////////////////////////////////////////////////////
                Internal misc. functions and validations
    //////////////////////////////////////////////////////////////*/

    /// @notice Validates the Pendle market configuration.
    /// @param _market The address of the Pendle market contract.
    function _validatePendleMarket(address _market) internal view {
        (IStandardizedYield stdYield, , ) = IPMarket(_market).readTokens();
        address yieldToken = stdYield.yieldToken();

        // Strategy asset must be the same as the Pendle yield token
        // SY-token is a wrapped version of the yield token
        // For example, for PT-sUSDe, yield token is sUSDe, etc.
        // More details here: https://docs.pendle.finance/Developers/Contracts/StandardizedYield#standard-sys
        if (address(asset) != yieldToken) {
            revert LibError.InvalidAsset();
        }
    }

    /// @notice Validates the Silo market configuration and identifies the deposit and borrow silos.
    /// @param _siloMarketConfig The address of the Silo market config contract.
    /// @return depositSiloAddress The address of the deposit silo.
    /// @return borrowSiloAddress The address of the borrow silo.
    function _validateSiloMarket(
        address _siloMarketConfig
    ) internal view returns (address depositSiloAddress, address borrowSiloAddress) {
        (address _silo0, address _silo1) = ISiloConfig(_siloMarketConfig).getSilos();

        if (PT == ISilo(_silo0).asset()) {
            depositSiloAddress = _silo0;
            borrowSiloAddress = _silo1;
        } else if (PT == ISilo(_silo1).asset()) {
            depositSiloAddress = _silo1;
            borrowSiloAddress = _silo0;
        } else {
            revert LibError.InvalidSiloMarket();
        }
    }

    /// @notice Internal function to rebalance the strategy using a flash loan.
    /// @param flashLoanAmount The amount for the flash loan.
    /// @param isLeverageUp True if leveraging up, false if leveraging down.
    function _rebalanceStrategy(uint256 flashLoanAmount, bool isLeverageUp, bytes memory swapData) internal {
        if (flashLoanAmount > 0) {
            // FlashLoan data contains the borrow amount and a flag to indicate that we are leveraging up
            // The flag is used to differentiate between leverage up and leverage down operations
            bytes memory flashLoanData = abi.encode(flashLoanAmount, isLeverageUp, swapData);
            ISilo(BORROW_SILO).flashLoan(this, SILO_BORROW_TOKEN, flashLoanAmount, flashLoanData);
            emit StrategyRebalance();
        }
    }

    /// @notice Rebalances the strategy by calculating the required flash loan amount and direction.
    /// @dev This internal function is called by the public `rebalanceStrategy` function.
    function _rebalanceStrategy(uint256 ltvBuffer) internal {
        (uint256 flashLoanAmount, bool isLeverageUp) = _calculateFlashLoanAmount(ltvBuffer);
        _rebalanceStrategy(flashLoanAmount, isLeverageUp, "");
    }

    /// @notice Calculates the required flash loan amount and direction (leverage up or down) for internal rebalancing.
    /// @return flashLoanAmount The calculated amount for the flash loan.
    /// @return isLeverageUp True if leveraging up, false if leveraging down.
    function _calculateFlashLoanAmount(
        uint256 ltvBuffer
    ) internal view returns (uint256 flashLoanAmount, bool isLeverageUp) {
        (uint256 totalAssets, , uint256 totalDebt) = _getRealAssetBalance();

        // Total max deposit amount for a given LTV is calculated as follows:
        // Max Deposit = Initial Deposit (Actual assets deposited) + Max flash loan amount (flMax)
        // where:
        // Initial Deposit = Total assets balance of the strategy
        // flMax = Total assets * Target LTV / (1 - Target LTV)
        uint256 totalAssetsInBorrowToken = _convertAssetToBorrowToken(totalAssets);

        uint256 maxLoan = (totalAssetsInBorrowToken * configTargetLtv) / (WAD - configTargetLtv);
        uint256 positionLtv = getPositionLtv();

        // Leverage up the position only if the position is below the target LTV range
        if (positionLtv + ltvBuffer < configTargetLtv) {
            return (maxLoan - totalDebt, true);
        } else if (positionLtv > configTargetLtv + ltvBuffer) {
            // Leverage down the position only if the position is above the target LTV range
            return (totalDebt - maxLoan, false);
        } else {
            return (0, false);
        }
    }

    /// @notice Leverages up the position onFlashLoan callback.
    /// @param amount The amount of tokens received on flashLoan
    /// @param amountWithFee The amount of tokens on flashLoan, including the flash loan fee.
    /// @param swapData Optional: Aggregator swap data
    function _leverageUpOnFlashLoan(uint256 amount, uint256 amountWithFee, bytes memory swapData) internal {
        // If aggregator swap data is provided, swap SiloBorrowToken to asset token,
        // else swap using on-chain dex
        uint256 assetsReceived;
        if (swapData.length > 0) {
            uint256 minAmountOut = _getSlippageAdjustedAmount(_convertBorrowTokenToAsset(amount));
            assetsReceived = ceresSwapper.swapUsingAggregator(
                SILO_BORROW_TOKEN,
                address(asset),
                amount,
                minAmountOut,
                address(this),
                swapData
            );
        } else {
            assetsReceived = _swapBorrowTokenToAsset(amount);
        }
        // Swap the asset to Pendle PT tokens
        uint256 ptReceived = _swapAssetForPendlePt(assetsReceived);

        // Supply the PT tokens to Silo
        _supplyCollateralToSilo(ptReceived);

        // Borrow from Silo to repay the flash loan
        _borrowFromSilo(amountWithFee);
    }

    /// @notice Leverages down the position onFlashLoan callback.
    /// @param amount The amount of tokens received on flashLoan
    /// @param amountWithFee The amount of tokens on flashLoan, including the flash loan fee.
    /// @param swapData Optional: Aggregator swap data
    function _leverageDownOnFlashLoan(uint256 amount, uint256 amountWithFee, bytes memory swapData) internal {
        // Repay silo debt
        _repaySiloDebt(amount);

        // Add some buffer to the PT amount for slippage
        uint256 ptAmount = (_convertBorrowTokenToPt(amountWithFee) * (MAX_BPS + configMaxSlippageBps)) / MAX_BPS;

        // Withdraw collateral from Silo
        uint256 amountWithdrawn = _withdrawCollateralFromSilo(ptAmount);

        // Swap the PT tokens to strategy asset
        uint256 assetAmount = _swapPendlePtForAsset(amountWithdrawn);

        // Swap the asset to borrowed token using Aggregator if swapData is provided,
        // else swap using on-chain dex
        if (swapData.length > 0) {
            uint256 minAmountOut = _getSlippageAdjustedAmount(_convertAssetToBorrowToken(assetAmount));
            ceresSwapper.swapUsingAggregator(
                address(asset),
                SILO_BORROW_TOKEN,
                assetAmount,
                minAmountOut,
                address(this),
                swapData
            );
        } else {
            // Swap and receive Exact amount of BORROW_TOKEN to repay the flash loan
            ceresSwapper.swapTo(address(asset), SILO_BORROW_TOKEN, amountWithFee, assetAmount, address(this), "");
        }
    }

    /// @notice Checks if the strategy is out of the configured LTV range.
    /// @return True if the LTV is out of range, false otherwise.
    function _isLtvOutOfRange() internal view returns (bool) {
        uint256 positionLtv = getPositionLtv();

        if (
            TokenizedStrategy.totalAssets() != 0 &&
            (positionLtv + DEFAULT_LTV_BUFFER < configTargetLtv || positionLtv > configTargetLtv + DEFAULT_LTV_BUFFER)
        ) {
            return true;
        }
        return false;
    }

    /// @notice Deposits any idle asset or PT tokens held by the strategy
    /// Swaps any idle asset tokens for Pendle PT tokens and supplies PT tokens to Silo.
    function _depositIdleStrategyAssets() internal {
        // Swap any idle Borrow token balance to assets
        uint256 borrowTokenBalance = _getSelfBalance(SILO_BORROW_TOKEN);
        if (borrowTokenBalance > 0) {
            _swapBorrowTokenToAsset(borrowTokenBalance);
        }

        // Deposit idle assets in the strategy
        uint256 assetBalance = _getSelfBalance(address(asset));
        if (assetBalance > 0) {
            _swapAssetForPendlePt(assetBalance);
        }

        // Deposit PT tokens in the strategy
        uint256 ptBalance = _getSelfBalance(PT);
        if (ptBalance > 0) {
            _supplyCollateralToSilo(ptBalance);
        }
        emit DepositIdleAssets(assetBalance, ptBalance, borrowTokenBalance);
    }

    /// @notice Set approvals for the swapper contract
    function _setSwapperApprovals() internal {
        address swapperAddress = address(ceresSwapper);
        if (swapperAddress == address(0)) return;

        IERC20(address(asset)).forceApprove(swapperAddress, type(uint256).max);
        IERC20(PT).forceApprove(swapperAddress, type(uint256).max);
        IERC20(SILO_BORROW_TOKEN).forceApprove(swapperAddress, type(uint256).max);
    }

    /// @notice Revoke approvals for the swapper contract
    function _revokeSwapperApprovals() internal {
        address swapperAddress = address(ceresSwapper);
        if (swapperAddress == address(0)) return;

        IERC20(address(asset)).forceApprove(swapperAddress, 0);
        IERC20(PT).forceApprove(swapperAddress, 0);
        IERC20(SILO_BORROW_TOKEN).forceApprove(swapperAddress, 0);
    }

    /// @notice Deallocates all funds from the strategy positions and converts them to the strategy asset.
    /// @dev This function completely unwinds the leveraged position by:
    ///      1. Repaying all outstanding debt using a flash loan
    ///      2. Withdrawing all collateral from the Silo deposit pool
    ///      3. Converting all Pendle PT tokens back to the strategy's base asset
    ///      4. Converting any remaining borrow tokens back to the base asset
    /// @dev This function is typically called during strategy migration or shutdown or emergency procedures
    ///      to ensure all positions are closed and funds are liquid.
    /// @dev After execution, the strategy should hold only the base asset tokens with no
    ///      outstanding positions in Silo or Pendle markets.
    function _deallocateAllFunds() internal {
        // Set target LTV to 0
        configTargetLtv = 0;
        uint256 strategyDebt = _getSiloDebtBalance();

        // Repay the entire debt using FlashLoan
        _rebalanceStrategy(strategyDebt, false, "");

        // Withdraw all collateral from the deposit silo
        uint256 collateralBalance = _getSiloCollateralBalance();
        if (collateralBalance > 0) {
            uint256 maxWithdraw = ISilo(DEPOSIT_SILO).maxWithdraw(address(this), SILO_COLLATERAL_TYPE);
            // To account of any remaining dust amount of debt or losses due to slippage during rebalance
            if (collateralBalance > maxWithdraw) {
                collateralBalance = maxWithdraw;
            }
            _withdrawCollateralFromSilo(collateralBalance);
        }

        // Swap all PT tokens to asset
        uint256 ptBalance = _getSelfBalance(PT);
        if (ptBalance > 0) {
            _swapPendlePtForAsset(ptBalance);
        }

        // Swap all Borrow tokens to asset
        uint256 borrowTokenBalance = _getSelfBalance(SILO_BORROW_TOKEN);
        if (borrowTokenBalance > 0) {
            _swapBorrowTokenToAsset(borrowTokenBalance);
        }

        emit StrategyFundsDeallocated();
    }

    /*//////////////////////////////////////////////////////////////
                    Internal getter functions
    //////////////////////////////////////////////////////////////*/

    /// @notice Gets the total real asset balance of the strategy, including assets, PT tokens, and Silo positions.
    /// @return totalAssets The total real asset balance in strategy asset units.
    /// @return totalCollateral Total collateral balance in Pendle PT units that the strategy holds.
    /// Includes deposited collateral + strategy PT balance
    /// @return totalDebt The debt balance of the strategy in SILO_BORROW_TOKEN units.
    /// Includes the Silo Debt of the strategy minus any token balance of the strategy
    function _getRealAssetBalance()
        internal
        view
        returns (uint256 totalAssets, uint256 totalCollateral, uint256 totalDebt)
    {
        // The total real asset balance of the strategy is calculated as follows:
        // Real balance = Asset_Bal + PT_Bal + PT_Deposited + BT_Bal - BT_Debt
        // Where:
        // Asset_Bal = Asset balance of the strategy
        // PT_Bal = PT balance of the strategy (converted to Asset)
        // PT_Deposited = PT tokens deposited to Silo as Collateral (converted to Asset)
        // BT_Bal = Silo Borrow Token balance of the strategy (converted to Asset)
        // BT_Debt = Total Debt (borrowed amount in SiloBorrowToken) (converted to Asset)
        totalCollateral = _getSelfBalance(PT) + _getSiloCollateralBalance();
        uint256 debt = _getSiloDebtBalance();
        uint256 btBalance = _getSelfBalance(SILO_BORROW_TOKEN);

        if (debt > btBalance) {
            totalDebt = debt - btBalance;
            totalAssets =
                _getSelfBalance(address(asset)) +
                _convertPtToAsset(totalCollateral) -
                _convertBorrowTokenToAsset(totalDebt);
        } else {
            // If the strategy has more SiloBorrowToken than debt, we can ignore the debt
            totalDebt = 0;
            totalAssets =
                _getSelfBalance(address(asset)) +
                _convertPtToAsset(totalCollateral) +
                _convertBorrowTokenToAsset(btBalance - debt);
        }
    }

    /// @notice Gets the collateral balance of the strategy in the deposit silo.
    /// @return collateralBalance The collateral balance in Pendle PT units.
    function _getSiloCollateralBalance() internal view returns (uint256 collateralBalance) {
        collateralBalance = ISiloLens(SILO_LENS_SONIC).collateralBalanceOfUnderlying(
            ISilo(DEPOSIT_SILO),
            address(this)
        );
    }

    /// @notice Gets the debt balance of the strategy in the borrow silo.
    /// @return debtBalance The debt balance in Silo borrow token units.
    function _getSiloDebtBalance() internal view returns (uint256 debtBalance) {
        debtBalance = ISiloLens(SILO_LENS_SONIC).debtBalanceOfUnderlying(ISilo(BORROW_SILO), address(this));
    }

    /// @notice Gets the price of the collateral token (PT) in terms of the borrow token from the Silo oracle.
    /// @return collateralTokenPrice The price of the collateral token in borrow token units
    function _getSiloCollateralTokenPrice() internal view returns (uint256 collateralTokenPrice) {
        address depositOracle = ISiloConfig(SILO_CONFIG).getConfig(DEPOSIT_SILO).solvencyOracle;

        // If only one oracle is set, Oracle must return the token's price in the exact decimals of the other token
        // that will not use an oracle. In this case, the Other token price is represented by its token amounts.
        // https://docs.silo.finance/docs/developers/dev-tutorials/oracles/#one-oracle-is-set
        if (depositOracle == address(0)) {
            collateralTokenPrice = 10 ** BORROW_TOKEN_DECIMALS; // Default price for the deposit token
        } else {
            collateralTokenPrice = ISiloOracle(depositOracle).quote(10 ** PT_TOKEN_DECIMALS, PT);
        }

        if (collateralTokenPrice == 0) revert LibError.InvalidPrice();
        return collateralTokenPrice;
    }

    /// @notice Gets the balance of a specific token held by the strategy contract.
    /// @param token The address of the token to check the balance of.
    /// @return The balance of the token held by the strategy.
    function _getSelfBalance(address token) internal view returns (uint256) {
        return IERC20(token).balanceOf(address(this));
    }
}

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

pragma solidity ^0.8.20;

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

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

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

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

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

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

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

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

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

pragma solidity ^0.8.20;

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

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

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

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

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

pragma solidity ^0.8.20;

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

pragma solidity ^0.8.20;

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

        uint256 mLen = m.length;

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

// TokenizedStrategy interface used for internal view delegateCalls.
import {ITokenizedStrategy} from "./interfaces/ITokenizedStrategy.sol";

/**
 * @title YearnV3 Base Strategy
 * @author yearn.finance
 * @notice
 *  BaseStrategy implements all of the required functionality to
 *  seamlessly integrate with the `TokenizedStrategy` implementation contract
 *  allowing anyone to easily build a fully permissionless ERC-4626 compliant
 *  Vault by inheriting this contract and overriding three simple functions.

 *  It utilizes an immutable proxy pattern that allows the BaseStrategy
 *  to remain simple and small. All standard logic is held within the
 *  `TokenizedStrategy` and is reused over any n strategies all using the
 *  `fallback` function to delegatecall the implementation so that strategists
 *  can only be concerned with writing their strategy specific code.
 *
 *  This contract should be inherited and the three main abstract methods
 *  `_deployFunds`, `_freeFunds` and `_harvestAndReport` implemented to adapt
 *  the Strategy to the particular needs it has to generate yield. There are
 *  other optional methods that can be implemented to further customize
 *  the strategy if desired.
 *
 *  All default storage for the strategy is controlled and updated by the
 *  `TokenizedStrategy`. The implementation holds a storage struct that
 *  contains all needed global variables in a manual storage slot. This
 *  means strategists can feel free to implement their own custom storage
 *  variables as they need with no concern of collisions. All global variables
 *  can be viewed within the Strategy by a simple call using the
 *  `TokenizedStrategy` variable. IE: TokenizedStrategy.globalVariable();.
 */
abstract contract BaseStrategy {
    /*//////////////////////////////////////////////////////////////
                            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;

    /*//////////////////////////////////////////////////////////////
                            IMMUTABLES
    //////////////////////////////////////////////////////////////*/

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

    /**
     * @dev This variable is set to address(this) during initialization of each strategy.
     *
     * This can be used to retrieve storage data within the strategy
     * contract as if it were a linked library.
     *
     *       i.e. uint256 totalAssets = TokenizedStrategy.totalAssets()
     *
     * Using address(this) will mean any calls using this variable will lead
     * to a call to itself. Which will hit the fallback function and
     * delegateCall that to the actual TokenizedStrategy.
     */
    ITokenizedStrategy internal immutable TokenizedStrategy;

    /**
     * @notice Used to initialize the strategy on deployment.
     *
     * This will set the `TokenizedStrategy` variable for easy
     * internal view calls to the implementation. As well as
     * initializing the default storage variables based on the
     * parameters and using the deployer for the permissioned roles.
     *
     * @param _asset Address of the underlying asset.
     * @param _name Name the strategy will use.
     */
    constructor(address _asset, string memory _name) {
        asset = ERC20(_asset);

        // Set instance of the implementation for internal use.
        TokenizedStrategy = ITokenizedStrategy(address(this));

        // Initialize the strategy's storage variables.
        _delegateCall(
            abi.encodeCall(
                ITokenizedStrategy.initialize,
                (_asset, _name, msg.sender, msg.sender, msg.sender)
            )
        );

        // Store the tokenizedStrategyAddress at the standard implementation
        // address storage slot so etherscan picks up the interface. This gets
        // stored on initialization and never updated.
        assembly {
            sstore(
                // keccak256('eip1967.proxy.implementation' - 1)
                0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc,
                tokenizedStrategyAddress
            )
        }
    }

    /*//////////////////////////////////////////////////////////////
                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())
            }
        }
    }
}

File 7 of 74 : TokenizedStrategyStorageAccess.sol
// SPDX-License-Identifier: AGPL-3.0
pragma solidity 0.8.28;

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

/// @notice This contract is used to access the storage of TokenizedStrategy variables
/// that are not directly accessible from the strategy contract.
/// The contract duplicates the struct and data from the original TokenizedStrategy contract
/// to expose the `nonReentrant` modifier.
contract TokenizedStrategyStorageAccess {
    /*//////////////////////////////////////////////////////////////
                        STORAGE STRUCT
    //////////////////////////////////////////////////////////////*/

    /**
     * @dev The struct that will hold all the storage data for each strategy
     * that uses this implementation.
     *
     * This replaces all state variables for a traditional contract. This
     * full struct will be initialized on the creation of the strategy
     * and continually updated and read from for the life of the contract.
     *
     * We combine all the variables into one struct to limit the amount of
     * times the custom storage slots need to be loaded during complex functions.
     *
     * Loading the corresponding storage slot for the struct does not
     * load any of the contents of the struct into memory. So the size
     * will not increase memory related gas usage.
     */
    // prettier-ignore
    struct StrategyData {
        // The ERC20 compliant underlying asset that will be
        // used by the Strategy
        ERC20 asset;


        // These are the corresponding ERC20 variables needed for the
        // strategies token that is issued and burned on each deposit or withdraw.
        uint8 decimals; // The amount of decimals that `asset` and strategy use.
        string name; // The name of the token for the strategy.
        uint256 totalSupply; // The total amount of shares currently issued.
        mapping(address => uint256) nonces; // Mapping of nonces used for permit functions.
        mapping(address => uint256) balances; // Mapping to track current balances for each account that holds shares.
        mapping(address => mapping(address => uint256)) allowances; // Mapping to track the allowances for the strategies shares.


        // We manually track `totalAssets` to prevent PPS manipulation through airdrops.
        uint256 totalAssets;


        // Variables for profit reporting and locking.
        // We use uint96 for timestamps to fit in the same slot as an address. That overflows in 2.5e+21 years.
        // I know Yearn moves slowly but surely V4 will be out by then.
        // If the timestamps ever overflow tell the cyborgs still using this code I'm sorry for being cheap.
        uint256 profitUnlockingRate; // The rate at which locked profit is unlocking.
        uint96 fullProfitUnlockDate; // The timestamp at which all locked shares will unlock.
        address keeper; // Address given permission to call {report} and {tend}.
        uint32 profitMaxUnlockTime; // The amount of seconds that the reported profit unlocks over.
        uint16 performanceFee; // The percent in basis points of profit that is charged as a fee.
        address performanceFeeRecipient; // The address to pay the `performanceFee` to.
        uint96 lastReport; // The last time a {report} was called.


        // Access management variables.
        address management; // Main address that can set all configurable variables.
        address pendingManagement; // Address that is pending to take over `management`.
        address emergencyAdmin; // Address to act in emergencies as well as `management`.

        // Strategy Status
        uint8 entered; // To prevent reentrancy. Use uint8 for gas savings.
        bool shutdown; // Bool that can be used to stop deposits into the strategy.
    }

    /*//////////////////////////////////////////////////////////////
                            MODIFIERS
    //////////////////////////////////////////////////////////////*/

    /**
     * @dev Prevents a contract from calling itself, directly or indirectly.
     * Placed over all state changing functions for increased safety.
     */
    modifier nonReentrant() {
        StrategyData storage S = _strategyStorage();

        // On the first call to nonReentrant, `entered` will be false (2)
        // Modified from the original Yearn implementation (Original implementation checks that `S.entered != ENTERED`)
        // The change below checks reentrancy, and also validates that the storage slot is correct.
        // With the original Yearn check, if the storage slot was not correct, it would bypass the check as the value would be the default value of 0.
        // This ensures that the storage slot is correct and that the reentrancy check is valid.
        require(S.entered == NOT_ENTERED, "ReentrancyGuard: reentrant call");

        // Any calls to nonReentrant after this point will fail
        S.entered = ENTERED;

        _;

        // Reset to false (1) once call has finished.
        S.entered = NOT_ENTERED;
    }

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

    /// @notice API version this TokenizedStrategy implements.
    string internal constant API_VERSION = "3.0.4";

    /// @notice Value to set the `entered` flag to during a call.
    uint8 internal constant ENTERED = 2;
    /// @notice Value to set the `entered` flag to at the end of the call.
    uint8 internal constant NOT_ENTERED = 1;

    /**
     * @dev Custom storage slot that will be used to store the
     * `StrategyData` struct that holds each strategies
     * specific storage variables.
     *
     * Any storage updates done by the TokenizedStrategy actually update
     * the storage of the calling contract. This variable points
     * to the specific location that will be used to store the
     * struct that holds all that data.
     *
     * We use a custom string in order to get a random
     * storage slot that will allow for strategists to use any
     * amount of storage in their strategy without worrying
     * about collisions.
     */
    bytes32 internal constant BASE_STRATEGY_STORAGE = bytes32(uint256(keccak256("yearn.base.strategy.storage")) - 1);

    /*//////////////////////////////////////////////////////////////
                            STORAGE GETTER
    //////////////////////////////////////////////////////////////*/

    /**
     * @dev will return the actual storage slot where the strategy
     * specific `StrategyData` struct is stored for both read
     * and write operations.
     *
     * This loads just the slot location, not the full struct
     * so it can be used in a gas efficient manner.
     */
    function _strategyStorage() internal pure returns (StrategyData storage S) {
        // Since STORAGE_SLOT is a constant, we have to put a variable
        // on the stack to access it from an inline assembly block.
        bytes32 slot = BASE_STRATEGY_STORAGE;
        assembly {
            S.slot := slot
        }
    }
}

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

import "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol";
import "./IPPrincipalToken.sol";
import "./IPYieldToken.sol";
import "./IStandardizedYield.sol";
import "./IPGauge.sol";
import "../core/Market/MarketMathCore.sol";

interface IPMarket is IERC20Metadata, IPGauge {
    event Mint(address indexed receiver, uint256 netLpMinted, uint256 netSyUsed, uint256 netPtUsed);

    event Burn(
        address indexed receiverSy,
        address indexed receiverPt,
        uint256 netLpBurned,
        uint256 netSyOut,
        uint256 netPtOut
    );

    event Swap(
        address indexed caller,
        address indexed receiver,
        int256 netPtOut,
        int256 netSyOut,
        uint256 netSyFee,
        uint256 netSyToReserve
    );

    event UpdateImpliedRate(uint256 indexed timestamp, uint256 lnLastImpliedRate);

    event IncreaseObservationCardinalityNext(
        uint16 observationCardinalityNextOld,
        uint16 observationCardinalityNextNew
    );

    function mint(
        address receiver,
        uint256 netSyDesired,
        uint256 netPtDesired
    ) external returns (uint256 netLpOut, uint256 netSyUsed, uint256 netPtUsed);

    function burn(
        address receiverSy,
        address receiverPt,
        uint256 netLpToBurn
    ) external returns (uint256 netSyOut, uint256 netPtOut);

    function swapExactPtForSy(
        address receiver,
        uint256 exactPtIn,
        bytes calldata data
    ) external returns (uint256 netSyOut, uint256 netSyFee);

    function swapSyForExactPt(
        address receiver,
        uint256 exactPtOut,
        bytes calldata data
    ) external returns (uint256 netSyIn, uint256 netSyFee);

    function redeemRewards(address user) external returns (uint256[] memory);

    function readState(address router) external view returns (MarketState memory market);

    function observe(uint32[] memory secondsAgos) external view returns (uint216[] memory lnImpliedRateCumulative);

    function increaseObservationsCardinalityNext(uint16 cardinalityNext) external;

    function readTokens() external view returns (IStandardizedYield _SY, IPPrincipalToken _PT, IPYieldToken _YT);

    function getRewardTokens() external view returns (address[] memory);

    function isExpired() external view returns (bool);

    function expiry() external view returns (uint256);

    function observations(
        uint256 index
    ) external view returns (uint32 blockTimestamp, uint216 lnImpliedRateCumulative, bool initialized);

    function _storage()
        external
        view
        returns (
            int128 totalPt,
            int128 totalSy,
            uint96 lastLnImpliedRate,
            uint16 observationIndex,
            uint16 observationCardinality,
            uint16 observationCardinalityNext
        );
}

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

import "../../interfaces/IPMarket.sol";
import "../../core/libraries/math/PMath.sol";

// This library can & should be integrated directly for optimal gas usage.
// If you prefer not to integrate it directly, the PendlePtOracle contract (a pre-deployed version of this contract) can be used.
library PendlePYOracleLib {
    using PMath for uint256;
    using PMath for int256;

    /**
     * This function returns the twap rate PT/Asset on market, but take into account the current rate of SY
     This is to account for special cases where underlying asset becomes insolvent and has decreasing exchangeRate
     * @param market market to get rate from
     * @param duration twap duration
     */
    function getPtToAssetRate(IPMarket market, uint32 duration) internal view returns (uint256) {
        (uint256 syIndex, uint256 pyIndex) = getSYandPYIndexCurrent(market);
        if (syIndex >= pyIndex) {
            return getPtToAssetRateRaw(market, duration);
        } else {
            return (getPtToAssetRateRaw(market, duration) * syIndex) / pyIndex;
        }
    }

    /**
     * This function returns the twap rate YT/Asset on market, but take into account the current rate of SY
     This is to account for special cases where underlying asset becomes insolvent and has decreasing exchangeRate
     * @param market market to get rate from
     * @param duration twap duration
     */
    function getYtToAssetRate(IPMarket market, uint32 duration) internal view returns (uint256) {
        (uint256 syIndex, uint256 pyIndex) = getSYandPYIndexCurrent(market);
        if (syIndex >= pyIndex) {
            return getYtToAssetRateRaw(market, duration);
        } else {
            return (getYtToAssetRateRaw(market, duration) * syIndex) / pyIndex;
        }
    }

    /// @notice Similar to getPtToAsset but returns the rate in SY instead
    function getPtToSyRate(IPMarket market, uint32 duration) internal view returns (uint256) {
        (uint256 syIndex, uint256 pyIndex) = getSYandPYIndexCurrent(market);
        if (syIndex >= pyIndex) {
            return getPtToAssetRateRaw(market, duration).divDown(syIndex);
        } else {
            return getPtToAssetRateRaw(market, duration).divDown(pyIndex);
        }
    }

    /// @notice Similar to getPtToAsset but returns the rate in SY instead
    function getYtToSyRate(IPMarket market, uint32 duration) internal view returns (uint256) {
        (uint256 syIndex, uint256 pyIndex) = getSYandPYIndexCurrent(market);
        if (syIndex >= pyIndex) {
            return getYtToAssetRateRaw(market, duration).divDown(syIndex);
        } else {
            return getYtToAssetRateRaw(market, duration).divDown(pyIndex);
        }
    }

    /// @notice returns the raw rate without taking into account whether SY is solvent
    function getPtToAssetRateRaw(IPMarket market, uint32 duration) internal view returns (uint256) {
        uint256 expiry = market.expiry();

        if (expiry <= block.timestamp) {
            return PMath.ONE;
        } else {
            uint256 lnImpliedRate = getMarketLnImpliedRate(market, duration);
            uint256 timeToExpiry = expiry - block.timestamp;
            uint256 assetToPtRate = MarketMathCore._getExchangeRateFromImpliedRate(lnImpliedRate, timeToExpiry).Uint();
            return PMath.ONE.divDown(assetToPtRate);
        }
    }

    /// @notice returns the raw rate without taking into account whether SY is solvent
    function getYtToAssetRateRaw(IPMarket market, uint32 duration) internal view returns (uint256) {
        return PMath.ONE - getPtToAssetRateRaw(market, duration);
    }

    function getSYandPYIndexCurrent(IPMarket market) internal view returns (uint256 syIndex, uint256 pyIndex) {
        (IStandardizedYield SY, , IPYieldToken YT) = market.readTokens();

        syIndex = SY.exchangeRate();
        uint256 pyIndexStored = YT.pyIndexStored();

        if (YT.doCacheIndexSameBlock() && YT.pyIndexLastUpdatedBlock() == block.number) {
            pyIndex = pyIndexStored;
        } else {
            pyIndex = PMath.max(syIndex, pyIndexStored);
        }
    }

    function getMarketLnImpliedRate(IPMarket market, uint32 duration) internal view returns (uint256) {
        if (duration == 0) {
            (, , uint96 lnImpliedRate, , , ) = IPMarket(market)._storage();
            return uint256(lnImpliedRate);
        }

        uint32[] memory durations = new uint32[](2);
        durations[0] = duration;

        uint216[] memory lnImpliedRateCumulative = market.observe(durations);
        return (lnImpliedRateCumulative[1] - lnImpliedRateCumulative[0]) / duration;
    }
}

File 10 of 74 : ISilo.sol
// SPDX-License-Identifier: MIT
pragma solidity >=0.5.0;

import {IERC4626, IERC20, IERC20Metadata} from "openzeppelin5/interfaces/IERC4626.sol";

import {IERC3156FlashLender} from "./IERC3156FlashLender.sol";
import {ISiloConfig} from "./ISiloConfig.sol";
import {ISiloFactory} from "./ISiloFactory.sol";

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

// solhint-disable ordering
interface ISilo is IERC20, IERC4626, IERC3156FlashLender {
    /// @dev Interest accrual happens on each deposit/withdraw/borrow/repay. View methods work on storage that might be
    ///      outdate. Some calculations require accrued interest to return current state of Silo. This struct is used
    ///      to make a decision inside functions if interest should be accrued in memory to work on updated values.
    enum AccrueInterestInMemory {
        No,
        Yes
    }

    /// @dev Silo has two separate oracles for solvency and maxLtv calculations. MaxLtv oracle is optional. Solvency
    ///      oracle can also be optional if asset is used as denominator in Silo config. For example, in ETH/USDC Silo
    ///      one could setup only solvency oracle for ETH that returns price in USDC. Then USDC does not need an oracle
    ///      because it's used as denominator for ETH and it's "price" can be assume as 1.
    enum OracleType {
        Solvency,
        MaxLtv
    }

    /// @dev There are 3 types of accounting in the system: for non-borrowable collateral deposit called "protected",
    ///      for borrowable collateral deposit called "collateral" and for borrowed tokens called "debt". System does
    ///      identical calculations for each type of accounting but it uses different data. To avoid code duplication
    ///      this enum is used to decide which data should be read.
    enum AssetType {
        Protected, // default
        Collateral,
        Debt
    }

    /// @dev There are 2 types of accounting in the system: for non-borrowable collateral deposit called "protected" and
    ///      for borrowable collateral deposit called "collateral". System does
    ///      identical calculations for each type of accounting but it uses different data. To avoid code duplication
    ///      this enum is used to decide which data should be read.
    enum CollateralType {
        Protected, // default
        Collateral
    }

    /// @dev Types of calls that can be made by the hook receiver on behalf of Silo via `callOnBehalfOfSilo` fn
    enum CallType {
        Call, // default
        Delegatecall
    }

    /// @param _assets Amount of assets the user wishes to withdraw. Use 0 if shares are provided.
    /// @param _shares Shares the user wishes to burn in exchange for the withdrawal. Use 0 if assets are provided.
    /// @param _receiver Address receiving the withdrawn assets
    /// @param _owner Address of the owner of the shares being burned
    /// @param _spender Address executing the withdrawal; may be different than `_owner` if an allowance was set
    /// @param _collateralType Type of the asset being withdrawn (Collateral or Protected)
    struct WithdrawArgs {
        uint256 assets;
        uint256 shares;
        address receiver;
        address owner;
        address spender;
        ISilo.CollateralType collateralType;
    }

    /// @param assets Number of assets the borrower intends to borrow. Use 0 if shares are provided.
    /// @param shares Number of shares corresponding to the assets that the borrower intends to borrow. Use 0 if
    /// assets are provided.
    /// @param receiver Address that will receive the borrowed assets
    /// @param borrower The user who is borrowing the assets
    struct BorrowArgs {
        uint256 assets;
        uint256 shares;
        address receiver;
        address borrower;
    }

    /// @param shares Amount of shares the user wishes to transit.
    /// @param owner owner of the shares after transition.
    /// @param transitionFrom type of collateral that will be transitioned.
    struct TransitionCollateralArgs {
        uint256 shares;
        address owner;
        ISilo.CollateralType transitionFrom;
    }

    struct UtilizationData {
        /// @dev COLLATERAL: Amount of asset token that has been deposited to Silo plus interest earned by depositors.
        /// It also includes token amount that has been borrowed.
        uint256 collateralAssets;
        /// @dev DEBT: Amount of asset token that has been borrowed plus accrued interest.
        uint256 debtAssets;
        /// @dev timestamp of the last interest accrual
        uint64 interestRateTimestamp;
    }

    /// @dev Interest and revenue may be rounded down to zero if the underlying token's decimal is low.
    /// Because of that, we need to store fractions for further calculation to minimize losses.
    struct Fractions {
        /// @dev interest value that we could not convert to full token in 36 decimals, max value for it is 1e18.
        /// this value was not yet apply as interest for borrowers
        uint64 interest;
        /// @dev revenue value that we could not convert to full token in 36 decimals, max value for it is 1e18.
        uint64 revenue;
    }

    struct SiloStorage {
        /// @param daoAndDeployerRevenue Current amount of assets (fees) accrued by DAO and Deployer
        /// but not yet withdrawn
        uint192 daoAndDeployerRevenue;
        /// @dev timestamp of the last interest accrual
        uint64 interestRateTimestamp;
        /// @dev Interest and revenue fractions for more precise calculations
        Fractions fractions;

        /// @dev silo is just for one asset,
        /// but this one asset can be of three types: mapping key is uint256(AssetType), so we store `assets` by type.
        /// Assets based on type:
        /// - PROTECTED COLLATERAL: Amount of asset token that has been deposited to Silo that can be ONLY used
        /// as collateral. These deposits do NOT earn interest and CANNOT be borrowed.
        /// - COLLATERAL: Amount of asset token that has been deposited to Silo plus interest earned by depositors.
        /// It also includes token amount that has been borrowed.
        /// - DEBT: Amount of asset token that has been borrowed plus accrued interest.
        /// `totalAssets` can have outdated value (without interest), if you doing view call (of off-chain call)
        /// please use getters eg `getCollateralAssets()` to fetch value that includes interest.
        mapping(AssetType assetType => uint256 assets) totalAssets;
    }

    /// @notice Emitted on protected deposit
    /// @param sender wallet address that deposited asset
    /// @param owner wallet address that received shares in Silo
    /// @param assets amount of asset that was deposited
    /// @param shares amount of shares that was minted
    event DepositProtected(address indexed sender, address indexed owner, uint256 assets, uint256 shares);

    /// @notice Emitted on protected withdraw
    /// @param sender wallet address that sent transaction
    /// @param receiver wallet address that received asset
    /// @param owner wallet address that owned asset
    /// @param assets amount of asset that was withdrew
    /// @param shares amount of shares that was burn
    event WithdrawProtected(
        address indexed sender, address indexed receiver, address indexed owner, uint256 assets, uint256 shares
    );

    /// @notice Emitted on borrow
    /// @param sender wallet address that sent transaction
    /// @param receiver wallet address that received asset
    /// @param owner wallet address that owes assets
    /// @param assets amount of asset that was borrowed
    /// @param shares amount of shares that was minted
    event Borrow(
        address indexed sender, address indexed receiver, address indexed owner, uint256 assets, uint256 shares
    );

    /// @notice Emitted on repayment
    /// @param sender wallet address that repaid asset
    /// @param owner wallet address that owed asset
    /// @param assets amount of asset that was repaid
    /// @param shares amount of shares that was burn
    event Repay(address indexed sender, address indexed owner, uint256 assets, uint256 shares);

    /// @notice emitted only when collateral has been switched to other one
    event CollateralTypeChanged(address indexed borrower);

    event HooksUpdated(uint24 hooksBefore, uint24 hooksAfter);

    event AccruedInterest(uint256 hooksBefore);

    event FlashLoan(uint256 amount);

    event WithdrawnFees(uint256 daoFees, uint256 deployerFees, bool redirectedDeployerFees);

    event DeployerFeesRedirected(uint256 deployerFees);

    error UnsupportedFlashloanToken();
    error FlashloanAmountTooBig();
    error NothingToWithdraw();
    error ProtectedProtection();
    error NotEnoughLiquidity();
    error NotSolvent();
    error BorrowNotPossible();
    error EarnedZero();
    error FlashloanFailed();
    error AboveMaxLtv();
    error SiloInitialized();
    error OnlyHookReceiver();
    error NoLiquidity();
    error InputCanBeAssetsOrShares();
    error CollateralSiloAlreadySet();
    error RepayTooHigh();
    error ZeroAmount();
    error InputZeroShares();
    error ReturnZeroAssets();
    error ReturnZeroShares();

    /// @return siloFactory The associated factory of the silo
    function factory() external view returns (ISiloFactory siloFactory);

    /// @notice Method for HookReceiver only to call on behalf of Silo
    /// @param _target address of the contract to call
    /// @param _value amount of ETH to send
    /// @param _callType type of the call (Call or Delegatecall)
    /// @param _input calldata for the call
    function callOnBehalfOfSilo(address _target, uint256 _value, CallType _callType, bytes calldata _input)
        external
        payable
        returns (bool success, bytes memory result);

    /// @notice Initialize Silo
    /// @param _siloConfig address of ISiloConfig with full config for this Silo
    function initialize(ISiloConfig _siloConfig) external;

    /// @notice Update hooks configuration for Silo
    /// @dev This function must be called after the hooks configuration is changed in the hook receiver
    function updateHooks() external;

    /// @notice Fetches the silo configuration contract
    /// @return siloConfig Address of the configuration contract associated with the silo
    function config() external view returns (ISiloConfig siloConfig);

    /// @notice Fetches the utilization data of the silo used by IRM
    function utilizationData() external view returns (UtilizationData memory utilizationData);

    /// @notice Fetches the real (available to borrow) liquidity in the silo, it does include interest
    /// @return liquidity The amount of liquidity
    function getLiquidity() external view returns (uint256 liquidity);

    /// @notice Determines if a borrower is solvent
    /// @param _borrower Address of the borrower to check for solvency
    /// @return True if the borrower is solvent, otherwise false
    function isSolvent(address _borrower) external view returns (bool);

    /// @notice Retrieves the raw total amount of assets based on provided type (direct storage access)
    function getTotalAssetsStorage(AssetType _assetType) external view returns (uint256);

    /// @notice Direct storage access to silo storage
    /// @dev See struct `SiloStorage` for more details
    function getSiloStorage()
        external
        view
        returns (
            uint192 daoAndDeployerRevenue,
            uint64 interestRateTimestamp,
            uint256 protectedAssets,
            uint256 collateralAssets,
            uint256 debtAssets
        );

    /// @notice Direct access to silo storage fractions variables
    function getFractionsStorage() external view returns (Fractions memory fractions);

    /// @notice Retrieves the total amount of collateral (borrowable) assets with interest
    /// @return totalCollateralAssets The total amount of assets of type 'Collateral'
    function getCollateralAssets() external view returns (uint256 totalCollateralAssets);

    /// @notice Retrieves the total amount of debt assets with interest
    /// @return totalDebtAssets The total amount of assets of type 'Debt'
    function getDebtAssets() external view returns (uint256 totalDebtAssets);

    /// @notice Retrieves the total amounts of collateral and protected (non-borrowable) assets
    /// @return totalCollateralAssets The total amount of assets of type 'Collateral'
    /// @return totalProtectedAssets The total amount of protected (non-borrowable) assets
    function getCollateralAndProtectedTotalsStorage()
        external
        view
        returns (uint256 totalCollateralAssets, uint256 totalProtectedAssets);

    /// @notice Retrieves the total amounts of collateral and debt assets
    /// @return totalCollateralAssets The total amount of assets of type 'Collateral'
    /// @return totalDebtAssets The total amount of debt assets of type 'Debt'
    function getCollateralAndDebtTotalsStorage()
        external
        view
        returns (uint256 totalCollateralAssets, uint256 totalDebtAssets);

    /// @notice Implements IERC4626.convertToShares for each asset type
    function convertToShares(uint256 _assets, AssetType _assetType) external view returns (uint256 shares);

    /// @notice Implements IERC4626.convertToAssets for each asset type
    function convertToAssets(uint256 _shares, AssetType _assetType) external view returns (uint256 assets);

    /// @notice Implements IERC4626.previewDeposit for protected (non-borrowable) collateral and collateral
    /// @dev Reverts for debt asset type
    function previewDeposit(uint256 _assets, CollateralType _collateralType) external view returns (uint256 shares);

    /// @notice Implements IERC4626.deposit for protected (non-borrowable) collateral and collateral
    /// @dev Reverts for debt asset type
    function deposit(uint256 _assets, address _receiver, CollateralType _collateralType)
        external
        returns (uint256 shares);

    /// @notice Implements IERC4626.previewMint for protected (non-borrowable) collateral and collateral
    /// @dev Reverts for debt asset type
    function previewMint(uint256 _shares, CollateralType _collateralType) external view returns (uint256 assets);

    /// @notice Implements IERC4626.mint for protected (non-borrowable) collateral and collateral
    /// @dev Reverts for debt asset type
    function mint(uint256 _shares, address _receiver, CollateralType _collateralType) external returns (uint256 assets);

    /// @notice Implements IERC4626.maxWithdraw for protected (non-borrowable) collateral and collateral
    /// @dev Reverts for debt asset type
    function maxWithdraw(address _owner, CollateralType _collateralType) external view returns (uint256 maxAssets);

    /// @notice Implements IERC4626.previewWithdraw for protected (non-borrowable) collateral and collateral
    /// @dev Reverts for debt asset type
    function previewWithdraw(uint256 _assets, CollateralType _collateralType) external view returns (uint256 shares);

    /// @notice Implements IERC4626.withdraw for protected (non-borrowable) collateral and collateral
    /// @dev Reverts for debt asset type
    function withdraw(uint256 _assets, address _receiver, address _owner, CollateralType _collateralType)
        external
        returns (uint256 shares);

    /// @notice Implements IERC4626.maxRedeem for protected (non-borrowable) collateral and collateral
    /// @dev Reverts for debt asset type
    function maxRedeem(address _owner, CollateralType _collateralType) external view returns (uint256 maxShares);

    /// @notice Implements IERC4626.previewRedeem for protected (non-borrowable) collateral and collateral
    /// @dev Reverts for debt asset type
    function previewRedeem(uint256 _shares, CollateralType _collateralType) external view returns (uint256 assets);

    /// @notice Implements IERC4626.redeem for protected (non-borrowable) collateral and collateral
    /// @dev Reverts for debt asset type
    function redeem(uint256 _shares, address _receiver, address _owner, CollateralType _collateralType)
        external
        returns (uint256 assets);

    /// @notice Calculates the maximum amount of assets that can be borrowed by the given address
    /// @param _borrower Address of the potential borrower
    /// @return maxAssets Maximum amount of assets that the borrower can borrow, this value is underestimated
    /// That means, in some cases when you borrow maxAssets, you will be able to borrow again eg. up to 2wei
    /// Reason for underestimation is to return value that will not cause borrow revert
    function maxBorrow(address _borrower) external view returns (uint256 maxAssets);

    /// @notice Previews the amount of shares equivalent to the given asset amount for borrowing
    /// @param _assets Amount of assets to preview the equivalent shares for
    /// @return shares Amount of shares equivalent to the provided asset amount
    function previewBorrow(uint256 _assets) external view returns (uint256 shares);

    /// @notice Allows an address to borrow a specified amount of assets
    /// @param _assets Amount of assets to borrow
    /// @param _receiver Address receiving the borrowed assets
    /// @param _borrower Address responsible for the borrowed assets
    /// @return shares Amount of shares equivalent to the borrowed assets
    function borrow(uint256 _assets, address _receiver, address _borrower)
        external returns (uint256 shares);

    /// @notice Calculates the maximum amount of shares that can be borrowed by the given address
    /// @param _borrower Address of the potential borrower
    /// @return maxShares Maximum number of shares that the borrower can borrow
    function maxBorrowShares(address _borrower) external view returns (uint256 maxShares);

    /// @notice Previews the amount of assets equivalent to the given share amount for borrowing
    /// @param _shares Amount of shares to preview the equivalent assets for
    /// @return assets Amount of assets equivalent to the provided share amount
    function previewBorrowShares(uint256 _shares) external view returns (uint256 assets);

    /// @notice Calculates the maximum amount of assets that can be borrowed by the given address
    /// @param _borrower Address of the potential borrower
    /// @return maxAssets Maximum amount of assets that the borrower can borrow, this value is underestimated
    /// That means, in some cases when you borrow maxAssets, you will be able to borrow again eg. up to 2wei
    /// Reason for underestimation is to return value that will not cause borrow revert
    function maxBorrowSameAsset(address _borrower) external view returns (uint256 maxAssets);

    /// @notice Allows an address to borrow a specified amount of assets that will be back up with deposit made with the
    /// same asset
    /// @param _assets Amount of assets to borrow
    /// @param _receiver Address receiving the borrowed assets
    /// @param _borrower Address responsible for the borrowed assets
    /// @return shares Amount of shares equivalent to the borrowed assets
    function borrowSameAsset(uint256 _assets, address _receiver, address _borrower)
        external returns (uint256 shares);

    /// @notice Allows a user to borrow assets based on the provided share amount
    /// @param _shares Amount of shares to borrow against
    /// @param _receiver Address to receive the borrowed assets
    /// @param _borrower Address responsible for the borrowed assets
    /// @return assets Amount of assets borrowed
    function borrowShares(uint256 _shares, address _receiver, address _borrower)
        external
        returns (uint256 assets);

    /// @notice Calculates the maximum amount an address can repay based on their debt shares
    /// @param _borrower Address of the borrower
    /// @return assets Maximum amount of assets the borrower can repay
    function maxRepay(address _borrower) external view returns (uint256 assets);

    /// @notice Provides an estimation of the number of shares equivalent to a given asset amount for repayment
    /// @param _assets Amount of assets to be repaid
    /// @return shares Estimated number of shares equivalent to the provided asset amount
    function previewRepay(uint256 _assets) external view returns (uint256 shares);

    /// @notice Repays a given asset amount and returns the equivalent number of shares
    /// @param _assets Amount of assets to be repaid
    /// @param _borrower Address of the borrower whose debt is being repaid
    /// @return shares The equivalent number of shares for the provided asset amount
    function repay(uint256 _assets, address _borrower) external returns (uint256 shares);

    /// @notice Calculates the maximum number of shares that can be repaid for a given borrower
    /// @param _borrower Address of the borrower
    /// @return shares The maximum number of shares that can be repaid for the borrower
    function maxRepayShares(address _borrower) external view returns (uint256 shares);

    /// @notice Provides a preview of the equivalent assets for a given number of shares to repay
    /// @param _shares Number of shares to preview repayment for
    /// @return assets Equivalent assets for the provided shares
    function previewRepayShares(uint256 _shares) external view returns (uint256 assets);

    /// @notice Allows a user to repay a loan using shares instead of assets
    /// @param _shares The number of shares the borrower wants to repay with
    /// @param _borrower The address of the borrower for whom to repay the loan
    /// @return assets The equivalent assets amount for the provided shares
    function repayShares(uint256 _shares, address _borrower) external returns (uint256 assets);

    /// @notice Transitions assets between borrowable (collateral) and non-borrowable (protected) states
    /// @dev This function allows assets to move between collateral and protected (non-borrowable) states without
    /// leaving the protocol
    /// @param _shares Amount of shares to be transitioned
    /// @param _owner Owner of the assets being transitioned
    /// @param _transitionFrom Specifies if the transition is from collateral or protected assets
    /// @return assets Amount of assets transitioned
    function transitionCollateral(uint256 _shares, address _owner, CollateralType _transitionFrom)
        external
        returns (uint256 assets);

    /// @notice Switches the collateral silo to this silo
    /// @dev Revert if the collateral silo is already set
    function switchCollateralToThisSilo() external;

    /// @notice Accrues interest for the asset and returns the accrued interest amount
    /// @return accruedInterest The total interest accrued during this operation
    function accrueInterest() external returns (uint256 accruedInterest);

    /// @notice only for SiloConfig
    function accrueInterestForConfig(
        address _interestRateModel,
        uint256 _daoFee,
        uint256 _deployerFee
    ) external;

    /// @notice Withdraws earned fees and distributes them to the DAO and deployer fee receivers
    function withdrawFees() external;
}

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

interface ISiloOracle {
    /// @notice Hook function to call before `quote` function reads price
    /// @dev This hook function can be used to change state right before the price is read. For example it can be used
    ///      for curve read only reentrancy protection. In majority of implementations this will be an empty function.
    ///      WARNING: reverts are propagated to Silo so if `beforeQuote` reverts, Silo reverts as well.
    /// @param _baseToken Address of priced token
    function beforeQuote(address _baseToken) external;

    /// @return quoteAmount Returns quote price for _baseAmount of _baseToken
    /// @param _baseAmount Amount of priced token
    /// @param _baseToken Address of priced token
    function quote(uint256 _baseAmount, address _baseToken) external view returns (uint256 quoteAmount);

    /// @return address of token in which quote (price) is denominated
    function quoteToken() external view returns (address);
}

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

import {ISilo} from "./ISilo.sol";
import {ICrossReentrancyGuard} from "./ICrossReentrancyGuard.sol";

interface ISiloConfig is ICrossReentrancyGuard {
    struct InitData {
        /// @notice Can be address zero if deployer fees are not to be collected. If deployer address is zero then
        /// deployer fee must be zero as well. Deployer will be minted an NFT that gives the right to claim deployer
        /// fees. NFT can be transferred with the right to claim.
        address deployer;

        /// @notice Address of the hook receiver called on every before/after action on Silo. Hook contract also
        /// implements liquidation logic and veSilo gauge connection.
        address hookReceiver;

        /// @notice Deployer's fee in 18 decimals points. Deployer will earn this fee based on the interest earned
        /// by the Silo. Max deployer fee is set by the DAO. At deployment it is 15%.
        uint256 deployerFee;

        /// @notice DAO's fee in 18 decimals points. DAO will earn this fee based on the interest earned
        /// by the Silo. Acceptable fee range fee is set by the DAO. Default at deployment is 5% - 50%.
        uint256 daoFee;

        /// @notice Address of the first token
        address token0;

        /// @notice Address of the solvency oracle. Solvency oracle is used to calculate LTV when deciding if borrower
        /// is solvent or should be liquidated. Solvency oracle is optional and if not set price of 1 will be assumed.
        address solvencyOracle0;

        /// @notice Address of the maxLtv oracle. Max LTV oracle is used to calculate LTV when deciding if borrower
        /// can borrow given amount of assets. Max LTV oracle is optional and if not set it defaults to solvency
        /// oracle. If neither is set price of 1 will be assumed.
        address maxLtvOracle0;

        /// @notice Address of the interest rate model
        address interestRateModel0;

        /// @notice Maximum LTV for first token. maxLTV is in 18 decimals points and is used to determine, if borrower
        /// can borrow given amount of assets. MaxLtv is in 18 decimals points. MaxLtv must be lower or equal to LT.
        uint256 maxLtv0;

        /// @notice Liquidation threshold for first token. LT is used to calculate solvency. LT is in 18 decimals
        /// points. LT must not be lower than maxLTV.
        uint256 lt0;

        /// @notice minimal acceptable LTV after liquidation, in 18 decimals points
        uint256 liquidationTargetLtv0;

        /// @notice Liquidation fee for the first token in 18 decimals points. Liquidation fee is what liquidator earns
        /// for repaying insolvent loan.
        uint256 liquidationFee0;

        /// @notice Flashloan fee sets the cost of taking a flashloan in 18 decimals points
        uint256 flashloanFee0;

        /// @notice Indicates if a beforeQuote on oracle contract should be called before quoting price
        bool callBeforeQuote0;

        /// @notice Address of the second token
        address token1;

        /// @notice Address of the solvency oracle. Solvency oracle is used to calculate LTV when deciding if borrower
        /// is solvent or should be liquidated. Solvency oracle is optional and if not set price of 1 will be assumed.
        address solvencyOracle1;

        /// @notice Address of the maxLtv oracle. Max LTV oracle is used to calculate LTV when deciding if borrower
        /// can borrow given amount of assets. Max LTV oracle is optional and if not set it defaults to solvency
        /// oracle. If neither is set price of 1 will be assumed.
        address maxLtvOracle1;

        /// @notice Address of the interest rate model
        address interestRateModel1;

        /// @notice Maximum LTV for first token. maxLTV is in 18 decimals points and is used to determine,
        /// if borrower can borrow given amount of assets. maxLtv is in 18 decimals points
        uint256 maxLtv1;

        /// @notice Liquidation threshold for first token. LT is used to calculate solvency. LT is in 18 decimals points
        uint256 lt1;

        /// @notice minimal acceptable LTV after liquidation, in 18 decimals points
        uint256 liquidationTargetLtv1;

        /// @notice Liquidation fee is what liquidator earns for repaying insolvent loan.
        uint256 liquidationFee1;

        /// @notice Flashloan fee sets the cost of taking a flashloan in 18 decimals points
        uint256 flashloanFee1;

        /// @notice Indicates if a beforeQuote on oracle contract should be called before quoting price
        bool callBeforeQuote1;
    }

    struct ConfigData {
        uint256 daoFee;
        uint256 deployerFee;
        address silo;
        address token;
        address protectedShareToken;
        address collateralShareToken;
        address debtShareToken;
        address solvencyOracle;
        address maxLtvOracle;
        address interestRateModel;
        uint256 maxLtv;
        uint256 lt;
        uint256 liquidationTargetLtv;
        uint256 liquidationFee;
        uint256 flashloanFee;
        address hookReceiver;
        bool callBeforeQuote;
    }

    struct DepositConfig {
        address silo;
        address token;
        address collateralShareToken;
        address protectedShareToken;
        uint256 daoFee;
        uint256 deployerFee;
        address interestRateModel;
    }

    error OnlySilo();
    error OnlySiloOrTokenOrHookReceiver();
    error WrongSilo();
    error OnlyDebtShareToken();
    error DebtExistInOtherSilo();
    error FeeTooHigh();

    /// @dev It should be called on debt transfer (debt share token transfer).
    /// In the case if the`_recipient` doesn't have configured a collateral silo,
    /// it will be set to the collateral silo of the `_sender`.
    /// @param _sender sender address
    /// @param _recipient recipient address
    function onDebtTransfer(address _sender, address _recipient) external;

    /// @notice Set collateral silo.
    /// @dev Revert if msg.sender is not a SILO_0 or SILO_1.
    /// @dev Always set collateral silo the same as msg.sender.
    /// @param _borrower borrower address
    /// @return collateralSiloChanged TRUE if collateral silo changed
    function setThisSiloAsCollateralSilo(address _borrower) external returns (bool collateralSiloChanged);

    /// @notice Set collateral silo
    /// @dev Revert if msg.sender is not a SILO_0 or SILO_1.
    /// @dev Always set collateral silo opposite to the msg.sender.
    /// @param _borrower borrower address
    /// @return collateralSiloChanged TRUE if collateral silo changed
    function setOtherSiloAsCollateralSilo(address _borrower) external returns (bool collateralSiloChanged);

    /// @notice Accrue interest for the silo
    /// @param _silo silo for which accrue interest
    function accrueInterestForSilo(address _silo) external;

    /// @notice Accrue interest for both silos (SILO_0 and SILO_1 in a config)
    function accrueInterestForBothSilos() external;

    /// @notice Retrieves the collateral silo for a specific borrower.
    /// @dev As a user can deposit into `Silo0` and `Silo1`, this property specifies which Silo
    /// will be used as collateral for the debt. Later on, it will be used for max LTV and solvency checks.
    /// After being set, the collateral silo is never set to `address(0)` again but such getters as
    /// `getConfigsForSolvency`, `getConfigsForBorrow`, `getConfigsForWithdraw` will return empty
    /// collateral silo config if borrower doesn't have debt.
    ///
    /// In the SiloConfig collateral silo is set by the following functions:
    /// `onDebtTransfer` - only if the recipient doesn't have collateral silo set (inherits it from the sender)
    /// This function is called on debt share token transfer (debt transfer).
    /// `setThisSiloAsCollateralSilo` - sets the same silo as the one that calls the function.
    /// `setOtherSiloAsCollateralSilo` - sets the opposite silo as collateral from the one that calls the function.
    ///
    /// In the Silo collateral silo is set by the following functions:
    /// `borrow` - always sets opposite silo as collateral.
    /// If Silo0 borrows, then Silo1 will be collateral and vice versa.
    /// `borrowSameAsset` - always sets the same silo as collateral.
    /// `switchCollateralToThisSilo` - always sets the same silo as collateral.
    /// @param _borrower The address of the borrower for which the collateral silo is being retrieved
    /// @return collateralSilo The address of the collateral silo for the specified borrower
    function borrowerCollateralSilo(address _borrower) external view returns (address collateralSilo);

    /// @notice Retrieves the silo ID
    /// @dev Each silo is assigned a unique ID. ERC-721 token is minted with identical ID to deployer.
    /// An owner of that token receives the deployer fees.
    /// @return siloId The ID of the silo
    function SILO_ID() external view returns (uint256 siloId); // solhint-disable-line func-name-mixedcase

    /// @notice Retrieves the addresses of the two silos
    /// @return silo0 The address of the first silo
    /// @return silo1 The address of the second silo
    function getSilos() external view returns (address silo0, address silo1);

    /// @notice Retrieves the asset associated with a specific silo
    /// @dev This function reverts for incorrect silo address input
    /// @param _silo The address of the silo for which the associated asset is being retrieved
    /// @return asset The address of the asset associated with the specified silo
    function getAssetForSilo(address _silo) external view returns (address asset);

    /// @notice Verifies if the borrower has debt in other silo by checking the debt share token balance
    /// @param _thisSilo The address of the silo in respect of which the debt is checked
    /// @param _borrower The address of the borrower for which the debt is checked
    /// @return hasDebt true if the borrower has debt in other silo
    function hasDebtInOtherSilo(address _thisSilo, address _borrower) external view returns (bool hasDebt);

    /// @notice Retrieves the debt silo associated with a specific borrower
    /// @dev This function reverts if debt present in two silo (should not happen)
    /// @param _borrower The address of the borrower for which the debt silo is being retrieved
    function getDebtSilo(address _borrower) external view returns (address debtSilo);

    /// @notice Retrieves configuration data for both silos. First config is for the silo that is asking for configs.
    /// @param borrower borrower address for which debtConfig will be returned
    /// @return collateralConfig The configuration data for collateral silo (empty if there is no debt).
    /// @return debtConfig The configuration data for debt silo (empty if there is no debt).
    function getConfigsForSolvency(address borrower)
        external
        view
        returns (ConfigData memory collateralConfig, ConfigData memory debtConfig);

    /// @notice Retrieves configuration data for a specific silo
    /// @dev This function reverts for incorrect silo address input.
    /// @param _silo The address of the silo for which configuration data is being retrieved
    /// @return config The configuration data for the specified silo
    function getConfig(address _silo) external view returns (ConfigData memory config);

    /// @notice Retrieves configuration data for a specific silo for withdraw fn.
    /// @dev This function reverts for incorrect silo address input.
    /// @param _silo The address of the silo for which configuration data is being retrieved
    /// @return depositConfig The configuration data for the specified silo (always config for `_silo`)
    /// @return collateralConfig The configuration data for the collateral silo (empty if there is no debt)
    /// @return debtConfig The configuration data for the debt silo (empty if there is no debt)
    function getConfigsForWithdraw(address _silo, address _borrower) external view returns (
        DepositConfig memory depositConfig,
        ConfigData memory collateralConfig,
        ConfigData memory debtConfig
    );

    /// @notice Retrieves configuration data for a specific silo for borrow fn.
    /// @dev This function reverts for incorrect silo address input.
    /// @param _debtSilo The address of the silo for which configuration data is being retrieved
    /// @return collateralConfig The configuration data for the collateral silo (always other than `_debtSilo`)
    /// @return debtConfig The configuration data for the debt silo (always config for `_debtSilo`)
    function getConfigsForBorrow(address _debtSilo)
        external
        view
        returns (ConfigData memory collateralConfig, ConfigData memory debtConfig);

    /// @notice Retrieves fee-related information for a specific silo
    /// @dev This function reverts for incorrect silo address input
    /// @param _silo The address of the silo for which fee-related information is being retrieved.
    /// @return daoFee The DAO fee percentage in 18 decimals points.
    /// @return deployerFee The deployer fee percentage in 18 decimals points.
    /// @return flashloanFee The flashloan fee percentage in 18 decimals points.
    /// @return asset The address of the asset associated with the specified silo.
    function getFeesWithAsset(address _silo)
        external
        view
        returns (uint256 daoFee, uint256 deployerFee, uint256 flashloanFee, address asset);

    /// @notice Retrieves share tokens associated with a specific silo
    /// @dev This function reverts for incorrect silo address input
    /// @param _silo The address of the silo for which share tokens are being retrieved
    /// @return protectedShareToken The address of the protected (non-borrowable) share token
    /// @return collateralShareToken The address of the collateral share token
    /// @return debtShareToken The address of the debt share token
    function getShareTokens(address _silo)
        external
        view
        returns (address protectedShareToken, address collateralShareToken, address debtShareToken);

    /// @notice Retrieves the share token and the silo token associated with a specific silo
    /// @param _silo The address of the silo for which the share token and silo token are being retrieved
    /// @param _collateralType The type of collateral
    /// @return shareToken The address of the share token (collateral or protected collateral)
    /// @return asset The address of the silo token
    function getCollateralShareTokenAndAsset(address _silo, ISilo.CollateralType _collateralType)
        external
        view
        returns (address shareToken, address asset);

    /// @notice Retrieves the share token and the silo token associated with a specific silo
    /// @param _silo The address of the silo for which the share token and silo token are being retrieved
    /// @return shareToken The address of the share token (debt)
    /// @return asset The address of the silo token
    function getDebtShareTokenAndAsset(address _silo)
        external
        view
        returns (address shareToken, address asset);
}

File 13 of 74 : SiloLens.sol
// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.28;

import {Strings} from "openzeppelin5/utils/Strings.sol";
import {IERC20} from "openzeppelin5/token/ERC20/IERC20.sol";
import {Utils} from "silo-foundry-utils/lib/Utils.sol";

import {ISiloLens, ISilo} from "./interfaces/ISiloLens.sol";
import {IShareToken} from "./interfaces/IShareToken.sol";
import {ISiloConfig} from "./interfaces/ISiloConfig.sol";
import {IPartialLiquidation} from "./interfaces/IPartialLiquidation.sol";
import {IInterestRateModel} from "./interfaces/IInterestRateModel.sol";

import {SiloLensLib} from "./lib/SiloLensLib.sol";
import {SiloStdLib} from "./lib/SiloStdLib.sol";
import {IPartialLiquidation} from "./interfaces/IPartialLiquidation.sol";
import {IDistributionManager} from "silo-core/contracts/incentives/interfaces/IDistributionManager.sol";


/// @title SiloLens is a helper contract for integrations and UI
contract SiloLens is ISiloLens {
    uint256 internal constant _PRECISION_DECIMALS = 1e18;

    /// @inheritdoc ISiloLens
    function isSolvent(ISilo _silo, address _borrower) external view returns (bool) {
        return _silo.isSolvent(_borrower);
    }

    /// @inheritdoc ISiloLens
    function liquidity(ISilo _silo) external view returns (uint256) {
        return _silo.getLiquidity();
    }

    /// @inheritdoc ISiloLens
    function getRawLiquidity(ISilo _silo) external view virtual returns (uint256 liquidity) {
        return SiloLensLib.getRawLiquidity(_silo);
    }

    /// @inheritdoc ISiloLens
    function getMaxLtv(ISilo _silo) external view virtual returns (uint256 maxLtv) {
        return SiloLensLib.getMaxLtv(_silo);
    }

    /// @inheritdoc ISiloLens
    function getLt(ISilo _silo) external view virtual returns (uint256 lt) {
        lt = SiloLensLib.getLt(_silo);
    }

    /// @inheritdoc ISiloLens
    function getUserLT(ISilo _silo, address _borrower) external view returns (uint256 userLT) {
        return SiloLensLib.getUserLt(_silo, _borrower);
    }

    function getUsersLT(Borrower[] calldata _borrowers) external view returns (uint256[] memory usersLTs) {
        usersLTs = new uint256[](_borrowers.length);

        for (uint256 i; i < _borrowers.length; i++) {
            Borrower memory borrower = _borrowers[i];
            usersLTs[i] = SiloLensLib.getUserLt(borrower.silo, borrower.wallet);
        }
    }

    function getUsersHealth(Borrower[] calldata _borrowers) external view returns (BorrowerHealth[] memory healths) {
        healths = new BorrowerHealth[](_borrowers.length);

        for (uint256 i; i < _borrowers.length; i++) {
            Borrower memory borrower = _borrowers[i];
            BorrowerHealth memory health = healths[i];

            (health.ltv, health.lt) = SiloLensLib.getLtvAndLt(borrower.silo, borrower.wallet);
        }
    }

    /// @inheritdoc ISiloLens
    function getUserLTV(ISilo _silo, address _borrower) external view returns (uint256 userLTV) {
        return SiloLensLib.getLtv(_silo, _borrower);
    }

    /// @inheritdoc ISiloLens
    function getLtv(ISilo _silo, address _borrower) external view virtual returns (uint256 ltv) {
        return SiloLensLib.getLtv(_silo, _borrower);
    }

    /// @inheritdoc ISiloLens
    function hasPosition(ISiloConfig _siloConfig, address _borrower) external view virtual returns (bool has) {
        has = SiloLensLib.hasPosition(_siloConfig, _borrower);
    }

    /// @inheritdoc ISiloLens
    function inDebt(ISiloConfig _siloConfig, address _borrower) external view returns (bool hasDebt) {
        hasDebt = SiloLensLib.inDebt(_siloConfig, _borrower);
    }

    /// @inheritdoc ISiloLens
    function getFeesAndFeeReceivers(ISilo _silo)
        external
        view
        virtual
        returns (address daoFeeReceiver, address deployerFeeReceiver, uint256 daoFee, uint256 deployerFee)
    {
        (daoFeeReceiver, deployerFeeReceiver, daoFee, deployerFee,) = SiloStdLib.getFeesAndFeeReceiversWithAsset(_silo);
    }

    /// @inheritdoc ISiloLens
    function collateralBalanceOfUnderlying(ISilo _silo, address _borrower)
        external
        view
        virtual
        returns (uint256 borrowerCollateral)
    {
        return SiloLensLib.collateralBalanceOfUnderlying(_silo, _borrower);
    }

    /// @inheritdoc ISiloLens
    function debtBalanceOfUnderlying(ISilo _silo, address _borrower) external view virtual returns (uint256) {
        return _silo.maxRepay(_borrower);
    }

    /// @inheritdoc ISiloLens
    function maxLiquidation(ISilo _silo, IPartialLiquidation _hook, address _borrower)
        external
        view
        virtual
        returns (uint256 collateralToLiquidate, uint256 debtToRepay, bool sTokenRequired, bool fullLiquidation)
    {
        (collateralToLiquidate, debtToRepay, sTokenRequired) = _hook.maxLiquidation(_borrower);

        uint256 maxRepay = _silo.maxRepay(_borrower);
        fullLiquidation = maxRepay == debtToRepay;
    }

    /// @inheritdoc ISiloLens
    function totalDeposits(ISilo _silo) external view returns (uint256 totalDeposits) {
        totalDeposits = _silo.getTotalAssetsStorage(ISilo.AssetType.Collateral);
    }

    /// @inheritdoc ISiloLens
    function totalDepositsWithInterest(ISilo _silo) external view returns (uint256 amount) {
        amount = _silo.totalAssets();
    }

    function totalBorrowAmountWithInterest(ISilo _silo) external view returns (uint256 amount) {
        amount = _silo.getDebtAssets();
    }

    /// @inheritdoc ISiloLens
    function collateralOnlyDeposits(ISilo _silo) external view returns (uint256) {
        return _silo.getTotalAssetsStorage(ISilo.AssetType.Protected);
    }

    /// @inheritdoc ISiloLens
    function getDepositAmount(ISilo _silo, address _borrower)
        external
        view
        returns (uint256 borrowerDeposits)
    {
        borrowerDeposits = _silo.previewRedeem(_silo.balanceOf(_borrower));
    }

    /// @inheritdoc ISiloLens
    function totalBorrowAmount(ISilo _silo) external view returns (uint256) {
        return _silo.getTotalAssetsStorage(ISilo.AssetType.Debt);
    }

    /// @inheritdoc ISiloLens
    function totalBorrowShare(ISilo _silo) external view returns (uint256) {
        return SiloLensLib.totalBorrowShare(_silo);
    }

    /// @inheritdoc ISiloLens
    function getBorrowAmount(ISilo _silo, address _borrower)
        external
        view
        returns (uint256 maxRepay)
    {
        maxRepay = _silo.maxRepay(_borrower);
    }

    /// @inheritdoc ISiloLens
    function borrowShare(ISilo _silo, address _borrower) external view returns (uint256) {
        return SiloLensLib.borrowShare(_silo, _borrower);
    }

    /// @inheritdoc ISiloLens
    function protocolFees(ISilo _silo) external view returns (uint256 daoAndDeployerRevenue) {
        (daoAndDeployerRevenue,,,,) = _silo.getSiloStorage();
    }

    /// @inheritdoc ISiloLens
    function calculateCollateralValue(ISiloConfig _siloConfig, address _borrower)
        external
        view
        returns (uint256 collateralValue)
    {
        (collateralValue,) = SiloLensLib.calculateValues(_siloConfig, _borrower);
    }

    /// @inheritdoc ISiloLens
    function calculateBorrowValue(ISiloConfig _siloConfig, address _borrower)
        external
        view
        returns (uint256 borrowValue)
    {
        (, borrowValue) = SiloLensLib.calculateValues(_siloConfig, _borrower);
    }

    /// @inheritdoc ISiloLens
    function getUtilization(ISilo _silo) external view returns (uint256) {
        ISilo.UtilizationData memory data = _silo.utilizationData();

        if (data.collateralAssets != 0) {
            return data.debtAssets * _PRECISION_DECIMALS / data.collateralAssets;
        }
    }

    /// @inheritdoc ISiloLens
    function getInterestRateModel(ISilo _silo) external view virtual returns (address irm) {
        return SiloLensLib.getInterestRateModel(_silo);
    }

    /// @inheritdoc ISiloLens
    function getBorrowAPR(ISilo _silo) external view virtual returns (uint256 borrowAPR) {
        return SiloLensLib.getBorrowAPR(_silo);
    }

    /// @inheritdoc ISiloLens
    function getDepositAPR(ISilo _silo) external view virtual returns (uint256 depositAPR) {
        return SiloLensLib.getDepositAPR(_silo);
    }

    /// @inheritdoc ISiloLens
    function getAPRs(ISilo[] calldata _silos) external view virtual returns (APR[] memory aprs) {
        aprs = new APR[](_silos.length);

        for (uint256 i; i < _silos.length; i++) {
            ISilo silo = _silos[i];

            aprs[i] = APR({
                borrowAPR: SiloLensLib.getBorrowAPR(silo),
                depositAPR: SiloLensLib.getDepositAPR(silo)
            });
        }
    }

    function getModel(ISilo _silo) public view returns (IInterestRateModel irm) {
        irm = IInterestRateModel(_silo.config().getConfig(address(_silo)).interestRateModel);
    }

    function getSiloIncentivesControllerProgramsNames(
        address _siloIncentivesController
    ) public view returns (string[] memory programsNames) {
        IDistributionManager distributionManager = IDistributionManager(_siloIncentivesController);
        string[] memory originalProgramsNames = distributionManager.getAllProgramsNames();

        programsNames = new string[](originalProgramsNames.length);

        for (uint256 i; i < originalProgramsNames.length; i++) {
            bytes memory originalProgramName = bytes(originalProgramsNames[i]);

            if (isTokenAddress(originalProgramName)) {
                address token = address(bytes20(originalProgramName));
                programsNames[i] = Strings.toHexString(token);
            } else {
                programsNames[i] = originalProgramsNames[i];
            }
        }
    }

    function isTokenAddress(bytes memory _name) private view returns (bool isToken) {
        if (_name.length != 20) return false;

        address token = address(bytes20(_name));

        if (Utils.getCodeAt(token).length == 0) return false;

        // Sanity check to be sure that it is a token
        try IERC20(token).balanceOf(address(this)) returns (uint256) {
            isToken = true;
        } catch {}
    }
}

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

interface IERC3156FlashBorrower {
    /// @notice During the execution of the flashloan, Silo methods are not taking into consideration the fact,
    /// that some (or all) tokens were transferred as flashloan, therefore some methods can return invalid state
    /// eg. maxWithdraw can return amount that are not available to withdraw during flashlon.
    /// @dev Receive a flash loan.
    /// @param _initiator The initiator of the loan.
    /// @param _token The loan currency.
    /// @param _amount The amount of tokens lent.
    /// @param _fee The additional amount of tokens to repay.
    /// @param _data Arbitrary data structure, intended to contain user-defined parameters.
    /// @return The keccak256 hash of "ERC3156FlashBorrower.onFlashLoan"
    function onFlashLoan(address _initiator, address _token, uint256 _amount, uint256 _fee, bytes calldata _data)
        external
        returns (bytes32);
}

File 15 of 74 : LibError.sol
// SPDX-License-Identifier: AGPL-3.0
pragma solidity ^0.8.18;

library LibError {
    // Common errors
    error InvalidAsset();
    error InvalidValue();
    error InvalidAddress();
    error InvalidPermissions();
    error InvalidToken();
    error AboveMaxValue(uint256 maxValue);

    // Swap errors
    error SwapFailed();
    error InvalidPrice();
    error InvalidSwapPath();
    error InvalidSwapConfig();
    error ScaledInputFailed();
    error SlippageLimitExceeded();

    // Pendle errors
    error InvalidPendleMarket();

    // Silo erros
    error InvalidSiloMarket();
    error InvalidFlashLoanCaller();
    error InvalidFlashLoanInitiator();
    error InvalidFlashLoanAmount();
    error InvalidFlashLoanToken();
    error AboveMaxWithdraw(uint256 currentAmount, uint256 maxAmount);
}

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

interface ICeresSwapper {
    enum SwapType {
        PENDLE_TOKEN_TO_PT,
        PENDLE_PT_TO_TOKEN,
        ALGEBRA_V4_SWAPX,
        KYBERSWAP_CACHED_PATH,
        KYBERSWAP_AGGREGATOR,
        BALANCER_V3_SWAP_MULTI
    }

    struct SwapProvider {
        SwapType swapType;
        address router;
    }

    function getTokenPairHash(address _tokenIn, address _tokenOut) external pure returns (bytes32);

    function isSwapPathSet(bytes32 _tokenPairHash, SwapType _swapType) external view returns (bool);

    function isSwapPathSet(address _tokenIn, address _tokenOut, SwapType _swapType) external view returns (bool);

    function setStrategyApproval(address strategyAddress, bool status) external;

    function setSwapPath(address _tokenIn, address _tokenOut, SwapType _swapType, bytes memory _path) external;

    function setSwapProvider(address _tokenIn, address _tokenOut, SwapProvider calldata _provider) external;

    function setSwapAggregator(address _tokenIn, address _tokenOut, SwapProvider calldata _provider) external;

    function swapFrom(
        address fromToken,
        address toToken,
        uint256 amountIn,
        uint256 minAmountOut,
        address receiver,
        bytes calldata extraData
    ) external returns (uint256 tokensReceived);

    function swapTo(
        address fromToken,
        address toToken,
        uint256 amountOut,
        uint256 maxAmountIn,
        address receiver,
        bytes calldata extraData
    ) external returns (uint256 actualAmountIn);

    function swapProvider(bytes32 tokenPairHash) external view returns (SwapType swapType, address router);

    function swapAggregator(bytes32 tokenPairHash) external view returns (SwapType swapType, address router);

    function swapUsingAggregator(
        address fromToken,
        address toToken,
        uint256 amountIn,
        uint256 minAmountOut,
        address receiver,
        bytes calldata encodedSwapData
    ) external returns (uint256 tokensReceived);

    function tokenSwapPath(bytes32 tokenPairHash, SwapType swapType) external view returns (bytes memory);
}

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

pragma solidity ^0.8.20;

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

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

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

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

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

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

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

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

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

pragma solidity ^0.8.20;

/**
 * @dev Helper library for emitting standardized panic codes.
 *
 * ```solidity
 * contract Example {
 *      using Panic for uint256;
 *
 *      // Use any of the declared internal constants
 *      function foo() { Panic.GENERIC.panic(); }
 *
 *      // Alternatively
 *      function foo() { Panic.panic(Panic.GENERIC); }
 * }
 * ```
 *
 * Follows the list from https://github.com/ethereum/solidity/blob/v0.8.24/libsolutil/ErrorCodes.h[libsolutil].
 *
 * _Available since v5.1._
 */
// slither-disable-next-line unused-state
library Panic {
    /// @dev generic / unspecified error
    uint256 internal constant GENERIC = 0x00;
    /// @dev used by the assert() builtin
    uint256 internal constant ASSERT = 0x01;
    /// @dev arithmetic underflow or overflow
    uint256 internal constant UNDER_OVERFLOW = 0x11;
    /// @dev division or modulo by zero
    uint256 internal constant DIVISION_BY_ZERO = 0x12;
    /// @dev enum conversion error
    uint256 internal constant ENUM_CONVERSION_ERROR = 0x21;
    /// @dev invalid encoding in storage
    uint256 internal constant STORAGE_ENCODING_ERROR = 0x22;
    /// @dev empty array pop
    uint256 internal constant EMPTY_ARRAY_POP = 0x31;
    /// @dev array out of bounds access
    uint256 internal constant ARRAY_OUT_OF_BOUNDS = 0x32;
    /// @dev resource error (too large allocation or too large array)
    uint256 internal constant RESOURCE_ERROR = 0x41;
    /// @dev calling invalid internal function
    uint256 internal constant INVALID_INTERNAL_FUNCTION = 0x51;

    /// @dev Reverts with a panic code. Recommended to use with
    /// the internal constants with predefined codes.
    function panic(uint256 code) internal pure {
        assembly ("memory-safe") {
            mstore(0x00, 0x4e487b71)
            mstore(0x20, code)
            revert(0x1c, 0x24)
        }
    }
}

File 19 of 74 : 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: MIT
// OpenZeppelin Contracts (last updated v5.3.0) (token/ERC20/ERC20.sol)

pragma solidity ^0.8.20;

import {IERC20} from "./IERC20.sol";
import {IERC20Metadata} from "./extensions/IERC20Metadata.sol";
import {Context} from "../../utils/Context.sol";
import {IERC20Errors} from "../../interfaces/draft-IERC6093.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}.
 *
 * 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 ERC-20
 * applications.
 */
abstract contract ERC20 is Context, IERC20, IERC20Metadata, IERC20Errors {
    mapping(address account => uint256) private _balances;

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

    uint256 private _totalSupply;

    string private _name;
    string private _symbol;

    /**
     * @dev Sets the values for {name} and {symbol}.
     *
     * Both 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 returns (string memory) {
        return _name;
    }

    /**
     * @dev Returns the symbol of the token, usually a shorter version of the
     * name.
     */
    function symbol() public view virtual 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 returns (uint8) {
        return 18;
    }

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

    /**
     * @dev See {IERC20-balanceOf}.
     */
    function balanceOf(address account) public view virtual 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 `value`.
     */
    function transfer(address to, uint256 value) public virtual returns (bool) {
        address owner = _msgSender();
        _transfer(owner, to, value);
        return true;
    }

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

    /**
     * @dev See {IERC20-approve}.
     *
     * NOTE: If `value` 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 value) public virtual returns (bool) {
        address owner = _msgSender();
        _approve(owner, spender, value);
        return true;
    }

    /**
     * @dev See {IERC20-transferFrom}.
     *
     * Skips emitting an {Approval} event indicating an allowance update. This is not
     * required by the ERC. See {xref-ERC20-_approve-address-address-uint256-bool-}[_approve].
     *
     * 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 `value`.
     * - the caller must have allowance for ``from``'s tokens of at least
     * `value`.
     */
    function transferFrom(address from, address to, uint256 value) public virtual returns (bool) {
        address spender = _msgSender();
        _spendAllowance(from, spender, value);
        _transfer(from, to, value);
        return true;
    }

    /**
     * @dev Moves a `value` 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.
     *
     * NOTE: This function is not virtual, {_update} should be overridden instead.
     */
    function _transfer(address from, address to, uint256 value) internal {
        if (from == address(0)) {
            revert ERC20InvalidSender(address(0));
        }
        if (to == address(0)) {
            revert ERC20InvalidReceiver(address(0));
        }
        _update(from, to, value);
    }

    /**
     * @dev Transfers a `value` amount of tokens from `from` to `to`, or alternatively mints (or burns) if `from`
     * (or `to`) is the zero address. All customizations to transfers, mints, and burns should be done by overriding
     * this function.
     *
     * Emits a {Transfer} event.
     */
    function _update(address from, address to, uint256 value) internal virtual {
        if (from == address(0)) {
            // Overflow check required: The rest of the code assumes that totalSupply never overflows
            _totalSupply += value;
        } else {
            uint256 fromBalance = _balances[from];
            if (fromBalance < value) {
                revert ERC20InsufficientBalance(from, fromBalance, value);
            }
            unchecked {
                // Overflow not possible: value <= fromBalance <= totalSupply.
                _balances[from] = fromBalance - value;
            }
        }

        if (to == address(0)) {
            unchecked {
                // Overflow not possible: value <= totalSupply or value <= fromBalance <= totalSupply.
                _totalSupply -= value;
            }
        } else {
            unchecked {
                // Overflow not possible: balance + value is at most totalSupply, which we know fits into a uint256.
                _balances[to] += value;
            }
        }

        emit Transfer(from, to, value);
    }

    /**
     * @dev Creates a `value` amount of tokens and assigns them to `account`, by transferring it from address(0).
     * Relies on the `_update` mechanism
     *
     * Emits a {Transfer} event with `from` set to the zero address.
     *
     * NOTE: This function is not virtual, {_update} should be overridden instead.
     */
    function _mint(address account, uint256 value) internal {
        if (account == address(0)) {
            revert ERC20InvalidReceiver(address(0));
        }
        _update(address(0), account, value);
    }

    /**
     * @dev Destroys a `value` amount of tokens from `account`, lowering the total supply.
     * Relies on the `_update` mechanism.
     *
     * Emits a {Transfer} event with `to` set to the zero address.
     *
     * NOTE: This function is not virtual, {_update} should be overridden instead
     */
    function _burn(address account, uint256 value) internal {
        if (account == address(0)) {
            revert ERC20InvalidSender(address(0));
        }
        _update(account, address(0), value);
    }

    /**
     * @dev Sets `value` 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.
     *
     * Overrides to this logic should be done to the variant with an additional `bool emitEvent` argument.
     */
    function _approve(address owner, address spender, uint256 value) internal {
        _approve(owner, spender, value, true);
    }

    /**
     * @dev Variant of {_approve} with an optional flag to enable or disable the {Approval} event.
     *
     * By default (when calling {_approve}) the flag is set to true. On the other hand, approval changes made by
     * `_spendAllowance` during the `transferFrom` operation set the flag to false. This saves gas by not emitting any
     * `Approval` event during `transferFrom` operations.
     *
     * Anyone who wishes to continue emitting `Approval` events on the`transferFrom` operation can force the flag to
     * true using the following override:
     *
     * ```solidity
     * function _approve(address owner, address spender, uint256 value, bool) internal virtual override {
     *     super._approve(owner, spender, value, true);
     * }
     * ```
     *
     * Requirements are the same as {_approve}.
     */
    function _approve(address owner, address spender, uint256 value, bool emitEvent) internal virtual {
        if (owner == address(0)) {
            revert ERC20InvalidApprover(address(0));
        }
        if (spender == address(0)) {
            revert ERC20InvalidSpender(address(0));
        }
        _allowances[owner][spender] = value;
        if (emitEvent) {
            emit Approval(owner, spender, value);
        }
    }

    /**
     * @dev Updates `owner`'s allowance for `spender` based on spent `value`.
     *
     * Does not update the allowance value in case of infinite allowance.
     * Revert if not enough allowance is available.
     *
     * Does not emit an {Approval} event.
     */
    function _spendAllowance(address owner, address spender, uint256 value) internal virtual {
        uint256 currentAllowance = allowance(owner, spender);
        if (currentAllowance < type(uint256).max) {
            if (currentAllowance < value) {
                revert ERC20InsufficientAllowance(spender, currentAllowance, value);
            }
            unchecked {
                _approve(owner, spender, currentAllowance - value, false);
            }
        }
    }
}

// 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: GPL-3.0-or-later
pragma solidity ^0.8.0;
import "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol";

interface IPPrincipalToken is IERC20Metadata {
    function burnByYT(address user, uint256 amount) external;

    function mintByYT(address user, uint256 amount) external;

    function initialize(address _YT) external;

    function SY() external view returns (address);

    function YT() external view returns (address);

    function factory() external view returns (address);

    function expiry() external view returns (uint256);

    function isExpired() external view returns (bool);
}

// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity ^0.8.0;
import "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol";
import "./IRewardManager.sol";
import "./IPInterestManagerYT.sol";

interface IPYieldToken is IERC20Metadata, IRewardManager, IPInterestManagerYT {
    event NewInterestIndex(uint256 indexed newIndex);

    event Mint(
        address indexed caller,
        address indexed receiverPT,
        address indexed receiverYT,
        uint256 amountSyToMint,
        uint256 amountPYOut
    );

    event Burn(address indexed caller, address indexed receiver, uint256 amountPYToRedeem, uint256 amountSyOut);

    event RedeemRewards(address indexed user, uint256[] amountRewardsOut);

    event RedeemInterest(address indexed user, uint256 interestOut);

    event CollectRewardFee(address indexed rewardToken, uint256 amountRewardFee);

    function mintPY(address receiverPT, address receiverYT) external returns (uint256 amountPYOut);

    function redeemPY(address receiver) external returns (uint256 amountSyOut);

    function redeemPYMulti(
        address[] calldata receivers,
        uint256[] calldata amountPYToRedeems
    ) external returns (uint256[] memory amountSyOuts);

    function redeemDueInterestAndRewards(
        address user,
        bool redeemInterest,
        bool redeemRewards
    ) external returns (uint256 interestOut, uint256[] memory rewardsOut);

    function rewardIndexesCurrent() external returns (uint256[] memory);

    function pyIndexCurrent() external returns (uint256);

    function pyIndexStored() external view returns (uint256);

    function getRewardTokens() external view returns (address[] memory);

    function SY() external view returns (address);

    function PT() external view returns (address);

    function factory() external view returns (address);

    function expiry() external view returns (uint256);

    function isExpired() external view returns (bool);

    function doCacheIndexSameBlock() external view returns (bool);

    function pyIndexLastUpdatedBlock() external view returns (uint128);
}

// SPDX-License-Identifier: GPL-3.0-or-later
/*
 * MIT License
 * ===========
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 */

pragma solidity ^0.8.0;
import "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol";

interface IStandardizedYield is IERC20Metadata {
    /// @dev Emitted when any base tokens is deposited to mint shares
    event Deposit(
        address indexed caller,
        address indexed receiver,
        address indexed tokenIn,
        uint256 amountDeposited,
        uint256 amountSyOut
    );

    /// @dev Emitted when any shares are redeemed for base tokens
    event Redeem(
        address indexed caller,
        address indexed receiver,
        address indexed tokenOut,
        uint256 amountSyToRedeem,
        uint256 amountTokenOut
    );

    /// @dev check `assetInfo()` for more information
    enum AssetType {
        TOKEN,
        LIQUIDITY
    }

    /// @dev Emitted when (`user`) claims their rewards
    event ClaimRewards(address indexed user, address[] rewardTokens, uint256[] rewardAmounts);

    /**
     * @notice mints an amount of shares by depositing a base token.
     * @param receiver shares recipient address
     * @param tokenIn address of the base tokens to mint shares
     * @param amountTokenToDeposit amount of base tokens to be transferred from (`msg.sender`)
     * @param minSharesOut reverts if amount of shares minted is lower than this
     * @return amountSharesOut amount of shares minted
     * @dev Emits a {Deposit} event
     *
     * Requirements:
     * - (`tokenIn`) must be a valid base token.
     */
    function deposit(
        address receiver,
        address tokenIn,
        uint256 amountTokenToDeposit,
        uint256 minSharesOut
    ) external payable returns (uint256 amountSharesOut);

    /**
     * @notice redeems an amount of base tokens by burning some shares
     * @param receiver recipient address
     * @param amountSharesToRedeem amount of shares to be burned
     * @param tokenOut address of the base token to be redeemed
     * @param minTokenOut reverts if amount of base token redeemed is lower than this
     * @param burnFromInternalBalance if true, burns from balance of `address(this)`, otherwise burns from `msg.sender`
     * @return amountTokenOut amount of base tokens redeemed
     * @dev Emits a {Redeem} event
     *
     * Requirements:
     * - (`tokenOut`) must be a valid base token.
     */
    function redeem(
        address receiver,
        uint256 amountSharesToRedeem,
        address tokenOut,
        uint256 minTokenOut,
        bool burnFromInternalBalance
    ) external returns (uint256 amountTokenOut);

    /**
     * @notice exchangeRate * syBalance / 1e18 must return the asset balance of the account
     * @notice vice-versa, if a user uses some amount of tokens equivalent to X asset, the amount of sy
     he can mint must be X * exchangeRate / 1e18
     * @dev SYUtils's assetToSy & syToAsset should be used instead of raw multiplication
     & division
     */
    function exchangeRate() external view returns (uint256 res);

    /**
     * @notice claims reward for (`user`)
     * @param user the user receiving their rewards
     * @return rewardAmounts an array of reward amounts in the same order as `getRewardTokens`
     * @dev
     * Emits a `ClaimRewards` event
     * See {getRewardTokens} for list of reward tokens
     */
    function claimRewards(address user) external returns (uint256[] memory rewardAmounts);

    /**
     * @notice get the amount of unclaimed rewards for (`user`)
     * @param user the user to check for
     * @return rewardAmounts an array of reward amounts in the same order as `getRewardTokens`
     */
    function accruedRewards(address user) external view returns (uint256[] memory rewardAmounts);

    function rewardIndexesCurrent() external returns (uint256[] memory indexes);

    function rewardIndexesStored() external view returns (uint256[] memory indexes);

    /**
     * @notice returns the list of reward token addresses
     */
    function getRewardTokens() external view returns (address[] memory);

    /**
     * @notice returns the address of the underlying yield token
     */
    function yieldToken() external view returns (address);

    /**
     * @notice returns all tokens that can mint this SY
     */
    function getTokensIn() external view returns (address[] memory res);

    /**
     * @notice returns all tokens that can be redeemed by this SY
     */
    function getTokensOut() external view returns (address[] memory res);

    function isValidTokenIn(address token) external view returns (bool);

    function isValidTokenOut(address token) external view returns (bool);

    function previewDeposit(
        address tokenIn,
        uint256 amountTokenToDeposit
    ) external view returns (uint256 amountSharesOut);

    function previewRedeem(
        address tokenOut,
        uint256 amountSharesToRedeem
    ) external view returns (uint256 amountTokenOut);

    /**
     * @notice This function contains information to interpret what the asset is
     * @return assetType the type of the asset (0 for ERC20 tokens, 1 for AMM liquidity tokens,
        2 for bridged yield bearing tokens like wstETH, rETH on Arbi whose the underlying asset doesn't exist on the chain)
     * @return assetAddress the address of the asset
     * @return assetDecimals the decimals of the asset
     */
    function assetInfo() external view returns (AssetType assetType, address assetAddress, uint8 assetDecimals);
}

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

interface IPGauge {
    function totalActiveSupply() external view returns (uint256);

    function activeBalance(address user) external view returns (uint256);

    // only available for newer factories. please check the verified contracts
    event RedeemRewards(address indexed user, uint256[] rewardsOut);
}

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

import "../libraries/math/PMath.sol";
import "../libraries/math/LogExpMath.sol";

import "../StandardizedYield/PYIndex.sol";
import "../libraries/MiniHelpers.sol";
import "../libraries/Errors.sol";

struct MarketState {
    int256 totalPt;
    int256 totalSy;
    int256 totalLp;
    address treasury;
    /// immutable variables ///
    int256 scalarRoot;
    uint256 expiry;
    /// fee data ///
    uint256 lnFeeRateRoot;
    uint256 reserveFeePercent; // base 100
    /// last trade data ///
    uint256 lastLnImpliedRate;
}

// params that are expensive to compute, therefore we pre-compute them
struct MarketPreCompute {
    int256 rateScalar;
    int256 totalAsset;
    int256 rateAnchor;
    int256 feeRate;
}

// solhint-disable ordering
library MarketMathCore {
    using PMath for uint256;
    using PMath for int256;
    using LogExpMath for int256;
    using PYIndexLib for PYIndex;

    int256 internal constant MINIMUM_LIQUIDITY = 10 ** 3;
    int256 internal constant PERCENTAGE_DECIMALS = 100;
    uint256 internal constant DAY = 86400;
    uint256 internal constant IMPLIED_RATE_TIME = 365 * DAY;

    int256 internal constant MAX_MARKET_PROPORTION = (1e18 * 96) / 100;

    using PMath for uint256;
    using PMath for int256;

    /*///////////////////////////////////////////////////////////////
                UINT FUNCTIONS TO PROXY TO CORE FUNCTIONS
    //////////////////////////////////////////////////////////////*/

    function addLiquidity(
        MarketState memory market,
        uint256 syDesired,
        uint256 ptDesired,
        uint256 blockTime
    ) internal pure returns (uint256 lpToReserve, uint256 lpToAccount, uint256 syUsed, uint256 ptUsed) {
        (int256 _lpToReserve, int256 _lpToAccount, int256 _syUsed, int256 _ptUsed) = addLiquidityCore(
            market,
            syDesired.Int(),
            ptDesired.Int(),
            blockTime
        );

        lpToReserve = _lpToReserve.Uint();
        lpToAccount = _lpToAccount.Uint();
        syUsed = _syUsed.Uint();
        ptUsed = _ptUsed.Uint();
    }

    function removeLiquidity(
        MarketState memory market,
        uint256 lpToRemove
    ) internal pure returns (uint256 netSyToAccount, uint256 netPtToAccount) {
        (int256 _syToAccount, int256 _ptToAccount) = removeLiquidityCore(market, lpToRemove.Int());

        netSyToAccount = _syToAccount.Uint();
        netPtToAccount = _ptToAccount.Uint();
    }

    function swapExactPtForSy(
        MarketState memory market,
        PYIndex index,
        uint256 exactPtToMarket,
        uint256 blockTime
    ) internal pure returns (uint256 netSyToAccount, uint256 netSyFee, uint256 netSyToReserve) {
        (int256 _netSyToAccount, int256 _netSyFee, int256 _netSyToReserve) = executeTradeCore(
            market,
            index,
            exactPtToMarket.neg(),
            blockTime
        );

        netSyToAccount = _netSyToAccount.Uint();
        netSyFee = _netSyFee.Uint();
        netSyToReserve = _netSyToReserve.Uint();
    }

    function swapSyForExactPt(
        MarketState memory market,
        PYIndex index,
        uint256 exactPtToAccount,
        uint256 blockTime
    ) internal pure returns (uint256 netSyToMarket, uint256 netSyFee, uint256 netSyToReserve) {
        (int256 _netSyToAccount, int256 _netSyFee, int256 _netSyToReserve) = executeTradeCore(
            market,
            index,
            exactPtToAccount.Int(),
            blockTime
        );

        netSyToMarket = _netSyToAccount.neg().Uint();
        netSyFee = _netSyFee.Uint();
        netSyToReserve = _netSyToReserve.Uint();
    }

    /*///////////////////////////////////////////////////////////////
                    CORE FUNCTIONS
    //////////////////////////////////////////////////////////////*/

    function addLiquidityCore(
        MarketState memory market,
        int256 syDesired,
        int256 ptDesired,
        uint256 blockTime
    ) internal pure returns (int256 lpToReserve, int256 lpToAccount, int256 syUsed, int256 ptUsed) {
        /// ------------------------------------------------------------
        /// CHECKS
        /// ------------------------------------------------------------
        if (syDesired == 0 || ptDesired == 0) revert Errors.MarketZeroAmountsInput();
        if (MiniHelpers.isExpired(market.expiry, blockTime)) revert Errors.MarketExpired();

        /// ------------------------------------------------------------
        /// MATH
        /// ------------------------------------------------------------
        if (market.totalLp == 0) {
            lpToAccount = PMath.sqrt((syDesired * ptDesired).Uint()).Int() - MINIMUM_LIQUIDITY;
            lpToReserve = MINIMUM_LIQUIDITY;
            syUsed = syDesired;
            ptUsed = ptDesired;
        } else {
            int256 netLpByPt = (ptDesired * market.totalLp) / market.totalPt;
            int256 netLpBySy = (syDesired * market.totalLp) / market.totalSy;
            if (netLpByPt < netLpBySy) {
                lpToAccount = netLpByPt;
                ptUsed = ptDesired;
                syUsed = (market.totalSy * lpToAccount).rawDivUp(market.totalLp);
            } else {
                lpToAccount = netLpBySy;
                syUsed = syDesired;
                ptUsed = (market.totalPt * lpToAccount).rawDivUp(market.totalLp);
            }
        }

        if (lpToAccount <= 0 || syUsed <= 0 || ptUsed <= 0) revert Errors.MarketZeroAmountsOutput();

        /// ------------------------------------------------------------
        /// WRITE
        /// ------------------------------------------------------------
        market.totalSy += syUsed;
        market.totalPt += ptUsed;
        market.totalLp += lpToAccount + lpToReserve;
    }

    function removeLiquidityCore(
        MarketState memory market,
        int256 lpToRemove
    ) internal pure returns (int256 netSyToAccount, int256 netPtToAccount) {
        /// ------------------------------------------------------------
        /// CHECKS
        /// ------------------------------------------------------------
        if (lpToRemove == 0) revert Errors.MarketZeroAmountsInput();

        /// ------------------------------------------------------------
        /// MATH
        /// ------------------------------------------------------------
        netSyToAccount = (lpToRemove * market.totalSy) / market.totalLp;
        netPtToAccount = (lpToRemove * market.totalPt) / market.totalLp;

        if (netSyToAccount == 0 && netPtToAccount == 0) revert Errors.MarketZeroAmountsOutput();

        /// ------------------------------------------------------------
        /// WRITE
        /// ------------------------------------------------------------
        market.totalLp = market.totalLp.subNoNeg(lpToRemove);
        market.totalPt = market.totalPt.subNoNeg(netPtToAccount);
        market.totalSy = market.totalSy.subNoNeg(netSyToAccount);
    }

    function executeTradeCore(
        MarketState memory market,
        PYIndex index,
        int256 netPtToAccount,
        uint256 blockTime
    ) internal pure returns (int256 netSyToAccount, int256 netSyFee, int256 netSyToReserve) {
        /// ------------------------------------------------------------
        /// CHECKS
        /// ------------------------------------------------------------
        if (MiniHelpers.isExpired(market.expiry, blockTime)) revert Errors.MarketExpired();
        if (market.totalPt <= netPtToAccount)
            revert Errors.MarketInsufficientPtForTrade(market.totalPt, netPtToAccount);

        /// ------------------------------------------------------------
        /// MATH
        /// ------------------------------------------------------------
        MarketPreCompute memory comp = getMarketPreCompute(market, index, blockTime);

        (netSyToAccount, netSyFee, netSyToReserve) = calcTrade(market, comp, index, netPtToAccount);

        /// ------------------------------------------------------------
        /// WRITE
        /// ------------------------------------------------------------
        _setNewMarketStateTrade(market, comp, index, netPtToAccount, netSyToAccount, netSyToReserve, blockTime);
    }

    function getMarketPreCompute(
        MarketState memory market,
        PYIndex index,
        uint256 blockTime
    ) internal pure returns (MarketPreCompute memory res) {
        if (MiniHelpers.isExpired(market.expiry, blockTime)) revert Errors.MarketExpired();

        uint256 timeToExpiry = market.expiry - blockTime;

        res.rateScalar = _getRateScalar(market, timeToExpiry);
        res.totalAsset = index.syToAsset(market.totalSy);

        if (market.totalPt == 0 || res.totalAsset == 0)
            revert Errors.MarketZeroTotalPtOrTotalAsset(market.totalPt, res.totalAsset);

        res.rateAnchor = _getRateAnchor(
            market.totalPt,
            market.lastLnImpliedRate,
            res.totalAsset,
            res.rateScalar,
            timeToExpiry
        );
        res.feeRate = _getExchangeRateFromImpliedRate(market.lnFeeRateRoot, timeToExpiry);
    }

    function calcTrade(
        MarketState memory market,
        MarketPreCompute memory comp,
        PYIndex index,
        int256 netPtToAccount
    ) internal pure returns (int256 netSyToAccount, int256 netSyFee, int256 netSyToReserve) {
        int256 preFeeExchangeRate = _getExchangeRate(
            market.totalPt,
            comp.totalAsset,
            comp.rateScalar,
            comp.rateAnchor,
            netPtToAccount
        );

        int256 preFeeAssetToAccount = netPtToAccount.divDown(preFeeExchangeRate).neg();
        int256 fee = comp.feeRate;

        if (netPtToAccount > 0) {
            int256 postFeeExchangeRate = preFeeExchangeRate.divDown(fee);
            if (postFeeExchangeRate < PMath.IONE) revert Errors.MarketExchangeRateBelowOne(postFeeExchangeRate);

            fee = preFeeAssetToAccount.mulDown(PMath.IONE - fee);
        } else {
            fee = ((preFeeAssetToAccount * (PMath.IONE - fee)) / fee).neg();
        }

        int256 netAssetToReserve = (fee * market.reserveFeePercent.Int()) / PERCENTAGE_DECIMALS;
        int256 netAssetToAccount = preFeeAssetToAccount - fee;

        netSyToAccount = netAssetToAccount < 0
            ? index.assetToSyUp(netAssetToAccount)
            : index.assetToSy(netAssetToAccount);
        netSyFee = index.assetToSy(fee);
        netSyToReserve = index.assetToSy(netAssetToReserve);
    }

    function _setNewMarketStateTrade(
        MarketState memory market,
        MarketPreCompute memory comp,
        PYIndex index,
        int256 netPtToAccount,
        int256 netSyToAccount,
        int256 netSyToReserve,
        uint256 blockTime
    ) internal pure {
        uint256 timeToExpiry = market.expiry - blockTime;

        market.totalPt = market.totalPt.subNoNeg(netPtToAccount);
        market.totalSy = market.totalSy.subNoNeg(netSyToAccount + netSyToReserve);

        market.lastLnImpliedRate = _getLnImpliedRate(
            market.totalPt,
            index.syToAsset(market.totalSy),
            comp.rateScalar,
            comp.rateAnchor,
            timeToExpiry
        );

        if (market.lastLnImpliedRate == 0) revert Errors.MarketZeroLnImpliedRate();
    }

    function _getRateAnchor(
        int256 totalPt,
        uint256 lastLnImpliedRate,
        int256 totalAsset,
        int256 rateScalar,
        uint256 timeToExpiry
    ) internal pure returns (int256 rateAnchor) {
        int256 newExchangeRate = _getExchangeRateFromImpliedRate(lastLnImpliedRate, timeToExpiry);

        if (newExchangeRate < PMath.IONE) revert Errors.MarketExchangeRateBelowOne(newExchangeRate);

        {
            int256 proportion = totalPt.divDown(totalPt + totalAsset);

            int256 lnProportion = _logProportion(proportion);

            rateAnchor = newExchangeRate - lnProportion.divDown(rateScalar);
        }
    }

    /// @notice Calculates the current market implied rate.
    /// @return lnImpliedRate the implied rate
    function _getLnImpliedRate(
        int256 totalPt,
        int256 totalAsset,
        int256 rateScalar,
        int256 rateAnchor,
        uint256 timeToExpiry
    ) internal pure returns (uint256 lnImpliedRate) {
        // This will check for exchange rates < PMath.IONE
        int256 exchangeRate = _getExchangeRate(totalPt, totalAsset, rateScalar, rateAnchor, 0);

        // exchangeRate >= 1 so its ln >= 0
        uint256 lnRate = exchangeRate.ln().Uint();

        lnImpliedRate = (lnRate * IMPLIED_RATE_TIME) / timeToExpiry;
    }

    /// @notice Converts an implied rate to an exchange rate given a time to expiry. The
    /// formula is E = e^rt
    function _getExchangeRateFromImpliedRate(
        uint256 lnImpliedRate,
        uint256 timeToExpiry
    ) internal pure returns (int256 exchangeRate) {
        uint256 rt = (lnImpliedRate * timeToExpiry) / IMPLIED_RATE_TIME;

        exchangeRate = LogExpMath.exp(rt.Int());
    }

    function _getExchangeRate(
        int256 totalPt,
        int256 totalAsset,
        int256 rateScalar,
        int256 rateAnchor,
        int256 netPtToAccount
    ) internal pure returns (int256 exchangeRate) {
        int256 numerator = totalPt.subNoNeg(netPtToAccount);

        int256 proportion = (numerator.divDown(totalPt + totalAsset));

        if (proportion > MAX_MARKET_PROPORTION)
            revert Errors.MarketProportionTooHigh(proportion, MAX_MARKET_PROPORTION);

        int256 lnProportion = _logProportion(proportion);

        exchangeRate = lnProportion.divDown(rateScalar) + rateAnchor;

        if (exchangeRate < PMath.IONE) revert Errors.MarketExchangeRateBelowOne(exchangeRate);
    }

    function _logProportion(int256 proportion) internal pure returns (int256 res) {
        if (proportion == PMath.IONE) revert Errors.MarketProportionMustNotEqualOne();

        int256 logitP = proportion.divDown(PMath.IONE - proportion);

        res = logitP.ln();
    }

    function _getRateScalar(MarketState memory market, uint256 timeToExpiry) internal pure returns (int256 rateScalar) {
        rateScalar = (market.scalarRoot * IMPLIED_RATE_TIME.Int()) / timeToExpiry.Int();
        if (rateScalar <= 0) revert Errors.MarketRateScalarBelowZero(rateScalar);
    }

    function setInitialLnImpliedRate(
        MarketState memory market,
        PYIndex index,
        int256 initialAnchor,
        uint256 blockTime
    ) internal pure {
        /// ------------------------------------------------------------
        /// CHECKS
        /// ------------------------------------------------------------
        if (MiniHelpers.isExpired(market.expiry, blockTime)) revert Errors.MarketExpired();

        /// ------------------------------------------------------------
        /// MATH
        /// ------------------------------------------------------------
        int256 totalAsset = index.syToAsset(market.totalSy);
        uint256 timeToExpiry = market.expiry - blockTime;
        int256 rateScalar = _getRateScalar(market, timeToExpiry);

        /// ------------------------------------------------------------
        /// WRITE
        /// ------------------------------------------------------------
        market.lastLnImpliedRate = _getLnImpliedRate(
            market.totalPt,
            totalAsset,
            rateScalar,
            initialAnchor,
            timeToExpiry
        );
    }
}

File 27 of 74 : PMath.sol
// SPDX-License-Identifier: GPL-3.0-or-later
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.

// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.

// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.

pragma solidity ^0.8.0;

/* solhint-disable private-vars-leading-underscore, reason-string */

library PMath {
    uint256 internal constant ONE = 1e18; // 18 decimal places
    int256 internal constant IONE = 1e18; // 18 decimal places

    function subMax0(uint256 a, uint256 b) internal pure returns (uint256) {
        unchecked {
            return (a >= b ? a - b : 0);
        }
    }

    function subNoNeg(int256 a, int256 b) internal pure returns (int256) {
        require(a >= b, "negative");
        return a - b; // no unchecked since if b is very negative, a - b might overflow
    }

    function mulDown(uint256 a, uint256 b) internal pure returns (uint256) {
        uint256 product = a * b;
        unchecked {
            return product / ONE;
        }
    }

    function mulDown(int256 a, int256 b) internal pure returns (int256) {
        int256 product = a * b;
        unchecked {
            return product / IONE;
        }
    }

    function divDown(uint256 a, uint256 b) internal pure returns (uint256) {
        uint256 aInflated = a * ONE;
        unchecked {
            return aInflated / b;
        }
    }

    function divDown(int256 a, int256 b) internal pure returns (int256) {
        int256 aInflated = a * IONE;
        unchecked {
            return aInflated / b;
        }
    }

    function rawDivUp(uint256 a, uint256 b) internal pure returns (uint256) {
        return (a + b - 1) / b;
    }

    function rawDivUp(int256 a, int256 b) internal pure returns (int256) {
        return (a + b - 1) / b;
    }

    function tweakUp(uint256 a, uint256 factor) internal pure returns (uint256) {
        return mulDown(a, ONE + factor);
    }

    function tweakDown(uint256 a, uint256 factor) internal pure returns (uint256) {
        return mulDown(a, ONE - factor);
    }

    /// @return res = min(a + b, bound)
    /// @dev This function should handle arithmetic operation and bound check without overflow/underflow
    function addWithUpperBound(uint256 a, uint256 b, uint256 bound) internal pure returns (uint256 res) {
        unchecked {
            if (type(uint256).max - b < a) res = bound;
            else res = min(bound, a + b);
        }
    }

    /// @return res = max(a - b, bound)
    /// @dev This function should handle arithmetic operation and bound check without overflow/underflow
    function subWithLowerBound(uint256 a, uint256 b, uint256 bound) internal pure returns (uint256 res) {
        unchecked {
            if (b > a) res = bound;
            else res = max(a - b, bound);
        }
    }

    function clamp(uint256 x, uint256 lower, uint256 upper) internal pure returns (uint256 res) {
        res = x;
        if (x < lower) res = lower;
        else if (x > upper) res = upper;
    }

    // @author Uniswap
    function sqrt(uint256 y) internal pure returns (uint256 z) {
        if (y > 3) {
            z = y;
            uint256 x = y / 2 + 1;
            while (x < z) {
                z = x;
                x = (y / x + x) / 2;
            }
        } else if (y != 0) {
            z = 1;
        }
    }

    function square(uint256 x) internal pure returns (uint256) {
        return x * x;
    }

    function squareDown(uint256 x) internal pure returns (uint256) {
        return mulDown(x, x);
    }

    function abs(int256 x) internal pure returns (uint256) {
        return uint256(x > 0 ? x : -x);
    }

    function neg(int256 x) internal pure returns (int256) {
        return x * (-1);
    }

    function neg(uint256 x) internal pure returns (int256) {
        return Int(x) * (-1);
    }

    function max(uint256 x, uint256 y) internal pure returns (uint256) {
        return (x > y ? x : y);
    }

    function max(int256 x, int256 y) internal pure returns (int256) {
        return (x > y ? x : y);
    }

    function min(uint256 x, uint256 y) internal pure returns (uint256) {
        return (x < y ? x : y);
    }

    function min(int256 x, int256 y) internal pure returns (int256) {
        return (x < y ? x : y);
    }

    /*///////////////////////////////////////////////////////////////
                               SIGNED CASTS
    //////////////////////////////////////////////////////////////*/

    function Int(uint256 x) internal pure returns (int256) {
        require(x <= uint256(type(int256).max));
        return int256(x);
    }

    function Int128(int256 x) internal pure returns (int128) {
        require(type(int128).min <= x && x <= type(int128).max);
        return int128(x);
    }

    function Int128(uint256 x) internal pure returns (int128) {
        return Int128(Int(x));
    }

    /*///////////////////////////////////////////////////////////////
                               UNSIGNED CASTS
    //////////////////////////////////////////////////////////////*/

    function Uint(int256 x) internal pure returns (uint256) {
        require(x >= 0);
        return uint256(x);
    }

    function Uint32(uint256 x) internal pure returns (uint32) {
        require(x <= type(uint32).max);
        return uint32(x);
    }

    function Uint64(uint256 x) internal pure returns (uint64) {
        require(x <= type(uint64).max);
        return uint64(x);
    }

    function Uint112(uint256 x) internal pure returns (uint112) {
        require(x <= type(uint112).max);
        return uint112(x);
    }

    function Uint96(uint256 x) internal pure returns (uint96) {
        require(x <= type(uint96).max);
        return uint96(x);
    }

    function Uint128(uint256 x) internal pure returns (uint128) {
        require(x <= type(uint128).max);
        return uint128(x);
    }

    function Uint192(uint256 x) internal pure returns (uint192) {
        require(x <= type(uint192).max);
        return uint192(x);
    }

    function Uint80(uint256 x) internal pure returns (uint80) {
        require(x <= type(uint80).max);
        return uint80(x);
    }

    function isAApproxB(uint256 a, uint256 b, uint256 eps) internal pure returns (bool) {
        return mulDown(b, ONE - eps) <= a && a <= mulDown(b, ONE + eps);
    }

    function isAGreaterApproxB(uint256 a, uint256 b, uint256 eps) internal pure returns (bool) {
        return a >= b && a <= mulDown(b, ONE + eps);
    }

    function isASmallerApproxB(uint256 a, uint256 b, uint256 eps) internal pure returns (bool) {
        return a <= b && a >= mulDown(b, ONE - eps);
    }
}

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

pragma solidity ^0.8.20;

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

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

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

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

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

/// @notice https://eips.ethereum.org/EIPS/eip-3156
interface IERC3156FlashLender {
    /// @notice Protected deposits are not available for a flash loan.
    /// During the execution of the flashloan, Silo methods are not taking into consideration the fact,
    /// that some (or all) tokens were transferred as flashloan, therefore some methods can return invalid state
    /// eg. maxWithdraw can return amount that are not available to withdraw during flashlon.
    /// @dev Initiate a flash loan.
    /// @param _receiver The receiver of the tokens in the loan, and the receiver of the callback.
    /// @param _token The loan currency.
    /// @param _amount The amount of tokens lent.
    /// @param _data Arbitrary data structure, intended to contain user-defined parameters.
    function flashLoan(IERC3156FlashBorrower _receiver, address _token, uint256 _amount, bytes calldata _data)
        external
        returns (bool);

    /// @dev The amount of currency available to be lent.
    /// @param _token The loan currency.
    /// @return The amount of `token` that can be borrowed.
    function maxFlashLoan(address _token) external view returns (uint256);

    /// @dev The fee to be charged for a given loan.
    /// @param _token The loan currency.
    /// @param _amount The amount of tokens lent.
    /// @return The amount of `token` to be charged for the loan, on top of the returned principal.
    function flashFee(address _token, uint256 _amount) external view returns (uint256);
}

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

import {IERC721} from "openzeppelin5/interfaces/IERC721.sol";
import {ISiloConfig} from "./ISiloConfig.sol";

interface ISiloFactory is IERC721 {
    struct Range {
        uint128 min;
        uint128 max;
    }

    /// @notice Emitted on the creation of a Silo.
    /// @param implementation Address of the Silo implementation.
    /// @param token0 Address of the first Silo token.
    /// @param token1 Address of the second Silo token.
    /// @param silo0 Address of the first Silo.
    /// @param silo1 Address of the second Silo.
    /// @param siloConfig Address of the SiloConfig.
    event NewSilo(
        address indexed implementation,
        address indexed token0,
        address indexed token1,
        address silo0,
        address silo1,
        address siloConfig
    );

    event BaseURI(string newBaseURI);

    /// @notice Emitted on the update of DAO fee.
    /// @param minDaoFee Value of the new minimal DAO fee.
    /// @param maxDaoFee Value of the new maximal DAO fee.
    event DaoFeeChanged(uint128 minDaoFee, uint128 maxDaoFee);

    /// @notice Emitted on the update of max deployer fee.
    /// @param maxDeployerFee Value of the new max deployer fee.
    event MaxDeployerFeeChanged(uint256 maxDeployerFee);

    /// @notice Emitted on the update of max flashloan fee.
    /// @param maxFlashloanFee Value of the new max flashloan fee.
    event MaxFlashloanFeeChanged(uint256 maxFlashloanFee);

    /// @notice Emitted on the update of max liquidation fee.
    /// @param maxLiquidationFee Value of the new max liquidation fee.
    event MaxLiquidationFeeChanged(uint256 maxLiquidationFee);

    /// @notice Emitted on the change of DAO fee receiver.
    /// @param daoFeeReceiver Address of the new DAO fee receiver.
    event DaoFeeReceiverChanged(address daoFeeReceiver);

    /// @notice Emitted on the change of DAO fee receiver for particular silo
    /// @param silo Address for which new DAO fee receiver is set.
    /// @param daoFeeReceiver Address of the new DAO fee receiver.
    event DaoFeeReceiverChangedForSilo(address silo, address daoFeeReceiver);

    /// @notice Emitted on the change of DAO fee receiver for particular asset
    /// @param asset Address for which new DAO fee receiver is set.
    /// @param daoFeeReceiver Address of the new DAO fee receiver.
    event DaoFeeReceiverChangedForAsset(address asset, address daoFeeReceiver);

    error MissingHookReceiver();
    error ZeroAddress();
    error DaoFeeReceiverZeroAddress();
    error SameDaoFeeReceiver();
    error EmptyToken0();
    error EmptyToken1();
    error MaxFeeExceeded();
    error InvalidFeeRange();
    error SameAsset();
    error SameRange();
    error InvalidIrm();
    error InvalidMaxLtv();
    error InvalidLt();
    error InvalidDeployer();
    error DaoMinRangeExceeded();
    error DaoMaxRangeExceeded();
    error MaxDeployerFeeExceeded();
    error MaxFlashloanFeeExceeded();
    error MaxLiquidationFeeExceeded();
    error InvalidCallBeforeQuote();
    error OracleMisconfiguration();
    error InvalidQuoteToken();
    error HookIsZeroAddress();
    error LiquidationTargetLtvTooHigh();
    error NotYourSilo();
    error ConfigMismatchSilo();
    error ConfigMismatchShareProtectedToken();
    error ConfigMismatchShareDebtToken();
    error ConfigMismatchShareCollateralToken();

    /// @notice Create a new Silo.
    /// @param _siloConfig Silo configuration.
    /// @param _siloImpl Address of the `Silo` implementation.
    /// @param _shareProtectedCollateralTokenImpl Address of the `ShareProtectedCollateralToken` implementation.
    /// @param _shareDebtTokenImpl Address of the `ShareDebtToken` implementation.
    /// @param _deployer Address of the deployer.
    /// @param _creator Address of the creator.
    function createSilo(
        ISiloConfig _siloConfig,
        address _siloImpl,
        address _shareProtectedCollateralTokenImpl,
        address _shareDebtTokenImpl,
        address _deployer,
        address _creator
    )
        external;

    /// @notice NFT ownership represents the deployer fee receiver for the each Silo ID.  After burning, 
    /// the deployer fee is sent to the DAO. Burning doesn't affect Silo's behavior. It is only about fee distribution.
    /// @param _siloIdToBurn silo ID to burn.
    function burn(uint256 _siloIdToBurn) external;

    /// @notice Update the value of DAO fee. Updated value will be used only for a new Silos.
    /// Previously deployed SiloConfigs are immutable.
    /// @param _minFee Value of the new DAO minimal fee.
    /// @param _maxFee Value of the new DAO maximal fee.
    function setDaoFee(uint128 _minFee, uint128 _maxFee) external;

    /// @notice Set the default DAO fee receiver.
    /// @param _newDaoFeeReceiver Address of the new DAO fee receiver.
    function setDaoFeeReceiver(address _newDaoFeeReceiver) external;

    /// @notice Set the new DAO fee receiver for asset, this setup will be used when fee receiver for silo is empty.
    /// @param _asset Address for which new DAO fee receiver is set.
    /// @param _newDaoFeeReceiver Address of the new DAO fee receiver.
    function setDaoFeeReceiverForAsset(address _asset, address _newDaoFeeReceiver) external;

    /// @notice Set the new DAO fee receiver for silo. This setup has highest priority.
    /// @param _silo Address for which new DAO fee receiver is set.
    /// @param _newDaoFeeReceiver Address of the new DAO fee receiver.
    function setDaoFeeReceiverForSilo(address _silo, address _newDaoFeeReceiver) external;

    /// @notice Update the value of max deployer fee. Updated value will be used only for a new Silos max deployer
    /// fee validation. Previously deployed SiloConfigs are immutable.
    /// @param _newMaxDeployerFee Value of the new max deployer fee.
    function setMaxDeployerFee(uint256 _newMaxDeployerFee) external;

    /// @notice Update the value of max flashloan fee. Updated value will be used only for a new Silos max flashloan
    /// fee validation. Previously deployed SiloConfigs are immutable.
    /// @param _newMaxFlashloanFee Value of the new max flashloan fee.
    function setMaxFlashloanFee(uint256 _newMaxFlashloanFee) external;

    /// @notice Update the value of max liquidation fee. Updated value will be used only for a new Silos max
    /// liquidation fee validation. Previously deployed SiloConfigs are immutable.
    /// @param _newMaxLiquidationFee Value of the new max liquidation fee.
    function setMaxLiquidationFee(uint256 _newMaxLiquidationFee) external;
   
    /// @notice Update the base URI.
    /// @param _newBaseURI Value of the new base URI.
    function setBaseURI(string calldata _newBaseURI) external;

    /// @notice Acceptable DAO fee range for new Silos. Denominated in 18 decimals points. 1e18 == 100%.
    function daoFeeRange() external view returns (Range memory);

    /// @notice Max deployer fee for a new Silos. Denominated in 18 decimals points. 1e18 == 100%.
    function maxDeployerFee() external view returns (uint256);

    /// @notice Max flashloan fee for a new Silos. Denominated in 18 decimals points. 1e18 == 100%.
    function maxFlashloanFee() external view returns (uint256);

    /// @notice Max liquidation fee for a new Silos. Denominated in 18 decimals points. 1e18 == 100%.
    function maxLiquidationFee() external view returns (uint256);

    /// @notice The recipient of DAO fees.
    function daoFeeReceiver() external view returns (address);

    /// @notice Get SiloConfig address by Silo id.
    function idToSiloConfig(uint256 _id) external view returns (address);

    /// @notice Get the counter of silos created by the wallet.
    function creatorSiloCounter(address _creator) external view returns (uint256);

    /// @notice Do not use this method to check if silo is secure. Anyone can deploy silo with any configuration
    /// and implementation. Most critical part of verification would be to check who deployed it.
    /// @dev True if the address was deployed using SiloFactory.
    function isSilo(address _silo) external view returns (bool);

    /// @notice Id of a next Silo to be deployed. This is an ID of non-existing Silo outside of createSilo
    /// function call. ID of a first Silo is 1.
    function getNextSiloId() external view returns (uint256);

    /// @notice Get the DAO and deployer fee receivers for a particular Silo address.
    /// @param _silo Silo address.
    /// @return dao DAO fee receiver.
    /// @return deployer Deployer fee receiver.
    function getFeeReceivers(address _silo) external view returns (address dao, address deployer);

    /// @notice Validate InitData for a new Silo. Config will be checked for the fee limits, missing parameters.
    /// @param _initData Silo init data.
    function validateSiloInitData(ISiloConfig.InitData memory _initData) external view returns (bool);
}

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

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

interface IHookReceiver {
    struct HookConfig {
        uint24 hooksBefore;
        uint24 hooksAfter;
    }

    event HookConfigured(address silo, uint24 hooksBefore, uint24 hooksAfter);

    /// @dev Revert if provided silo configuration during initialization is empty
    error EmptySiloConfig();
    /// @dev Revert if the hook receiver is already configured/initialized
    error AlreadyConfigured();

    /// @notice Initialize a hook receiver
    /// @param _siloConfig Silo configuration with all the details about the silo
    /// @param _data Data to initialize the hook receiver (if needed)
    function initialize(ISiloConfig _siloConfig, bytes calldata _data) external;

    /// @notice state of Silo before action, can be also without interest, if you need them, call silo.accrueInterest()
    function beforeAction(address _silo, uint256 _action, bytes calldata _input) external;

    function afterAction(address _silo, uint256 _action, bytes calldata _inputAndOutput) external;

    /// @notice return hooksBefore and hooksAfter configuration
    function hookReceiverConfig(address _silo) external view returns (uint24 hooksBefore, uint24 hooksAfter);
}

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

interface ICrossReentrancyGuard {
    error CrossReentrantCall();
    error CrossReentrancyNotActive();

    /// @notice only silo method for cross Silo reentrancy
    function turnOnReentrancyProtection() external;

    /// @notice only silo method for cross Silo reentrancy
    function turnOffReentrancyProtection() external;

    /// @notice view method for checking cross Silo reentrancy flag
    /// @return entered true if the reentrancy guard is currently set to "entered", which indicates there is a
    /// `nonReentrant` function in the call stack.
    function reentrancyGuardEntered() external view returns (bool entered);
}

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

pragma solidity ^0.8.20;

import {Math} from "./math/Math.sol";
import {SignedMath} from "./math/SignedMath.sol";

/**
 * @dev String operations.
 */
library Strings {
    bytes16 private constant HEX_DIGITS = "0123456789abcdef";
    uint8 private constant ADDRESS_LENGTH = 20;

    /**
     * @dev The `value` string doesn't fit in the specified `length`.
     */
    error StringsInsufficientHexLength(uint256 value, uint256 length);

    /**
     * @dev Converts a `uint256` to its ASCII `string` decimal representation.
     */
    function toString(uint256 value) internal pure returns (string memory) {
        unchecked {
            uint256 length = Math.log10(value) + 1;
            string memory buffer = new string(length);
            uint256 ptr;
            /// @solidity memory-safe-assembly
            assembly {
                ptr := add(buffer, add(32, length))
            }
            while (true) {
                ptr--;
                /// @solidity memory-safe-assembly
                assembly {
                    mstore8(ptr, byte(mod(value, 10), HEX_DIGITS))
                }
                value /= 10;
                if (value == 0) break;
            }
            return buffer;
        }
    }

    /**
     * @dev Converts a `int256` to its ASCII `string` decimal representation.
     */
    function toStringSigned(int256 value) internal pure returns (string memory) {
        return string.concat(value < 0 ? "-" : "", toString(SignedMath.abs(value)));
    }

    /**
     * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation.
     */
    function toHexString(uint256 value) internal pure returns (string memory) {
        unchecked {
            return toHexString(value, Math.log256(value) + 1);
        }
    }

    /**
     * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length.
     */
    function toHexString(uint256 value, uint256 length) internal pure returns (string memory) {
        uint256 localValue = value;
        bytes memory buffer = new bytes(2 * length + 2);
        buffer[0] = "0";
        buffer[1] = "x";
        for (uint256 i = 2 * length + 1; i > 1; --i) {
            buffer[i] = HEX_DIGITS[localValue & 0xf];
            localValue >>= 4;
        }
        if (localValue != 0) {
            revert StringsInsufficientHexLength(value, length);
        }
        return string(buffer);
    }

    /**
     * @dev Converts an `address` with fixed length of 20 bytes to its not checksummed ASCII `string` hexadecimal
     * representation.
     */
    function toHexString(address addr) internal pure returns (string memory) {
        return toHexString(uint256(uint160(addr)), ADDRESS_LENGTH);
    }

    /**
     * @dev Returns true if the two strings are equal.
     */
    function equal(string memory a, string memory b) internal pure returns (bool) {
        return bytes(a).length == bytes(b).length && keccak256(bytes(a)) == keccak256(bytes(b));
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.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
pragma solidity >=0.6.2 <0.9.0;

library Utils {
    // https://docs.soliditylang.org/en/latest/assembly.html#example
    function getCodeAt(address _addr) internal view returns (bytes memory oCode) {
        assembly {
            // retrieve the size of the code, this needs assembly
            let size := extcodesize(_addr)
            // allocate output byte array - this could also be done without assembly
            // by using o_code = new bytes(size)
            oCode := mload(0x40)
            // new "memory end" including padding
            mstore(0x40, add(oCode, and(add(add(size, 0x20), 0x1f), not(0x1f))))
            // store length in memory
            mstore(oCode, size)
            // actually retrieve the code, this needs assembly
            extcodecopy(_addr, add(oCode, 0x20), 0, size)
        }
    }

    // https://ethereum.stackexchange.com/questions/10932/how-to-convert-string-to-int
    function stringToUint(string memory s) internal pure returns (uint256) {
        bytes memory b = bytes(s);
        uint256 result = 0;
        for (uint256 i = 0; i < b.length; i++) {
            if (uint8(b[i]) >= 48 && uint8(b[i]) <= 57) {
                result = result * 10 + (uint256(uint8(b[i])) - 48);
            }
        }
        return result;
    }

    function encodeGetAddressCall(bytes4 _fnSig, uint256 _chainId, string memory _key)
        internal
        pure
        returns (bytes memory fnPayload)
    {
        assembly {
            function allocate(length) -> pos {
                pos := mload(0x40)
                mstore(0x40, add(pos, length))
            }

            let keyLength := mload(_key)

            fnPayload := mload(0x40)

            let p := allocate(0x20)

            let keySlots := mul(add(div(keyLength, 0x20), 0x01), 0x20)

            mstore(p, add(0x64, keySlots))

            p := allocate(0x04)
            mstore(p, _fnSig)

            p := allocate(0x20)
            mstore(p, _chainId)

            p := allocate(0x20)
            mstore(p, 0x40)

            p := allocate(0x20)
            mstore(p, keyLength)

            for { let i := 0 } lt(i, keyLength) { i := add(i, 0x20) } {
                p := allocate(0x20)
                mstore(p, mload(add(add(_key, 0x20), i)))
            }
        }
    }

    function encodeSingleSelectorCall(bytes4 _fnSig) internal pure returns (bytes memory fnPayload) {
        assembly {
            function allocate(length) -> pos {
                pos := mload(0x40)
                mstore(0x40, add(pos, length))
            }

            fnPayload := mload(0x40)

            let p := allocate(0x20)
            mstore(fnPayload, 0x04)

            p := allocate(0x04)
            mstore(p, _fnSig)
        }
    }

    // https://ethereum.stackexchange.com/questions/15350/how-to-convert-an-bytes-to-address-in-solidity
    function asciiBytesToAddress(bytes memory b) internal pure returns (address) {
        uint256 result = 0;
        for (uint256 i = 0; i < b.length; i++) {
            uint256 c = uint256(uint8(b[i]));
            if (c >= 48 && c <= 57) {
                result = result * 16 + (c - 48);
            }
            if (c >= 65 && c <= 90) {
                result = result * 16 + (c - 55);
            }
            if (c >= 97 && c <= 122) {
                result = result * 16 + (c - 87);
            }
        }
        return address(uint160(result));
    }
}

File 36 of 74 : ISiloLens.sol
// SPDX-License-Identifier: MIT
pragma solidity >=0.5.0;

import {ISiloConfig} from "./ISiloConfig.sol";
import {ISilo} from "./ISilo.sol";
import {IInterestRateModel} from "./IInterestRateModel.sol";
import {IPartialLiquidation} from "./IPartialLiquidation.sol";

interface ISiloLens {
    struct Borrower {
        ISilo silo;
        address wallet;
    }

    struct BorrowerHealth {
        uint256 lt;
        uint256 ltv;
    }

    struct APR {
        uint256 depositAPR;
        uint256 borrowAPR;
    }

    error InvalidAsset();

    /// @dev calculates solvency
    /// @notice this is backwards compatible method, you can use `_silo.isSolvent(_borrower)` directly.
    /// @param _silo Silo address from which to read data
    /// @param _borrower wallet address
    /// @return true if solvent, false otherwise
    function isSolvent(ISilo _silo, address _borrower) external view returns (bool);

    /// @dev Amount of token that is available for borrowing.
    /// @notice this is backwards compatible method, you can use `_silo.getLiquidity()`
    /// @param _silo Silo address from which to read data
    /// @return Silo liquidity
    function liquidity(ISilo _silo) external view returns (uint256);

    /// @return liquidity based on contract state (without interest, fees)
    function getRawLiquidity(ISilo _silo) external view returns (uint256 liquidity);

    /// @notice Retrieves the maximum loan-to-value (LTV) ratio
    /// @param _silo Address of the silo
    /// @return maxLtv The maximum LTV ratio configured for the silo in 18 decimals points
    function getMaxLtv(ISilo _silo) external view returns (uint256 maxLtv);

    /// @notice Retrieves the LT value
    /// @param _silo Address of the silo
    /// @return lt The LT value in 18 decimals points
    function getLt(ISilo _silo) external view returns (uint256 lt);

    /// @notice Retrieves the LT for a specific borrower
    /// @param _silo Address of the silo
    /// @param _borrower Address of the borrower
    /// @return userLT The LT for the borrower in 18 decimals points, returns 0 if no debt
    function getUserLT(ISilo _silo, address _borrower) external view returns (uint256 userLT);

    /// @notice Retrieves the LT for a specific borrowers
    /// @param _borrowers list of borrowers with corresponding silo addresses
    /// @return usersLTs The LTs for the borrowers in 18 decimals points, returns 0 for users with no debt
    function getUsersLT(Borrower[] calldata _borrowers) external view returns (uint256[] memory usersLTs);

    /// @notice Retrieves the LT and LTV for a specific borrowers
    /// @param _borrowers list of borrowers with corresponding silo addresses
    /// @return healths The LTs and LTVs for the borrowers in 18 decimals points, returns 0 for users with no debt
    function getUsersHealth(Borrower[] calldata _borrowers) external view returns (BorrowerHealth[] memory healths);

    /// @notice Retrieves the loan-to-value (LTV) for a specific borrower
    /// @param _silo Address of the silo
    /// @param _borrower Address of the borrower
    /// @return userLTV The LTV for the borrower in 18 decimals points
    function getUserLTV(ISilo _silo, address _borrower) external view returns (uint256 userLTV);

    /// @notice Retrieves the loan-to-value (LTV) for a specific borrower
    /// @param _silo Address of the silo
    /// @param _borrower Address of the borrower
    /// @return ltv The LTV for the borrower in 18 decimals points
    function getLtv(ISilo _silo, address _borrower) external view returns (uint256 ltv);

    /// @notice Check if user has position (collateral, protected or debt)
    /// in any asset in a market (both silos are checked)
    /// @param _siloConfig Market address (silo config address)
    /// @param _borrower wallet address for which to read data
    /// @return TRUE if user has position in any asset
    function hasPosition(ISiloConfig _siloConfig, address _borrower) external view returns (bool);

    /// @notice Check if user is in debt
    /// @param _siloConfig Market address (silo config address)
    /// @param _borrower wallet address for which to read data
    /// @return TRUE if user borrowed any amount of any asset, otherwise FALSE
    function inDebt(ISiloConfig _siloConfig, address _borrower) external view returns (bool);

    /// @notice Retrieves the fee details in 18 decimals points and the addresses of the DAO and deployer fee receivers
    /// @param _silo Address of the silo
    /// @return daoFeeReceiver The address of the DAO fee receiver
    /// @return deployerFeeReceiver The address of the deployer fee receiver
    /// @return daoFee The total fee for the DAO in 18 decimals points
    /// @return deployerFee The total fee for the deployer in 18 decimals points
    function getFeesAndFeeReceivers(ISilo _silo)
        external
        view
        returns (address daoFeeReceiver, address deployerFeeReceiver, uint256 daoFee, uint256 deployerFee);

    /// @notice Get underlying balance of all deposits of silo asset of given user including "protected"
    /// deposits, with interest
    /// @param _silo Address of the silo
    /// @param _borrower wallet address for which to read data
    /// @return balance of underlying tokens for the given `_borrower`
    function collateralBalanceOfUnderlying(ISilo _silo, address _borrower)
        external
        view
        returns (uint256);

    /// @notice Get amount of debt of underlying token for given user
    /// @param _silo Silo address from which to read data
    /// @param _borrower wallet address for which to read data
    /// @return balance of underlying token owed
    function debtBalanceOfUnderlying(ISilo _silo, address _borrower) external view returns (uint256);

    /// @param _silo silo where borrower has debt
    /// @param _hook hook for silo with debt
    /// @param _borrower borrower address
    /// @return collateralToLiquidate underestimated amount of collateral liquidator will get
    /// @return debtToRepay debt amount needed to be repay to get `collateralToLiquidate`
    /// @return sTokenRequired TRUE, when liquidation with underlying asset is not possible because of not enough
    /// liquidity
    /// @return fullLiquidation TRUE if position has to be fully liquidated
    function maxLiquidation(ISilo _silo, IPartialLiquidation _hook, address _borrower)
        external
        view
        returns (uint256 collateralToLiquidate, uint256 debtToRepay, bool sTokenRequired, bool fullLiquidation);

    /// @notice Get amount of underlying asset that has been deposited to Silo
    /// @dev It reads directly from storage so interest generated between last update and now is not
    /// taken for account
    /// @param _silo Silo address from which to read data
    /// @return totalDeposits amount of all deposits made for given asset
    function totalDeposits(ISilo _silo) external view returns (uint256 totalDeposits);

    /// @notice returns total deposits with interest dynamically calculated at current block timestamp
    /// @return total deposits amount with interest
    function totalDepositsWithInterest(ISilo _silo) external view returns (uint256);

    /// @notice returns total borrow amount with interest dynamically calculated at current block timestamp
    /// @return _totalBorrowAmount total deposits amount with interest
    function totalBorrowAmountWithInterest(ISilo _silo)
        external
        view
        returns (uint256 _totalBorrowAmount);

    /// @notice Get amount of protected asset token that has been deposited to Silo
    /// @param _silo Silo address from which to read data
    /// @return amount of all "protected" deposits
    function collateralOnlyDeposits(ISilo _silo) external view returns (uint256);

    /// @notice Calculates current deposit (with interest) for user
    /// without protected deposits
    /// @param _silo Silo address from which to read data
    /// @param _borrower account for which calculation are done
    /// @return borrowerDeposits amount of asset _borrower posses
    function getDepositAmount(ISilo _silo, address _borrower)
        external
        view
        returns (uint256 borrowerDeposits);

    /// @notice Get amount of asset that has been borrowed
    /// @dev It reads directly from storage so interest generated between last update and now is not
    /// taken for account
    /// @param _silo Silo address from which to read data
    /// @return amount of asset that has been borrowed
    function totalBorrowAmount(ISilo _silo) external view returns (uint256);

    /// @notice Get totalSupply of debt token
    /// @dev Debt token represents a share in total debt of given asset
    /// @param _silo Silo address from which to read data
    /// @return totalSupply of debt token
    function totalBorrowShare(ISilo _silo) external view returns (uint256);

    /// @notice Calculates current borrow amount for user with interest
    /// @param _silo Silo address from which to read data
    /// @param _borrower account for which calculation are done
    /// @return total amount of asset user needs to repay at provided timestamp
    function getBorrowAmount(ISilo _silo, address _borrower)
        external
        view
        returns (uint256);

    /// @notice Get debt token balance of a user
    /// @dev Debt token represents a share in total debt of given asset.
    /// This method calls balanceOf(_borrower) on that token.
    /// @param _silo Silo address from which to read data
    /// @param _borrower wallet address for which to read data
    /// @return balance of debt token of given user
    function borrowShare(ISilo _silo, address _borrower) external view returns (uint256);

    /// @notice Get amount of fees earned by protocol to date
    /// @dev It reads directly from storage so interest generated between last update and now is not
    /// taken for account. In SiloLens v1 this was total (ever growing) amount, in this one is since last withdraw.
    /// @param _silo Silo address from which to read data
    /// @return amount of fees earned by protocol to date since last withdraw
    function protocolFees(ISilo _silo) external view returns (uint256);

    /// @notice Calculate value of collateral asset for user
    /// @dev It dynamically adds interest earned. Takes for account protected deposits as well.
    /// In v1 result is always in 18 decimals, here it depends on oracle setup.
    /// @param _siloConfig Market address (silo config address)
    /// @param _borrower account for which calculation are done
    /// @return value of collateral denominated in quote token, decimal depends on oracle setup.
    function calculateCollateralValue(ISiloConfig _siloConfig, address _borrower) external view returns (uint256);

    /// @notice Calculate value of borrowed asset by user
    /// @dev It dynamically adds interest earned to borrowed amount
    /// In v1 result is always in 18 decimals, here it depends on oracle setup.
    /// @param _siloConfig Market address (silo config address)
    /// @param _borrower account for which calculation are done
    /// @return value of debt denominated in quote token, decimal depends on oracle setup.
    function calculateBorrowValue(ISiloConfig _siloConfig, address _borrower) external view returns (uint256);

    /// @notice Calculates fraction between borrowed amount and the current liquidity of tokens for given asset
    /// denominated in percentage
    /// @dev [v1 NOT compatible] Utilization is calculated current values in storage so it does not take for account
    /// earned interest and ever-increasing total borrow amount. It assumes `Model.DP()` = 100%.
    /// @param _silo Silo address from which to read data
    /// @return utilization value
    function getUtilization(ISilo _silo) external view returns (uint256);

    /// @notice Retrieves the interest rate model
    /// @param _silo Address of the silo
    /// @return irm InterestRateModel contract address
    function getInterestRateModel(ISilo _silo) external view returns (address irm);

    /// @notice Calculates current borrow interest rate
    /// @param _silo Address of the silo
    /// @return borrowAPR The interest rate value in 18 decimals points. 10**18 is equal to 100% per year
    function getBorrowAPR(ISilo _silo) external view returns (uint256 borrowAPR);

    /// @notice Calculates current deposit interest rate.
    /// @param _silo Address of the silo
    /// @return depositAPR The interest rate value in 18 decimals points. 10**18 is equal to 100% per year.
    function getDepositAPR(ISilo _silo) external view returns (uint256 depositAPR);

    /// @notice Calculates current deposit and borrow interest rates (bulk method).
    /// @param _silos Addresses of the silos
    /// @return aprs The interest rate values in 18 decimals points. 10**18 is equal to 100% per year.
    function getAPRs(ISilo[] calldata _silos) external view returns (APR[] memory aprs);

    /// @dev gets interest rates model object
    /// @param _silo Silo address from which to read data
    /// @return IInterestRateModel interest rates model object
    function getModel(ISilo _silo) external view returns (IInterestRateModel);

    /// @notice Get names of all programs in Silo incentives controller
    /// @param _siloIncentivesController Address of the Silo incentives controller
    /// @return programsNames Names of all programs in Silo incentives controller
    function getSiloIncentivesControllerProgramsNames(
        address _siloIncentivesController
    ) external view returns (string[] memory programsNames);
}

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

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

import {ISiloConfig} from "./ISiloConfig.sol";
import {ISilo} from "./ISilo.sol";

interface IShareToken is IERC20Metadata {
    struct HookSetup {
        /// @param this is the same as in siloConfig
        address hookReceiver;
        /// @param hooks bitmap
        uint24 hooksBefore;
        /// @param hooks bitmap
        uint24 hooksAfter;
        /// @param tokenType must be one of this hooks values: COLLATERAL_TOKEN, PROTECTED_TOKEN, DEBT_TOKEN
        uint24 tokenType;
    }

    struct ShareTokenStorage {
        /// @notice Silo address for which tokens was deployed
        ISilo silo;

        /// @dev cached silo config address
        ISiloConfig siloConfig;

        /// @notice Copy of hooks setup from SiloConfig for optimisation purposes
        HookSetup hookSetup;

        bool transferWithChecks;
    }

    /// @notice Emitted every time receiver is notified about token transfer
    /// @param notificationReceiver receiver address
    /// @param success false if TX reverted on `notificationReceiver` side, otherwise true
    event NotificationSent(address indexed notificationReceiver, bool success);

    error OnlySilo();
    error OnlySiloConfig();
    error OwnerIsZero();
    error RecipientIsZero();
    error AmountExceedsAllowance();
    error RecipientNotSolventAfterTransfer();
    error SenderNotSolventAfterTransfer();
    error ZeroTransfer();

    /// @notice method for SiloConfig to synchronize hooks
    /// @param _hooksBefore hooks bitmap to trigger hooks BEFORE action
    /// @param _hooksAfter hooks bitmap to trigger hooks AFTER action
    function synchronizeHooks(uint24 _hooksBefore, uint24 _hooksAfter) external;

    /// @notice Mint method for Silo to create debt
    /// @param _owner wallet for which to mint token
    /// @param _spender wallet that asks for mint
    /// @param _amount amount of token to be minted
    function mint(address _owner, address _spender, uint256 _amount) external;

    /// @notice Burn method for Silo to close debt
    /// @param _owner wallet for which to burn token
    /// @param _spender wallet that asks for burn
    /// @param _amount amount of token to be burned
    function burn(address _owner, address _spender, uint256 _amount) external;

    /// @notice TransferFrom method for liquidation
    /// @param _from wallet from which we transferring tokens
    /// @param _to wallet that will get tokens
    /// @param _amount amount of token to transfer
    function forwardTransferFromNoChecks(address _from, address _to, uint256 _amount) external;

    /// @dev Returns the amount of tokens owned by `account`.
    /// @param _account address for which to return data
    /// @return balance of the _account
    /// @return totalSupply total supply of the token
    function balanceOfAndTotalSupply(address _account) external view returns (uint256 balance, uint256 totalSupply);

    /// @notice Returns silo address for which token was deployed
    /// @return silo address
    function silo() external view returns (ISilo silo);

    function siloConfig() external view returns (ISiloConfig silo);

    /// @notice Returns hook setup
    function hookSetup() external view returns (HookSetup memory);

    /// @notice Returns hook receiver address
    function hookReceiver() external view returns (address);
}

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

interface IPartialLiquidation {
    struct HookSetup {
        /// @param this is the same as in siloConfig
        address hookReceiver;
        /// @param hooks bitmap
        uint24 hooksBefore;
        /// @param hooks bitmap
        uint24 hooksAfter;
    }

    /// @dev Emitted when a borrower is liquidated.
    /// @param liquidator The address of the liquidator
    /// @param silo The address of the silo on which position was liquidated
    /// @param borrower The address of the borrower
    /// @param repayDebtAssets Repay amount
    /// @param withdrawCollateral Total (collateral + protected) withdraw amount, in case `receiveSToken` is TRUE
    /// then this is estimated withdraw, and representation of this amount in sToken was transferred
    /// @param receiveSToken True if the liquidators wants to receive the collateral sTokens, `false` if he wants
    /// to receive the underlying collateral asset directly
    event LiquidationCall(
        address indexed liquidator,
        address indexed silo,
        address indexed borrower,
        uint256 repayDebtAssets,
        uint256 withdrawCollateral,
        bool receiveSToken
    );

    error UnexpectedCollateralToken();
    error UnexpectedDebtToken();
    error NoDebtToCover();
    error FullLiquidationRequired();
    error UserIsSolvent();
    error UnknownRatio();
    error NoRepayAssets();

    /// @notice Function to liquidate insolvent position
    /// - The caller (liquidator) covers `debtToCover` amount of debt of the user getting liquidated, and receives
    ///   an equivalent amount in `collateralAsset` plus a liquidation fee to cover market risk
    /// @dev this method reverts when:
    /// - `_maxDebtToCover` is zero
    /// - `_collateralAsset` is not `_user` collateral token (note, that user can have both tokens in Silo, but only one
    ///   is for backing debt
    /// - `_debtAsset` is not a token that `_user` borrow
    /// - `_user` is solvent and there is no debt to cover
    /// - `_maxDebtToCover` is set to cover only part of the debt but full liquidation is required
    /// - when not enough liquidity to transfer from `_user` collateral to liquidator
    ///   (use `_receiveSToken == true` in that case)
    /// @param _collateralAsset The address of the underlying asset used as collateral, to receive as result
    /// @param _debtAsset The address of the underlying borrowed asset to be repaid with the liquidation
    /// @param _user The address of the borrower getting liquidated
    /// @param _maxDebtToCover The maximum debt amount of borrowed `asset` the liquidator wants to cover,
    /// in case this amount is too big, it will be reduced to maximum allowed liquidation amount
    /// @param _receiveSToken True if the liquidators wants to receive the collateral sTokens, `false` if he wants
    /// to receive the underlying collateral asset directly
    /// @return withdrawCollateral collateral that was send to `msg.sender`, in case of `_receiveSToken` is TRUE,
    /// `withdrawCollateral` will be estimated, on redeem one can expect this value to be rounded down
    /// @return repayDebtAssets actual debt value that was repaid by `msg.sender`
    function liquidationCall(
        address _collateralAsset,
        address _debtAsset,
        address _user,
        uint256 _maxDebtToCover,
        bool _receiveSToken
    )
        external
        returns (uint256 withdrawCollateral, uint256 repayDebtAssets);

    /// @dev debt is keep growing over time, so when dApp use this view to calculate max, tx should never revert
    /// because actual max can be only higher
    /// @return collateralToLiquidate underestimated amount of collateral liquidator will get
    /// @return debtToRepay debt amount needed to be repay to get `collateralToLiquidate`
    /// @return sTokenRequired TRUE, when liquidation with underlying asset is not possible because of not enough
    /// liquidity
    function maxLiquidation(address _borrower)
        external
        view
        returns (uint256 collateralToLiquidate, uint256 debtToRepay, bool sTokenRequired);
}

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

interface IInterestRateModel {
    event InterestRateModelError();

    /// @dev Sets config address for all Silos that will use this model
    /// @param _irmConfig address of IRM config contract
    function initialize(address _irmConfig) external;

    /// @dev get compound interest rate and update model storage for current block.timestamp
    /// @param _collateralAssets total silo collateral assets
    /// @param _debtAssets total silo debt assets
    /// @param _interestRateTimestamp last IRM timestamp
    /// @return rcomp compounded interest rate from last update until now (1e18 == 100%)
    function getCompoundInterestRateAndUpdate(
        uint256 _collateralAssets,
        uint256 _debtAssets,
        uint256 _interestRateTimestamp
    )
        external
        returns (uint256 rcomp);

    /// @dev get compound interest rate
    /// @param _silo address of Silo for which interest rate should be calculated
    /// @param _blockTimestamp current block timestamp
    /// @return rcomp compounded interest rate from last update until now (1e18 == 100%)
    function getCompoundInterestRate(address _silo, uint256 _blockTimestamp)
        external
        view
        returns (uint256 rcomp);

    /// @dev get current annual interest rate
    /// @param _silo address of Silo for which interest rate should be calculated
    /// @param _blockTimestamp current block timestamp
    /// @return rcur current annual interest rate (1e18 == 100%)
    function getCurrentInterestRate(address _silo, uint256 _blockTimestamp)
        external
        view
        returns (uint256 rcur);

    /// @dev returns decimal points used by model
    function decimals() external view returns (uint256);
}

// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.28;

import {ISilo} from "../interfaces/ISilo.sol";
import {IShareToken} from "../interfaces/IShareToken.sol";

import {ISiloConfig} from "../interfaces/ISiloConfig.sol";
import {IInterestRateModel} from "../interfaces/IInterestRateModel.sol";

import {SiloSolvencyLib} from "./SiloSolvencyLib.sol";
import {SiloMathLib} from "./SiloMathLib.sol";

library SiloLensLib {
    uint256 internal constant _PRECISION_DECIMALS = 1e18;

    function getRawLiquidity(ISilo _silo) internal view returns (uint256 liquidity) {
        return SiloMathLib.liquidity(
            _silo.getTotalAssetsStorage(ISilo.AssetType.Collateral),
            _silo.getTotalAssetsStorage(ISilo.AssetType.Debt)
        );
    }

    function getMaxLtv(ISilo _silo) internal view returns (uint256 maxLtv) {
        maxLtv = _silo.config().getConfig(address(_silo)).maxLtv;
    }

    function getLt(ISilo _silo) internal view returns (uint256 lt) {
        lt = _silo.config().getConfig(address(_silo)).lt;
    }

    function getInterestRateModel(ISilo _silo) internal view returns (address irm) {
        irm = _silo.config().getConfig(address(_silo)).interestRateModel;
    }

    function getBorrowAPR(ISilo _silo) internal view returns (uint256 borrowAPR) {
        IInterestRateModel model = IInterestRateModel(_silo.config().getConfig((address(_silo))).interestRateModel);
        borrowAPR = model.getCurrentInterestRate(address(_silo), block.timestamp);
    }

    function getDepositAPR(ISilo _silo) internal view returns (uint256 depositAPR) {
        uint256 collateralAssets = _silo.getCollateralAssets();

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

        ISiloConfig.ConfigData memory cfg = _silo.config().getConfig((address(_silo)));
        depositAPR = getBorrowAPR(_silo) * _silo.getDebtAssets() / collateralAssets;
        depositAPR = depositAPR * (_PRECISION_DECIMALS - cfg.daoFee - cfg.deployerFee) / _PRECISION_DECIMALS;
    }

    function getLtv(ISilo _silo, address _borrower) internal view returns (uint256 ltv) {
        (
            ISiloConfig.ConfigData memory collateralConfig,
            ISiloConfig.ConfigData memory debtConfig
        ) = _silo.config().getConfigsForSolvency(_borrower);

        if (debtConfig.silo != address(0)) {
            ltv = SiloSolvencyLib.getLtv(
                collateralConfig,
                debtConfig,
                _borrower,
                ISilo.OracleType.Solvency,
                ISilo.AccrueInterestInMemory.Yes,
                IShareToken(debtConfig.debtShareToken).balanceOf(_borrower)
            );
        }
    }

    function getUserLt(ISilo _silo, address _borrower) internal view returns (uint256 lt) {
        (
            ISiloConfig.ConfigData memory collateralConfig,
            ISiloConfig.ConfigData memory debtConfig
        ) = _silo.config().getConfigsForSolvency(_borrower);

        if (debtConfig.silo != address(0)) lt = collateralConfig.lt;
    }

    function getLtvAndLt(ISilo _silo, address _borrower) internal view returns (uint256 ltv, uint256 lt) {
        (
            ISiloConfig.ConfigData memory collateralConfig,
            ISiloConfig.ConfigData memory debtConfig
        ) = _silo.config().getConfigsForSolvency(_borrower);

        if (debtConfig.silo != address(0)) {
            ltv = SiloSolvencyLib.getLtv(
                collateralConfig,
                debtConfig,
                _borrower,
                ISilo.OracleType.Solvency,
                ISilo.AccrueInterestInMemory.Yes,
                IShareToken(debtConfig.debtShareToken).balanceOf(_borrower)
            );

            lt = collateralConfig.lt;
        }
    }

    function hasPosition(ISiloConfig _siloConfig, address _borrower) internal view returns (bool has) {
        (address silo0, address silo1) = _siloConfig.getSilos();
        ISiloConfig.ConfigData memory cfg0 = _siloConfig.getConfig(silo0);
        ISiloConfig.ConfigData memory cfg1 = _siloConfig.getConfig(silo1);

        if (IShareToken(cfg0.collateralShareToken).balanceOf(_borrower) != 0) return true;
        if (IShareToken(cfg0.protectedShareToken).balanceOf(_borrower) != 0) return true;
        if (IShareToken(cfg1.collateralShareToken).balanceOf(_borrower) != 0) return true;
        if (IShareToken(cfg1.protectedShareToken).balanceOf(_borrower) != 0) return true;

        if (IShareToken(cfg0.debtShareToken).balanceOf(_borrower) != 0) return true;
        if (IShareToken(cfg1.debtShareToken).balanceOf(_borrower) != 0) return true;

        return false;
      }

    function inDebt(ISiloConfig _siloConfig, address _borrower) internal view returns (bool has) {
        (, ISiloConfig.ConfigData memory debtConfig) = _siloConfig.getConfigsForSolvency(_borrower);

        has = debtConfig.debtShareToken != address(0)
            && IShareToken(debtConfig.debtShareToken).balanceOf(_borrower) != 0;
    }

    function collateralBalanceOfUnderlying(ISilo _silo, address _borrower)
        internal
        view
        returns (uint256 borrowerCollateral)
    {
        (
            address protectedShareToken, address collateralShareToken,
        ) = _silo.config().getShareTokens(address(_silo));

        uint256 protectedShareBalance = IShareToken(protectedShareToken).balanceOf(_borrower);
        uint256 collateralShareBalance = IShareToken(collateralShareToken).balanceOf(_borrower);

        if (protectedShareBalance != 0) {
            borrowerCollateral = _silo.previewRedeem(protectedShareBalance, ISilo.CollateralType.Protected);
        }

        if (collateralShareBalance != 0) {
            borrowerCollateral += _silo.previewRedeem(collateralShareBalance, ISilo.CollateralType.Collateral);
        }
    }

    function totalBorrowShare(ISilo _silo) internal view returns (uint256) {
        (,, address debtShareToken) = _silo.config().getShareTokens(address(_silo));
        return IShareToken(debtShareToken).totalSupply();
    }

    function borrowShare(ISilo _silo, address _borrower) external view returns (uint256) {
        (,, address debtShareToken) = _silo.config().getShareTokens(address(_silo));
        return IShareToken(debtShareToken).balanceOf(_borrower);
    }

    function calculateValues(ISiloConfig _siloConfig, address _borrower)
        internal
        view
        returns (uint256 sumOfBorrowerCollateralValue, uint256 totalBorrowerDebtValue)
    {
        (
            ISiloConfig.ConfigData memory collateralConfig,
            ISiloConfig.ConfigData memory debtConfig
        ) = _siloConfig.getConfigsForSolvency(_borrower);

        // if no debt collateralConfig and debtConfig are empty
        if (collateralConfig.token == address(0)) return (0, 0);

        SiloSolvencyLib.LtvData memory ltvData = SiloSolvencyLib.getAssetsDataForLtvCalculations(
            collateralConfig,
            debtConfig,
            _borrower,
            ISilo.OracleType.Solvency,
            ISilo.AccrueInterestInMemory.Yes,
            IShareToken(debtConfig.debtShareToken).balanceOf(_borrower)
        );

        (
            sumOfBorrowerCollateralValue, totalBorrowerDebtValue,
        ) = SiloSolvencyLib.calculateLtv(ltvData, collateralConfig.token, debtConfig.token);
    }
}

// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.28;

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

import {ISiloConfig} from "../interfaces/ISiloConfig.sol";
import {ISilo} from "../interfaces/ISilo.sol";
import {IInterestRateModel} from "../interfaces/IInterestRateModel.sol";
import {IShareToken} from "../interfaces/IShareToken.sol";
import {SiloMathLib} from "./SiloMathLib.sol";

library SiloStdLib {
    using SafeERC20 for IERC20;

    uint256 internal constant _PRECISION_DECIMALS = 1e18;

    /// @notice Returns flash fee amount
    /// @param _config address of config contract for Silo
    /// @param _token for which fee is calculated
    /// @param _amount for which fee is calculated
    /// @return fee flash fee amount
    function flashFee(ISiloConfig _config, address _token, uint256 _amount) internal view returns (uint256 fee) {
        if (_amount == 0) return 0;

        // all user set fees are in 18 decimals points
        (,, uint256 flashloanFee, address asset) = _config.getFeesWithAsset(address(this));
        require(_token == asset, ISilo.UnsupportedFlashloanToken());
        if (flashloanFee == 0) return 0;

        require(type(uint256).max / _amount >= flashloanFee, ISilo.FlashloanAmountTooBig());
        fee = _amount * flashloanFee / _PRECISION_DECIMALS;

        // round up
        if (fee == 0) return 1;
    }

    /// @notice Returns totalAssets and totalShares for conversion math (convertToAssets and convertToShares)
    /// @dev This is useful for view functions that do not accrue interest before doing calculations. To work on
    ///      updated numbers, interest should be added on the fly.
    /// @param _configData for a single token for which to do calculations
    /// @param _assetType used to read proper storage data
    /// @return totalAssets total assets in Silo with interest for given asset type
    /// @return totalShares total shares in Silo for given asset type
    function getTotalAssetsAndTotalSharesWithInterest(
        ISiloConfig.ConfigData memory _configData,
        ISilo.AssetType _assetType
    )
        internal
        view
        returns (uint256 totalAssets, uint256 totalShares)
    {
        if (_assetType == ISilo.AssetType.Protected) {
            totalAssets = ISilo(_configData.silo).getTotalAssetsStorage(ISilo.AssetType.Protected);
            totalShares = IShareToken(_configData.protectedShareToken).totalSupply();
        } else if (_assetType == ISilo.AssetType.Collateral) {
            totalAssets = getTotalCollateralAssetsWithInterest(
                _configData.silo,
                _configData.interestRateModel,
                _configData.daoFee,
                _configData.deployerFee
            );

            totalShares = IShareToken(_configData.collateralShareToken).totalSupply();
        } else { // ISilo.AssetType.Debt
            totalAssets = getTotalDebtAssetsWithInterest(_configData.silo, _configData.interestRateModel);
            totalShares = IShareToken(_configData.debtShareToken).totalSupply();
        }
    }

    /// @notice Retrieves fee amounts in 18 decimals points and their respective receivers along with the asset
    /// @param _silo Silo address
    /// @return daoFeeReceiver Address of the DAO fee receiver
    /// @return deployerFeeReceiver Address of the deployer fee receiver
    /// @return daoFee DAO fee amount in 18 decimals points
    /// @return deployerFee Deployer fee amount in 18 decimals points
    /// @return asset Address of the associated asset
    function getFeesAndFeeReceiversWithAsset(ISilo _silo)
        internal
        view
        returns (
            address daoFeeReceiver,
            address deployerFeeReceiver,
            uint256 daoFee,
            uint256 deployerFee,
            address asset
        )
    {
        (daoFee, deployerFee,, asset) = _silo.config().getFeesWithAsset(address(_silo));
        (daoFeeReceiver, deployerFeeReceiver) = _silo.factory().getFeeReceivers(address(_silo));
    }

    /// @notice Calculates the total collateral assets with accrued interest
    /// @dev Do not use this method when accrueInterest were executed already, in that case total does not change
    /// @param _silo Address of the silo contract
    /// @param _interestRateModel Interest rate model to fetch compound interest rates
    /// @param _daoFee DAO fee in 18 decimals points
    /// @param _deployerFee Deployer fee in 18 decimals points
    /// @return totalCollateralAssetsWithInterest Accumulated collateral amount with interest
    function getTotalCollateralAssetsWithInterest(
        address _silo,
        address _interestRateModel,
        uint256 _daoFee,
        uint256 _deployerFee
    ) internal view returns (uint256 totalCollateralAssetsWithInterest) {
        uint256 rcomp;

        try IInterestRateModel(_interestRateModel).getCompoundInterestRate(_silo, block.timestamp) returns (uint256 r) {
            rcomp = r;
        } catch {
            // do not lock silo
        }

        (uint256 collateralAssets, uint256 debtAssets) = ISilo(_silo).getCollateralAndDebtTotalsStorage();

        (totalCollateralAssetsWithInterest,,,) = SiloMathLib.getCollateralAmountsWithInterest({
            _collateralAssets: collateralAssets,
            _debtAssets: debtAssets,
            _rcomp: rcomp,
            _daoFee: _daoFee,
            _deployerFee: _deployerFee
        });
    }

    /// @param _balanceCached if balance of `_owner` is unknown beforehand, then pass `0`
    function getSharesAndTotalSupply(address _shareToken, address _owner, uint256 _balanceCached)
        internal
        view
        returns (uint256 shares, uint256 totalSupply)
    {
        if (_balanceCached == 0) {
            (shares, totalSupply) = IShareToken(_shareToken).balanceOfAndTotalSupply(_owner);
        } else {
            shares = _balanceCached;
            totalSupply = IShareToken(_shareToken).totalSupply();
        }
    }

    /// @notice Calculates the total debt assets with accrued interest
    /// @param _silo Address of the silo contract
    /// @param _interestRateModel Interest rate model to fetch compound interest rates
    /// @return totalDebtAssetsWithInterest Accumulated debt amount with interest
    function getTotalDebtAssetsWithInterest(address _silo, address _interestRateModel)
        internal
        view
        returns (uint256 totalDebtAssetsWithInterest)
    {
        uint256 rcomp;

        try IInterestRateModel(_interestRateModel).getCompoundInterestRate(_silo, block.timestamp) returns (uint256 r) {
            rcomp = r;
        } catch {
            // do not lock silo
        }

        (
            totalDebtAssetsWithInterest,
        ) = SiloMathLib.getDebtAmountsWithInterest(ISilo(_silo).getTotalAssetsStorage(ISilo.AssetType.Debt), rcomp);
    }
}

// SPDX-License-Identifier: agpl-3.0
pragma solidity 0.8.28;

import {DistributionTypes} from "../lib/DistributionTypes.sol";

interface IDistributionManager {
    struct IncentivesProgram {
        uint256 index;
        address rewardToken; // can't be updated after creation
        uint104 emissionPerSecond; // configured by owner
        uint40 lastUpdateTimestamp;
        uint40 distributionEnd; // configured by owner
        mapping(address user => uint256 userIndex) users;
    }

    struct IncentiveProgramDetails {
        uint256 index;
        address rewardToken;
        uint104 emissionPerSecond;
        uint40 lastUpdateTimestamp;
        uint40 distributionEnd;
    }

    struct AccruedRewards {
        uint256 amount;
        bytes32 programId;
        address rewardToken;
    }

    event AssetConfigUpdated(address indexed asset, uint256 emission);
    event AssetIndexUpdated(address indexed asset, uint256 index);
    event DistributionEndUpdated(string incentivesProgram, uint256 newDistributionEnd);
    event IncentivesProgramIndexUpdated(string incentivesProgram, uint256 newIndex);
    event UserIndexUpdated(address indexed user, string incentivesProgram, uint256 newIndex);

    error OnlyNotifier();
    error TooLongProgramName();
    error InvalidIncentivesProgramName();
    error OnlyNotifierOrOwner();
    error ZeroAddress();

    /**
     * @dev Sets the end date for the distribution
     * @param _incentivesProgram The incentives program name
     * @param _distributionEnd The end date timestamp
     */
    function setDistributionEnd(string calldata _incentivesProgram, uint40 _distributionEnd) external;

    /**
     * @dev Gets the end date for the distribution  
     * @param _incentivesProgram The incentives program name
     * @return The end of the distribution
     */
    function getDistributionEnd(string calldata _incentivesProgram) external view returns (uint256);

    /**
     * @dev Returns the data of an user on a distribution
     * @param _user Address of the user
     * @param _incentivesProgram The incentives program name
     * @return The new index
     */
    function getUserData(address _user, string calldata _incentivesProgram) external view returns (uint256);

    /**
     * @dev Returns the configuration of the distribution for a certain incentives program
     * @param _incentivesProgram The incentives program name
     * @return details The configuration of the incentives program
     */
    function incentivesProgram(string calldata _incentivesProgram)
        external
        view
        returns (IncentiveProgramDetails memory details);

    /**
     * @dev Returns the program id for the given program name.
     * This method TRUNCATES the program name to 32 bytes.
     * If provided strings only differ after the 32nd byte they would result in the same ProgramId.
     * Ensure to use inputs that will result in 32 bytes or less.
     * @param _programName The incentives program name
     * @return programId
     */
    function getProgramId(string calldata _programName) external pure returns (bytes32 programId);

    /**
     * @dev returns the names of all the incentives programs
     * @return programsNames the names of all the incentives programs
     */
    function getAllProgramsNames() external view returns (string[] memory programsNames);

    /**
     * @dev returns the name of an incentives program
     * @param _programName the name (bytes32) of the incentives program
     * @return programName the name (string) of the incentives program
     */
    function getProgramName(bytes32 _programName) external view returns (string memory programName);
}

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

pragma solidity ^0.8.20;

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

File 44 of 74 : IERC165.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (interfaces/IERC165.sol)

pragma solidity ^0.8.20;

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

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.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 v5.1.0) (interfaces/draft-IERC6093.sol)
pragma solidity ^0.8.20;

/**
 * @dev Standard ERC-20 Errors
 * Interface of the https://eips.ethereum.org/EIPS/eip-6093[ERC-6093] custom errors for ERC-20 tokens.
 */
interface IERC20Errors {
    /**
     * @dev Indicates an error related to the current `balance` of a `sender`. Used in transfers.
     * @param sender Address whose tokens are being transferred.
     * @param balance Current balance for the interacting account.
     * @param needed Minimum amount required to perform a transfer.
     */
    error ERC20InsufficientBalance(address sender, uint256 balance, uint256 needed);

    /**
     * @dev Indicates a failure with the token `sender`. Used in transfers.
     * @param sender Address whose tokens are being transferred.
     */
    error ERC20InvalidSender(address sender);

    /**
     * @dev Indicates a failure with the token `receiver`. Used in transfers.
     * @param receiver Address to which tokens are being transferred.
     */
    error ERC20InvalidReceiver(address receiver);

    /**
     * @dev Indicates a failure with the `spender`’s `allowance`. Used in transfers.
     * @param spender Address that may be allowed to operate on tokens without being their owner.
     * @param allowance Amount of tokens a `spender` is allowed to operate with.
     * @param needed Minimum amount required to perform a transfer.
     */
    error ERC20InsufficientAllowance(address spender, uint256 allowance, uint256 needed);

    /**
     * @dev Indicates a failure with the `approver` of a token to be approved. Used in approvals.
     * @param approver Address initiating an approval operation.
     */
    error ERC20InvalidApprover(address approver);

    /**
     * @dev Indicates a failure with the `spender` to be approved. Used in approvals.
     * @param spender Address that may be allowed to operate on tokens without being their owner.
     */
    error ERC20InvalidSpender(address spender);
}

/**
 * @dev Standard ERC-721 Errors
 * Interface of the https://eips.ethereum.org/EIPS/eip-6093[ERC-6093] custom errors for ERC-721 tokens.
 */
interface IERC721Errors {
    /**
     * @dev Indicates that an address can't be an owner. For example, `address(0)` is a forbidden owner in ERC-20.
     * Used in balance queries.
     * @param owner Address of the current owner of a token.
     */
    error ERC721InvalidOwner(address owner);

    /**
     * @dev Indicates a `tokenId` whose `owner` is the zero address.
     * @param tokenId Identifier number of a token.
     */
    error ERC721NonexistentToken(uint256 tokenId);

    /**
     * @dev Indicates an error related to the ownership over a particular token. Used in transfers.
     * @param sender Address whose tokens are being transferred.
     * @param tokenId Identifier number of a token.
     * @param owner Address of the current owner of a token.
     */
    error ERC721IncorrectOwner(address sender, uint256 tokenId, address owner);

    /**
     * @dev Indicates a failure with the token `sender`. Used in transfers.
     * @param sender Address whose tokens are being transferred.
     */
    error ERC721InvalidSender(address sender);

    /**
     * @dev Indicates a failure with the token `receiver`. Used in transfers.
     * @param receiver Address to which tokens are being transferred.
     */
    error ERC721InvalidReceiver(address receiver);

    /**
     * @dev Indicates a failure with the `operator`’s approval. Used in transfers.
     * @param operator Address that may be allowed to operate on tokens without being their owner.
     * @param tokenId Identifier number of a token.
     */
    error ERC721InsufficientApproval(address operator, uint256 tokenId);

    /**
     * @dev Indicates a failure with the `approver` of a token to be approved. Used in approvals.
     * @param approver Address initiating an approval operation.
     */
    error ERC721InvalidApprover(address approver);

    /**
     * @dev Indicates a failure with the `operator` to be approved. Used in approvals.
     * @param operator Address that may be allowed to operate on tokens without being their owner.
     */
    error ERC721InvalidOperator(address operator);
}

/**
 * @dev Standard ERC-1155 Errors
 * Interface of the https://eips.ethereum.org/EIPS/eip-6093[ERC-6093] custom errors for ERC-1155 tokens.
 */
interface IERC1155Errors {
    /**
     * @dev Indicates an error related to the current `balance` of a `sender`. Used in transfers.
     * @param sender Address whose tokens are being transferred.
     * @param balance Current balance for the interacting account.
     * @param needed Minimum amount required to perform a transfer.
     * @param tokenId Identifier number of a token.
     */
    error ERC1155InsufficientBalance(address sender, uint256 balance, uint256 needed, uint256 tokenId);

    /**
     * @dev Indicates a failure with the token `sender`. Used in transfers.
     * @param sender Address whose tokens are being transferred.
     */
    error ERC1155InvalidSender(address sender);

    /**
     * @dev Indicates a failure with the token `receiver`. Used in transfers.
     * @param receiver Address to which tokens are being transferred.
     */
    error ERC1155InvalidReceiver(address receiver);

    /**
     * @dev Indicates a failure with the `operator`’s approval. Used in transfers.
     * @param operator Address that may be allowed to operate on tokens without being their owner.
     * @param owner Address of the current owner of a token.
     */
    error ERC1155MissingApprovalForAll(address operator, address owner);

    /**
     * @dev Indicates a failure with the `approver` of a token to be approved. Used in approvals.
     * @param approver Address initiating an approval operation.
     */
    error ERC1155InvalidApprover(address approver);

    /**
     * @dev Indicates a failure with the `operator` to be approved. Used in approvals.
     * @param operator Address that may be allowed to operate on tokens without being their owner.
     */
    error ERC1155InvalidOperator(address operator);

    /**
     * @dev Indicates an array length mismatch between ids and values in a safeBatchTransferFrom operation.
     * Used in batch transfers.
     * @param idsLength Length of the array of token identifiers
     * @param valuesLength Length of the array of token amounts
     */
    error ERC1155InvalidArrayLength(uint256 idsLength, uint256 valuesLength);
}

// 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.1.0) (token/ERC20/extensions/IERC20Permit.sol)

pragma solidity ^0.8.20;

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

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

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

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

interface IRewardManager {
    function userReward(address token, address user) external view returns (uint128 index, uint128 accrued);
}

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

interface IPInterestManagerYT {
    event CollectInterestFee(uint256 amountInterestFee);

    function userInterest(address user) external view returns (uint128 lastPYIndex, uint128 accruedInterest);
}

// SPDX-License-Identifier: GPL-3.0-or-later
// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
// documentation files (the “Software”), to deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to the following conditions:

// The above copyright notice and this permission notice shall be included in all copies or substantial portions of the
// Software.

// THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
// WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

pragma solidity ^0.8.0;

/* solhint-disable */

/**
 * @dev Exponentiation and logarithm functions for 18 decimal fixed point numbers (both base and exponent/argument).
 *
 * Exponentiation and logarithm with arbitrary bases (x^y and log_x(y)) are implemented by conversion to natural
 * exponentiation and logarithm (where the base is Euler's number).
 *
 * @author Fernando Martinelli - @fernandomartinelli
 * @author Sergio Yuhjtman - @sergioyuhjtman
 * @author Daniel Fernandez - @dmf7z
 */
library LogExpMath {
    // All fixed point multiplications and divisions are inlined. This means we need to divide by ONE when multiplying
    // two numbers, and multiply by ONE when dividing them.

    // All arguments and return values are 18 decimal fixed point numbers.
    int256 constant ONE_18 = 1e18;

    // Internally, intermediate values are computed with higher precision as 20 decimal fixed point numbers, and in the
    // case of ln36, 36 decimals.
    int256 constant ONE_20 = 1e20;
    int256 constant ONE_36 = 1e36;

    // The domain of natural exponentiation is bound by the word size and number of decimals used.
    //
    // Because internally the result will be stored using 20 decimals, the largest possible result is
    // (2^255 - 1) / 10^20, which makes the largest exponent ln((2^255 - 1) / 10^20) = 130.700829182905140221.
    // The smallest possible result is 10^(-18), which makes largest negative argument
    // ln(10^(-18)) = -41.446531673892822312.
    // We use 130.0 and -41.0 to have some safety margin.
    int256 constant MAX_NATURAL_EXPONENT = 130e18;
    int256 constant MIN_NATURAL_EXPONENT = -41e18;

    // Bounds for ln_36's argument. Both ln(0.9) and ln(1.1) can be represented with 36 decimal places in a fixed point
    // 256 bit integer.
    int256 constant LN_36_LOWER_BOUND = ONE_18 - 1e17;
    int256 constant LN_36_UPPER_BOUND = ONE_18 + 1e17;

    uint256 constant MILD_EXPONENT_BOUND = 2 ** 254 / uint256(ONE_20);

    // 18 decimal constants
    int256 constant x0 = 128000000000000000000; // 2ˆ7
    int256 constant a0 = 38877084059945950922200000000000000000000000000000000000; // eˆ(x0) (no decimals)
    int256 constant x1 = 64000000000000000000; // 2ˆ6
    int256 constant a1 = 6235149080811616882910000000; // eˆ(x1) (no decimals)

    // 20 decimal constants
    int256 constant x2 = 3200000000000000000000; // 2ˆ5
    int256 constant a2 = 7896296018268069516100000000000000; // eˆ(x2)
    int256 constant x3 = 1600000000000000000000; // 2ˆ4
    int256 constant a3 = 888611052050787263676000000; // eˆ(x3)
    int256 constant x4 = 800000000000000000000; // 2ˆ3
    int256 constant a4 = 298095798704172827474000; // eˆ(x4)
    int256 constant x5 = 400000000000000000000; // 2ˆ2
    int256 constant a5 = 5459815003314423907810; // eˆ(x5)
    int256 constant x6 = 200000000000000000000; // 2ˆ1
    int256 constant a6 = 738905609893065022723; // eˆ(x6)
    int256 constant x7 = 100000000000000000000; // 2ˆ0
    int256 constant a7 = 271828182845904523536; // eˆ(x7)
    int256 constant x8 = 50000000000000000000; // 2ˆ-1
    int256 constant a8 = 164872127070012814685; // eˆ(x8)
    int256 constant x9 = 25000000000000000000; // 2ˆ-2
    int256 constant a9 = 128402541668774148407; // eˆ(x9)
    int256 constant x10 = 12500000000000000000; // 2ˆ-3
    int256 constant a10 = 113314845306682631683; // eˆ(x10)
    int256 constant x11 = 6250000000000000000; // 2ˆ-4
    int256 constant a11 = 106449445891785942956; // eˆ(x11)

    /**
     * @dev Natural exponentiation (e^x) with signed 18 decimal fixed point exponent.
     *
     * Reverts if `x` is smaller than MIN_NATURAL_EXPONENT, or larger than `MAX_NATURAL_EXPONENT`.
     */
    function exp(int256 x) internal pure returns (int256) {
        unchecked {
            require(x >= MIN_NATURAL_EXPONENT && x <= MAX_NATURAL_EXPONENT, "Invalid exponent");

            if (x < 0) {
                // We only handle positive exponents: e^(-x) is computed as 1 / e^x. We can safely make x positive since it
                // fits in the signed 256 bit range (as it is larger than MIN_NATURAL_EXPONENT).
                // Fixed point division requires multiplying by ONE_18.
                return ((ONE_18 * ONE_18) / exp(-x));
            }

            // First, we use the fact that e^(x+y) = e^x * e^y to decompose x into a sum of powers of two, which we call x_n,
            // where x_n == 2^(7 - n), and e^x_n = a_n has been precomputed. We choose the first x_n, x0, to equal 2^7
            // because all larger powers are larger than MAX_NATURAL_EXPONENT, and therefore not present in the
            // decomposition.
            // At the end of this process we will have the product of all e^x_n = a_n that apply, and the remainder of this
            // decomposition, which will be lower than the smallest x_n.
            // exp(x) = k_0 * a_0 * k_1 * a_1 * ... + k_n * a_n * exp(remainder), where each k_n equals either 0 or 1.
            // We mutate x by subtracting x_n, making it the remainder of the decomposition.

            // The first two a_n (e^(2^7) and e^(2^6)) are too large if stored as 18 decimal numbers, and could cause
            // intermediate overflows. Instead we store them as plain integers, with 0 decimals.
            // Additionally, x0 + x1 is larger than MAX_NATURAL_EXPONENT, which means they will not both be present in the
            // decomposition.

            // For each x_n, we test if that term is present in the decomposition (if x is larger than it), and if so deduct
            // it and compute the accumulated product.

            int256 firstAN;
            if (x >= x0) {
                x -= x0;
                firstAN = a0;
            } else if (x >= x1) {
                x -= x1;
                firstAN = a1;
            } else {
                firstAN = 1; // One with no decimal places
            }

            // We now transform x into a 20 decimal fixed point number, to have enhanced precision when computing the
            // smaller terms.
            x *= 100;

            // `product` is the accumulated product of all a_n (except a0 and a1), which starts at 20 decimal fixed point
            // one. Recall that fixed point multiplication requires dividing by ONE_20.
            int256 product = ONE_20;

            if (x >= x2) {
                x -= x2;
                product = (product * a2) / ONE_20;
            }
            if (x >= x3) {
                x -= x3;
                product = (product * a3) / ONE_20;
            }
            if (x >= x4) {
                x -= x4;
                product = (product * a4) / ONE_20;
            }
            if (x >= x5) {
                x -= x5;
                product = (product * a5) / ONE_20;
            }
            if (x >= x6) {
                x -= x6;
                product = (product * a6) / ONE_20;
            }
            if (x >= x7) {
                x -= x7;
                product = (product * a7) / ONE_20;
            }
            if (x >= x8) {
                x -= x8;
                product = (product * a8) / ONE_20;
            }
            if (x >= x9) {
                x -= x9;
                product = (product * a9) / ONE_20;
            }

            // x10 and x11 are unnecessary here since we have high enough precision already.

            // Now we need to compute e^x, where x is small (in particular, it is smaller than x9). We use the Taylor series
            // expansion for e^x: 1 + x + (x^2 / 2!) + (x^3 / 3!) + ... + (x^n / n!).

            int256 seriesSum = ONE_20; // The initial one in the sum, with 20 decimal places.
            int256 term; // Each term in the sum, where the nth term is (x^n / n!).

            // The first term is simply x.
            term = x;
            seriesSum += term;

            // Each term (x^n / n!) equals the previous one times x, divided by n. Since x is a fixed point number,
            // multiplying by it requires dividing by ONE_20, but dividing by the non-fixed point n values does not.

            term = ((term * x) / ONE_20) / 2;
            seriesSum += term;

            term = ((term * x) / ONE_20) / 3;
            seriesSum += term;

            term = ((term * x) / ONE_20) / 4;
            seriesSum += term;

            term = ((term * x) / ONE_20) / 5;
            seriesSum += term;

            term = ((term * x) / ONE_20) / 6;
            seriesSum += term;

            term = ((term * x) / ONE_20) / 7;
            seriesSum += term;

            term = ((term * x) / ONE_20) / 8;
            seriesSum += term;

            term = ((term * x) / ONE_20) / 9;
            seriesSum += term;

            term = ((term * x) / ONE_20) / 10;
            seriesSum += term;

            term = ((term * x) / ONE_20) / 11;
            seriesSum += term;

            term = ((term * x) / ONE_20) / 12;
            seriesSum += term;

            // 12 Taylor terms are sufficient for 18 decimal precision.

            // We now have the first a_n (with no decimals), and the product of all other a_n present, and the Taylor
            // approximation of the exponentiation of the remainder (both with 20 decimals). All that remains is to multiply
            // all three (one 20 decimal fixed point multiplication, dividing by ONE_20, and one integer multiplication),
            // and then drop two digits to return an 18 decimal value.

            return (((product * seriesSum) / ONE_20) * firstAN) / 100;
        }
    }

    /**
     * @dev Natural logarithm (ln(a)) with signed 18 decimal fixed point argument.
     */
    function ln(int256 a) internal pure returns (int256) {
        unchecked {
            // The real natural logarithm is not defined for negative numbers or zero.
            require(a > 0, "out of bounds");
            if (LN_36_LOWER_BOUND < a && a < LN_36_UPPER_BOUND) {
                return _ln_36(a) / ONE_18;
            } else {
                return _ln(a);
            }
        }
    }

    /**
     * @dev Exponentiation (x^y) with unsigned 18 decimal fixed point base and exponent.
     *
     * Reverts if ln(x) * y is smaller than `MIN_NATURAL_EXPONENT`, or larger than `MAX_NATURAL_EXPONENT`.
     */
    function pow(uint256 x, uint256 y) internal pure returns (uint256) {
        unchecked {
            if (y == 0) {
                // We solve the 0^0 indetermination by making it equal one.
                return uint256(ONE_18);
            }

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

            // Instead of computing x^y directly, we instead rely on the properties of logarithms and exponentiation to
            // arrive at that r`esult. In particular, exp(ln(x)) = x, and ln(x^y) = y * ln(x). This means
            // x^y = exp(y * ln(x)).

            // The ln function takes a signed value, so we need to make sure x fits in the signed 256 bit range.
            require(x < 2 ** 255, "x out of bounds");
            int256 x_int256 = int256(x);

            // We will compute y * ln(x) in a single step. Depending on the value of x, we can either use ln or ln_36. In
            // both cases, we leave the division by ONE_18 (due to fixed point multiplication) to the end.

            // This prevents y * ln(x) from overflowing, and at the same time guarantees y fits in the signed 256 bit range.
            require(y < MILD_EXPONENT_BOUND, "y out of bounds");
            int256 y_int256 = int256(y);

            int256 logx_times_y;
            if (LN_36_LOWER_BOUND < x_int256 && x_int256 < LN_36_UPPER_BOUND) {
                int256 ln_36_x = _ln_36(x_int256);

                // ln_36_x has 36 decimal places, so multiplying by y_int256 isn't as straightforward, since we can't just
                // bring y_int256 to 36 decimal places, as it might overflow. Instead, we perform two 18 decimal
                // multiplications and add the results: one with the first 18 decimals of ln_36_x, and one with the
                // (downscaled) last 18 decimals.
                logx_times_y = ((ln_36_x / ONE_18) * y_int256 + ((ln_36_x % ONE_18) * y_int256) / ONE_18);
            } else {
                logx_times_y = _ln(x_int256) * y_int256;
            }
            logx_times_y /= ONE_18;

            // Finally, we compute exp(y * ln(x)) to arrive at x^y
            require(
                MIN_NATURAL_EXPONENT <= logx_times_y && logx_times_y <= MAX_NATURAL_EXPONENT,
                "product out of bounds"
            );

            return uint256(exp(logx_times_y));
        }
    }

    /**
     * @dev Internal natural logarithm (ln(a)) with signed 18 decimal fixed point argument.
     */
    function _ln(int256 a) private pure returns (int256) {
        unchecked {
            if (a < ONE_18) {
                // Since ln(a^k) = k * ln(a), we can compute ln(a) as ln(a) = ln((1/a)^(-1)) = - ln((1/a)). If a is less
                // than one, 1/a will be greater than one, and this if statement will not be entered in the recursive call.
                // Fixed point division requires multiplying by ONE_18.
                return (-_ln((ONE_18 * ONE_18) / a));
            }

            // First, we use the fact that ln^(a * b) = ln(a) + ln(b) to decompose ln(a) into a sum of powers of two, which
            // we call x_n, where x_n == 2^(7 - n), which are the natural logarithm of precomputed quantities a_n (that is,
            // ln(a_n) = x_n). We choose the first x_n, x0, to equal 2^7 because the exponential of all larger powers cannot
            // be represented as 18 fixed point decimal numbers in 256 bits, and are therefore larger than a.
            // At the end of this process we will have the sum of all x_n = ln(a_n) that apply, and the remainder of this
            // decomposition, which will be lower than the smallest a_n.
            // ln(a) = k_0 * x_0 + k_1 * x_1 + ... + k_n * x_n + ln(remainder), where each k_n equals either 0 or 1.
            // We mutate a by subtracting a_n, making it the remainder of the decomposition.

            // For reasons related to how `exp` works, the first two a_n (e^(2^7) and e^(2^6)) are not stored as fixed point
            // numbers with 18 decimals, but instead as plain integers with 0 decimals, so we need to multiply them by
            // ONE_18 to convert them to fixed point.
            // For each a_n, we test if that term is present in the decomposition (if a is larger than it), and if so divide
            // by it and compute the accumulated sum.

            int256 sum = 0;
            if (a >= a0 * ONE_18) {
                a /= a0; // Integer, not fixed point division
                sum += x0;
            }

            if (a >= a1 * ONE_18) {
                a /= a1; // Integer, not fixed point division
                sum += x1;
            }

            // All other a_n and x_n are stored as 20 digit fixed point numbers, so we convert the sum and a to this format.
            sum *= 100;
            a *= 100;

            // Because further a_n are  20 digit fixed point numbers, we multiply by ONE_20 when dividing by them.

            if (a >= a2) {
                a = (a * ONE_20) / a2;
                sum += x2;
            }

            if (a >= a3) {
                a = (a * ONE_20) / a3;
                sum += x3;
            }

            if (a >= a4) {
                a = (a * ONE_20) / a4;
                sum += x4;
            }

            if (a >= a5) {
                a = (a * ONE_20) / a5;
                sum += x5;
            }

            if (a >= a6) {
                a = (a * ONE_20) / a6;
                sum += x6;
            }

            if (a >= a7) {
                a = (a * ONE_20) / a7;
                sum += x7;
            }

            if (a >= a8) {
                a = (a * ONE_20) / a8;
                sum += x8;
            }

            if (a >= a9) {
                a = (a * ONE_20) / a9;
                sum += x9;
            }

            if (a >= a10) {
                a = (a * ONE_20) / a10;
                sum += x10;
            }

            if (a >= a11) {
                a = (a * ONE_20) / a11;
                sum += x11;
            }

            // a is now a small number (smaller than a_11, which roughly equals 1.06). This means we can use a Taylor series
            // that converges rapidly for values of `a` close to one - the same one used in ln_36.
            // Let z = (a - 1) / (a + 1).
            // ln(a) = 2 * (z + z^3 / 3 + z^5 / 5 + z^7 / 7 + ... + z^(2 * n + 1) / (2 * n + 1))

            // Recall that 20 digit fixed point division requires multiplying by ONE_20, and multiplication requires
            // division by ONE_20.
            int256 z = ((a - ONE_20) * ONE_20) / (a + ONE_20);
            int256 z_squared = (z * z) / ONE_20;

            // num is the numerator of the series: the z^(2 * n + 1) term
            int256 num = z;

            // seriesSum holds the accumulated sum of each term in the series, starting with the initial z
            int256 seriesSum = num;

            // In each step, the numerator is multiplied by z^2
            num = (num * z_squared) / ONE_20;
            seriesSum += num / 3;

            num = (num * z_squared) / ONE_20;
            seriesSum += num / 5;

            num = (num * z_squared) / ONE_20;
            seriesSum += num / 7;

            num = (num * z_squared) / ONE_20;
            seriesSum += num / 9;

            num = (num * z_squared) / ONE_20;
            seriesSum += num / 11;

            // 6 Taylor terms are sufficient for 36 decimal precision.

            // Finally, we multiply by 2 (non fixed point) to compute ln(remainder)
            seriesSum *= 2;

            // We now have the sum of all x_n present, and the Taylor approximation of the logarithm of the remainder (both
            // with 20 decimals). All that remains is to sum these two, and then drop two digits to return a 18 decimal
            // value.

            return (sum + seriesSum) / 100;
        }
    }

    /**
     * @dev Intrnal high precision (36 decimal places) natural logarithm (ln(x)) with signed 18 decimal fixed point argument,
     * for x close to one.
     *
     * Should only be used if x is between LN_36_LOWER_BOUND and LN_36_UPPER_BOUND.
     */
    function _ln_36(int256 x) private pure returns (int256) {
        unchecked {
            // Since ln(1) = 0, a value of x close to one will yield a very small result, which makes using 36 digits
            // worthwhile.

            // First, we transform x to a 36 digit fixed point value.
            x *= ONE_18;

            // We will use the following Taylor expansion, which converges very rapidly. Let z = (x - 1) / (x + 1).
            // ln(x) = 2 * (z + z^3 / 3 + z^5 / 5 + z^7 / 7 + ... + z^(2 * n + 1) / (2 * n + 1))

            // Recall that 36 digit fixed point division requires multiplying by ONE_36, and multiplication requires
            // division by ONE_36.
            int256 z = ((x - ONE_36) * ONE_36) / (x + ONE_36);
            int256 z_squared = (z * z) / ONE_36;

            // num is the numerator of the series: the z^(2 * n + 1) term
            int256 num = z;

            // seriesSum holds the accumulated sum of each term in the series, starting with the initial z
            int256 seriesSum = num;

            // In each step, the numerator is multiplied by z^2
            num = (num * z_squared) / ONE_36;
            seriesSum += num / 3;

            num = (num * z_squared) / ONE_36;
            seriesSum += num / 5;

            num = (num * z_squared) / ONE_36;
            seriesSum += num / 7;

            num = (num * z_squared) / ONE_36;
            seriesSum += num / 9;

            num = (num * z_squared) / ONE_36;
            seriesSum += num / 11;

            num = (num * z_squared) / ONE_36;
            seriesSum += num / 13;

            num = (num * z_squared) / ONE_36;
            seriesSum += num / 15;

            // 8 Taylor terms are sufficient for 36 decimal precision.

            // All that remains is multiplying by 2 (non fixed point).
            return seriesSum * 2;
        }
    }
}

// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity ^0.8.0;
import "../../interfaces/IPYieldToken.sol";
import "../../interfaces/IPPrincipalToken.sol";

import "./SYUtils.sol";
import "../libraries/math/PMath.sol";

type PYIndex is uint256;

library PYIndexLib {
    using PMath for uint256;
    using PMath for int256;

    function newIndex(IPYieldToken YT) internal returns (PYIndex) {
        return PYIndex.wrap(YT.pyIndexCurrent());
    }

    function syToAsset(PYIndex index, uint256 syAmount) internal pure returns (uint256) {
        return SYUtils.syToAsset(PYIndex.unwrap(index), syAmount);
    }

    function assetToSy(PYIndex index, uint256 assetAmount) internal pure returns (uint256) {
        return SYUtils.assetToSy(PYIndex.unwrap(index), assetAmount);
    }

    function assetToSyUp(PYIndex index, uint256 assetAmount) internal pure returns (uint256) {
        return SYUtils.assetToSyUp(PYIndex.unwrap(index), assetAmount);
    }

    function syToAssetUp(PYIndex index, uint256 syAmount) internal pure returns (uint256) {
        uint256 _index = PYIndex.unwrap(index);
        return SYUtils.syToAssetUp(_index, syAmount);
    }

    function syToAsset(PYIndex index, int256 syAmount) internal pure returns (int256) {
        int256 sign = syAmount < 0 ? int256(-1) : int256(1);
        return sign * (SYUtils.syToAsset(PYIndex.unwrap(index), syAmount.abs())).Int();
    }

    function assetToSy(PYIndex index, int256 assetAmount) internal pure returns (int256) {
        int256 sign = assetAmount < 0 ? int256(-1) : int256(1);
        return sign * (SYUtils.assetToSy(PYIndex.unwrap(index), assetAmount.abs())).Int();
    }

    function assetToSyUp(PYIndex index, int256 assetAmount) internal pure returns (int256) {
        int256 sign = assetAmount < 0 ? int256(-1) : int256(1);
        return sign * (SYUtils.assetToSyUp(PYIndex.unwrap(index), assetAmount.abs())).Int();
    }
}

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

library MiniHelpers {
    function isCurrentlyExpired(uint256 expiry) internal view returns (bool) {
        return (expiry <= block.timestamp);
    }

    function isExpired(uint256 expiry, uint256 blockTime) internal pure returns (bool) {
        return (expiry <= blockTime);
    }

    function isTimeInThePast(uint256 timestamp) internal view returns (bool) {
        return (timestamp <= block.timestamp); // same definition as isCurrentlyExpired
    }
}

File 54 of 74 : Errors.sol
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity ^0.8.0;

library Errors {
    // BulkSeller
    error BulkInsufficientSyForTrade(uint256 currentAmount, uint256 requiredAmount);
    error BulkInsufficientTokenForTrade(uint256 currentAmount, uint256 requiredAmount);
    error BulkInSufficientSyOut(uint256 actualSyOut, uint256 requiredSyOut);
    error BulkInSufficientTokenOut(uint256 actualTokenOut, uint256 requiredTokenOut);
    error BulkInsufficientSyReceived(uint256 actualBalance, uint256 requiredBalance);
    error BulkNotMaintainer();
    error BulkNotAdmin();
    error BulkSellerAlreadyExisted(address token, address SY, address bulk);
    error BulkSellerInvalidToken(address token, address SY);
    error BulkBadRateTokenToSy(uint256 actualRate, uint256 currentRate, uint256 eps);
    error BulkBadRateSyToToken(uint256 actualRate, uint256 currentRate, uint256 eps);

    // APPROX
    error ApproxFail();
    error ApproxParamsInvalid(uint256 guessMin, uint256 guessMax, uint256 eps);
    error ApproxBinarySearchInputInvalid(
        uint256 approxGuessMin,
        uint256 approxGuessMax,
        uint256 minGuessMin,
        uint256 maxGuessMax
    );

    // MARKET + MARKET MATH CORE
    error MarketExpired();
    error MarketZeroAmountsInput();
    error MarketZeroAmountsOutput();
    error MarketZeroLnImpliedRate();
    error MarketInsufficientPtForTrade(int256 currentAmount, int256 requiredAmount);
    error MarketInsufficientPtReceived(uint256 actualBalance, uint256 requiredBalance);
    error MarketInsufficientSyReceived(uint256 actualBalance, uint256 requiredBalance);
    error MarketZeroTotalPtOrTotalAsset(int256 totalPt, int256 totalAsset);
    error MarketExchangeRateBelowOne(int256 exchangeRate);
    error MarketProportionMustNotEqualOne();
    error MarketRateScalarBelowZero(int256 rateScalar);
    error MarketScalarRootBelowZero(int256 scalarRoot);
    error MarketProportionTooHigh(int256 proportion, int256 maxProportion);

    error OracleUninitialized();
    error OracleTargetTooOld(uint32 target, uint32 oldest);
    error OracleZeroCardinality();

    error MarketFactoryExpiredPt();
    error MarketFactoryInvalidPt();
    error MarketFactoryMarketExists();

    error MarketFactoryLnFeeRateRootTooHigh(uint80 lnFeeRateRoot, uint256 maxLnFeeRateRoot);
    error MarketFactoryOverriddenFeeTooHigh(uint80 overriddenFee, uint256 marketLnFeeRateRoot);
    error MarketFactoryReserveFeePercentTooHigh(uint8 reserveFeePercent, uint8 maxReserveFeePercent);
    error MarketFactoryZeroTreasury();
    error MarketFactoryInitialAnchorTooLow(int256 initialAnchor, int256 minInitialAnchor);
    error MFNotPendleMarket(address addr);

    // ROUTER
    error RouterInsufficientLpOut(uint256 actualLpOut, uint256 requiredLpOut);
    error RouterInsufficientSyOut(uint256 actualSyOut, uint256 requiredSyOut);
    error RouterInsufficientPtOut(uint256 actualPtOut, uint256 requiredPtOut);
    error RouterInsufficientYtOut(uint256 actualYtOut, uint256 requiredYtOut);
    error RouterInsufficientPYOut(uint256 actualPYOut, uint256 requiredPYOut);
    error RouterInsufficientTokenOut(uint256 actualTokenOut, uint256 requiredTokenOut);
    error RouterInsufficientSyRepay(uint256 actualSyRepay, uint256 requiredSyRepay);
    error RouterInsufficientPtRepay(uint256 actualPtRepay, uint256 requiredPtRepay);
    error RouterNotAllSyUsed(uint256 netSyDesired, uint256 netSyUsed);

    error RouterTimeRangeZero();
    error RouterCallbackNotPendleMarket(address caller);
    error RouterInvalidAction(bytes4 selector);
    error RouterInvalidFacet(address facet);

    error RouterKyberSwapDataZero();

    error SimulationResults(bool success, bytes res);

    // YIELD CONTRACT
    error YCExpired();
    error YCNotExpired();
    error YieldContractInsufficientSy(uint256 actualSy, uint256 requiredSy);
    error YCNothingToRedeem();
    error YCPostExpiryDataNotSet();
    error YCNoFloatingSy();

    // YieldFactory
    error YCFactoryInvalidExpiry();
    error YCFactoryYieldContractExisted();
    error YCFactoryZeroExpiryDivisor();
    error YCFactoryZeroTreasury();
    error YCFactoryInterestFeeRateTooHigh(uint256 interestFeeRate, uint256 maxInterestFeeRate);
    error YCFactoryRewardFeeRateTooHigh(uint256 newRewardFeeRate, uint256 maxRewardFeeRate);

    // SY
    error SYInvalidTokenIn(address token);
    error SYInvalidTokenOut(address token);
    error SYZeroDeposit();
    error SYZeroRedeem();
    error SYInsufficientSharesOut(uint256 actualSharesOut, uint256 requiredSharesOut);
    error SYInsufficientTokenOut(uint256 actualTokenOut, uint256 requiredTokenOut);

    // SY-specific
    error SYQiTokenMintFailed(uint256 errCode);
    error SYQiTokenRedeemFailed(uint256 errCode);
    error SYQiTokenRedeemRewardsFailed(uint256 rewardAccruedType0, uint256 rewardAccruedType1);
    error SYQiTokenBorrowRateTooHigh(uint256 borrowRate, uint256 borrowRateMax);

    error SYCurveInvalidPid();
    error SYCurve3crvPoolNotFound();

    error SYApeDepositAmountTooSmall(uint256 amountDeposited);
    error SYBalancerInvalidPid();
    error SYInvalidRewardToken(address token);

    error SYStargateRedeemCapExceeded(uint256 amountLpDesired, uint256 amountLpRedeemable);

    error SYBalancerReentrancy();

    error NotFromTrustedRemote(uint16 srcChainId, bytes path);

    error ApxETHNotEnoughBuffer();

    // Liquidity Mining
    error VCInactivePool(address pool);
    error VCPoolAlreadyActive(address pool);
    error VCZeroVePendle(address user);
    error VCExceededMaxWeight(uint256 totalWeight, uint256 maxWeight);
    error VCEpochNotFinalized(uint256 wTime);
    error VCPoolAlreadyAddAndRemoved(address pool);

    error VEInvalidNewExpiry(uint256 newExpiry);
    error VEExceededMaxLockTime();
    error VEInsufficientLockTime();
    error VENotAllowedReduceExpiry();
    error VEZeroAmountLocked();
    error VEPositionNotExpired();
    error VEZeroPosition();
    error VEZeroSlope(uint128 bias, uint128 slope);
    error VEReceiveOldSupply(uint256 msgTime);

    error GCNotPendleMarket(address caller);
    error GCNotVotingController(address caller);

    error InvalidWTime(uint256 wTime);
    error ExpiryInThePast(uint256 expiry);
    error ChainNotSupported(uint256 chainId);

    error FDTotalAmountFundedNotMatch(uint256 actualTotalAmount, uint256 expectedTotalAmount);
    error FDEpochLengthMismatch();
    error FDInvalidPool(address pool);
    error FDPoolAlreadyExists(address pool);
    error FDInvalidNewFinishedEpoch(uint256 oldFinishedEpoch, uint256 newFinishedEpoch);
    error FDInvalidStartEpoch(uint256 startEpoch);
    error FDInvalidWTimeFund(uint256 lastFunded, uint256 wTime);
    error FDFutureFunding(uint256 lastFunded, uint256 currentWTime);

    error BDInvalidEpoch(uint256 epoch, uint256 startTime);

    // Cross-Chain
    error MsgNotFromSendEndpoint(uint16 srcChainId, bytes path);
    error MsgNotFromReceiveEndpoint(address sender);
    error InsufficientFeeToSendMsg(uint256 currentFee, uint256 requiredFee);
    error ApproxDstExecutionGasNotSet();
    error InvalidRetryData();

    // GENERIC MSG
    error ArrayLengthMismatch();
    error ArrayEmpty();
    error ArrayOutOfBounds();
    error ZeroAddress();
    error FailedToSendEther();
    error InvalidMerkleProof();

    error OnlyLayerZeroEndpoint();
    error OnlyYT();
    error OnlyYCFactory();
    error OnlyWhitelisted();

    // Swap Aggregator
    error SAInsufficientTokenIn(address tokenIn, uint256 amountExpected, uint256 amountActual);
    error UnsupportedSelector(uint256 aggregatorType, bytes4 selector);
}

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

pragma solidity ^0.8.20;

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

/**
 * @dev Interface for the optional metadata functions from the 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);
}

File 56 of 74 : IERC721.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (interfaces/IERC721.sol)

pragma solidity ^0.8.20;

import {IERC721} from "../token/ERC721/IERC721.sol";

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.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 Returns the addition of two unsigned integers, with an success flag (no overflow).
     */
    function tryAdd(uint256 a, uint256 b) internal pure returns (bool success, uint256 result) {
        unchecked {
            uint256 c = a + b;
            if (c < a) return (false, 0);
            return (true, c);
        }
    }

    /**
     * @dev Returns the subtraction of two unsigned integers, with an success flag (no overflow).
     */
    function trySub(uint256 a, uint256 b) internal pure returns (bool success, uint256 result) {
        unchecked {
            if (b > a) return (false, 0);
            return (true, a - b);
        }
    }

    /**
     * @dev Returns the multiplication of two unsigned integers, with an success flag (no overflow).
     */
    function tryMul(uint256 a, uint256 b) internal pure returns (bool success, uint256 result) {
        unchecked {
            // Gas optimization: this is cheaper than requiring 'a' not being zero, but the
            // benefit is lost if 'b' is also tested.
            // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522
            if (a == 0) return (true, 0);
            uint256 c = a * b;
            if (c / a != b) return (false, 0);
            return (true, c);
        }
    }

    /**
     * @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 {
            if (b == 0) return (false, 0);
            return (true, 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 {
            if (b == 0) return (false, 0);
            return (true, a % b);
        }
    }

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

    /**
     * @dev Returns the smallest of two numbers.
     */
    function min(uint256 a, uint256 b) internal pure returns (uint256) {
        return 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 a == 0 ? 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 {
            // 512-bit multiply [prod1 prod0] = x * y. Compute the product mod 2²⁵⁶ and mod 2²⁵⁶ - 1, then use
            // use the Chinese Remainder Theorem to reconstruct the 512 bit result. The result is stored in two 256
            // variables such that product = prod1 * 2²⁵⁶ + prod0.
            uint256 prod0 = x * y; // Least significant 256 bits of the product
            uint256 prod1; // Most significant 256 bits of the product
            assembly {
                let mm := mulmod(x, y, not(0))
                prod1 := sub(sub(mm, prod0), lt(mm, prod0))
            }

            // Handle non-overflow cases, 256 by 256 division.
            if (prod1 == 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 prod0 / denominator;
            }

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

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

            // Make division exact by subtracting the remainder from [prod1 prod0].
            uint256 remainder;
            assembly {
                // Compute remainder using mulmod.
                remainder := mulmod(x, y, denominator)

                // Subtract 256 bit number from 512 bit number.
                prod1 := sub(prod1, gt(remainder, prod0))
                prod0 := sub(prod0, 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 {
                // Divide denominator by twos.
                denominator := div(denominator, twos)

                // Divide [prod1 prod0] by twos.
                prod0 := div(prod0, 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 prod1 into prod0.
            prod0 |= prod1 * 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 prod1
            // is no longer required.
            result = prod0 * 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 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, expect 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 Ferma's little theorem and get the
     * inverse using `Math.modExp(a, n - 2, n)`.
     */
    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 x < 0 ? (n - uint256(-x)) : uint256(x); // Wrap the result if it's negative.
        }
    }

    /**
     * @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 has 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);
        /// @solidity memory-safe-assembly
        assembly {
            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);

        /// @solidity memory-safe-assembly
        assembly {
            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 value) internal pure returns (uint256) {
        uint256 result = 0;
        uint256 exp;
        unchecked {
            exp = 128 * SafeCast.toUint(value > (1 << 128) - 1);
            value >>= exp;
            result += exp;

            exp = 64 * SafeCast.toUint(value > (1 << 64) - 1);
            value >>= exp;
            result += exp;

            exp = 32 * SafeCast.toUint(value > (1 << 32) - 1);
            value >>= exp;
            result += exp;

            exp = 16 * SafeCast.toUint(value > (1 << 16) - 1);
            value >>= exp;
            result += exp;

            exp = 8 * SafeCast.toUint(value > (1 << 8) - 1);
            value >>= exp;
            result += exp;

            exp = 4 * SafeCast.toUint(value > (1 << 4) - 1);
            value >>= exp;
            result += exp;

            exp = 2 * SafeCast.toUint(value > (1 << 2) - 1);
            value >>= exp;
            result += exp;

            result += SafeCast.toUint(value > 1);
        }
        return result;
    }

    /**
     * @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 value) internal pure returns (uint256) {
        uint256 result = 0;
        uint256 isGt;
        unchecked {
            isGt = SafeCast.toUint(value > (1 << 128) - 1);
            value >>= isGt * 128;
            result += isGt * 16;

            isGt = SafeCast.toUint(value > (1 << 64) - 1);
            value >>= isGt * 64;
            result += isGt * 8;

            isGt = SafeCast.toUint(value > (1 << 32) - 1);
            value >>= isGt * 32;
            result += isGt * 4;

            isGt = SafeCast.toUint(value > (1 << 16) - 1);
            value >>= isGt * 16;
            result += isGt * 2;

            result += SafeCast.toUint(value > (1 << 8) - 1);
        }
        return result;
    }

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

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

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/math/SignedMath.sol)

pragma solidity ^0.8.20;

/**
 * @dev Standard signed math utilities missing in the Solidity language.
 */
library SignedMath {
    /**
     * @dev Returns the largest of two signed numbers.
     */
    function max(int256 a, int256 b) internal pure returns (int256) {
        return a > b ? a : b;
    }

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

    /**
     * @dev Returns the average of two signed numbers without overflow.
     * The result is rounded towards zero.
     */
    function average(int256 a, int256 b) internal pure returns (int256) {
        // Formula from the book "Hacker's Delight"
        int256 x = (a & b) + ((a ^ b) >> 1);
        return x + (int256(uint256(x) >> 255) & (a ^ b));
    }

    /**
     * @dev Returns the absolute unsigned value of a signed value.
     */
    function abs(int256 n) internal pure returns (uint256) {
        unchecked {
            // Formula from the "Bit Twiddling Hacks" by Sean Eron Anderson.
            // Since `n` is a signed integer, the generated bytecode will use the SAR opcode to perform the right shift,
            // taking advantage of the most significant (or "sign" bit) in two's complement representation.
            // This opcode adds new most significant bits set to the value of the previous most significant bit. As a result,
            // the mask will either be `bytes(0)` (if n is positive) or `~bytes32(0)` (if n is negative).
            int256 mask = n >> 255;

            // A `bytes(0)` mask leaves the input unchanged, while a `~bytes32(0)` mask complements it.
            return uint256((n + mask) ^ mask);
        }
    }
}

// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.28;

import {Math} from "openzeppelin5/utils/math/Math.sol";

import {ISiloOracle} from "../interfaces/ISiloOracle.sol";
import {SiloStdLib, ISiloConfig, IShareToken, ISilo} from "./SiloStdLib.sol";
import {SiloMathLib} from "./SiloMathLib.sol";
import {Rounding} from "./Rounding.sol";

library SiloSolvencyLib {
    using Math for uint256;

    struct LtvData {
        ISiloOracle collateralOracle;
        ISiloOracle debtOracle;
        uint256 borrowerProtectedAssets;
        uint256 borrowerCollateralAssets;
        uint256 borrowerDebtAssets;
    }

    uint256 internal constant _PRECISION_DECIMALS = 1e18;
    uint256 internal constant _INFINITY = type(uint256).max;

    /// @notice Determines if a borrower is solvent based on the Loan-to-Value (LTV) ratio
    /// @param _collateralConfig Configuration data for the collateral
    /// @param _debtConfig Configuration data for the debt
    /// @param _borrower Address of the borrower to check solvency for
    /// @param _accrueInMemory Determines whether or not to consider un-accrued interest in calculations
    /// @return True if the borrower is solvent, false otherwise
    function isSolvent(
        ISiloConfig.ConfigData memory _collateralConfig,
        ISiloConfig.ConfigData memory _debtConfig,
        address _borrower,
        ISilo.AccrueInterestInMemory _accrueInMemory
    ) internal view returns (bool) {
        if (_debtConfig.silo == address(0)) return true; // no debt, so solvent

        uint256 ltv = getLtv(
            _collateralConfig,
            _debtConfig,
            _borrower,
            ISilo.OracleType.Solvency,
            _accrueInMemory,
            IShareToken(_debtConfig.debtShareToken).balanceOf(_borrower)
        );

        return ltv <= _collateralConfig.lt;
    }

    /// @notice Determines if a borrower's Loan-to-Value (LTV) ratio is below the maximum allowed LTV
    /// @param _collateralConfig Configuration data for the collateral
    /// @param _debtConfig Configuration data for the debt
    /// @param _borrower Address of the borrower to check against max LTV
    /// @param _accrueInMemory Determines whether or not to consider un-accrued interest in calculations
    /// @return True if the borrower's LTV is below the maximum, false otherwise
    function isBelowMaxLtv(
        ISiloConfig.ConfigData memory _collateralConfig,
        ISiloConfig.ConfigData memory _debtConfig,
        address _borrower,
        ISilo.AccrueInterestInMemory _accrueInMemory
    ) internal view returns (bool) {
        uint256 debtShareBalance = IShareToken(_debtConfig.debtShareToken).balanceOf(_borrower);
        if (debtShareBalance == 0) return true;

        uint256 ltv = getLtv(
            _collateralConfig,
            _debtConfig,
            _borrower,
            ISilo.OracleType.MaxLtv,
            _accrueInMemory,
            debtShareBalance
        );

        return ltv <= _collateralConfig.maxLtv;
    }

    /// @notice Retrieves assets data required for LTV calculations
    /// @param _collateralConfig Configuration data for the collateral
    /// @param _debtConfig Configuration data for the debt
    /// @param _borrower Address of the borrower whose LTV data is to be calculated
    /// @param _oracleType Specifies whether to use the MaxLTV or Solvency oracle type for calculations
    /// @param _accrueInMemory Determines whether or not to consider un-accrued interest in calculations
    /// @param _debtShareBalanceCached Cached value of debt share balance for the borrower. If debt shares of
    /// `_borrower` is unknown, simply pass `0`.
    /// @return ltvData Data structure containing necessary data to compute LTV
    function getAssetsDataForLtvCalculations( // solhint-disable-line function-max-lines
        ISiloConfig.ConfigData memory _collateralConfig,
        ISiloConfig.ConfigData memory _debtConfig,
        address _borrower,
        ISilo.OracleType _oracleType,
        ISilo.AccrueInterestInMemory _accrueInMemory,
        uint256 _debtShareBalanceCached
    ) internal view returns (LtvData memory ltvData) {
        if (_collateralConfig.token != _debtConfig.token) {
            // When calculating maxLtv, use maxLtv oracle.
            (ltvData.collateralOracle, ltvData.debtOracle) = _oracleType == ISilo.OracleType.MaxLtv
                ? (ISiloOracle(_collateralConfig.maxLtvOracle), ISiloOracle(_debtConfig.maxLtvOracle))
                : (ISiloOracle(_collateralConfig.solvencyOracle), ISiloOracle(_debtConfig.solvencyOracle));
        }

        uint256 totalShares;
        uint256 shares;

        (shares, totalShares) = SiloStdLib.getSharesAndTotalSupply(
            _collateralConfig.protectedShareToken, _borrower, 0 /* no cache */
        );

        (
            uint256 totalCollateralAssets, uint256 totalProtectedAssets
        ) = ISilo(_collateralConfig.silo).getCollateralAndProtectedTotalsStorage();

        ltvData.borrowerProtectedAssets = SiloMathLib.convertToAssets(
            shares, totalProtectedAssets, totalShares, Rounding.COLLATERAL_TO_ASSETS, ISilo.AssetType.Protected
        );

        (shares, totalShares) = SiloStdLib.getSharesAndTotalSupply(
            _collateralConfig.collateralShareToken, _borrower, 0 /* no cache */
        );

        totalCollateralAssets = _accrueInMemory == ISilo.AccrueInterestInMemory.Yes
            ? SiloStdLib.getTotalCollateralAssetsWithInterest(
                _collateralConfig.silo,
                _collateralConfig.interestRateModel,
                _collateralConfig.daoFee,
                _collateralConfig.deployerFee
            )
            : totalCollateralAssets;

        ltvData.borrowerCollateralAssets = SiloMathLib.convertToAssets(
            shares, totalCollateralAssets, totalShares, Rounding.COLLATERAL_TO_ASSETS, ISilo.AssetType.Collateral
        );

        (shares, totalShares) = SiloStdLib.getSharesAndTotalSupply(
            _debtConfig.debtShareToken, _borrower, _debtShareBalanceCached
        );

        uint256 totalDebtAssets = _accrueInMemory == ISilo.AccrueInterestInMemory.Yes
            ? SiloStdLib.getTotalDebtAssetsWithInterest(_debtConfig.silo, _debtConfig.interestRateModel)
            : ISilo(_debtConfig.silo).getTotalAssetsStorage(ISilo.AssetType.Debt);

        // BORROW value -> to assets -> UP
        ltvData.borrowerDebtAssets = SiloMathLib.convertToAssets(
            shares, totalDebtAssets, totalShares, Rounding.DEBT_TO_ASSETS, ISilo.AssetType.Debt
        );
    }

    /// @notice Calculates the Loan-To-Value (LTV) ratio for a given borrower
    /// @param _collateralConfig Configuration data related to the collateral asset
    /// @param _debtConfig Configuration data related to the debt asset
    /// @param _borrower Address of the borrower whose LTV is to be computed
    /// @param _oracleType Oracle type to use for fetching the asset prices
    /// @param _accrueInMemory Determines whether or not to consider un-accrued interest in calculations
    /// @return ltvInDp The computed LTV ratio in 18 decimals precision
    function getLtv(
        ISiloConfig.ConfigData memory _collateralConfig,
        ISiloConfig.ConfigData memory _debtConfig,
        address _borrower,
        ISilo.OracleType _oracleType,
        ISilo.AccrueInterestInMemory _accrueInMemory,
        uint256 _debtShareBalance
    ) internal view returns (uint256 ltvInDp) {
        if (_debtShareBalance == 0) return 0;

        LtvData memory ltvData = getAssetsDataForLtvCalculations(
            _collateralConfig, _debtConfig, _borrower, _oracleType, _accrueInMemory, _debtShareBalance
        );

        if (ltvData.borrowerDebtAssets == 0) return 0;

        (,, ltvInDp) = calculateLtv(ltvData, _collateralConfig.token, _debtConfig.token);
    }

    /// @notice Calculates the Loan-to-Value (LTV) ratio based on provided collateral and debt data
    /// @dev calculation never reverts, if there is revert, then it is because of oracle
    /// @param _ltvData Data structure containing relevant information to calculate LTV
    /// @param _collateralToken Address of the collateral token
    /// @param _debtAsset Address of the debt token
    /// @return sumOfBorrowerCollateralValue Total value of borrower's collateral
    /// @return totalBorrowerDebtValue Total debt value for the borrower
    /// @return ltvInDp Calculated LTV in 18 decimal precision
    function calculateLtv(
        SiloSolvencyLib.LtvData memory _ltvData, address _collateralToken, address _debtAsset)
        internal
        view
        returns (uint256 sumOfBorrowerCollateralValue, uint256 totalBorrowerDebtValue, uint256 ltvInDp)
    {
        (
            sumOfBorrowerCollateralValue, totalBorrowerDebtValue
        ) = getPositionValues(_ltvData, _collateralToken, _debtAsset);

        if (sumOfBorrowerCollateralValue == 0 && totalBorrowerDebtValue == 0) {
            return (0, 0, 0);
        } else if (sumOfBorrowerCollateralValue == 0) {
            ltvInDp = _INFINITY;
        } else {
            ltvInDp = ltvMath(totalBorrowerDebtValue, sumOfBorrowerCollateralValue);
        }
    }

    /// @notice Computes the value of collateral and debt based on given LTV data and asset addresses
    /// @param _ltvData Data structure containing the assets data required for LTV calculations
    /// @param _collateralAsset Address of the collateral asset
    /// @param _debtAsset Address of the debt asset
    /// @return sumOfCollateralValue Total value of collateral assets considering both protected and regular collateral
    /// assets
    /// @return debtValue Total value of debt assets
    function getPositionValues(LtvData memory _ltvData, address _collateralAsset, address _debtAsset)
        internal
        view
        returns (uint256 sumOfCollateralValue, uint256 debtValue)
    {
        uint256 sumOfCollateralAssets;
        
        sumOfCollateralAssets = _ltvData.borrowerProtectedAssets + _ltvData.borrowerCollateralAssets;

        if (sumOfCollateralAssets != 0) {
            // if no oracle is set, assume price 1, we should also not set oracle for quote token
            sumOfCollateralValue = address(_ltvData.collateralOracle) != address(0)
                ? _ltvData.collateralOracle.quote(sumOfCollateralAssets, _collateralAsset)
                : sumOfCollateralAssets;
        }

        if (_ltvData.borrowerDebtAssets != 0) {
            // if no oracle is set, assume price 1, we should also not set oracle for quote token
            debtValue = address(_ltvData.debtOracle) != address(0)
                ? _ltvData.debtOracle.quote(_ltvData.borrowerDebtAssets, _debtAsset)
                : _ltvData.borrowerDebtAssets;
        }
    }

    function ltvMath(uint256 _totalBorrowerDebtValue, uint256 _sumOfBorrowerCollateralValue)
        internal
        pure
        returns (uint256 ltvInDp)
    {
        ltvInDp = _totalBorrowerDebtValue.mulDiv(_PRECISION_DECIMALS, _sumOfBorrowerCollateralValue, Rounding.LTV);
    }
}

// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.28;

import {Math} from "openzeppelin5/utils/math/Math.sol";
import {Rounding} from "../lib/Rounding.sol";
import {ISilo} from "../interfaces/ISilo.sol";

library SiloMathLib {
    using Math for uint256;

    uint256 internal constant _PRECISION_DECIMALS = 1e18;

    uint256 internal constant _DECIMALS_OFFSET = 3;

    /// @dev this is constant version of openzeppelin5/contracts/token/ERC20/extensions/ERC4626._decimalsOffset
    uint256 internal constant _DECIMALS_OFFSET_POW = 10 ** _DECIMALS_OFFSET;

    /// @notice Returns available liquidity to be borrowed
    /// @dev Accrued interest is entirely added to `debtAssets` but only part of it is added to `collateralAssets`. The
    ///      difference is DAO's and deployer's cut. That means DAO's and deployer's cut is not considered a borrowable
    ///      liquidity.
    function liquidity(uint256 _collateralAssets, uint256 _debtAssets) internal pure returns (uint256 liquidAssets) {
        unchecked {
            // we checked the underflow
            liquidAssets = _debtAssets > _collateralAssets ? 0 : _collateralAssets - _debtAssets;
        }
    }

    /// @notice Calculate collateral assets with accrued interest and associated fees
    /// @param _collateralAssets The total amount of collateral assets
    /// @param _debtAssets The total amount of debt assets
    /// @param _rcomp Compound interest rate for debt
    /// @param _daoFee The fee (in 18 decimals points) to be taken for the DAO
    /// @param _deployerFee The fee (in 18 decimals points) to be taken for the deployer
    /// @return collateralAssetsWithInterest The total collateral assets including the accrued interest
    /// @return debtAssetsWithInterest The debt assets with accrued interest
    /// @return daoAndDeployerRevenue Total fees amount to be split between DAO and deployer
    /// @return accruedInterest The total accrued interest
    function getCollateralAmountsWithInterest(
        uint256 _collateralAssets,
        uint256 _debtAssets,
        uint256 _rcomp,
        uint256 _daoFee,
        uint256 _deployerFee
    )
        internal
        pure
        returns (
            uint256 collateralAssetsWithInterest,
            uint256 debtAssetsWithInterest,
            uint256 daoAndDeployerRevenue,
            uint256 accruedInterest
        )
    {
        (debtAssetsWithInterest, accruedInterest) = getDebtAmountsWithInterest(_debtAssets, _rcomp);

        uint256 fees;

        // _daoFee and _deployerFee are expected to be less than 1e18, so we will not overflow
        unchecked { fees = _daoFee + _deployerFee; }

        daoAndDeployerRevenue = mulDivOverflow(accruedInterest, fees, _PRECISION_DECIMALS);

        // we will not underflow because daoAndDeployerRevenue is chunk of accruedInterest
        uint256 collateralInterest = accruedInterest - daoAndDeployerRevenue;

        uint256 cap;
        // save to uncheck because variable can not be more than max
        unchecked { cap = type(uint256).max - _collateralAssets; }

        if (cap < collateralInterest) {
            // avoid overflow on interest
            collateralInterest = cap;
        }

        // safe to uncheck because of cap
        unchecked {  collateralAssetsWithInterest = _collateralAssets + collateralInterest; }
    }

    /// @notice Calculate the debt assets with accrued interest, it should never revert with over/under flow
    /// @param _totalDebtAssets The total amount of debt assets before accrued interest
    /// @param _rcomp Compound interest rate for the debt in 18 decimal precision
    /// @return debtAssetsWithInterest The debt assets including the accrued interest
    /// @return accruedInterest The total amount of interest accrued on the debt assets
    function getDebtAmountsWithInterest(uint256 _totalDebtAssets, uint256 _rcomp)
        internal
        pure
        returns (uint256 debtAssetsWithInterest, uint256 accruedInterest)
    {
        if (_totalDebtAssets == 0 || _rcomp == 0) {
            return (_totalDebtAssets, 0);
        }

        accruedInterest = mulDivOverflow(_totalDebtAssets, _rcomp, _PRECISION_DECIMALS);

        unchecked {
            // We intentionally allow overflow here, to prevent transaction revert due to interest calculation.
            debtAssetsWithInterest = _totalDebtAssets + accruedInterest;

            // If overflow occurs, we skip accruing interest.
            if (debtAssetsWithInterest < _totalDebtAssets) {
                debtAssetsWithInterest = _totalDebtAssets;
                accruedInterest = 0;
            }
        }
    }

    /// @notice Calculates fraction between borrowed and deposited amount of tokens denominated in percentage
    /// @dev It assumes `_dp` = 100%.
    /// @param _dp decimal points used by model
    /// @param _collateralAssets current total deposits for assets
    /// @param _debtAssets current total borrows for assets
    /// @return utilization value, capped to 100%
    /// Limiting utilization ratio by 100% max will allows us to perform better interest rate computations
    /// and should not affect any other part of protocol. It is possible to go over 100% only when bad debt.
    function calculateUtilization(uint256 _dp, uint256 _collateralAssets, uint256 _debtAssets)
        internal
        pure
        returns (uint256 utilization)
    {
        if (_collateralAssets == 0 || _debtAssets == 0 || _dp == 0) return 0;

        /*
            how to prevent overflow on: _debtAssets.mulDiv(_dp, _collateralAssets, Rounding.ACCRUED_INTEREST):
            1. max > _debtAssets * _dp / _collateralAssets
            2. max / _dp > _debtAssets / _collateralAssets
        */
        if (type(uint256).max / _dp > _debtAssets / _collateralAssets) {
            utilization = _debtAssets.mulDiv(_dp, _collateralAssets, Rounding.ACCRUED_INTEREST);
            // cap at 100%
            if (utilization > _dp) utilization = _dp;
        } else {
            // we have overflow
            utilization = _dp;
        }
    }

    function convertToAssetsOrToShares(
        uint256 _assets,
        uint256 _shares,
        uint256 _totalAssets,
        uint256 _totalShares,
        Math.Rounding _roundingToAssets,
        Math.Rounding _roundingToShares,
        ISilo.AssetType _assetType
    ) internal pure returns (uint256 assets, uint256 shares) {
        if (_assets == 0) {
            require(_shares != 0, ISilo.InputZeroShares());
            shares = _shares;
            assets = convertToAssets(_shares, _totalAssets, _totalShares, _roundingToAssets, _assetType);
            require(assets != 0, ISilo.ReturnZeroAssets());
        } else if (_shares == 0) {
            shares = convertToShares(_assets, _totalAssets, _totalShares, _roundingToShares, _assetType);
            assets = _assets;
            require(shares != 0, ISilo.ReturnZeroShares());
        } else {
            revert ISilo.InputCanBeAssetsOrShares();
        }
    }

    /// @dev Math for collateral is exact copy of
    ///      openzeppelin5/contracts/token/ERC20/extensions/ERC4626._convertToShares
    function convertToShares(
        uint256 _assets,
        uint256 _totalAssets,
        uint256 _totalShares,
        Math.Rounding _rounding,
        ISilo.AssetType _assetType
    ) internal pure returns (uint256 shares) {
        (uint256 totalShares, uint256 totalAssets) = _commonConvertTo(_totalAssets, _totalShares, _assetType);

        // initially, in case of debt, if silo is empty we return shares==assets
        // for collateral, this will never be the case, because we are adding `+1` and offset in `_commonConvertTo`
        if (totalShares == 0) return _assets;

        shares = _assets.mulDiv(totalShares, totalAssets, _rounding);
    }

    /// @dev Math for collateral is exact copy of
    ///      openzeppelin5/contracts/token/ERC20/extensions/ERC4626._convertToAssets
    function convertToAssets(
        uint256 _shares,
        uint256 _totalAssets,
        uint256 _totalShares,
        Math.Rounding _rounding,
        ISilo.AssetType _assetType
    ) internal pure returns (uint256 assets) {
        (uint256 totalShares, uint256 totalAssets) = _commonConvertTo(_totalAssets, _totalShares, _assetType);

        // initially, in case of debt, if silo is empty we return shares==assets
        // for collateral, this will never be the case, because of `+1` in line above
        if (totalShares == 0) return _shares;

        assets = _shares.mulDiv(totalAssets, totalShares, _rounding);
    }

    /// @param _collateralMaxLtv maxLTV in 18 decimals that is set for debt asset
    /// @param _sumOfBorrowerCollateralValue borrower total collateral value (including protected)
    /// @param _borrowerDebtValue total value of borrower debt
    /// @return maxBorrowValue max borrow value yet available for borrower
    function calculateMaxBorrowValue(
        uint256 _collateralMaxLtv,
        uint256 _sumOfBorrowerCollateralValue,
        uint256 _borrowerDebtValue
    ) internal pure returns (uint256 maxBorrowValue) {
        if (_sumOfBorrowerCollateralValue == 0) {
            return 0;
        }

        uint256 maxDebtValue = _sumOfBorrowerCollateralValue.mulDiv(
            _collateralMaxLtv, _PRECISION_DECIMALS, Rounding.MAX_BORROW_VALUE
        );

        unchecked {
            // we will not underflow because we checking `maxDebtValue > _borrowerDebtValue`
            maxBorrowValue = maxDebtValue > _borrowerDebtValue ? maxDebtValue - _borrowerDebtValue : 0;
        }
    }

    /// @notice Calculate the maximum assets a borrower can withdraw without breaching the liquidation threshold
    /// @param _sumOfCollateralsValue The combined value of collateral and protected assets of the borrower
    /// @param _debtValue The total debt value of the borrower
    /// @param _lt The liquidation threshold in 18 decimal points
    /// @param _borrowerCollateralAssets The borrower's collateral assets before the withdrawal
    /// @param _borrowerProtectedAssets The borrower's protected assets before the withdrawal
    /// @return maxAssets The maximum assets the borrower can safely withdraw
    function calculateMaxAssetsToWithdraw(
        uint256 _sumOfCollateralsValue,
        uint256 _debtValue,
        uint256 _lt,
        uint256 _borrowerCollateralAssets,
        uint256 _borrowerProtectedAssets
    ) internal pure returns (uint256 maxAssets) {
        if (_sumOfCollateralsValue == 0) return 0;
        if (_debtValue == 0) return _sumOfCollateralsValue;
        if (_lt == 0) return 0;

        // using Rounding.LT (up) to have highest collateralValue that we have to leave for user to stay solvent
        uint256 minimumCollateralValue = _debtValue.mulDiv(_PRECISION_DECIMALS, _lt, Rounding.LTV);

        // if we over LT, we can not withdraw
        if (_sumOfCollateralsValue <= minimumCollateralValue) {
            return 0;
        }

        uint256 spareCollateralValue;
        // safe because we checked `if (_sumOfCollateralsValue <= minimumCollateralValue)`
        unchecked { spareCollateralValue = _sumOfCollateralsValue - minimumCollateralValue; }

        maxAssets = (_borrowerProtectedAssets + _borrowerCollateralAssets)
                .mulDiv(spareCollateralValue, _sumOfCollateralsValue, Rounding.MAX_WITHDRAW_TO_ASSETS);
    }

    /// @notice Determines the maximum number of assets and corresponding shares a borrower can safely withdraw
    /// @param _maxAssets The calculated limit on how many assets can be withdrawn without breaching the liquidation
    /// threshold
    /// @param _borrowerCollateralAssets Amount of collateral assets currently held by the borrower
    /// @param _borrowerProtectedAssets Amount of protected assets currently held by the borrower
    /// @param _collateralType Specifies whether the asset is of type Collateral or Protected
    /// @param _totalAssets The entire quantity of assets available in the system for withdrawal
    /// @param _assetTypeShareTokenTotalSupply Total supply of share tokens for the specified asset type
    /// @param _liquidity Current liquidity in the system for the asset type
    /// @return assets Maximum assets the borrower can withdraw
    /// @return shares Corresponding number of shares for the derived `assets` amount
    function maxWithdrawToAssetsAndShares(
        uint256 _maxAssets,
        uint256 _borrowerCollateralAssets,
        uint256 _borrowerProtectedAssets,
        ISilo.CollateralType _collateralType,
        uint256 _totalAssets,
        uint256 _assetTypeShareTokenTotalSupply,
        uint256 _liquidity
    ) internal pure returns (uint256 assets, uint256 shares) {
        if (_maxAssets == 0) return (0, 0);
        if (_assetTypeShareTokenTotalSupply == 0) return (0, 0);

        if (_collateralType == ISilo.CollateralType.Collateral) {
            assets = _maxAssets > _borrowerCollateralAssets ? _borrowerCollateralAssets : _maxAssets;

            if (assets > _liquidity) {
                assets = _liquidity;
            }
        } else {
            assets = _maxAssets > _borrowerProtectedAssets ? _borrowerProtectedAssets : _maxAssets;
        }

        shares = SiloMathLib.convertToShares(
            assets,
            _totalAssets,
            _assetTypeShareTokenTotalSupply,
            Rounding.MAX_WITHDRAW_TO_SHARES,
            ISilo.AssetType(uint256(_collateralType))
        );
    }

    /// @dev executed `_a * _b / _c`, reverts on _c == 0
    /// @return mulDivResult on overflow returns 0
    function mulDivOverflow(uint256 _a, uint256 _b, uint256 _c)
        internal
        pure
        returns (uint256 mulDivResult)
    {
        if (_a == 0) return (0);

        unchecked {
            // we have to uncheck to detect overflow
            mulDivResult = _a * _b;
            if (mulDivResult / _a != _b) return 0;

            mulDivResult /= _c;
        }
    }

    /// @dev Debt calculations should not lower the result. Debt is a liability so protocol should not take any for
    /// itself. It should return actual result and round it up.
    function _commonConvertTo(
        uint256 _totalAssets,
        uint256 _totalShares,
        ISilo.AssetType _assetType
    ) private pure returns (uint256 totalShares, uint256 totalAssets) {
        if (_totalShares == 0) {
            // silo is empty and we have dust to redistribute: this can only happen when everyone exits silo
            // this case can happen only for collateral, because for collateral we rounding in favorite of protocol
            // by resetting totalAssets, the dust that we have will go to first depositor and we starts from clean state
            _totalAssets = 0;
        }

            (totalShares, totalAssets) = _assetType == ISilo.AssetType.Debt
                ? (_totalShares, _totalAssets)
                : (_totalShares + _DECIMALS_OFFSET_POW, _totalAssets + 1);
    }

    /// @dev Calculates the fraction of a given total and percentage
    /// @param _total The total value to calculate the fraction from
    /// @param _percent The percentage to calculate the fraction from
    /// @param _currentFraction The current fraction to add to the result
    /// @return integral The integral part of the fraction
    /// @return fraction The fractional part of the fraction
    function calculateFraction(
        uint256 _total,
        uint256 _percent,
        uint64 _currentFraction
    ) internal pure returns (uint256 integral, uint64 fraction) {
        if (_total == 0) {
            return (0, _currentFraction);
        }

        unchecked {
            // safe to unchecked because: _currentFraction if never more than max uint256, div is safe
            if (type(uint256).max / _total < _percent) {
                // when overflow, reset `_currentFraction ` to zero as part of circuit breaker
                return (0, 0);
            }

            // `_total * _percent` safe to unchecked because we checked for overflow in above `if`
            // `% _PRECISION_DECIMALS` safe, because max value after modulo will be 1e18 - 1  (_PRECISION_DECIMALS - 1)
            // and this is less than 2 ** 64
            // calculate remainder for current interest
            uint256 remainder = (_total * _percent) % _PRECISION_DECIMALS;
            // integral is amount above 1e18 after adding _currentFraction and remainder
            integral = (_currentFraction + remainder) / _PRECISION_DECIMALS;
            // fraction is what we get below 1e18
            fraction = uint64((_currentFraction + remainder) % _PRECISION_DECIMALS);
        }
    }
}

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

pragma solidity ^0.8.20;

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

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

    /**
     * @dev An operation with an ERC-20 token failed.
     */
    error SafeERC20FailedOperation(address token);

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

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

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

    /**
     * @dev 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);
        forceApprove(token, spender, oldAllowance + value);
    }

    /**
     * @dev Decrease the calling contract's allowance toward `spender` by `requestedDecrease`. If `token` returns no
     * value, non-reverting calls are assumed to be successful.
     */
    function safeDecreaseAllowance(IERC20 token, address spender, uint256 requestedDecrease) internal {
        unchecked {
            uint256 currentAllowance = token.allowance(address(this), spender);
            if (currentAllowance < requestedDecrease) {
                revert SafeERC20FailedDecreaseAllowance(spender, currentAllowance, requestedDecrease);
            }
            forceApprove(token, spender, currentAllowance - requestedDecrease);
        }
    }

    /**
     * @dev Set the calling contract's allowance toward `spender` to `value`. If `token` returns no value,
     * non-reverting calls are assumed to be successful. Meant to be used with tokens that require the approval
     * to be set to zero before setting it to a non-zero value, such as USDT.
     */
    function forceApprove(IERC20 token, address spender, uint256 value) internal {
        bytes memory approvalCall = abi.encodeCall(token.approve, (spender, value));

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

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

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

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

    /**
     * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
     * on the return value: the return value is optional (but if data is returned, it must not be false).
     * @param token The token targeted by the call.
     * @param data The call data (encoded using abi.encode or one of its variants).
     */
    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);
        if (returndata.length != 0 && !abi.decode(returndata, (bool))) {
            revert SafeERC20FailedOperation(address(token));
        }
    }

    /**
     * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
     * on the return value: the return value is optional (but if data is returned, it must not be false).
     * @param token The token targeted by the call.
     * @param data The call data (encoded using abi.encode or one of its variants).
     *
     * This is a variant of {_callOptionalReturn} that 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(token).code.length > 0;
    }
}

File 62 of 74 : DistributionTypes.sol
// SPDX-License-Identifier: agpl-3.0
pragma solidity 0.8.28;

library DistributionTypes {
    struct IncentivesProgramCreationInput {
        string name;
        address rewardToken;
        uint104 emissionPerSecond;
        uint40 distributionEnd;
    }

    struct AssetConfigInput {
        uint104 emissionPerSecond;
        uint256 totalStaked;
        address underlyingAsset;
    }

    struct UserStakeInput {
        address underlyingAsset;
        uint256 stakedByUser;
        uint256 totalStaked;
    }
}

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

pragma solidity ^0.8.20;

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

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

library SYUtils {
    uint256 internal constant ONE = 1e18;

    function syToAsset(uint256 exchangeRate, uint256 syAmount) internal pure returns (uint256) {
        return (syAmount * exchangeRate) / ONE;
    }

    function syToAssetUp(uint256 exchangeRate, uint256 syAmount) internal pure returns (uint256) {
        return (syAmount * exchangeRate + ONE - 1) / ONE;
    }

    function assetToSy(uint256 exchangeRate, uint256 assetAmount) internal pure returns (uint256) {
        return (assetAmount * ONE) / exchangeRate;
    }

    function assetToSyUp(uint256 exchangeRate, uint256 assetAmount) internal pure returns (uint256) {
        return (assetAmount * ONE + exchangeRate - 1) / exchangeRate;
    }
}

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

pragma solidity ^0.8.20;

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

/**
 * @dev Required interface of an ERC-721 compliant contract.
 */
interface IERC721 is IERC165 {
    /**
     * @dev Emitted when `tokenId` token is transferred from `from` to `to`.
     */
    event Transfer(address indexed from, address indexed to, uint256 indexed tokenId);

    /**
     * @dev Emitted when `owner` enables `approved` to manage the `tokenId` token.
     */
    event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId);

    /**
     * @dev Emitted when `owner` enables or disables (`approved`) `operator` to manage all of its assets.
     */
    event ApprovalForAll(address indexed owner, address indexed operator, bool approved);

    /**
     * @dev Returns the number of tokens in ``owner``'s account.
     */
    function balanceOf(address owner) external view returns (uint256 balance);

    /**
     * @dev Returns the owner of the `tokenId` token.
     *
     * Requirements:
     *
     * - `tokenId` must exist.
     */
    function ownerOf(uint256 tokenId) external view returns (address owner);

    /**
     * @dev Safely transfers `tokenId` token from `from` to `to`.
     *
     * Requirements:
     *
     * - `from` cannot be the zero address.
     * - `to` cannot be the zero address.
     * - `tokenId` token must exist and be owned by `from`.
     * - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}.
     * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon
     *   a safe transfer.
     *
     * Emits a {Transfer} event.
     */
    function safeTransferFrom(address from, address to, uint256 tokenId, bytes calldata data) external;

    /**
     * @dev Safely transfers `tokenId` token from `from` to `to`, checking first that contract recipients
     * are aware of the ERC-721 protocol to prevent tokens from being forever locked.
     *
     * Requirements:
     *
     * - `from` cannot be the zero address.
     * - `to` cannot be the zero address.
     * - `tokenId` token must exist and be owned by `from`.
     * - If the caller is not `from`, it must have been allowed to move this token by either {approve} or
     *   {setApprovalForAll}.
     * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon
     *   a safe transfer.
     *
     * Emits a {Transfer} event.
     */
    function safeTransferFrom(address from, address to, uint256 tokenId) external;

    /**
     * @dev Transfers `tokenId` token from `from` to `to`.
     *
     * WARNING: Note that the caller is responsible to confirm that the recipient is capable of receiving ERC-721
     * or else they may be permanently lost. Usage of {safeTransferFrom} prevents loss, though the caller must
     * understand this adds an external call which potentially creates a reentrancy vulnerability.
     *
     * Requirements:
     *
     * - `from` cannot be the zero address.
     * - `to` cannot be the zero address.
     * - `tokenId` token must be owned by `from`.
     * - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}.
     *
     * Emits a {Transfer} event.
     */
    function transferFrom(address from, address to, uint256 tokenId) external;

    /**
     * @dev Gives permission to `to` to transfer `tokenId` token to another account.
     * The approval is cleared when the token is transferred.
     *
     * Only a single account can be approved at a time, so approving the zero address clears previous approvals.
     *
     * Requirements:
     *
     * - The caller must own the token or be an approved operator.
     * - `tokenId` must exist.
     *
     * Emits an {Approval} event.
     */
    function approve(address to, uint256 tokenId) external;

    /**
     * @dev Approve or remove `operator` as an operator for the caller.
     * Operators can call {transferFrom} or {safeTransferFrom} for any token owned by the caller.
     *
     * Requirements:
     *
     * - The `operator` cannot be the address zero.
     *
     * Emits an {ApprovalForAll} event.
     */
    function setApprovalForAll(address operator, bool approved) external;

    /**
     * @dev Returns the account approved for `tokenId` token.
     *
     * Requirements:
     *
     * - `tokenId` must exist.
     */
    function getApproved(uint256 tokenId) external view returns (address operator);

    /**
     * @dev Returns if the `operator` is allowed to manage all of the assets of `owner`.
     *
     * See {setApprovalForAll}
     */
    function isApprovedForAll(address owner, address operator) external view returns (bool);
}

// SPDX-License-Identifier: MIT

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].
 */
// 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 {
        /// @solidity memory-safe-assembly
        assembly {
            mstore(0x00, 0x4e487b71)
            mstore(0x20, code)
            revert(0x1c, 0x24)
        }
    }
}

File 67 of 74 : SafeCast.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.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) {
        /// @solidity memory-safe-assembly
        assembly {
            u := iszero(iszero(b))
        }
    }
}

File 68 of 74 : Rounding.sol
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.8.28;

import {Math} from "openzeppelin5/utils/math/Math.sol";

// solhint-disable private-vars-leading-underscore
library Rounding {
    Math.Rounding internal constant UP = Math.Rounding.Ceil;
    Math.Rounding internal constant DOWN = Math.Rounding.Floor;
    Math.Rounding internal constant DEBT_TO_ASSETS = Math.Rounding.Ceil;
    // COLLATERAL_TO_ASSETS is used to calculate borrower collateral (so we want to round down)
    Math.Rounding internal constant COLLATERAL_TO_ASSETS = Math.Rounding.Floor;
    // why DEPOSIT_TO_ASSETS is Up if COLLATERAL_TO_ASSETS is Down?
    // DEPOSIT_TO_ASSETS is used for preview deposit and deposit, based on provided shares we want to pull "more" tokens
    // so we rounding up, "token flow" is in different direction than for COLLATERAL_TO_ASSETS, that's why
    // different rounding policy
    Math.Rounding internal constant DEPOSIT_TO_ASSETS = Math.Rounding.Ceil;
    Math.Rounding internal constant DEPOSIT_TO_SHARES = Math.Rounding.Floor;
    Math.Rounding internal constant BORROW_TO_ASSETS = Math.Rounding.Floor;
    Math.Rounding internal constant BORROW_TO_SHARES = Math.Rounding.Ceil;
    Math.Rounding internal constant MAX_BORROW_TO_ASSETS = Math.Rounding.Floor;
    Math.Rounding internal constant MAX_BORROW_TO_SHARES = Math.Rounding.Floor;
    Math.Rounding internal constant MAX_BORROW_VALUE = Math.Rounding.Floor;
    Math.Rounding internal constant REPAY_TO_ASSETS = Math.Rounding.Ceil;
    Math.Rounding internal constant REPAY_TO_SHARES = Math.Rounding.Floor;
    Math.Rounding internal constant MAX_REPAY_TO_ASSETS = Math.Rounding.Ceil;
    Math.Rounding internal constant WITHDRAW_TO_ASSETS = Math.Rounding.Floor;
    Math.Rounding internal constant WITHDRAW_TO_SHARES = Math.Rounding.Ceil;
    Math.Rounding internal constant MAX_WITHDRAW_TO_ASSETS = Math.Rounding.Floor;
    Math.Rounding internal constant MAX_WITHDRAW_TO_SHARES = Math.Rounding.Floor;
    Math.Rounding internal constant LIQUIDATE_TO_SHARES = Math.Rounding.Floor;
    Math.Rounding internal constant LTV = Math.Rounding.Ceil;
    Math.Rounding internal constant ACCRUED_INTEREST = Math.Rounding.Floor;
    Math.Rounding internal constant DAO_REVENUE = Math.Rounding.Ceil;
}

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

pragma solidity ^0.8.20;

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

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

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

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

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

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

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

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

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

pragma solidity ^0.8.20;

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

/**
 * @dev Collection of functions related to the address type
 */
library Address {
    /**
     * @dev There's no code at `target` (it is not a contract).
     */
    error AddressEmptyCode(address target);

    /**
     * @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.20/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
     */
    function sendValue(address payable recipient, uint256 amount) internal {
        if (address(this).balance < amount) {
            revert Errors.InsufficientBalance(address(this).balance, amount);
        }

        (bool success, ) = recipient.call{value: amount}("");
        if (!success) {
            revert Errors.FailedCall();
        }
    }

    /**
     * @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 or custom error, it is bubbled
     * up by this function (like regular Solidity function calls). However, if
     * the call reverted with no returned reason, this function reverts with a
     * {Errors.FailedCall} error.
     *
     * 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.
     */
    function functionCall(address target, bytes memory data) internal returns (bytes memory) {
        return functionCallWithValue(target, data, 0);
    }

    /**
     * @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`.
     */
    function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
        if (address(this).balance < value) {
            revert Errors.InsufficientBalance(address(this).balance, value);
        }
        (bool success, bytes memory returndata) = target.call{value: value}(data);
        return verifyCallResultFromTarget(target, success, returndata);
    }

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

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

    /**
     * @dev Tool to verify that a low level call to smart-contract was successful, and reverts if the target
     * was not a contract or bubbling up the revert reason (falling back to {Errors.FailedCall}) in case
     * of an unsuccessful call.
     */
    function verifyCallResultFromTarget(
        address target,
        bool success,
        bytes memory returndata
    ) internal view returns (bytes memory) {
        if (!success) {
            _revert(returndata);
        } else {
            // only check if target is a contract if the call was successful and the return data is empty
            // otherwise we already know that it was a contract
            if (returndata.length == 0 && target.code.length == 0) {
                revert AddressEmptyCode(target);
            }
            return returndata;
        }
    }

    /**
     * @dev Tool to verify that a low level call was successful, and reverts if it wasn't, either by bubbling the
     * revert reason or with a default {Errors.FailedCall} error.
     */
    function verifyCallResult(bool success, bytes memory returndata) internal pure returns (bytes memory) {
        if (!success) {
            _revert(returndata);
        } else {
            return returndata;
        }
    }

    /**
     * @dev Reverts with returndata if present. Otherwise reverts with {Errors.FailedCall}.
     */
    function _revert(bytes memory returndata) 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 Errors.FailedCall();
        }
    }
}

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

pragma solidity ^0.8.20;

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

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

pragma solidity ^0.8.20;

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

File 73 of 74 : IERC165.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (interfaces/IERC165.sol)

pragma solidity ^0.8.20;

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

File 74 of 74 : Errors.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.20;

/**
 * @dev Collection of common custom errors used in multiple contracts
 *
 * IMPORTANT: Backwards compatibility is not guaranteed in future versions of the library.
 * It is recommended to avoid relying on the error API for critical functionality.
 */
library Errors {
    /**
     * @dev The ETH balance of the account is not enough to perform the operation.
     */
    error InsufficientBalance(uint256 balance, uint256 needed);

    /**
     * @dev A call to an address target failed. The target may have reverted.
     */
    error FailedCall();

    /**
     * @dev The deployment failed.
     */
    error FailedDeployment();
}

Settings
{
  "remappings": [
    "forge-std/=lib/forge-std/src/",
    "@openzeppelin/contracts-upgradeable/=node_modules/@openzeppelin/contracts-upgradeable/",
    "@openzeppelin/contracts/=lib/openzeppelin-contracts/contracts/",
    "@tokenized-strategy/=lib/tokenized-strategy/src/",
    "@periphery/=lib/tokenized-strategy-periphery/src/",
    "@pendle/core-v2/=node_modules/@pendle/core-v2/",
    "@balancer-v3-monorepo/interfaces/=lib/balancer-v3-monorepo/pkg/interfaces/contracts/",
    "@balancer-v3-monorepo/vault/=lib/balancer-v3-monorepo/pkg/vault/contracts/",
    "@permit2/=lib/permit2/src/",
    "silo-core/=lib/silo-contracts-v2/silo-core/",
    "@uniswap/v3-core/=lib/silo-contracts-v2/gitmodules/uniswap/v3-core/",
    "@yearn-vaults/=lib/tokenized-strategy-periphery/lib/yearn-vaults-v3/contracts/",
    "ERC4626/=lib/silo-contracts-v2/gitmodules/crytic/properties/lib/ERC4626/contracts/",
    "a16z-erc4626-tests/=lib/silo-contracts-v2/gitmodules/a16z-erc4626-tests/",
    "balancer-labs/v2-interfaces/=lib/silo-contracts-v2/external/balancer-v2-monorepo/pkg/interfaces/contracts/",
    "balancer-labs/v2-liquidity-mining/=lib/silo-contracts-v2/external/balancer-v2-monorepo/pkg/liquidity-mining/contracts/",
    "balancer-labs/v2-solidity-utils/=lib/silo-contracts-v2/external/balancer-v2-monorepo/pkg/solidity-utils/contracts/",
    "balancer-v3-monorepo/=lib/balancer-v3-monorepo/",
    "chainlink-ccip/=lib/silo-contracts-v2/gitmodules/chainlink-ccip/contracts/src/",
    "chainlink/=lib/silo-contracts-v2/gitmodules/chainlink/contracts/src/",
    "createx/=lib/silo-contracts-v2/gitmodules/pyth-sdk-solidity/lazer/contracts/evm/lib/createx/src/",
    "crytic/=lib/silo-contracts-v2/gitmodules/crytic/",
    "ds-test/=lib/forge-std/lib/ds-test/src/",
    "erc4626-tests/=lib/tokenized-strategy/lib/erc4626-tests/",
    "forge-gas-snapshot/=lib/permit2/lib/forge-gas-snapshot/src/",
    "halmos-cheatcodes/=lib/openzeppelin-contracts/lib/halmos-cheatcodes/src/",
    "morpho-blue/=lib/silo-contracts-v2/gitmodules/morpho-blue/src/",
    "openzeppelin-contracts-5/=lib/silo-contracts-v2/gitmodules/openzeppelin-contracts-5/",
    "openzeppelin-contracts-upgradeable-5/=lib/silo-contracts-v2/gitmodules/openzeppelin-contracts-upgradeable-5/",
    "openzeppelin-contracts-upgradeable/=lib/silo-contracts-v2/gitmodules/pyth-sdk-solidity/lazer/contracts/evm/lib/openzeppelin-contracts-upgradeable/",
    "openzeppelin-contracts/=lib/openzeppelin-contracts/",
    "openzeppelin/=lib/tokenized-strategy/lib/openzeppelin-contracts/contracts/",
    "openzeppelin5-upgradeable/=lib/silo-contracts-v2/gitmodules/openzeppelin-contracts-upgradeable-5/contracts/",
    "openzeppelin5/=lib/silo-contracts-v2/gitmodules/openzeppelin-contracts-5/contracts/",
    "permit2/=lib/permit2/",
    "properties/=lib/silo-contracts-v2/gitmodules/crytic/properties/contracts/",
    "pyth-sdk-solidity/=lib/silo-contracts-v2/gitmodules/pyth-sdk-solidity/target_chains/ethereum/sdk/solidity/",
    "silo-contracts-v2/=lib/silo-contracts-v2/",
    "silo-foundry-utils/=lib/silo-contracts-v2/gitmodules/silo-foundry-utils/contracts/",
    "silo-oracles/=lib/silo-contracts-v2/silo-oracles/",
    "silo-vaults/=lib/silo-contracts-v2/silo-vaults/",
    "solady/=lib/silo-contracts-v2/gitmodules/pyth-sdk-solidity/lazer/contracts/evm/lib/createx/lib/solady/",
    "solmate/=lib/permit2/lib/solmate/",
    "tokenized-strategy-periphery/=lib/tokenized-strategy-periphery/",
    "tokenized-strategy/=lib/tokenized-strategy/",
    "uniswap/=lib/silo-contracts-v2/gitmodules/uniswap/",
    "ve-silo/=lib/silo-contracts-v2/ve-silo/",
    "yearn-vaults-v3/=lib/tokenized-strategy-periphery/lib/yearn-vaults-v3/"
  ],
  "optimizer": {
    "enabled": true,
    "runs": 200
  },
  "metadata": {
    "useLiteralContent": false,
    "bytecodeHash": "ipfs",
    "appendCBOR": true
  },
  "outputSelection": {
    "*": {
      "*": [
        "evm.bytecode",
        "evm.deployedBytecode",
        "devdoc",
        "userdoc",
        "metadata",
        "abi"
      ]
    }
  },
  "evmVersion": "cancun",
  "viaIR": false
}

Contract Security Audit

Contract ABI

API
[{"inputs":[{"internalType":"address","name":"_asset","type":"address"},{"internalType":"string","name":"_name","type":"string"},{"internalType":"address","name":"_ceresSwapper","type":"address"},{"internalType":"address","name":"_pendleMarket","type":"address"},{"internalType":"address","name":"_siloMarketConfig","type":"address"},{"internalType":"bool","name":"isCollateralProtected","type":"bool"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[{"internalType":"uint256","name":"maxValue","type":"uint256"}],"name":"AboveMaxValue","type":"error"},{"inputs":[],"name":"InvalidAddress","type":"error"},{"inputs":[],"name":"InvalidAsset","type":"error"},{"inputs":[],"name":"InvalidFlashLoanAmount","type":"error"},{"inputs":[],"name":"InvalidFlashLoanCaller","type":"error"},{"inputs":[],"name":"InvalidFlashLoanInitiator","type":"error"},{"inputs":[],"name":"InvalidFlashLoanToken","type":"error"},{"inputs":[],"name":"InvalidPendleMarket","type":"error"},{"inputs":[],"name":"InvalidPrice","type":"error"},{"inputs":[],"name":"InvalidSiloMarket","type":"error"},{"inputs":[],"name":"InvalidToken","type":"error"},{"inputs":[],"name":"InvalidValue","type":"error"},{"inputs":[{"internalType":"address","name":"token","type":"address"}],"name":"SafeERC20FailedOperation","type":"error"},{"inputs":[],"name":"SlippageLimitExceeded","type":"error"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"assetAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"ptAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"borrowTokenBalance","type":"uint256"}],"name":"DepositIdleAssets","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"","type":"uint256"}],"name":"DepositLimitSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"amountRepaid","type":"uint256"}],"name":"FlashLoan","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"lossAmount","type":"uint256"}],"name":"LossOnWithdrawal","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"slippage","type":"uint256"}],"name":"MaxSlippageSet","type":"event"},{"anonymous":false,"inputs":[],"name":"StrategyFundsDeallocated","type":"event"},{"anonymous":false,"inputs":[],"name":"StrategyRebalance","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"updatedSwapper","type":"address"}],"name":"SwapperSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"targetLtv","type":"uint256"}],"name":"TargetLtvSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"token","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"TokensRecovered","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint32","name":"","type":"uint32"}],"name":"TwapDurationSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"","type":"uint256"}],"name":"WithdrawLimitSet","type":"event"},{"stateMutability":"nonpayable","type":"fallback"},{"inputs":[],"name":"BORROW_SILO","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"BORROW_TOKEN_DECIMALS","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"DEPOSIT_SILO","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"PENDLE_MARKET","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"PT","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"PT_TOKEN_DECIMALS","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"SILO_BORROW_TOKEN","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"SILO_COLLATERAL_TYPE","outputs":[{"internalType":"enum ISilo.CollateralType","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"SILO_CONFIG","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"availableDepositLimit","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"availableWithdrawLimit","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"ltvBuffer","type":"uint256"}],"name":"calculateFlashLoanAmount","outputs":[{"internalType":"uint256","name":"flashLoanAmount","type":"uint256"},{"internalType":"bool","name":"isLeverageUp","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"ceresSwapper","outputs":[{"internalType":"contract ICeresSwapper","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"configMaxSlippageBps","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"configTargetLtv","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"configTwapDuration","outputs":[{"internalType":"uint32","name":"","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"assetAmount","type":"uint256"}],"name":"convertAssetToBorrowToken","outputs":[{"internalType":"uint256","name":"borrowTokenAmount","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"assetAmount","type":"uint256"}],"name":"convertAssetToPt","outputs":[{"internalType":"uint256","name":"ptAmount","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenAmount","type":"uint256"}],"name":"convertBorrowTokenToAsset","outputs":[{"internalType":"uint256","name":"assetAmount","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"borrowTokenAmount","type":"uint256"}],"name":"convertBorrowTokenToPt","outputs":[{"internalType":"uint256","name":"ptAmount","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"ptAmount","type":"uint256"}],"name":"convertPtToAsset","outputs":[{"internalType":"uint256","name":"assetAmount","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"ptAmount","type":"uint256"}],"name":"convertPtToBorrowToken","outputs":[{"internalType":"uint256","name":"borrowTokenAmount","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"deployFunds","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"depositIdleStrategyAssets","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"depositLimit","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"freeFunds","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"getMaxLtvSilo","outputs":[{"internalType":"uint256","name":"maxLtv","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getPositionCollateral","outputs":[{"internalType":"uint256","name":"collateralBalance","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getPositionDebt","outputs":[{"internalType":"uint256","name":"debtBalance","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getPositionLtv","outputs":[{"internalType":"uint256","name":"positionLtv","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getRealAssetBalance","outputs":[{"internalType":"uint256","name":"totalAssets","type":"uint256"},{"internalType":"uint256","name":"totalCollateral","type":"uint256"},{"internalType":"uint256","name":"totalDebt","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getSiloCollateralTokenPrice","outputs":[{"internalType":"uint256","name":"price","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"harvestAndReport","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"initiator","type":"address"},{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"fee","type":"uint256"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"onFlashLoan","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"flashLoanAmount","type":"uint256"},{"internalType":"bool","name":"isLeverageUp","type":"bool"},{"internalType":"bool","name":"shouldDepositIdleAssets","type":"bool"},{"internalType":"bytes","name":"swapData","type":"bytes"}],"name":"rebalanceStrategy","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"ltvBuffer","type":"uint256"},{"internalType":"bool","name":"shouldDepositIdleAssets","type":"bool"}],"name":"rebalanceStrategy","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_token","type":"address"}],"name":"rescueTokens","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"revokeSwapperApprovals","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_depositLimit","type":"uint256"}],"name":"setDepositLimit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_maxSlippageBps","type":"uint256"}],"name":"setMaxSlippageBps","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_swapper","type":"address"}],"name":"setSwapper","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"setSwapperApprovals","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_targetLtv","type":"uint256"}],"name":"setTargetLtv","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint32","name":"_twapDuration","type":"uint32"}],"name":"setTwapDuration","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_withdrawLimit","type":"uint256"}],"name":"setWithdrawLimit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"shutdownWithdraw","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":[],"name":"withdrawLimit","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"}]

6101e06040526706f05b59d3b2000060015560646002556003805463ffffffff19166107081790555f19600481905560055534801561003c575f5ffd5b5060405161577e38038061577e83398101604081905261005b91610945565b6001600160a01b0386166080523060a052604051869086906100c09061008d9084908490339081908190602401610a48565b60408051601f198184030181529190526020810180516001600160e01b03908116634b839d7360e11b1790915261040616565b505073d377919fa87120584b21279a491f82d5265a139c7f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc55506001600160a01b038416158061011757506001600160a01b038316155b8061012957506001600160a01b038216155b156101475760405163e6c4247b60e01b815260040160405180910390fd5b61015083610490565b6001600160a01b03831660c081905260408051630b2339af60e21b815290515f9291632c8ce6bc9160048083019260609291908290030181865afa15801561019a573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906101be9190610aac565b506001600160a01b03811660e05291506101d9905083610591565b6001600160a01b0390811661014081905291811661012052841661010052604080516338d52e0f60e01b815290516338d52e0f916004808201926020929091908290030181865afa158015610230573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906102549190610af6565b6001600160a01b03166101608190526040805163313ce56760e01b8152905163313ce567916004808201926020929091908290030181865afa15801561029c573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906102c09190610b11565b60ff166101808160ff168152505060e0516001600160a01b031663313ce5676040518163ffffffff1660e01b8152600401602060405180830381865afa15801561030c573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906103309190610b11565b60ff166101a08190526040805163313ce56760e01b815290516001600160a01b038a169163313ce5679160048083019260209291908290030181865afa15801561037c573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906103a09190610b11565b60ff16146103c15760405163a1ea7a6f60e01b815260040160405180910390fd5b81156103d1575f6101c0526103d8565b60016101c0525b5f80546001600160a01b0319166001600160a01b0387161790556103fa610722565b50505050505050610b7f565b60605f5f73d377919fa87120584b21279a491f82d5265a139c6001600160a01b0316846040516104369190610b31565b5f60405180830381855af49150503d805f811461046e576040519150601f19603f3d011682016040523d82523d5f602084013e610473565b606091505b509150915081610489576040513d805f833e8082fd5b9392505050565b5f816001600160a01b0316632c8ce6bc6040518163ffffffff1660e01b8152600401606060405180830381865afa1580156104cd573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906104f19190610aac565b505090505f816001600160a01b03166376d5de856040518163ffffffff1660e01b8152600401602060405180830381865afa158015610532573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906105569190610af6565b9050806001600160a01b03166080516001600160a01b03161461058c57604051636448d6e960e11b815260040160405180910390fd5b505050565b5f5f5f5f846001600160a01b031663aecc90cb6040518163ffffffff1660e01b81526004016040805180830381865afa1580156105d0573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906105f49190610b47565b91509150816001600160a01b03166338d52e0f6040518163ffffffff1660e01b8152600401602060405180830381865afa158015610634573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906106589190610af6565b6001600160a01b031660e0516001600160a01b03160361067d5781935080925061071b565b806001600160a01b03166338d52e0f6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156106b9573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906106dd9190610af6565b6001600160a01b031660e0516001600160a01b0316036107025780935081925061071b565b6040516336d336ef60e01b815260040160405180910390fd5b5050915091565b5f546001600160a01b0316806107355750565b60805161074d906001600160a01b0316825f19610781565b60e051610765906001600160a01b0316825f19610781565b6101605161077e906001600160a01b0316825f19610781565b50565b604080516001600160a01b038416602482015260448082018490528251808303909101815260649091019091526020810180516001600160e01b0390811663095ea7b360e01b179091526107d9908590839061084516565b61083f57604080516001600160a01b03851660248201525f6044808301919091528251808303909101815260649091019091526020810180516001600160e01b0390811663095ea7b360e01b1790915261083591869161088e16565b61083f848261088e565b50505050565b5f5f5f5f60205f8651602088015f8a5af192503d91505f519050828015610884575081156108765780600114610884565b5f866001600160a01b03163b115b9695505050505050565b5f5f60205f8451602086015f885af1806108ad576040513d5f823e3d81fd5b50505f513d915081156108c45780600114156108d1565b6001600160a01b0384163b155b1561083f57604051635274afe760e01b81526001600160a01b038516600482015260240160405180910390fd5b6001600160a01b038116811461077e575f5ffd5b805161091d816108fe565b919050565b634e487b7160e01b5f52604160045260245ffd5b8051801515811461091d575f5ffd5b5f5f5f5f5f5f60c0878903121561095a575f5ffd5b8651610965816108fe565b60208801519096506001600160401b03811115610980575f5ffd5b8701601f81018913610990575f5ffd5b80516001600160401b038111156109a9576109a9610922565b604051601f8201601f19908116603f011681016001600160401b03811182821017156109d7576109d7610922565b6040528181528282016020018b10156109ee575f5ffd5b8160208401602083015e5f60208383010152809750505050610a1260408801610912565b9350610a2060608801610912565b9250610a2e60808801610912565b9150610a3c60a08801610936565b90509295509295509295565b60018060a01b038616815260a060208201525f85518060a0840152806020880160c085015e5f60c08285018101919091526001600160a01b0396871660408501529486166060840152929094166080820152601f909101601f191601019392505050565b5f5f5f60608486031215610abe575f5ffd5b8351610ac9816108fe565b6020850151909350610ada816108fe565b6040850151909250610aeb816108fe565b809150509250925092565b5f60208284031215610b06575f5ffd5b8151610489816108fe565b5f60208284031215610b21575f5ffd5b815160ff81168114610489575f5ffd5b5f82518060208501845e5f920191825250919050565b5f5f60408385031215610b58575f5ffd5b8251610b63816108fe565b6020840151909250610b74816108fe565b809150509250929050565b60805160a05160c05160e05161010051610120516101405161016051610180516101a0516101c051614933610e4b5f395f818161054e01528181611fc90152818161241d01528181612ed801526131cc01525f8181610450015281816121200152818161283d0152612a0801525f8181610708015281816120e2015281816127fc0152612a4501525f818161058a0152818161086201528181610ad701528181610bca015281816118550152818161198d01528181611b0c01528181611bcc01528181611f11015281816121f401528181612373015281816124e1015281816125b8015281816129ba01528181612cb2015261308c01525f818161032001528181610a6e01528181610bec01528181610e2401528181611ee201528181612589015281816126e501528181612f8701528181612fd5015281816130ae01526130ef01525f81816103870152818161174501528181611f9a015281816123ee0152818161274d01528181612b4201528181612e6b01528181612ea7015261319901525f818161072f015261277901525f81816106e101528181610826015281816118cf01528181611de301528181612051015281816121b30152818161233e015281816124a4015281816128890152818161298601528181612de401528181612e49015261324a01525f81816103ae0152818161266e01528181612aaf01528181612d8b015261321301525f818161078b015281816109e601528181610c5601528181610d7a01528181610ee301528181610faf0152818161100f01528181611176015281816112a0015281816113af01528181611434015281816114dc0152818161160f0152818161166f01526132ef01525f81816107eb01528181611892015281816119af01528181611aea01528181611baa01528181612245015281816122a7015281816123090152818161295201528181612cd401528181612dc2015261326c01526149335ff3fe608060405234801561000f575f5ffd5b50600436106102d3575f3560e01c8063836e5c7c11610180578063c73730e9116100e7578063d34fde93116100a0578063e54f6c661161007a578063e54f6c661461072a578063ecf7085814610751578063f848d5411461075a578063fde813a814610763576102d3565b8063d34fde93146106d4578063d94073d4146106dc578063dab6f76514610703576102d3565b8063c73730e91461066f578063c7ba912714610678578063c96184ca1461068b578063ccb603d814610693578063cf2a546d146106a6578063d19a3bb8146106b9576102d3565b80639c82f2a4116101395780639c82f2a4146106085780639d7fb70c1461061b578063ac5804541461062e578063bb94ea4b14610636578063bdc8144b14610649578063c433c80a1461065c576102d3565b8063836e5c7c146105855780638c2c679e146105ac578063908ee91c146105b4578063995113be146105c75780639a26ee56146105d05780639c635a46146105e3576102d3565b8063419a7ce31161023f57806350743cbf116101f85780635d265d3f116101d25780635d265d3f146105205780636579f2e514610536578063771f6b1f1461054957806381400a3e1461057d576102d3565b806350743cbf146104e25780635159dd68146105055780635322f9b81461050d576102d3565b8063419a7ce31461048457806346aa2f121461049757806349317f1d146104ac5780634abdf2e0146104b45780634c00ed9b146104c7578063503160d9146104cf576102d3565b8063146135a311610291578063146135a3146103d857806323e30c8b146103ea57806325fc1b3d146103fd5780633722cb4b146104105780633d6cb575146104385780633fc928cd1461044b576102d3565b8062ae3bf81461030857806302f141c11461031b57806304bd46291461035f5780630a2cc185146103825780630f61ac82146103a95780630fbe8b3c146103d0575b73d377919fa87120584b21279a491f82d5265a139c365f80375f5f365f845af43d5f5f3e808015610302573d5ff35b3d5ffd5b005b610306610316366004613eac565b610776565b6103427f000000000000000000000000000000000000000000000000000000000000000081565b6040516001600160a01b0390911681526020015b60405180910390f35b61037461036d366004613eac565b5060055490565b604051908152602001610356565b6103427f000000000000000000000000000000000000000000000000000000000000000081565b6103427f000000000000000000000000000000000000000000000000000000000000000081565b610306610979565b5f54610342906001600160a01b031681565b6103746103f8366004613f0c565b610a62565b61030661040b366004613f83565b610c41565b61042361041e366004613f83565b610d17565b60408051928352901515602083015201610356565b610306610446366004613f83565b610d2b565b6104727f000000000000000000000000000000000000000000000000000000000000000081565b60405160ff9091168152602001610356565b610374610492366004613f83565b610d3f565b6103746104a5366004613eac565b5060045490565b610374610d4f565b6103066104c2366004613f83565b610d65565b610374610e0d565b6103066104dd366004613f83565b610eb0565b6104ea610eb8565b60408051938452602084019290925290820152606001610356565b610306610ece565b61030661051b366004613fa7565b610f4b565b610528611108565b604051610356929190614043565b610374610544366004613f83565b611145565b6105707f000000000000000000000000000000000000000000000000000000000000000081565b6040516103569190614091565b61037461114f565b6103427f000000000000000000000000000000000000000000000000000000000000000081565b610374611158565b6103066105c2366004613f83565b611161565b61037460025481565b6103746105de366004613f83565b611281565b6003546105f39063ffffffff1681565b60405163ffffffff9091168152602001610356565b610306610616366004613eac565b61128b565b610306610629366004613f83565b611389565b61030661139a565b610374610644366004613f83565b611415565b610306610657366004613f83565b61141f565b61030661066a36600461409f565b6114c7565b61037460015481565b6103066106863660046140c2565b6115ab565b61037461172e565b6103746106a1366004613f83565b611790565b6103746106b4366004613f83565b61179a565b61034273d377919fa87120584b21279a491f82d5265a139c81565b6103746117a4565b6103427f000000000000000000000000000000000000000000000000000000000000000081565b6104727f000000000000000000000000000000000000000000000000000000000000000081565b6103427f000000000000000000000000000000000000000000000000000000000000000081565b61037460045481565b61037460055481565b610306610771366004613f83565b6117ad565b6040516320b8029160e21b81523360048201527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906382e00a44906024015f6040518083038186803b1580156107d3575f5ffd5b505afa1580156107e5573d5f5f3e3d5ffd5b505050507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316816001600160a01b0316148061085a57507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316816001600160a01b0316145b8061089657507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316816001600160a01b0316145b156108b45760405163c1ab6dc160e01b815260040160405180910390fd5b6040516370a0823160e01b81523060048201525f906001600160a01b038316906370a0823190602401602060405180830381865afa1580156108f8573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061091c91906140f0565b90506109326001600160a01b03831633836117be565b816001600160a01b03167f46d2e6e71fc567877b817ff3d940571f989d4ee4d40f2b70806d36e738feef6f8260405161096d91815260200190565b60405180910390a25050565b5f610982611822565b600c810154909150600160a01b900460ff166001146109bc5760405162461bcd60e51b81526004016109b390614107565b60405180910390fd5b600c8101805460ff60a01b1916600160a11b17905560405163d43fdcf760e01b81523360048201527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03169063d43fdcf7906024015f6040518083038186803b158015610a2e575f5ffd5b505afa158015610a40573d5f5f3e3d5ffd5b50505050610a4c61184f565b600c01805460ff60a01b1916600160a01b179055565b5f336001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001614610aac5760405163e17c49b760e01b815260040160405180910390fd5b6001600160a01b0387163014610ad5576040516377be635b60e11b815260040160405180910390fd5b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316866001600160a01b031614610b2757604051633dc3c03d60e01b815260040160405180910390fd5b5f8080610b36858701876141ad565b925092509250878314610b5c5760405163dc25cb1560e01b815260040160405180910390fd5b5f610b67888a614270565b90508215610b7f57610b7a89828461194a565b610b8a565b610b8a898284611a58565b6040518181527f97bf554031869ec4edcf72b0dcdc2234dd406afe091f3631be088f348e1795749060200160405180910390a1610c116001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000167f000000000000000000000000000000000000000000000000000000000000000083611c44565b507f439148f0bbc682ca079e46d6e2c2f0c1e3b820f1a291b069d8882abf8cf18dd99a9950505050505050505050565b6040516348e4a64960e01b81523360048201527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906348e4a649906024015f6040518083038186803b158015610c9e575f5ffd5b505afa158015610cb0573d5f5f3e3d5ffd5b505050506101f4811115610cdb57604051634f70461160e01b81526101f460048201526024016109b3565b60028190556040518181527f655eeddda94c0a9de22c1474e6b5aa4f18d3e8048dc9eff185437c7fe3bfb505906020015b60405180910390a150565b5f5f610d2283611cd9565b91509150915091565b610d33611d9a565b610d3c81611dd1565b50565b5f610d49826120cb565b92915050565b5f610d58611d9a565b610d6061216d565b905090565b6040516348e4a64960e01b81523360048201527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906348e4a649906024015f6040518083038186803b158015610dc2575f5ffd5b505afa158015610dd4573d5f5f3e3d5ffd5b5050506005829055506040518181527fbc8c1565f6722bdddca67f542dddabedb4e66481264dafda5d475b7327b5b94c90602001610d0c565b60405163f5125d3f60e01b81526001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001660048201523060248201525f9073925d5466d4d5b01995e20e1245924ada6415126a9063f5125d3f906044015b602060405180830381865afa158015610e8c573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610d6091906140f0565b610d3c611d9a565b5f5f5f610ec36121a3565b925092509250909192565b6040516348e4a64960e01b81523360048201527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906348e4a649906024015f6040518083038186803b158015610f2b575f5ffd5b505afa158015610f3d573d5f5f3e3d5ffd5b50505050610f496122e9565b565b5f610f54611822565b600c810154909150600160a01b900460ff16600114610f855760405162461bcd60e51b81526004016109b390614107565b600c8101805460ff60a01b1916600160a11b17905560405163d43fdcf760e01b81523360048201527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03169063d43fdcf7906024015f6040518083038186803b158015610ff7575f5ffd5b505afa158015611009573d5f5f3e3d5ffd5b505050507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663bf86d6906040518163ffffffff1660e01b8152600401602060405180830381865afa158015611069573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061108d919061428e565b1561109f5761109a61239b565b6110ed565b83156110ad576110ad61184f565b6110ed868685858080601f0160208091040260200160405190810160405280939291908181526020018383808284375f9201919091525061254692505050565b600c01805460ff60a01b1916600160a01b1790555050505050565b5f6060611113612653565b6040805160048152602481019091526020810180516001600160e01b031663440368a360e01b17905290939092509050565b5f610d498261265c565b5f610d606126ce565b5f610d60612736565b60405163d43fdcf760e01b81523360048201527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03169063d43fdcf7906024015f6040518083038186803b1580156111be575f5ffd5b505afa1580156111d0573d5f5f3e3d5ffd5b50505050670de0b6b3a7640000811061120657604051634f70461160e01b8152670de0b6b3a764000060048201526024016109b3565b5f61120f61172e565b90508061122366b1a2bc2ec5000084614270565b111561124557604051634f70461160e01b8152600481018290526024016109b3565b60018290556040518281527fc008cb9fa00383ebb3760e80ded049adfa410a42627f3dfb6fcc143f919814619060200160405180910390a15050565b5f610d4982612916565b6040516348e4a64960e01b81523360048201527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906348e4a649906024015f6040518083038186803b1580156112e8575f5ffd5b505afa1580156112fa573d5f5f3e3d5ffd5b5050506001600160a01b03821690506113265760405163e6c4247b60e01b815260040160405180910390fd5b61132e612932565b5f80546001600160a01b0319166001600160a01b0383161790556113506122e9565b6040516001600160a01b03821681527f673779832598d6a388768ee342f8de96fdd5c39a468a6955377cf1405b9652b990602001610d0c565b611391611d9a565b610d3c816129e1565b6040516348e4a64960e01b81523360048201527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906348e4a649906024015f6040518083038186803b1580156113f7575f5ffd5b505afa158015611409573d5f5f3e3d5ffd5b50505050610f49612932565b5f610d49826129f1565b6040516348e4a64960e01b81523360048201527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906348e4a649906024015f6040518083038186803b15801561147c575f5ffd5b505afa15801561148e573d5f5f3e3d5ffd5b5050506004829055506040518181527f5d2e73196f8ba1b44e887e2bcc9bcd1f3e657add341d4a0a632ffff00d6903f290602001610d0c565b6040516348e4a64960e01b81523360048201527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906348e4a649906024015f6040518083038186803b158015611524575f5ffd5b505afa158015611536573d5f5f3e3d5ffd5b5050505061012c8163ffffffff16101561156357604051632a9ffab760e21b815260040160405180910390fd5b6003805463ffffffff191663ffffffff83169081179091556040519081527fae45eae27fdd572bcc5daa11e5155fef7d0b5081d88a374f0580bf91bfdc29b990602001610d0c565b5f6115b4611822565b600c810154909150600160a01b900460ff166001146115e55760405162461bcd60e51b81526004016109b390614107565b600c8101805460ff60a01b1916600160a11b17905560405163d43fdcf760e01b81523360048201527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03169063d43fdcf7906024015f6040518083038186803b158015611657575f5ffd5b505afa158015611669573d5f5f3e3d5ffd5b505050507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663bf86d6906040518163ffffffff1660e01b8152600401602060405180830381865afa1580156116c9573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906116ed919061428e565b156116ff576116fa61239b565b611716565b811561170d5761170d61184f565b61171683612a75565b600c01805460ff60a01b1916600160a01b1790555050565b604051630323f06160e51b81526001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001660048201525f9073925d5466d4d5b01995e20e1245924ada6415126a9063647e0c2090602401610e71565b5f610d4982612a9d565b5f610d4982612b0f565b5f610d60612b2b565b6117b5611d9a565b610d3c81612b93565b6040516001600160a01b0383811660248301526044820183905261181d91859182169063a9059cbb906064015b604051602081830303815290604052915060e01b6020820180516001600160e01b038381831617835250505050612ba3565b505050565b5f80610d4960017fd2841a5d2692465040bd5e06a6f3b37483952c866e0f304dc0e03f76a1f8a0b16142a9565b5f6118797f0000000000000000000000000000000000000000000000000000000000000000612c0f565b9050801561188c5761188a81612c77565b505b5f6118b67f0000000000000000000000000000000000000000000000000000000000000000612c0f565b905080156118c9576118c781612d6c565b505b5f6118f37f0000000000000000000000000000000000000000000000000000000000000000612c0f565b905080156119045761190481612e3c565b60408051838152602081018390529081018490527f72c4c0049880524690614a131b2c97912dedae3fe43e6fcfb9083f1d113f8e2b9060600160405180910390a1505050565b80515f9015611a27575f61196561196086612916565b612f44565b5f546040516356ddd23d60e01b81529192506001600160a01b0316906356ddd23d906119df907f0000000000000000000000000000000000000000000000000000000000000000907f0000000000000000000000000000000000000000000000000000000000000000908a90879030908b906004016142bc565b6020604051808303815f875af11580156119fb573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611a1f91906140f0565b915050611a33565b611a3084612c77565b90505b5f611a3d82612d6c565b9050611a4881612e3c565b611a5184612f64565b5050505050565b611a6183612fbe565b505f612710600254612710611a769190614270565b611a7f856120cb565b611a899190614309565b611a939190614334565b90505f611a9f8261316a565b90505f611aab826131f4565b845190915015611b83575f611ac261196083612b0f565b5f546040516356ddd23d60e01b81529192506001600160a01b0316906356ddd23d90611b3c907f0000000000000000000000000000000000000000000000000000000000000000907f0000000000000000000000000000000000000000000000000000000000000000908790879030908d906004016142bc565b6020604051808303815f875af1158015611b58573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611b7c91906140f0565b5050611c3c565b5f5460405163efe6bda160e01b81526001600160a01b039091169063efe6bda190611bfa907f0000000000000000000000000000000000000000000000000000000000000000907f0000000000000000000000000000000000000000000000000000000000000000908a9087903090600401614347565b6020604051808303815f875af1158015611c16573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611c3a91906140f0565b505b505050505050565b604080516001600160a01b038416602482015260448082018490528251808303909101815260649091019091526020810180516001600160e01b031663095ea7b360e01b179052611c958482613298565b611cd3576040516001600160a01b0384811660248301525f6044830152611cc991869182169063095ea7b3906064016117eb565b611cd38482612ba3565b50505050565b5f5f5f5f611ce56121a3565b92505091505f611cf483612b0f565b90505f600154670de0b6b3a7640000611d0d91906142a9565b600154611d1a9084614309565b611d249190614334565b90505f611d2f610e0d565b600154909150611d3f8983614270565b1015611d5e57611d4f84836142a9565b98600198509650505050505050565b87600154611d6c9190614270565b811115611d8b57611d7d82856142a9565b985f98509650505050505050565b505f9788975095505050505050565b333014610f495760405162461bcd60e51b815260206004820152600560248201526410b9b2b63360d91b60448201526064016109b3565b5f611ddb82612a9d565b90505f611e077f0000000000000000000000000000000000000000000000000000000000000000612c0f565b9050818110611e1957611cd3826131f4565b611e2381836142a9565b91505f5f611e2f6121a3565b925050915081851115611e40578194505b5f600154670de0b6b3a7640000611e5791906142a9565b600154611e6c611e6789876142a9565b612b0f565b611e769190614309565b611e809190614334565b905081811015611f81575f611e9582846142a9565b60408051602081018390525f918101829052606080820152608081018290529192509060a00160408051601f1981840301815290829052632e7ff4ef60e11b825291506001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001690635cffe9de90611f3d9030907f00000000000000000000000000000000000000000000000000000000000000009087908790600401614385565b6020604051808303815f875af1158015611f59573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611f7d919061428e565b5050505b6040516304b5315d60e21b81525f906001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016906312d4c57490611ff19030907f0000000000000000000000000000000000000000000000000000000000000000906004016143b7565b602060405180830381865afa15801561200c573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061203091906140f0565b90508086111561203e578095505b6120478661316a565b505f61207a6120757f0000000000000000000000000000000000000000000000000000000000000000612c0f565b6131f4565b9050878110156120c1577f88f5063ca4c0af21d7235eb8fb972b73ec8c3dfc382456cdba6c695035e1b9736120af828a6142a9565b60405190815260200160405180910390a15b5050505050505050565b5f8115612168575f6120db612736565b90506121087f0000000000000000000000000000000000000000000000000000000000000000600a6144b7565b6121129082614309565b670de0b6b3a76400006121467f0000000000000000000000000000000000000000000000000000000000000000600a6144b7565b6121509086614309565b61215a9190614309565b6121649190614334565b9150505b919050565b5f6121766132e1565b156121935761218361184f565b61219366b1a2bc2ec50000612a75565b61219b6121a3565b509092915050565b5f5f5f6121ae612b2b565b6121d77f0000000000000000000000000000000000000000000000000000000000000000612c0f565b6121e19190614270565b91505f6121ec6126ce565b90505f6122187f0000000000000000000000000000000000000000000000000000000000000000612c0f565b9050808211156122845761222c81836142a9565b925061223783612916565b6122408561265c565b6122697f0000000000000000000000000000000000000000000000000000000000000000612c0f565b6122739190614270565b61227d91906142a9565b94506122e2565b5f925061229961229483836142a9565b612916565b6122a28561265c565b6122cb7f0000000000000000000000000000000000000000000000000000000000000000612c0f565b6122d59190614270565b6122df9190614270565b94505b5050909192565b5f546001600160a01b0316806122fc5750565b6123316001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016825f19611c44565b6123666001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016825f19611c44565b610d3c6001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016825f19611c44565b5f60018190556123a96126ce565b90506123c4815f60405180602001604052805f815250612546565b5f6123cd612b2b565b9050801561249e576040516304b5315d60e21b81525f906001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016906312d4c574906124459030907f0000000000000000000000000000000000000000000000000000000000000000906004016143b7565b602060405180830381865afa158015612460573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061248491906140f0565b905080821115612492578091505b61249b8261316a565b50505b5f6124c87f0000000000000000000000000000000000000000000000000000000000000000612c0f565b905080156124db576124d9816131f4565b505b5f6125057f0000000000000000000000000000000000000000000000000000000000000000612c0f565b905080156125185761251681612c77565b505b6040517f8d522e0e5c0b3f31d932913d4f534af13f714a3e10cca18fd09ddc9cff2055b3905f90a150505050565b821561181d575f838383604051602001612562939291906144c5565b60408051601f1981840301815290829052632e7ff4ef60e11b825291506001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001690635cffe9de906125e49030907f00000000000000000000000000000000000000000000000000000000000000009089908790600401614385565b6020604051808303815f875af1158015612600573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190612624919061428e565b506040517f430ab3f3198f60d411e429a537683d330a9296d687fa5da362a742676ef4512f905f90a150505050565b5f610d606132e1565b5f8115612168576003545f90612699907f00000000000000000000000000000000000000000000000000000000000000009063ffffffff166133bd565b9050805f036126ba5760405162bfc92160e01b815260040160405180910390fd5b6121648382670de0b6b3a76400005f6133ff565b604051636cde875160e11b81526001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001660048201523060248201525f9073925d5466d4d5b01995e20e1245924ada6415126a9063d9bd0ea290604401610e71565b60405163e48a5f7b60e01b81526001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000811660048301525f9182917f0000000000000000000000000000000000000000000000000000000000000000169063e48a5f7b9060240161022060405180830381865afa1580156127bf573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906127e391906144f0565b60e0015190506001600160a01b038116612829576128227f0000000000000000000000000000000000000000000000000000000000000000600a6144b7565b91506128f3565b6001600160a01b0381166313b0be336128637f0000000000000000000000000000000000000000000000000000000000000000600a6144b7565b6040516001600160e01b031960e084901b16815260048101919091526001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000166024820152604401602060405180830381865afa1580156128cc573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906128f091906140f0565b91505b815f036129125760405162bfc92160e01b815260040160405180910390fd5b5090565b5f8115612168575f612927836120cb565b90506121648161265c565b5f546001600160a01b0316806129455750565b6129796001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016825f611c44565b6129ad6001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016825f611c44565b610d3c6001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016825f611c44565b610d3c66b1a2bc2ec50000612a75565b5f8115612168575f612a01612736565b9050612a2e7f0000000000000000000000000000000000000000000000000000000000000000600a6144b7565b612a4090670de0b6b3a7640000614309565b612a6b7f0000000000000000000000000000000000000000000000000000000000000000600a6144b7565b6121508386614309565b5f5f612a8083611cd9565b9150915061181d828260405180602001604052805f815250612546565b5f8115612168576003545f90612ada907f00000000000000000000000000000000000000000000000000000000000000009063ffffffff166133bd565b9050805f03612afb5760405162bfc92160e01b815260040160405180910390fd5b61216483670de0b6b3a7640000835f6133ff565b5f8115612168575f612b2083612a9d565b9050612164816129f1565b60405163360ae4dd60e11b81526001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001660048201523060248201525f9073925d5466d4d5b01995e20e1245924ada6415126a90636c15c9ba90604401610e71565b5f198103610d3357610d3c61239b565b5f5f60205f8451602086015f885af180612bc2576040513d5f823e3d81fd5b50505f513d91508115612bd9578060011415612be6565b6001600160a01b0384163b155b15611cd357604051635274afe760e01b81526001600160a01b03851660048201526024016109b3565b6040516370a0823160e01b81523060048201525f906001600160a01b038316906370a0823190602401602060405180830381865afa158015612c53573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610d4991906140f0565b5f5f612c8561196084612916565b5f805460405163fd52722760e01b815292935090916001600160a01b039091169063fd52722790612d02907f0000000000000000000000000000000000000000000000000000000000000000907f000000000000000000000000000000000000000000000000000000000000000090899088903090600401614347565b6020604051808303815f875af1158015612d1e573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190612d4291906140f0565b905081811015612d65576040516322cb457760e11b815260040160405180910390fd5b9392505050565b5f5f612d7a61196084612a9d565b5f8054604080516001600160a01b037f00000000000000000000000000000000000000000000000000000000000000008116602083015294955092939091169163fd527227917f0000000000000000000000000000000000000000000000000000000000000000917f000000000000000000000000000000000000000000000000000000000000000091899188913091015b6040516020818303038152906040526040518763ffffffff1660e01b8152600401612d02969594939291906142bc565b612e906001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000167f000000000000000000000000000000000000000000000000000000000000000083611c44565b60405163b7ec8d4b60e01b81526001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000169063b7ec8d4b90612f0090849030907f000000000000000000000000000000000000000000000000000000000000000090600401614609565b6020604051808303815f875af1158015612f1c573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190612f4091906140f0565b5050565b5f610d49600254612710612f5891906142a9565b839061271060016133ff565b604051633545906160e21b815260048101829052306024820181905260448201527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03169063d516418490606401612f00565b6040516338bad5e560e01b81526001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001660048201523060248201525f90819073925d5466d4d5b01995e20e1245924ada6415126a906338bad5e590604401602060405180830381865afa15801561303e573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061306291906140f0565b905080831115613070578092505b825f0361307f57505f92915050565b6130d36001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000167f000000000000000000000000000000000000000000000000000000000000000085611c44565b60405163acb7081560e01b8152600481018490523060248201527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03169063acb70815906044015b6020604051808303815f875af115801561313e573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061316291906140f0565b509192915050565b5f5f613174612b2b565b905080831115613182578092505b604051635c19be1560e11b81526001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000169063b8337c2a90613122908690309081907f00000000000000000000000000000000000000000000000000000000000000009060040161462c565b5f5f6132026119608461265c565b5f8054604080516001600160a01b037f00000000000000000000000000000000000000000000000000000000000000008116602083015294955092939091169163fd527227917f0000000000000000000000000000000000000000000000000000000000000000917f00000000000000000000000000000000000000000000000000000000000000009189918891309101612e0c565b5f5f5f5f60205f8651602088015f8a5af192503d91505f5190508280156132d7575081156132c957806001146132d7565b5f866001600160a01b03163b115b9695505050505050565b5f5f6132eb610e0d565b90507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03166301e1d1146040518163ffffffff1660e01b8152600401602060405180830381865afa158015613349573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061336d91906140f0565b158015906133a9575060015461338a66b1a2bc2ec5000083614270565b10806133a9575066b1a2bc2ec500006001546133a69190614270565b81115b156133b657600191505090565b5f91505090565b5f5f5f6133c98561344c565b915091508082106133f1576133e8826133e2878761366f565b90613737565b92505050610d49565b6133e8816133e2878761366f565b5f61342c61340c83613765565b801561342757505f848061342257613422614320565b868809115b151590565b613437868686613791565b6134419190614270565b90505b949350505050565b5f5f5f5f846001600160a01b0316632c8ce6bc6040518163ffffffff1660e01b8152600401606060405180830381865afa15801561348c573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906134b09190614660565b9250509150816001600160a01b0316633ba0b9a96040518163ffffffff1660e01b8152600401602060405180830381865afa1580156134f1573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061351591906140f0565b93505f816001600160a01b031663d2a3584e6040518163ffffffff1660e01b8152600401602060405180830381865afa158015613554573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061357891906140f0565b9050816001600160a01b031663516399df6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156135b6573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906135da919061428e565b801561364d575043826001600160a01b03166360e0a9e16040518163ffffffff1660e01b8152600401602060405180830381865afa15801561361e573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061364291906146aa565b6001600160801b0316145b1561365a57809350613667565b6136648582613841565b93505b505050915091565b5f5f836001600160a01b031663e184c9be6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156136ad573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906136d191906140f0565b90504281116136eb57670de0b6b3a7640000915050610d49565b5f6136f68585613856565b90505f61370342846142a9565b90505f61371861371384846139fe565b613a36565b905061372c670de0b6b3a764000082613737565b945050505050610d49565b5f8061374b670de0b6b3a764000085614309565b905082818161375c5761375c614320565b04949350505050565b5f600282600381111561377a5761377a61405d565b61378491906146d0565b60ff166001149050919050565b5f5f5f61379e8686613a43565b91509150815f036137c2578381816137b8576137b8614320565b0492505050612d65565b8184116137d9576137d96003851502601118613a5f565b5f848688095f868103871696879004966002600389028118808a02820302808a02820302808a02820302808a02820302808a02820302808a02909103029181900381900460010185841190960395909502919093039390930492909217029150509392505050565b5f81831161384f5781612d65565b5090919050565b5f8163ffffffff165f036138e3575f836001600160a01b031663c3fb90d66040518163ffffffff1660e01b815260040160c060405180830381865afa1580156138a1573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906138c59190614713565b50505092505050806bffffffffffffffffffffffff16915050610d49565b6040805160028082526060820183525f9260208301908036833701905050905082815f8151811061391657613916614795565b63ffffffff9092166020928302919091019091015260405163883bdbfd60e01b81525f906001600160a01b0386169063883bdbfd906139599085906004016147a9565b5f60405180830381865afa158015613973573d5f5f3e3d5ffd5b505050506040513d5f823e601f3d908101601f1916820160405261399a91908101906147f1565b90508363ffffffff16815f815181106139b5576139b5614795565b6020026020010151826001815181106139d0576139d0614795565b60200260200101516139e291906148b0565b6139ec91906148cf565b6001600160d81b031695945050505050565b5f80613a0f6201518061016d614309565b613a198486614309565b613a239190614334565b9050613444613a3182613a70565b613a84565b5f5f821215612912575f5ffd5b5f805f1983850993909202808410938190039390930393915050565b634e487b715f52806020526024601cfd5b5f6001600160ff1b03821115612912575f5ffd5b5f680238fd42c5cf03ffff198212158015613aa8575068070c1cc73b00c800008213155b613ae75760405162461bcd60e51b815260206004820152601060248201526f125b9d985b1a5908195e1c1bdb995b9d60821b60448201526064016109b3565b5f821215613b1e57613afa825f03613a84565b6ec097ce7bc90715b34b9f100000000081613b1757613b17614320565b0592915050565b5f6806f05b59d3b20000008312613b5d57506806f05b59d3b1ffffff1990910190770195e54c5dd42177f53a27172fa9ec630262827000000000613b93565b6803782dace9d90000008312613b8f57506803782dace9d8ffffff19909101906b1425982cf597cd205cef7380613b93565b5060015b6064929092029168056bc75e2d6310000068ad78ebc5ac620000008412613be35768ad78ebc5ac61ffffff199093019268056bc75e2d631000006e01855144814a7ff805980ff008400082020590505b6856bc75e2d6310000008412613c1f576856bc75e2d630ffffff199093019268056bc75e2d631000006b02df0ab5a80a22c61ab5a70082020590505b682b5e3af16b188000008412613c5957682b5e3af16b187fffff199093019268056bc75e2d63100000693f1fce3da636ea5cf85082020590505b6815af1d78b58c4000008412613c93576815af1d78b58c3fffff199093019268056bc75e2d63100000690127fa27722cc06cc5e282020590505b680ad78ebc5ac62000008412613ccc57680ad78ebc5ac61fffff199093019268056bc75e2d6310000068280e60114edb805d0382020590505b68056bc75e2d631000008412613d055768056bc75e2d630fffff199093019268056bc75e2d63100000680ebc5fb4174612111082020590505b6802b5e3af16b18800008412613d3e576802b5e3af16b187ffff199093019268056bc75e2d631000006808f00f760a4b2db55d82020590505b68015af1d78b58c400008412613d775768015af1d78b58c3ffff199093019268056bc75e2d631000006806f5f177578893793782020590505b68056bc75e2d631000008481019085906002908280020505918201919050600368056bc75e2d631000008783020505918201919050600468056bc75e2d631000008783020505918201919050600568056bc75e2d631000008783020505918201919050600668056bc75e2d631000008783020505918201919050600768056bc75e2d631000008783020505918201919050600868056bc75e2d631000008783020505918201919050600968056bc75e2d631000008783020505918201919050600a68056bc75e2d631000008783020505918201919050600b68056bc75e2d631000008783020505918201919050600c68056bc75e2d631000008783020505918201919050606468056bc75e2d63100000848402058502059695505050505050565b6001600160a01b0381168114610d3c575f5ffd5b5f60208284031215613ebc575f5ffd5b8135612d6581613e98565b5f5f83601f840112613ed7575f5ffd5b50813567ffffffffffffffff811115613eee575f5ffd5b602083019150836020828501011115613f05575f5ffd5b9250929050565b5f5f5f5f5f5f60a08789031215613f21575f5ffd5b8635613f2c81613e98565b95506020870135613f3c81613e98565b94506040870135935060608701359250608087013567ffffffffffffffff811115613f65575f5ffd5b613f7189828a01613ec7565b979a9699509497509295939492505050565b5f60208284031215613f93575f5ffd5b5035919050565b8015158114610d3c575f5ffd5b5f5f5f5f5f60808688031215613fbb575f5ffd5b853594506020860135613fcd81613f9a565b93506040860135613fdd81613f9a565b9250606086013567ffffffffffffffff811115613ff8575f5ffd5b61400488828901613ec7565b969995985093965092949392505050565b5f81518084528060208401602086015e5f602082860101526020601f19601f83011685010191505092915050565b8215158152604060208201525f6134446040830184614015565b634e487b7160e01b5f52602160045260245ffd5b6002811061408d57634e487b7160e01b5f52602160045260245ffd5b9052565b60208101610d498284614071565b5f602082840312156140af575f5ffd5b813563ffffffff81168114612d65575f5ffd5b5f5f604083850312156140d3575f5ffd5b8235915060208301356140e581613f9a565b809150509250929050565b5f60208284031215614100575f5ffd5b5051919050565b6020808252601f908201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c00604082015260600190565b634e487b7160e01b5f52604160045260245ffd5b604051610220810167ffffffffffffffff811182821017156141765761417661413e565b60405290565b604051601f8201601f1916810167ffffffffffffffff811182821017156141a5576141a561413e565b604052919050565b5f5f5f606084860312156141bf575f5ffd5b8335925060208401356141d181613f9a565b9150604084013567ffffffffffffffff8111156141ec575f5ffd5b8401601f810186136141fc575f5ffd5b803567ffffffffffffffff8111156142165761421661413e565b614229601f8201601f191660200161417c565b81815287602083850101111561423d575f5ffd5b816020840160208301375f602083830101528093505050509250925092565b634e487b7160e01b5f52601160045260245ffd5b80820180821115610d4957610d4961425c565b805161216881613f9a565b5f6020828403121561429e575f5ffd5b8151612d6581613f9a565b81810381811115610d4957610d4961425c565b6001600160a01b038781168252868116602083015260408201869052606082018590528316608082015260c060a082018190525f906142fd90830184614015565b98975050505050505050565b8082028115828204841417610d4957610d4961425c565b634e487b7160e01b5f52601260045260245ffd5b5f8261434257614342614320565b500490565b6001600160a01b039586168152938516602085015260408401929092526060830152909116608082015260c060a082018190525f9082015260e00190565b6001600160a01b03858116825284166020820152604081018390526080606082018190525f906132d790830184614015565b6001600160a01b038316815260408101612d656020830184614071565b6001815b600184111561440f578085048111156143f3576143f361425c565b600184161561440157908102905b60019390931c9280026143d8565b935093915050565b5f8261442557506001610d49565b8161443157505f610d49565b816001811461444757600281146144515761446d565b6001915050610d49565b60ff8411156144625761446261425c565b50506001821b610d49565b5060208310610133831016604e8410600b8410161715614490575081810a610d49565b61449c5f1984846143d4565b805f19048211156144af576144af61425c565b029392505050565b5f612d6560ff841683614417565b8381528215156020820152606060408201525f6134416060830184614015565b805161216881613e98565b5f610220828403128015614502575f5ffd5b5061450b614152565b8251815260208084015190820152614525604084016144e5565b6040820152614536606084016144e5565b6060820152614547608084016144e5565b608082015261455860a084016144e5565b60a082015261456960c084016144e5565b60c082015261457a60e084016144e5565b60e082015261458c61010084016144e5565b61010082015261459f61012084016144e5565b6101208201526101408381015190820152610160808401519082015261018080840151908201526101a080840151908201526101c080840151908201526145e96101e084016144e5565b6101e08201526145fc6102008401614283565b6102008201529392505050565b8381526001600160a01b0383166020820152606081016134446040830184614071565b8481526001600160a01b03848116602083015283166040820152608081016146576060830184614071565b95945050505050565b5f5f5f60608486031215614672575f5ffd5b835161467d81613e98565b602085015190935061468e81613e98565b604085015190925061469f81613e98565b809150509250925092565b5f602082840312156146ba575f5ffd5b81516001600160801b0381168114612d65575f5ffd5b5f60ff8316806146e2576146e2614320565b8060ff84160691505092915050565b8051600f81900b8114612168575f5ffd5b805161ffff81168114612168575f5ffd5b5f5f5f5f5f5f60c08789031215614728575f5ffd5b614731876146f1565b955061473f602088016146f1565b945060408701516bffffffffffffffffffffffff8116811461475f575f5ffd5b935061476d60608801614702565b925061477b60808801614702565b915061478960a08801614702565b90509295509295509295565b634e487b7160e01b5f52603260045260245ffd5b602080825282518282018190525f918401906040840190835b818110156147e657835163ffffffff168352602093840193909201916001016147c2565b509095945050505050565b5f60208284031215614801575f5ffd5b815167ffffffffffffffff811115614817575f5ffd5b8201601f81018413614827575f5ffd5b805167ffffffffffffffff8111156148415761484161413e565b8060051b6148516020820161417c565b9182526020818401810192908101908784111561486c575f5ffd5b6020850194505b838510156148a557845192506001600160d81b0383168314614893575f5ffd5b82825260209485019490910190614873565b979650505050505050565b6001600160d81b038281168282160390811115610d4957610d4961425c565b5f6001600160d81b038316806148e7576148e7614320565b6001600160d81b0392909216919091049291505056fea2646970667358221220593b94554e7acf962df35070b744d131bc864d9d2c27c9f9f4cc5e7ca3ad6f2764736f6c634300081c0033000000000000000000000000c7990369da608c2f4903715e3bd22f2970536c2900000000000000000000000000000000000000000000000000000000000000c00000000000000000000000002c13bb26cbf37381a0b816f30de33bd70fd7c4300000000000000000000000004aee8d5e242c2485bd124299203c7fa5108c25fc000000000000000000000000cb2dcdc5e4016d0be706df5e312fb3e7c23414970000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000001f436572657320736d73555344204c657665726167656420537472617465677900

Deployed Bytecode

0x608060405234801561000f575f5ffd5b50600436106102d3575f3560e01c8063836e5c7c11610180578063c73730e9116100e7578063d34fde93116100a0578063e54f6c661161007a578063e54f6c661461072a578063ecf7085814610751578063f848d5411461075a578063fde813a814610763576102d3565b8063d34fde93146106d4578063d94073d4146106dc578063dab6f76514610703576102d3565b8063c73730e91461066f578063c7ba912714610678578063c96184ca1461068b578063ccb603d814610693578063cf2a546d146106a6578063d19a3bb8146106b9576102d3565b80639c82f2a4116101395780639c82f2a4146106085780639d7fb70c1461061b578063ac5804541461062e578063bb94ea4b14610636578063bdc8144b14610649578063c433c80a1461065c576102d3565b8063836e5c7c146105855780638c2c679e146105ac578063908ee91c146105b4578063995113be146105c75780639a26ee56146105d05780639c635a46146105e3576102d3565b8063419a7ce31161023f57806350743cbf116101f85780635d265d3f116101d25780635d265d3f146105205780636579f2e514610536578063771f6b1f1461054957806381400a3e1461057d576102d3565b806350743cbf146104e25780635159dd68146105055780635322f9b81461050d576102d3565b8063419a7ce31461048457806346aa2f121461049757806349317f1d146104ac5780634abdf2e0146104b45780634c00ed9b146104c7578063503160d9146104cf576102d3565b8063146135a311610291578063146135a3146103d857806323e30c8b146103ea57806325fc1b3d146103fd5780633722cb4b146104105780633d6cb575146104385780633fc928cd1461044b576102d3565b8062ae3bf81461030857806302f141c11461031b57806304bd46291461035f5780630a2cc185146103825780630f61ac82146103a95780630fbe8b3c146103d0575b73d377919fa87120584b21279a491f82d5265a139c365f80375f5f365f845af43d5f5f3e808015610302573d5ff35b3d5ffd5b005b610306610316366004613eac565b610776565b6103427f0000000000000000000000007184bea7743ccfbe390f9cd830095a13ef86794181565b6040516001600160a01b0390911681526020015b60405180910390f35b61037461036d366004613eac565b5060055490565b604051908152602001610356565b6103427f00000000000000000000000094b4bdd55bac19da3b0a31edc0fc899ae918699e81565b6103427f0000000000000000000000004aee8d5e242c2485bd124299203c7fa5108c25fc81565b610306610979565b5f54610342906001600160a01b031681565b6103746103f8366004613f0c565b610a62565b61030661040b366004613f83565b610c41565b61042361041e366004613f83565b610d17565b60408051928352901515602083015201610356565b610306610446366004613f83565b610d2b565b6104727f000000000000000000000000000000000000000000000000000000000000001281565b60405160ff9091168152602001610356565b610374610492366004613f83565b610d3f565b6103746104a5366004613eac565b5060045490565b610374610d4f565b6103066104c2366004613f83565b610d65565b610374610e0d565b6103066104dd366004613f83565b610eb0565b6104ea610eb8565b60408051938452602084019290925290820152606001610356565b610306610ece565b61030661051b366004613fa7565b610f4b565b610528611108565b604051610356929190614043565b610374610544366004613f83565b611145565b6105707f000000000000000000000000000000000000000000000000000000000000000081565b6040516103569190614091565b61037461114f565b6103427f00000000000000000000000029219dd400f2bf60e5a23d13be72b486d403889481565b610374611158565b6103066105c2366004613f83565b611161565b61037460025481565b6103746105de366004613f83565b611281565b6003546105f39063ffffffff1681565b60405163ffffffff9091168152602001610356565b610306610616366004613eac565b61128b565b610306610629366004613f83565b611389565b61030661139a565b610374610644366004613f83565b611415565b610306610657366004613f83565b61141f565b61030661066a36600461409f565b6114c7565b61037460015481565b6103066106863660046140c2565b6115ab565b61037461172e565b6103746106a1366004613f83565b611790565b6103746106b4366004613f83565b61179a565b61034273d377919fa87120584b21279a491f82d5265a139c81565b6103746117a4565b6103427f0000000000000000000000005ee17fd12ede62b508f9615db384ce7b834ba65781565b6104727f000000000000000000000000000000000000000000000000000000000000000681565b6103427f000000000000000000000000cb2dcdc5e4016d0be706df5e312fb3e7c234149781565b61037460045481565b61037460055481565b610306610771366004613f83565b6117ad565b6040516320b8029160e21b81523360048201527f00000000000000000000000085bba6f69055e1cd712745bd8566807fd875d5436001600160a01b0316906382e00a44906024015f6040518083038186803b1580156107d3575f5ffd5b505afa1580156107e5573d5f5f3e3d5ffd5b505050507f000000000000000000000000c7990369da608c2f4903715e3bd22f2970536c296001600160a01b0316816001600160a01b0316148061085a57507f0000000000000000000000005ee17fd12ede62b508f9615db384ce7b834ba6576001600160a01b0316816001600160a01b0316145b8061089657507f00000000000000000000000029219dd400f2bf60e5a23d13be72b486d40388946001600160a01b0316816001600160a01b0316145b156108b45760405163c1ab6dc160e01b815260040160405180910390fd5b6040516370a0823160e01b81523060048201525f906001600160a01b038316906370a0823190602401602060405180830381865afa1580156108f8573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061091c91906140f0565b90506109326001600160a01b03831633836117be565b816001600160a01b03167f46d2e6e71fc567877b817ff3d940571f989d4ee4d40f2b70806d36e738feef6f8260405161096d91815260200190565b60405180910390a25050565b5f610982611822565b600c810154909150600160a01b900460ff166001146109bc5760405162461bcd60e51b81526004016109b390614107565b60405180910390fd5b600c8101805460ff60a01b1916600160a11b17905560405163d43fdcf760e01b81523360048201527f00000000000000000000000085bba6f69055e1cd712745bd8566807fd875d5436001600160a01b03169063d43fdcf7906024015f6040518083038186803b158015610a2e575f5ffd5b505afa158015610a40573d5f5f3e3d5ffd5b50505050610a4c61184f565b600c01805460ff60a01b1916600160a01b179055565b5f336001600160a01b037f0000000000000000000000007184bea7743ccfbe390f9cd830095a13ef8679411614610aac5760405163e17c49b760e01b815260040160405180910390fd5b6001600160a01b0387163014610ad5576040516377be635b60e11b815260040160405180910390fd5b7f00000000000000000000000029219dd400f2bf60e5a23d13be72b486d40388946001600160a01b0316866001600160a01b031614610b2757604051633dc3c03d60e01b815260040160405180910390fd5b5f8080610b36858701876141ad565b925092509250878314610b5c5760405163dc25cb1560e01b815260040160405180910390fd5b5f610b67888a614270565b90508215610b7f57610b7a89828461194a565b610b8a565b610b8a898284611a58565b6040518181527f97bf554031869ec4edcf72b0dcdc2234dd406afe091f3631be088f348e1795749060200160405180910390a1610c116001600160a01b037f00000000000000000000000029219dd400f2bf60e5a23d13be72b486d4038894167f0000000000000000000000007184bea7743ccfbe390f9cd830095a13ef86794183611c44565b507f439148f0bbc682ca079e46d6e2c2f0c1e3b820f1a291b069d8882abf8cf18dd99a9950505050505050505050565b6040516348e4a64960e01b81523360048201527f00000000000000000000000085bba6f69055e1cd712745bd8566807fd875d5436001600160a01b0316906348e4a649906024015f6040518083038186803b158015610c9e575f5ffd5b505afa158015610cb0573d5f5f3e3d5ffd5b505050506101f4811115610cdb57604051634f70461160e01b81526101f460048201526024016109b3565b60028190556040518181527f655eeddda94c0a9de22c1474e6b5aa4f18d3e8048dc9eff185437c7fe3bfb505906020015b60405180910390a150565b5f5f610d2283611cd9565b91509150915091565b610d33611d9a565b610d3c81611dd1565b50565b5f610d49826120cb565b92915050565b5f610d58611d9a565b610d6061216d565b905090565b6040516348e4a64960e01b81523360048201527f00000000000000000000000085bba6f69055e1cd712745bd8566807fd875d5436001600160a01b0316906348e4a649906024015f6040518083038186803b158015610dc2575f5ffd5b505afa158015610dd4573d5f5f3e3d5ffd5b5050506005829055506040518181527fbc8c1565f6722bdddca67f542dddabedb4e66481264dafda5d475b7327b5b94c90602001610d0c565b60405163f5125d3f60e01b81526001600160a01b037f0000000000000000000000007184bea7743ccfbe390f9cd830095a13ef8679411660048201523060248201525f9073925d5466d4d5b01995e20e1245924ada6415126a9063f5125d3f906044015b602060405180830381865afa158015610e8c573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610d6091906140f0565b610d3c611d9a565b5f5f5f610ec36121a3565b925092509250909192565b6040516348e4a64960e01b81523360048201527f00000000000000000000000085bba6f69055e1cd712745bd8566807fd875d5436001600160a01b0316906348e4a649906024015f6040518083038186803b158015610f2b575f5ffd5b505afa158015610f3d573d5f5f3e3d5ffd5b50505050610f496122e9565b565b5f610f54611822565b600c810154909150600160a01b900460ff16600114610f855760405162461bcd60e51b81526004016109b390614107565b600c8101805460ff60a01b1916600160a11b17905560405163d43fdcf760e01b81523360048201527f00000000000000000000000085bba6f69055e1cd712745bd8566807fd875d5436001600160a01b03169063d43fdcf7906024015f6040518083038186803b158015610ff7575f5ffd5b505afa158015611009573d5f5f3e3d5ffd5b505050507f00000000000000000000000085bba6f69055e1cd712745bd8566807fd875d5436001600160a01b031663bf86d6906040518163ffffffff1660e01b8152600401602060405180830381865afa158015611069573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061108d919061428e565b1561109f5761109a61239b565b6110ed565b83156110ad576110ad61184f565b6110ed868685858080601f0160208091040260200160405190810160405280939291908181526020018383808284375f9201919091525061254692505050565b600c01805460ff60a01b1916600160a01b1790555050505050565b5f6060611113612653565b6040805160048152602481019091526020810180516001600160e01b031663440368a360e01b17905290939092509050565b5f610d498261265c565b5f610d606126ce565b5f610d60612736565b60405163d43fdcf760e01b81523360048201527f00000000000000000000000085bba6f69055e1cd712745bd8566807fd875d5436001600160a01b03169063d43fdcf7906024015f6040518083038186803b1580156111be575f5ffd5b505afa1580156111d0573d5f5f3e3d5ffd5b50505050670de0b6b3a7640000811061120657604051634f70461160e01b8152670de0b6b3a764000060048201526024016109b3565b5f61120f61172e565b90508061122366b1a2bc2ec5000084614270565b111561124557604051634f70461160e01b8152600481018290526024016109b3565b60018290556040518281527fc008cb9fa00383ebb3760e80ded049adfa410a42627f3dfb6fcc143f919814619060200160405180910390a15050565b5f610d4982612916565b6040516348e4a64960e01b81523360048201527f00000000000000000000000085bba6f69055e1cd712745bd8566807fd875d5436001600160a01b0316906348e4a649906024015f6040518083038186803b1580156112e8575f5ffd5b505afa1580156112fa573d5f5f3e3d5ffd5b5050506001600160a01b03821690506113265760405163e6c4247b60e01b815260040160405180910390fd5b61132e612932565b5f80546001600160a01b0319166001600160a01b0383161790556113506122e9565b6040516001600160a01b03821681527f673779832598d6a388768ee342f8de96fdd5c39a468a6955377cf1405b9652b990602001610d0c565b611391611d9a565b610d3c816129e1565b6040516348e4a64960e01b81523360048201527f00000000000000000000000085bba6f69055e1cd712745bd8566807fd875d5436001600160a01b0316906348e4a649906024015f6040518083038186803b1580156113f7575f5ffd5b505afa158015611409573d5f5f3e3d5ffd5b50505050610f49612932565b5f610d49826129f1565b6040516348e4a64960e01b81523360048201527f00000000000000000000000085bba6f69055e1cd712745bd8566807fd875d5436001600160a01b0316906348e4a649906024015f6040518083038186803b15801561147c575f5ffd5b505afa15801561148e573d5f5f3e3d5ffd5b5050506004829055506040518181527f5d2e73196f8ba1b44e887e2bcc9bcd1f3e657add341d4a0a632ffff00d6903f290602001610d0c565b6040516348e4a64960e01b81523360048201527f00000000000000000000000085bba6f69055e1cd712745bd8566807fd875d5436001600160a01b0316906348e4a649906024015f6040518083038186803b158015611524575f5ffd5b505afa158015611536573d5f5f3e3d5ffd5b5050505061012c8163ffffffff16101561156357604051632a9ffab760e21b815260040160405180910390fd5b6003805463ffffffff191663ffffffff83169081179091556040519081527fae45eae27fdd572bcc5daa11e5155fef7d0b5081d88a374f0580bf91bfdc29b990602001610d0c565b5f6115b4611822565b600c810154909150600160a01b900460ff166001146115e55760405162461bcd60e51b81526004016109b390614107565b600c8101805460ff60a01b1916600160a11b17905560405163d43fdcf760e01b81523360048201527f00000000000000000000000085bba6f69055e1cd712745bd8566807fd875d5436001600160a01b03169063d43fdcf7906024015f6040518083038186803b158015611657575f5ffd5b505afa158015611669573d5f5f3e3d5ffd5b505050507f00000000000000000000000085bba6f69055e1cd712745bd8566807fd875d5436001600160a01b031663bf86d6906040518163ffffffff1660e01b8152600401602060405180830381865afa1580156116c9573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906116ed919061428e565b156116ff576116fa61239b565b611716565b811561170d5761170d61184f565b61171683612a75565b600c01805460ff60a01b1916600160a01b1790555050565b604051630323f06160e51b81526001600160a01b037f00000000000000000000000094b4bdd55bac19da3b0a31edc0fc899ae918699e1660048201525f9073925d5466d4d5b01995e20e1245924ada6415126a9063647e0c2090602401610e71565b5f610d4982612a9d565b5f610d4982612b0f565b5f610d60612b2b565b6117b5611d9a565b610d3c81612b93565b6040516001600160a01b0383811660248301526044820183905261181d91859182169063a9059cbb906064015b604051602081830303815290604052915060e01b6020820180516001600160e01b038381831617835250505050612ba3565b505050565b5f80610d4960017fd2841a5d2692465040bd5e06a6f3b37483952c866e0f304dc0e03f76a1f8a0b16142a9565b5f6118797f00000000000000000000000029219dd400f2bf60e5a23d13be72b486d4038894612c0f565b9050801561188c5761188a81612c77565b505b5f6118b67f000000000000000000000000c7990369da608c2f4903715e3bd22f2970536c29612c0f565b905080156118c9576118c781612d6c565b505b5f6118f37f0000000000000000000000005ee17fd12ede62b508f9615db384ce7b834ba657612c0f565b905080156119045761190481612e3c565b60408051838152602081018390529081018490527f72c4c0049880524690614a131b2c97912dedae3fe43e6fcfb9083f1d113f8e2b9060600160405180910390a1505050565b80515f9015611a27575f61196561196086612916565b612f44565b5f546040516356ddd23d60e01b81529192506001600160a01b0316906356ddd23d906119df907f00000000000000000000000029219dd400f2bf60e5a23d13be72b486d4038894907f000000000000000000000000c7990369da608c2f4903715e3bd22f2970536c29908a90879030908b906004016142bc565b6020604051808303815f875af11580156119fb573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611a1f91906140f0565b915050611a33565b611a3084612c77565b90505b5f611a3d82612d6c565b9050611a4881612e3c565b611a5184612f64565b5050505050565b611a6183612fbe565b505f612710600254612710611a769190614270565b611a7f856120cb565b611a899190614309565b611a939190614334565b90505f611a9f8261316a565b90505f611aab826131f4565b845190915015611b83575f611ac261196083612b0f565b5f546040516356ddd23d60e01b81529192506001600160a01b0316906356ddd23d90611b3c907f000000000000000000000000c7990369da608c2f4903715e3bd22f2970536c29907f00000000000000000000000029219dd400f2bf60e5a23d13be72b486d4038894908790879030908d906004016142bc565b6020604051808303815f875af1158015611b58573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611b7c91906140f0565b5050611c3c565b5f5460405163efe6bda160e01b81526001600160a01b039091169063efe6bda190611bfa907f000000000000000000000000c7990369da608c2f4903715e3bd22f2970536c29907f00000000000000000000000029219dd400f2bf60e5a23d13be72b486d4038894908a9087903090600401614347565b6020604051808303815f875af1158015611c16573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611c3a91906140f0565b505b505050505050565b604080516001600160a01b038416602482015260448082018490528251808303909101815260649091019091526020810180516001600160e01b031663095ea7b360e01b179052611c958482613298565b611cd3576040516001600160a01b0384811660248301525f6044830152611cc991869182169063095ea7b3906064016117eb565b611cd38482612ba3565b50505050565b5f5f5f5f611ce56121a3565b92505091505f611cf483612b0f565b90505f600154670de0b6b3a7640000611d0d91906142a9565b600154611d1a9084614309565b611d249190614334565b90505f611d2f610e0d565b600154909150611d3f8983614270565b1015611d5e57611d4f84836142a9565b98600198509650505050505050565b87600154611d6c9190614270565b811115611d8b57611d7d82856142a9565b985f98509650505050505050565b505f9788975095505050505050565b333014610f495760405162461bcd60e51b815260206004820152600560248201526410b9b2b63360d91b60448201526064016109b3565b5f611ddb82612a9d565b90505f611e077f0000000000000000000000005ee17fd12ede62b508f9615db384ce7b834ba657612c0f565b9050818110611e1957611cd3826131f4565b611e2381836142a9565b91505f5f611e2f6121a3565b925050915081851115611e40578194505b5f600154670de0b6b3a7640000611e5791906142a9565b600154611e6c611e6789876142a9565b612b0f565b611e769190614309565b611e809190614334565b905081811015611f81575f611e9582846142a9565b60408051602081018390525f918101829052606080820152608081018290529192509060a00160408051601f1981840301815290829052632e7ff4ef60e11b825291506001600160a01b037f0000000000000000000000007184bea7743ccfbe390f9cd830095a13ef8679411690635cffe9de90611f3d9030907f00000000000000000000000029219dd400f2bf60e5a23d13be72b486d40388949087908790600401614385565b6020604051808303815f875af1158015611f59573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611f7d919061428e565b5050505b6040516304b5315d60e21b81525f906001600160a01b037f00000000000000000000000094b4bdd55bac19da3b0a31edc0fc899ae918699e16906312d4c57490611ff19030907f0000000000000000000000000000000000000000000000000000000000000000906004016143b7565b602060405180830381865afa15801561200c573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061203091906140f0565b90508086111561203e578095505b6120478661316a565b505f61207a6120757f0000000000000000000000005ee17fd12ede62b508f9615db384ce7b834ba657612c0f565b6131f4565b9050878110156120c1577f88f5063ca4c0af21d7235eb8fb972b73ec8c3dfc382456cdba6c695035e1b9736120af828a6142a9565b60405190815260200160405180910390a15b5050505050505050565b5f8115612168575f6120db612736565b90506121087f0000000000000000000000000000000000000000000000000000000000000006600a6144b7565b6121129082614309565b670de0b6b3a76400006121467f0000000000000000000000000000000000000000000000000000000000000012600a6144b7565b6121509086614309565b61215a9190614309565b6121649190614334565b9150505b919050565b5f6121766132e1565b156121935761218361184f565b61219366b1a2bc2ec50000612a75565b61219b6121a3565b509092915050565b5f5f5f6121ae612b2b565b6121d77f0000000000000000000000005ee17fd12ede62b508f9615db384ce7b834ba657612c0f565b6121e19190614270565b91505f6121ec6126ce565b90505f6122187f00000000000000000000000029219dd400f2bf60e5a23d13be72b486d4038894612c0f565b9050808211156122845761222c81836142a9565b925061223783612916565b6122408561265c565b6122697f000000000000000000000000c7990369da608c2f4903715e3bd22f2970536c29612c0f565b6122739190614270565b61227d91906142a9565b94506122e2565b5f925061229961229483836142a9565b612916565b6122a28561265c565b6122cb7f000000000000000000000000c7990369da608c2f4903715e3bd22f2970536c29612c0f565b6122d59190614270565b6122df9190614270565b94505b5050909192565b5f546001600160a01b0316806122fc5750565b6123316001600160a01b037f000000000000000000000000c7990369da608c2f4903715e3bd22f2970536c2916825f19611c44565b6123666001600160a01b037f0000000000000000000000005ee17fd12ede62b508f9615db384ce7b834ba65716825f19611c44565b610d3c6001600160a01b037f00000000000000000000000029219dd400f2bf60e5a23d13be72b486d403889416825f19611c44565b5f60018190556123a96126ce565b90506123c4815f60405180602001604052805f815250612546565b5f6123cd612b2b565b9050801561249e576040516304b5315d60e21b81525f906001600160a01b037f00000000000000000000000094b4bdd55bac19da3b0a31edc0fc899ae918699e16906312d4c574906124459030907f0000000000000000000000000000000000000000000000000000000000000000906004016143b7565b602060405180830381865afa158015612460573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061248491906140f0565b905080821115612492578091505b61249b8261316a565b50505b5f6124c87f0000000000000000000000005ee17fd12ede62b508f9615db384ce7b834ba657612c0f565b905080156124db576124d9816131f4565b505b5f6125057f00000000000000000000000029219dd400f2bf60e5a23d13be72b486d4038894612c0f565b905080156125185761251681612c77565b505b6040517f8d522e0e5c0b3f31d932913d4f534af13f714a3e10cca18fd09ddc9cff2055b3905f90a150505050565b821561181d575f838383604051602001612562939291906144c5565b60408051601f1981840301815290829052632e7ff4ef60e11b825291506001600160a01b037f0000000000000000000000007184bea7743ccfbe390f9cd830095a13ef8679411690635cffe9de906125e49030907f00000000000000000000000029219dd400f2bf60e5a23d13be72b486d40388949089908790600401614385565b6020604051808303815f875af1158015612600573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190612624919061428e565b506040517f430ab3f3198f60d411e429a537683d330a9296d687fa5da362a742676ef4512f905f90a150505050565b5f610d606132e1565b5f8115612168576003545f90612699907f0000000000000000000000004aee8d5e242c2485bd124299203c7fa5108c25fc9063ffffffff166133bd565b9050805f036126ba5760405162bfc92160e01b815260040160405180910390fd5b6121648382670de0b6b3a76400005f6133ff565b604051636cde875160e11b81526001600160a01b037f0000000000000000000000007184bea7743ccfbe390f9cd830095a13ef8679411660048201523060248201525f9073925d5466d4d5b01995e20e1245924ada6415126a9063d9bd0ea290604401610e71565b60405163e48a5f7b60e01b81526001600160a01b037f00000000000000000000000094b4bdd55bac19da3b0a31edc0fc899ae918699e811660048301525f9182917f000000000000000000000000cb2dcdc5e4016d0be706df5e312fb3e7c2341497169063e48a5f7b9060240161022060405180830381865afa1580156127bf573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906127e391906144f0565b60e0015190506001600160a01b038116612829576128227f0000000000000000000000000000000000000000000000000000000000000006600a6144b7565b91506128f3565b6001600160a01b0381166313b0be336128637f0000000000000000000000000000000000000000000000000000000000000012600a6144b7565b6040516001600160e01b031960e084901b16815260048101919091526001600160a01b037f0000000000000000000000005ee17fd12ede62b508f9615db384ce7b834ba657166024820152604401602060405180830381865afa1580156128cc573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906128f091906140f0565b91505b815f036129125760405162bfc92160e01b815260040160405180910390fd5b5090565b5f8115612168575f612927836120cb565b90506121648161265c565b5f546001600160a01b0316806129455750565b6129796001600160a01b037f000000000000000000000000c7990369da608c2f4903715e3bd22f2970536c2916825f611c44565b6129ad6001600160a01b037f0000000000000000000000005ee17fd12ede62b508f9615db384ce7b834ba65716825f611c44565b610d3c6001600160a01b037f00000000000000000000000029219dd400f2bf60e5a23d13be72b486d403889416825f611c44565b610d3c66b1a2bc2ec50000612a75565b5f8115612168575f612a01612736565b9050612a2e7f0000000000000000000000000000000000000000000000000000000000000012600a6144b7565b612a4090670de0b6b3a7640000614309565b612a6b7f0000000000000000000000000000000000000000000000000000000000000006600a6144b7565b6121508386614309565b5f5f612a8083611cd9565b9150915061181d828260405180602001604052805f815250612546565b5f8115612168576003545f90612ada907f0000000000000000000000004aee8d5e242c2485bd124299203c7fa5108c25fc9063ffffffff166133bd565b9050805f03612afb5760405162bfc92160e01b815260040160405180910390fd5b61216483670de0b6b3a7640000835f6133ff565b5f8115612168575f612b2083612a9d565b9050612164816129f1565b60405163360ae4dd60e11b81526001600160a01b037f00000000000000000000000094b4bdd55bac19da3b0a31edc0fc899ae918699e1660048201523060248201525f9073925d5466d4d5b01995e20e1245924ada6415126a90636c15c9ba90604401610e71565b5f198103610d3357610d3c61239b565b5f5f60205f8451602086015f885af180612bc2576040513d5f823e3d81fd5b50505f513d91508115612bd9578060011415612be6565b6001600160a01b0384163b155b15611cd357604051635274afe760e01b81526001600160a01b03851660048201526024016109b3565b6040516370a0823160e01b81523060048201525f906001600160a01b038316906370a0823190602401602060405180830381865afa158015612c53573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610d4991906140f0565b5f5f612c8561196084612916565b5f805460405163fd52722760e01b815292935090916001600160a01b039091169063fd52722790612d02907f00000000000000000000000029219dd400f2bf60e5a23d13be72b486d4038894907f000000000000000000000000c7990369da608c2f4903715e3bd22f2970536c2990899088903090600401614347565b6020604051808303815f875af1158015612d1e573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190612d4291906140f0565b905081811015612d65576040516322cb457760e11b815260040160405180910390fd5b9392505050565b5f5f612d7a61196084612a9d565b5f8054604080516001600160a01b037f0000000000000000000000004aee8d5e242c2485bd124299203c7fa5108c25fc8116602083015294955092939091169163fd527227917f000000000000000000000000c7990369da608c2f4903715e3bd22f2970536c29917f0000000000000000000000005ee17fd12ede62b508f9615db384ce7b834ba65791899188913091015b6040516020818303038152906040526040518763ffffffff1660e01b8152600401612d02969594939291906142bc565b612e906001600160a01b037f0000000000000000000000005ee17fd12ede62b508f9615db384ce7b834ba657167f00000000000000000000000094b4bdd55bac19da3b0a31edc0fc899ae918699e83611c44565b60405163b7ec8d4b60e01b81526001600160a01b037f00000000000000000000000094b4bdd55bac19da3b0a31edc0fc899ae918699e169063b7ec8d4b90612f0090849030907f000000000000000000000000000000000000000000000000000000000000000090600401614609565b6020604051808303815f875af1158015612f1c573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190612f4091906140f0565b5050565b5f610d49600254612710612f5891906142a9565b839061271060016133ff565b604051633545906160e21b815260048101829052306024820181905260448201527f0000000000000000000000007184bea7743ccfbe390f9cd830095a13ef8679416001600160a01b03169063d516418490606401612f00565b6040516338bad5e560e01b81526001600160a01b037f0000000000000000000000007184bea7743ccfbe390f9cd830095a13ef8679411660048201523060248201525f90819073925d5466d4d5b01995e20e1245924ada6415126a906338bad5e590604401602060405180830381865afa15801561303e573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061306291906140f0565b905080831115613070578092505b825f0361307f57505f92915050565b6130d36001600160a01b037f00000000000000000000000029219dd400f2bf60e5a23d13be72b486d4038894167f0000000000000000000000007184bea7743ccfbe390f9cd830095a13ef86794185611c44565b60405163acb7081560e01b8152600481018490523060248201527f0000000000000000000000007184bea7743ccfbe390f9cd830095a13ef8679416001600160a01b03169063acb70815906044015b6020604051808303815f875af115801561313e573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061316291906140f0565b509192915050565b5f5f613174612b2b565b905080831115613182578092505b604051635c19be1560e11b81526001600160a01b037f00000000000000000000000094b4bdd55bac19da3b0a31edc0fc899ae918699e169063b8337c2a90613122908690309081907f00000000000000000000000000000000000000000000000000000000000000009060040161462c565b5f5f6132026119608461265c565b5f8054604080516001600160a01b037f0000000000000000000000004aee8d5e242c2485bd124299203c7fa5108c25fc8116602083015294955092939091169163fd527227917f0000000000000000000000005ee17fd12ede62b508f9615db384ce7b834ba657917f000000000000000000000000c7990369da608c2f4903715e3bd22f2970536c299189918891309101612e0c565b5f5f5f5f60205f8651602088015f8a5af192503d91505f5190508280156132d7575081156132c957806001146132d7565b5f866001600160a01b03163b115b9695505050505050565b5f5f6132eb610e0d565b90507f00000000000000000000000085bba6f69055e1cd712745bd8566807fd875d5436001600160a01b03166301e1d1146040518163ffffffff1660e01b8152600401602060405180830381865afa158015613349573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061336d91906140f0565b158015906133a9575060015461338a66b1a2bc2ec5000083614270565b10806133a9575066b1a2bc2ec500006001546133a69190614270565b81115b156133b657600191505090565b5f91505090565b5f5f5f6133c98561344c565b915091508082106133f1576133e8826133e2878761366f565b90613737565b92505050610d49565b6133e8816133e2878761366f565b5f61342c61340c83613765565b801561342757505f848061342257613422614320565b868809115b151590565b613437868686613791565b6134419190614270565b90505b949350505050565b5f5f5f5f846001600160a01b0316632c8ce6bc6040518163ffffffff1660e01b8152600401606060405180830381865afa15801561348c573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906134b09190614660565b9250509150816001600160a01b0316633ba0b9a96040518163ffffffff1660e01b8152600401602060405180830381865afa1580156134f1573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061351591906140f0565b93505f816001600160a01b031663d2a3584e6040518163ffffffff1660e01b8152600401602060405180830381865afa158015613554573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061357891906140f0565b9050816001600160a01b031663516399df6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156135b6573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906135da919061428e565b801561364d575043826001600160a01b03166360e0a9e16040518163ffffffff1660e01b8152600401602060405180830381865afa15801561361e573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061364291906146aa565b6001600160801b0316145b1561365a57809350613667565b6136648582613841565b93505b505050915091565b5f5f836001600160a01b031663e184c9be6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156136ad573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906136d191906140f0565b90504281116136eb57670de0b6b3a7640000915050610d49565b5f6136f68585613856565b90505f61370342846142a9565b90505f61371861371384846139fe565b613a36565b905061372c670de0b6b3a764000082613737565b945050505050610d49565b5f8061374b670de0b6b3a764000085614309565b905082818161375c5761375c614320565b04949350505050565b5f600282600381111561377a5761377a61405d565b61378491906146d0565b60ff166001149050919050565b5f5f5f61379e8686613a43565b91509150815f036137c2578381816137b8576137b8614320565b0492505050612d65565b8184116137d9576137d96003851502601118613a5f565b5f848688095f868103871696879004966002600389028118808a02820302808a02820302808a02820302808a02820302808a02820302808a02909103029181900381900460010185841190960395909502919093039390930492909217029150509392505050565b5f81831161384f5781612d65565b5090919050565b5f8163ffffffff165f036138e3575f836001600160a01b031663c3fb90d66040518163ffffffff1660e01b815260040160c060405180830381865afa1580156138a1573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906138c59190614713565b50505092505050806bffffffffffffffffffffffff16915050610d49565b6040805160028082526060820183525f9260208301908036833701905050905082815f8151811061391657613916614795565b63ffffffff9092166020928302919091019091015260405163883bdbfd60e01b81525f906001600160a01b0386169063883bdbfd906139599085906004016147a9565b5f60405180830381865afa158015613973573d5f5f3e3d5ffd5b505050506040513d5f823e601f3d908101601f1916820160405261399a91908101906147f1565b90508363ffffffff16815f815181106139b5576139b5614795565b6020026020010151826001815181106139d0576139d0614795565b60200260200101516139e291906148b0565b6139ec91906148cf565b6001600160d81b031695945050505050565b5f80613a0f6201518061016d614309565b613a198486614309565b613a239190614334565b9050613444613a3182613a70565b613a84565b5f5f821215612912575f5ffd5b5f805f1983850993909202808410938190039390930393915050565b634e487b715f52806020526024601cfd5b5f6001600160ff1b03821115612912575f5ffd5b5f680238fd42c5cf03ffff198212158015613aa8575068070c1cc73b00c800008213155b613ae75760405162461bcd60e51b815260206004820152601060248201526f125b9d985b1a5908195e1c1bdb995b9d60821b60448201526064016109b3565b5f821215613b1e57613afa825f03613a84565b6ec097ce7bc90715b34b9f100000000081613b1757613b17614320565b0592915050565b5f6806f05b59d3b20000008312613b5d57506806f05b59d3b1ffffff1990910190770195e54c5dd42177f53a27172fa9ec630262827000000000613b93565b6803782dace9d90000008312613b8f57506803782dace9d8ffffff19909101906b1425982cf597cd205cef7380613b93565b5060015b6064929092029168056bc75e2d6310000068ad78ebc5ac620000008412613be35768ad78ebc5ac61ffffff199093019268056bc75e2d631000006e01855144814a7ff805980ff008400082020590505b6856bc75e2d6310000008412613c1f576856bc75e2d630ffffff199093019268056bc75e2d631000006b02df0ab5a80a22c61ab5a70082020590505b682b5e3af16b188000008412613c5957682b5e3af16b187fffff199093019268056bc75e2d63100000693f1fce3da636ea5cf85082020590505b6815af1d78b58c4000008412613c93576815af1d78b58c3fffff199093019268056bc75e2d63100000690127fa27722cc06cc5e282020590505b680ad78ebc5ac62000008412613ccc57680ad78ebc5ac61fffff199093019268056bc75e2d6310000068280e60114edb805d0382020590505b68056bc75e2d631000008412613d055768056bc75e2d630fffff199093019268056bc75e2d63100000680ebc5fb4174612111082020590505b6802b5e3af16b18800008412613d3e576802b5e3af16b187ffff199093019268056bc75e2d631000006808f00f760a4b2db55d82020590505b68015af1d78b58c400008412613d775768015af1d78b58c3ffff199093019268056bc75e2d631000006806f5f177578893793782020590505b68056bc75e2d631000008481019085906002908280020505918201919050600368056bc75e2d631000008783020505918201919050600468056bc75e2d631000008783020505918201919050600568056bc75e2d631000008783020505918201919050600668056bc75e2d631000008783020505918201919050600768056bc75e2d631000008783020505918201919050600868056bc75e2d631000008783020505918201919050600968056bc75e2d631000008783020505918201919050600a68056bc75e2d631000008783020505918201919050600b68056bc75e2d631000008783020505918201919050600c68056bc75e2d631000008783020505918201919050606468056bc75e2d63100000848402058502059695505050505050565b6001600160a01b0381168114610d3c575f5ffd5b5f60208284031215613ebc575f5ffd5b8135612d6581613e98565b5f5f83601f840112613ed7575f5ffd5b50813567ffffffffffffffff811115613eee575f5ffd5b602083019150836020828501011115613f05575f5ffd5b9250929050565b5f5f5f5f5f5f60a08789031215613f21575f5ffd5b8635613f2c81613e98565b95506020870135613f3c81613e98565b94506040870135935060608701359250608087013567ffffffffffffffff811115613f65575f5ffd5b613f7189828a01613ec7565b979a9699509497509295939492505050565b5f60208284031215613f93575f5ffd5b5035919050565b8015158114610d3c575f5ffd5b5f5f5f5f5f60808688031215613fbb575f5ffd5b853594506020860135613fcd81613f9a565b93506040860135613fdd81613f9a565b9250606086013567ffffffffffffffff811115613ff8575f5ffd5b61400488828901613ec7565b969995985093965092949392505050565b5f81518084528060208401602086015e5f602082860101526020601f19601f83011685010191505092915050565b8215158152604060208201525f6134446040830184614015565b634e487b7160e01b5f52602160045260245ffd5b6002811061408d57634e487b7160e01b5f52602160045260245ffd5b9052565b60208101610d498284614071565b5f602082840312156140af575f5ffd5b813563ffffffff81168114612d65575f5ffd5b5f5f604083850312156140d3575f5ffd5b8235915060208301356140e581613f9a565b809150509250929050565b5f60208284031215614100575f5ffd5b5051919050565b6020808252601f908201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c00604082015260600190565b634e487b7160e01b5f52604160045260245ffd5b604051610220810167ffffffffffffffff811182821017156141765761417661413e565b60405290565b604051601f8201601f1916810167ffffffffffffffff811182821017156141a5576141a561413e565b604052919050565b5f5f5f606084860312156141bf575f5ffd5b8335925060208401356141d181613f9a565b9150604084013567ffffffffffffffff8111156141ec575f5ffd5b8401601f810186136141fc575f5ffd5b803567ffffffffffffffff8111156142165761421661413e565b614229601f8201601f191660200161417c565b81815287602083850101111561423d575f5ffd5b816020840160208301375f602083830101528093505050509250925092565b634e487b7160e01b5f52601160045260245ffd5b80820180821115610d4957610d4961425c565b805161216881613f9a565b5f6020828403121561429e575f5ffd5b8151612d6581613f9a565b81810381811115610d4957610d4961425c565b6001600160a01b038781168252868116602083015260408201869052606082018590528316608082015260c060a082018190525f906142fd90830184614015565b98975050505050505050565b8082028115828204841417610d4957610d4961425c565b634e487b7160e01b5f52601260045260245ffd5b5f8261434257614342614320565b500490565b6001600160a01b039586168152938516602085015260408401929092526060830152909116608082015260c060a082018190525f9082015260e00190565b6001600160a01b03858116825284166020820152604081018390526080606082018190525f906132d790830184614015565b6001600160a01b038316815260408101612d656020830184614071565b6001815b600184111561440f578085048111156143f3576143f361425c565b600184161561440157908102905b60019390931c9280026143d8565b935093915050565b5f8261442557506001610d49565b8161443157505f610d49565b816001811461444757600281146144515761446d565b6001915050610d49565b60ff8411156144625761446261425c565b50506001821b610d49565b5060208310610133831016604e8410600b8410161715614490575081810a610d49565b61449c5f1984846143d4565b805f19048211156144af576144af61425c565b029392505050565b5f612d6560ff841683614417565b8381528215156020820152606060408201525f6134416060830184614015565b805161216881613e98565b5f610220828403128015614502575f5ffd5b5061450b614152565b8251815260208084015190820152614525604084016144e5565b6040820152614536606084016144e5565b6060820152614547608084016144e5565b608082015261455860a084016144e5565b60a082015261456960c084016144e5565b60c082015261457a60e084016144e5565b60e082015261458c61010084016144e5565b61010082015261459f61012084016144e5565b6101208201526101408381015190820152610160808401519082015261018080840151908201526101a080840151908201526101c080840151908201526145e96101e084016144e5565b6101e08201526145fc6102008401614283565b6102008201529392505050565b8381526001600160a01b0383166020820152606081016134446040830184614071565b8481526001600160a01b03848116602083015283166040820152608081016146576060830184614071565b95945050505050565b5f5f5f60608486031215614672575f5ffd5b835161467d81613e98565b602085015190935061468e81613e98565b604085015190925061469f81613e98565b809150509250925092565b5f602082840312156146ba575f5ffd5b81516001600160801b0381168114612d65575f5ffd5b5f60ff8316806146e2576146e2614320565b8060ff84160691505092915050565b8051600f81900b8114612168575f5ffd5b805161ffff81168114612168575f5ffd5b5f5f5f5f5f5f60c08789031215614728575f5ffd5b614731876146f1565b955061473f602088016146f1565b945060408701516bffffffffffffffffffffffff8116811461475f575f5ffd5b935061476d60608801614702565b925061477b60808801614702565b915061478960a08801614702565b90509295509295509295565b634e487b7160e01b5f52603260045260245ffd5b602080825282518282018190525f918401906040840190835b818110156147e657835163ffffffff168352602093840193909201916001016147c2565b509095945050505050565b5f60208284031215614801575f5ffd5b815167ffffffffffffffff811115614817575f5ffd5b8201601f81018413614827575f5ffd5b805167ffffffffffffffff8111156148415761484161413e565b8060051b6148516020820161417c565b9182526020818401810192908101908784111561486c575f5ffd5b6020850194505b838510156148a557845192506001600160d81b0383168314614893575f5ffd5b82825260209485019490910190614873565b979650505050505050565b6001600160d81b038281168282160390811115610d4957610d4961425c565b5f6001600160d81b038316806148e7576148e7614320565b6001600160d81b0392909216919091049291505056fea2646970667358221220593b94554e7acf962df35070b744d131bc864d9d2c27c9f9f4cc5e7ca3ad6f2764736f6c634300081c0033

Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)

000000000000000000000000c7990369da608c2f4903715e3bd22f2970536c2900000000000000000000000000000000000000000000000000000000000000c00000000000000000000000002c13bb26cbf37381a0b816f30de33bd70fd7c4300000000000000000000000004aee8d5e242c2485bd124299203c7fa5108c25fc000000000000000000000000cb2dcdc5e4016d0be706df5e312fb3e7c23414970000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000001f436572657320736d73555344204c657665726167656420537472617465677900

-----Decoded View---------------
Arg [0] : _asset (address): 0xc7990369DA608C2F4903715E3bD22f2970536C29
Arg [1] : _name (string): Ceres smsUSD Leveraged Strategy
Arg [2] : _ceresSwapper (address): 0x2C13bB26CbF37381A0B816F30de33bd70fD7C430
Arg [3] : _pendleMarket (address): 0x4aEe8d5e242c2485bd124299203C7Fa5108C25Fc
Arg [4] : _siloMarketConfig (address): 0xCB2dcdC5E4016d0BE706df5e312Fb3E7c2341497
Arg [5] : isCollateralProtected (bool): True

-----Encoded View---------------
8 Constructor Arguments found :
Arg [0] : 000000000000000000000000c7990369da608c2f4903715e3bd22f2970536c29
Arg [1] : 00000000000000000000000000000000000000000000000000000000000000c0
Arg [2] : 0000000000000000000000002c13bb26cbf37381a0b816f30de33bd70fd7c430
Arg [3] : 0000000000000000000000004aee8d5e242c2485bd124299203c7fa5108c25fc
Arg [4] : 000000000000000000000000cb2dcdc5e4016d0be706df5e312fb3e7c2341497
Arg [5] : 0000000000000000000000000000000000000000000000000000000000000001
Arg [6] : 000000000000000000000000000000000000000000000000000000000000001f
Arg [7] : 436572657320736d73555344204c657665726167656420537472617465677900


Block Transaction Gas Used Reward
view all blocks ##produced##

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

Validator Index Block Amount
View All Withdrawals

Transaction Hash Block Value Eth2 PubKey Valid
View All Deposits
Loading...
Loading
[ Download: CSV Export  ]
[ Download: CSV Export  ]

A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.