S Price: $0.833497 (-2.80%)

Token

ERC20 ***

Overview

Max Total Supply

10 ERC20 ***

Holders

1

Market

Price

$0.00 @ 0.000000 S

Onchain Market Cap

$0.00

Circulating Supply Market Cap

-

Other Info

Token Contract (WITH 18 Decimals)

Balance
0 ERC20 ***

Value
$0.00
0x0000000000000000000000000000000000000000
Loading...
Loading
Loading...
Loading
Loading...
Loading

Click here to update the token information / general information

Contract Source Code Verified (Exact Match)

Contract Name:
Multistrategy

Compiler Version
v0.8.27+commit.40a35a09

Optimization Enabled:
Yes with 200 runs

Other Settings:
shanghai EvmVersion
File 1 of 23 : Multistrategy.sol
// SPDX-License-Identifier: GNU AGPLv3

pragma solidity 0.8.27;

import { 
    IERC20,
    IERC4626,
    ERC20,
    ERC4626
} from "@openzeppelin/contracts/token/ERC20/extensions/ERC4626.sol";
import { ReentrancyGuard } from "@openzeppelin/contracts/utils/ReentrancyGuard.sol";
import { SafeERC20 } from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import { IERC20Metadata } from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol";
import { Math } from "@openzeppelin/contracts/utils/math/Math.sol";
import { MultistrategyManageable } from "src/abstracts/MultistrategyManageable.sol";
import { IMultistrategy } from "interfaces/infra/multistrategy/IMultistrategy.sol";
import { IStrategyAdapter } from "interfaces/infra/multistrategy/IStrategyAdapter.sol";
import { Errors } from "src/infra/libraries/Errors.sol";

contract Multistrategy is IMultistrategy, MultistrategyManageable, ERC4626, ReentrancyGuard {
    using SafeERC20 for IERC20;
    using Math for uint256;
    
    /// @dev How much time it takes for the profit of a strategy to be unlocked.
    uint256 public constant PROFIT_UNLOCK_TIME = 3 days;

    /// @dev Used for locked profit calculations.
    uint256 public constant DEGRADATION_COEFFICIENT = 1 ether;

    /// @notice OpenZeppelin decimals offset used by the ERC4626 implementation.
    /// @dev Calculated to be max(0, 18 - underlyingDecimals) at construction, so the initial conversion rate maximizes
    /// precision between shares and assets.
    uint8 public immutable DECIMALS_OFFSET;

    /// @inheritdoc IMultistrategy
    uint256 public lastReport;
    
    /// @inheritdoc IMultistrategy
    uint256 public lockedProfit;

    /// @inheritdoc IMultistrategy
    uint256 public immutable lockedProfitDegradation;

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

    /// @dev Transfers ownership to the deployer of this contract
    /// @param _asset Address of the token used in this Multistrategy
    /// @param _manager Address of the initial Multistrategy manager
    /// @param _protocolFeeRecipient Address that will receive the performance fees
    /// @param _name Name of this Multistrategy receipt token
    /// @param _symbol Symbol of this Multistrategy receipt token
    constructor(
        address _asset,
        address _manager,
        address _protocolFeeRecipient,
        string memory _name,
        string memory _symbol
    ) 
        MultistrategyManageable(msg.sender, _manager, _protocolFeeRecipient)
        ERC4626(IERC20(_asset))
        ERC20(_name, _symbol)
    {   
        DECIMALS_OFFSET = uint8(Math.max(0, uint256(18) - IERC20Metadata(_asset).decimals()));
        performanceFee = 1000;
        lastReport = block.timestamp;
        lockedProfitDegradation = DEGRADATION_COEFFICIENT / PROFIT_UNLOCK_TIME;
    }

    /*//////////////////////////////////////////////////////////////////////////
                        USER FACING CONSTANT FUNCTIONS
    //////////////////////////////////////////////////////////////////////////*/

    /// @inheritdoc IERC4626
    function totalAssets() public view override returns (uint256) {
        return _liquidity() + totalDebt;
    }

    /// @inheritdoc IERC4626
    /// @dev Limited by the deposit limit
    function maxDeposit(address) public view override returns (uint256) {
        if(totalAssets() >= depositLimit) {
            return 0;
        } else {
            return depositLimit - totalAssets();
        }
    }

    /// @inheritdoc IERC4626
    /// @dev Limited by the deposit limit
    function maxMint(address _receiver) public view override returns (uint256) {
        return convertToShares(maxDeposit(_receiver));
    }

    /// @inheritdoc IERC4626
    function previewWithdraw(uint256 _assets) public view override returns (uint256) {
        uint256 shares = _convertToShares(_assets, Math.Rounding.Ceil);
        if(_assets <= _liquidity()) {
            return shares;
        } else {
            if(slippageLimit == MAX_BPS) return type(uint256).max;
            // Return the number of shares required at the current rate, accounting for slippage.
            return shares.mulDiv(MAX_BPS, MAX_BPS - slippageLimit, Math.Rounding.Ceil);
        }
    }

    /// @inheritdoc IERC4626
    function previewRedeem(uint256 _shares) public view override returns (uint256) {
        uint256 assets = _convertToAssets(_shares, Math.Rounding.Floor);
        if(assets <= _liquidity()) {
            return assets;
        } else {
            // Return the number of assets redeemable at the maximum permitted slippage.
            return assets.mulDiv(MAX_BPS - slippageLimit, MAX_BPS, Math.Rounding.Floor);
        }
    }

    /// @inheritdoc IMultistrategy
    function pricePerShare() external view returns (uint256) {
        return convertToAssets(1 ether);
    }

    /// @inheritdoc IMultistrategy
    function creditAvailable(address _strategy) external view returns (uint256) {
        return _creditAvailable(_strategy);
    }

    /// @inheritdoc IMultistrategy
    function debtExcess(address _strategy) external view returns (uint256) {
        return _debtExcess(_strategy);
    }

    /// @inheritdoc IMultistrategy
    function strategyTotalDebt(address _strategy) external view returns (uint256) {
        return strategies[_strategy].totalDebt;
    }

    function currentPnL() external view returns (uint256, uint256) {
        return _currentPnL();
    }

    /*//////////////////////////////////////////////////////////////////////////
                        USER FACING NON-CONSTANT FUNCTIONS
    //////////////////////////////////////////////////////////////////////////*/

    /// @inheritdoc IERC4626
    function deposit(uint256 _assets, address _receiver) public override whenNotPaused whenNotRetired nonReentrant returns (uint256) {
        uint256 maxAssets = maxDeposit(_receiver);
        require(_assets <= maxAssets, ERC4626ExceededMaxDeposit(_receiver, _assets, maxAssets));

        uint256 shares = previewDeposit(_assets);
        _deposit(msg.sender, _receiver, _assets, shares);

        return shares;
    }

    /// @inheritdoc IERC4626
    function mint(uint256 _shares, address _receiver) public override whenNotPaused whenNotRetired nonReentrant returns (uint256) {
        uint256 maxShares = maxMint(_receiver);
        require(_shares <= maxShares, ERC4626ExceededMaxMint(_receiver, _shares, maxShares));

        uint256 assets = previewMint(_shares);
        _deposit(msg.sender, _receiver, assets, _shares);

        return assets;
    }

    /// @inheritdoc IERC4626
    function withdraw(uint256 _assets, address _receiver, address _owner) public override whenNotPaused nonReentrant returns (uint256) {
        uint256 maxAssets = maxWithdraw(_owner);
        require(_assets <= maxAssets, ERC4626ExceededMaxWithdraw(_owner, _assets, maxAssets));

        uint256 maxShares = previewWithdraw(_assets);
        (, uint256 shares) = _withdraw(msg.sender, _receiver, _owner, _assets, maxShares, false);

        require(shares <= maxShares, Errors.SlippageCheckFailed(maxShares, shares));

        return shares;
    }

    /// @inheritdoc IERC4626
    function redeem(uint256 _shares, address _receiver, address _owner) public override whenNotPaused nonReentrant returns (uint256) {
        uint256 maxShares = maxRedeem(_owner);
        require(_shares <= maxShares, ERC4626ExceededMaxRedeem(_owner, _shares, maxShares));

        uint256 minAssets = previewRedeem(_shares);
        (uint256 assets, ) = _withdraw(msg.sender, _receiver, _owner, minAssets, _shares, true);

        require(assets >= minAssets, Errors.SlippageCheckFailed(minAssets, assets));

        return assets;
    }

    /// @inheritdoc IMultistrategy
    function requestCredit() external whenNotPaused onlyActiveStrategy(msg.sender) returns (uint256) {
        return _requestCredit();
    }

    /// @inheritdoc IMultistrategy
    function strategyReport(uint256 _debtRepayment, uint256 _gain, uint256 _loss) 
        external 
        whenNotPaused
        onlyActiveStrategy(msg.sender)
    {
        _report(_debtRepayment, _gain, _loss);
    }

    /// @inheritdoc IMultistrategy
    function rescueToken(address _token, address _recipient) external onlyGuardian {
        _rescueToken(_token, _recipient);
    }

    /*//////////////////////////////////////////////////////////////////////////
                            INTERNAL CONSTANT FUNCTIONS
    //////////////////////////////////////////////////////////////////////////*/

    /// @notice Internal view function to retrieve the current liquidity of the contract.
    /// @return The current liquidity (balance of the asset) of the contract.
    function _liquidity() internal view returns (uint256) {
        return IERC20(asset()).balanceOf(address(this));
    }

    /// @notice Converts a given amount of assets to shares, with specified rounding.
    /// @param _assets The amount of assets to convert to shares.
    /// @param rounding The rounding direction to apply during the conversion.
    /// @return The number of shares corresponding to the given amount of assets.
    function _convertToShares(uint256 _assets, Math.Rounding rounding) internal view override returns (uint256) {
        return _assets.mulDiv(totalSupply() + 10 ** _decimalsOffset(), _freeFunds() + 1, rounding);
    }

    /// @notice Convert a given amount of shares to assets, with specified rounding.
    /// @param _shares The number of shares to convert to assets.
    /// @param rounding The rounding direction to apply during the conversion.
    /// @return The amount of assets corresponding to the given number of shares.
    function _convertToAssets(uint256 _shares, Math.Rounding rounding) internal view override returns (uint256) {
        return _shares.mulDiv(_freeFunds() + 1, totalSupply() + 10 ** _decimalsOffset(), rounding);
    }

    /// @notice Calculates the available credit for a strategy.
    /// 
    /// This function performs the following actions:
    /// - Determines the total assets and debt limits for both the multi-strategy and the specific strategy.
    /// - Checks if the strategy or the multi-strategy has exceeded their respective debt limits, in which case no new credit is offered.
    /// - Calculates the potential credit as the difference between the strategy's debt limit and its current debt.
    /// - Limits the potential credit by the maximum available credit of the multi-strategy.
    /// - Ensures the potential credit is within the strategy's minimum and maximum debt delta bounds.
    /// - Returns zero if the available credit is below the strategy's minimum debt delta.
    /// - Returns the available credit, ensuring it does not exceed the strategy's maximum debt delta.
    /// 
    /// @param _strategy The address of the strategy for which to determine the available credit.
    /// @return The amount of credit available for the given strategy.
    function _creditAvailable(address _strategy) internal view returns (uint256) {
        uint256 mult_totalAssets = totalAssets();
        uint256 mult_debtLimit = debtRatio.mulDiv(mult_totalAssets, MAX_BPS);
        uint256 mult_totalDebt = totalDebt;

        uint256 strat_debtLimit = strategies[_strategy].debtRatio.mulDiv(mult_totalAssets, MAX_BPS);
        uint256 strat_totalDebt = strategies[_strategy].totalDebt;
        uint256 strat_minDebtDelta = strategies[_strategy].minDebtDelta;
        uint256 strat_maxDebtDelta = strategies[_strategy].maxDebtDelta;

        if(strat_totalDebt >= strat_debtLimit || mult_totalDebt >= mult_debtLimit){
            return 0;
        }

        uint256 credit = strat_debtLimit - strat_totalDebt;
        uint256 maxAvailableCredit = mult_debtLimit - mult_totalDebt;
        credit = Math.min(credit, maxAvailableCredit);

        // Bound to the minimum and maximum borrow limits
        if(credit < strat_minDebtDelta) {
            return 0;
        } else {
            return Math.min(credit, strat_maxDebtDelta);
        }
    }

    /// @notice Calculates the excess debt of a strategy.
    /// 
    /// This function performs the following actions:
    /// - If the overall debt ratio is zero, it returns the total debt of the strategy as excess debt.
    /// - Calculates the strategy's debt limit based on its debt ratio and the total assets.
    /// - If the strategy's total debt is less than or equal to its debt limit, it returns zero indicating no excess debt.
    /// - If the strategy's total debt exceeds its debt limit, it returns the difference as the excess debt.
    /// 
    /// @param _strategy The address of the strategy for which to determine the debt excess.
    /// @return The amount of excess debt for the given strategy.
    function _debtExcess(address _strategy) internal view returns (uint256) {
        if(debtRatio == 0) {
            return strategies[_strategy].totalDebt;
        }

        uint256 strat_debtLimit = strategies[_strategy].debtRatio.mulDiv(totalAssets(), MAX_BPS);
        uint256 strat_totalDebt = strategies[_strategy].totalDebt;

        if(strat_totalDebt <= strat_debtLimit) {
            return 0;
        } else {
            return strat_totalDebt - strat_debtLimit;
        }
    }
    
    /// @notice Calculates the free funds available in the contract.
    /// @return The amount of free funds available.
    function _freeFunds() internal view returns (uint256) {
        return totalAssets() - _calculateLockedProfit();
    }

    /// @notice Calculate the current locked profit.
    /// 
    /// This function performs the following actions:
    /// - Calculates the locked funds ratio based on the time elapsed since the last report and the locked profit degradation rate.
    /// - If the locked funds ratio is less than the degradation coefficient, it computes the remaining locked profit by reducing it proportionally.
    /// - If the locked funds ratio is greater than or equal to the degradation coefficient, it returns zero indicating no locked profit remains.
    /// 
    /// @return The calculated current locked profit.
    function _calculateLockedProfit() internal view returns (uint256) {
        uint256 lockedFundsRatio = (block.timestamp - lastReport) * lockedProfitDegradation;

        if(lockedFundsRatio < DEGRADATION_COEFFICIENT) {
            return lockedProfit - lockedFundsRatio.mulDiv(lockedProfit, DEGRADATION_COEFFICIENT);
        }
        return 0;
    }

    /**
     * @notice Calculates the current profit and loss (PnL) across all active strategies.
     * 
     * This function performs the following actions:
     * - Iterates through the `withdrawOrder` array, which defines the order in which strategies are withdrawn from.
     * - For each strategy in the `withdrawOrder`:
     *   - If the strategy address is zero, it breaks the loop, indicating the end of the list.
     *   - If the strategy has no debt, it skips to the next strategy.
     *   - Otherwise, it retrieves the current profit and loss (PnL) from the strategy by calling `currentPnL`.
     *   - Adds the strategy's profit to the total profit, after deducting the performance fee.
     *   - Adds the strategy's loss to the total loss.
     * - Returns the total profit and total loss across all active strategies.
     * 
     * @return totalProfit The total profit across all active strategies, after deducting the performance fee.
     * @return totalLoss The total loss across all active strategies.
     */
    function _currentPnL() internal view returns (uint256, uint256) {
        if (activeStrategies == 0) return (0, 0);
        uint256 totalProfit = 0;
        uint256 totalLoss = 0;

        for(uint8 i = 0; i < activeStrategies; ++i){
            address strategy = withdrawOrder[i];
            if(strategy == address(0)) break;
            if(strategies[strategy].totalDebt == 0) continue;

            (uint256 gain, uint256 loss) = IStrategyAdapter(strategy).currentPnL();
            totalProfit += gain.mulDiv(MAX_BPS - performanceFee, MAX_BPS);
            totalLoss += loss;
        }

        return (totalProfit, totalLoss);
    }

    function _decimalsOffset() internal view override returns (uint8) {
        return DECIMALS_OFFSET;
    }

    /*//////////////////////////////////////////////////////////////////////////
                            INTERNAL NON-CONSTANT FUNCTIONS
    //////////////////////////////////////////////////////////////////////////*/

    /// @notice Handles deposits into the contract.
    /// 
    /// This function performs the following actions:
    /// - Validates that the receiver address is not zero or the contract address itself.
    /// - Ensures that the deposited amount is greater than zero.
    /// - Transfers the assets from the caller to the contract.
    /// - Mints the corresponding shares for the receiver.
    /// - Emits a `Deposit` event with the caller, receiver, amount of assets, and number of shares.
    /// 
    /// @param _caller The address of the entity initiating the deposit.
    /// @param _receiver The address of the recipient to receive the shares.
    /// @param _assets The amount of assets being deposited.
    /// @param _shares The number of shares to be minted for the receiver.
    function _deposit(address _caller, address _receiver, uint256 _assets, uint256 _shares) internal override {
        require(_receiver != address(0) && _receiver != address(this), Errors.InvalidAddress(_receiver));
        require(_assets > 0, Errors.ZeroAmount(_assets));

        IERC20(asset()).safeTransferFrom(_caller, address(this), _assets);
        _mint(_receiver, _shares);

        emit Deposit(_caller, _receiver, _assets, _shares);
    }

    /// @notice Handles withdrawals from the contract.
    /// 
    /// This function performs the following actions:
    /// - If the caller is not the owner, it checks and spends the allowance for the withdrawal.
    /// - Ensures that the amount to be withdrawn is greater than zero.
    /// - If the requested withdrawal amount exceeds the available liquidity, it withdraws the necessary amount from the strategies in the withdrawal order.
    ///   - Iterates through the withdrawal queue, withdrawing from each strategy until the liquidity requirement is met or the queue is exhausted.
    ///   - Updates the total debt of both the strategy and the contract as assets are withdrawn.
    ///   - Requests the strategy to report, accounting for potential gains or losses.
    /// - Reverts if the withdrawal process does not result in sufficient liquidity.
    /// - Burns the corresponding shares and transfers the requested assets to the receiver.
    /// - Emits a `Withdraw` event with the caller, receiver, owner, amount of assets withdrawn, and shares burned.
    /// 
    /// @param _caller The address of the entity initiating the withdrawal.
    /// @param _receiver The address of the recipient to receive the withdrawn assets.
    /// @param _owner The address of the owner of the shares being withdrawn.
    /// @param _assets The amount of assets to withdraw.
    /// @param _shares The amount of shares to burn.
    /// @param _consumeAllShares True if all `_shares` should be used to withdraw. False if it should withdraw just `_assets`.
    /// @return The number of assets withdrawn and the shares burned as a result of the withdrawal.
    function _withdraw(
        address _caller,
        address _receiver,
        address _owner,
        uint256 _assets,
        uint256 _shares,
        bool _consumeAllShares
    ) internal returns (uint256, uint256) {
        require(_shares > 0, Errors.ZeroAmount(_shares));

        if (_caller != _owner) {
            _spendAllowance(_owner, _caller, _shares);
        }

        uint256 assets = _consumeAllShares ? _convertToAssets(_shares, Math.Rounding.Floor) : _assets;

        if(assets > _liquidity()) {
            for(uint8 i = 0; i <= withdrawOrder.length; ++i){
                address strategy = withdrawOrder[i];

                // We reached the end of the withdraw queue and assets are still higher than the liquidity
                require(strategy != address(0), Errors.InsufficientLiquidity(assets, _liquidity()));

                // We can't withdraw from a strategy more than what it has asked as credit.
                uint256 assetsToWithdraw = Math.min(assets - _liquidity(), strategies[strategy].totalDebt);
                if(assetsToWithdraw == 0) continue;

                uint256 withdrawn = IStrategyAdapter(strategy).withdraw(assetsToWithdraw);
                strategies[strategy].totalDebt -= withdrawn;
                totalDebt -= withdrawn;

                IStrategyAdapter(strategy).askReport();

                // Update assets, as a loss could have been reported and user should get less assets for
                // the same amount of shares.
                if(_consumeAllShares) assets = _convertToAssets(_shares, Math.Rounding.Floor);
                if(assets <= _liquidity()) break;
            }
        }

        uint256 shares = _consumeAllShares ? _shares : _convertToShares(assets, Math.Rounding.Ceil);
        _burn(_owner, shares);
        IERC20(asset()).safeTransfer(_receiver, assets);

        emit Withdraw(_caller, _receiver, _owner, assets, shares);

        return (assets, shares);
    }

    /// @notice Requests credit for an active strategy.
    /// 
    /// This function performs the following actions:
    /// - Calculates the available credit for the strategy using `_creditAvailable`.
    /// - If credit is available, it updates the total debt for the strategy and the multistrategy contract.
    /// - Transfers the calculated credit amount to the strategy.
    ///
    /// Emits a `CreditRequested` event.
    ///
    /// @dev This function should be called only by active strategies when they need to request credit.
    function _requestCredit() internal returns (uint256){
        uint256 credit = _creditAvailable(msg.sender);

        if(credit > 0) {
            strategies[msg.sender].totalDebt += credit;
            totalDebt += credit;
            IERC20(asset()).safeTransfer(msg.sender, credit);
            emit CreditRequested(msg.sender, credit);
        }
        
        return credit;
    }

    /// @notice Reports the performance of a strategy.
    /// 
    /// This function performs the following actions:
    /// - Validates that the reporting strategy does not claim both a gain and a loss simultaneously.
    /// - Checks that the strategy has sufficient tokens to cover the debt repayment and the gain.
    /// - If there is a loss, it realizes the loss.
    /// - Calculates and deducts the performance fee from the gain.
    /// - Determines the excess debt of the strategy.
    /// - Adjusts the strategy's and contract's total debt accordingly.
    /// - Calculates and updates the new locked profit after accounting for any losses.
    /// - Updates the reporting timestamps for the strategy and the contract.
    /// - Transfers the debt repayment and the gains to this contract.
    ///
    /// Emits a `StrategyReported` event.
    ///
    /// @param _debtRepayment The amount of debt being repaid by the strategy.
    /// @param _gain The amount of profit reported by the strategy.
    /// @param _loss The amount of loss reported by the strategy.
    function _report(uint256 _debtRepayment, uint256 _gain, uint256 _loss) internal {
        uint256 strategyBalance = IERC20(asset()).balanceOf(msg.sender);
        require(!(_gain > 0 && _loss > 0), Errors.GainLossMismatch());
        require(strategyBalance >= _debtRepayment + _gain, Errors.InsufficientBalance(strategyBalance, _debtRepayment + _gain));

        uint256 profit = 0;
        uint256 feesCollected = 0;
        if(_loss > 0) _reportLoss(msg.sender, _loss);
        if(_gain > 0) {
            feesCollected = _gain.mulDiv(performanceFee, MAX_BPS);
            profit = _gain - feesCollected;
        } 

        uint256 debtToRepay = Math.min(_debtRepayment, _debtExcess(msg.sender));
        if(debtToRepay > 0) {
            strategies[msg.sender].totalDebt -= debtToRepay;
            totalDebt -= debtToRepay;
        }
        
        uint256 newLockedProfit = _calculateLockedProfit() + profit;
        if(newLockedProfit > _loss) {
            lockedProfit = newLockedProfit - _loss;
        } else {
            lockedProfit = 0;
        }

        strategies[msg.sender].lastReport = block.timestamp;
        lastReport = block.timestamp;

        if(debtToRepay + _gain > 0) IERC20(asset()).safeTransferFrom(msg.sender, address(this), debtToRepay + _gain);
        if(feesCollected > 0) IERC20(asset()).safeTransfer(protocolFeeRecipient, feesCollected);

        emit StrategyReported(msg.sender, debtToRepay, profit, _loss);
    }

    /// @notice Reports a loss for a strategy.
    /// 
    /// This function performs the following actions:
    /// - Validates that the loss reported by the strategy does not exceed its total debt.
    /// - Updates the strategy's total loss by adding the reported loss.
    /// - Reduces the strategy's total debt by the reported loss.
    /// - Adjusts the contract's total debt by reducing it with the reported loss.
    ///
    /// @param _strategy The address of the strategy reporting the loss.
    /// @param _loss The amount of loss reported by the strategy.
    function _reportLoss(address _strategy, uint256 _loss) internal {
        require(_loss <= strategies[_strategy].totalDebt, Errors.InvalidStrategyLoss());

        strategies[_strategy].totalLoss += _loss;
        strategies[_strategy].totalDebt -= _loss;
        totalDebt -= _loss;
    }

    /// @notice Rescues tokens from the contract.
    /// 
    /// This function performs the following actions:
    /// - Retrieves the balance of the specified token in the contract.
    /// - Transfers the entire balance of the specified token to the recipient address.
    ///
    /// @param _token The address of the token to be rescued.
    /// @param _recipient The address to receive the rescued tokens.
    function _rescueToken(address _token, address _recipient) internal {
        require(_token != asset(), Errors.InvalidAddress(_token));

        uint256 amount = IERC20(_token).balanceOf(address(this));
        IERC20(_token).safeTransfer(_recipient, amount);
    }
}

File 2 of 23 : ERC4626.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/extensions/ERC4626.sol)

pragma solidity ^0.8.20;

import {IERC20, IERC20Metadata, ERC20} from "../ERC20.sol";
import {SafeERC20} from "../utils/SafeERC20.sol";
import {IERC4626} from "../../../interfaces/IERC4626.sol";
import {Math} from "../../../utils/math/Math.sol";

/**
 * @dev Implementation of the ERC4626 "Tokenized Vault Standard" as defined in
 * https://eips.ethereum.org/EIPS/eip-4626[EIP-4626].
 *
 * This extension allows the minting and burning of "shares" (represented using the ERC20 inheritance) in exchange for
 * underlying "assets" through standardized {deposit}, {mint}, {redeem} and {burn} workflows. This contract extends
 * the ERC20 standard. Any additional extensions included along it would affect the "shares" token represented by this
 * contract and not the "assets" token which is an independent contract.
 *
 * [CAUTION]
 * ====
 * In empty (or nearly empty) ERC-4626 vaults, deposits are at high risk of being stolen through frontrunning
 * with a "donation" to the vault that inflates the price of a share. This is variously known as a donation or inflation
 * attack and is essentially a problem of slippage. Vault deployers can protect against this attack by making an initial
 * deposit of a non-trivial amount of the asset, such that price manipulation becomes infeasible. Withdrawals may
 * similarly be affected by slippage. Users can protect against this attack as well as unexpected slippage in general by
 * verifying the amount received is as expected, using a wrapper that performs these checks such as
 * https://github.com/fei-protocol/ERC4626#erc4626router-and-base[ERC4626Router].
 *
 * Since v4.9, this implementation uses virtual assets and shares to mitigate that risk. The `_decimalsOffset()`
 * corresponds to an offset in the decimal representation between the underlying asset's decimals and the vault
 * decimals. This offset also determines the rate of virtual shares to virtual assets in the vault, which itself
 * determines the initial exchange rate. While not fully preventing the attack, analysis shows that the default offset
 * (0) makes it non-profitable, as a result of the value being captured by the virtual shares (out of the attacker's
 * donation) matching the attacker's expected gains. With a larger offset, the attack becomes orders of magnitude more
 * expensive than it is profitable. More details about the underlying math can be found
 * xref:erc4626.adoc#inflation-attack[here].
 *
 * The drawback of this approach is that the virtual shares do capture (a very small) part of the value being accrued
 * to the vault. Also, if the vault experiences losses, the users try to exit the vault, the virtual shares and assets
 * will cause the first user to exit to experience reduced losses in detriment to the last users that will experience
 * bigger losses. Developers willing to revert back to the pre-v4.9 behavior just need to override the
 * `_convertToShares` and `_convertToAssets` functions.
 *
 * To learn more, check out our xref:ROOT:erc4626.adoc[ERC-4626 guide].
 * ====
 */
abstract contract ERC4626 is ERC20, IERC4626 {
    using Math for uint256;

    IERC20 private immutable _asset;
    uint8 private immutable _underlyingDecimals;

    /**
     * @dev Attempted to deposit more assets than the max amount for `receiver`.
     */
    error ERC4626ExceededMaxDeposit(address receiver, uint256 assets, uint256 max);

    /**
     * @dev Attempted to mint more shares than the max amount for `receiver`.
     */
    error ERC4626ExceededMaxMint(address receiver, uint256 shares, uint256 max);

    /**
     * @dev Attempted to withdraw more assets than the max amount for `receiver`.
     */
    error ERC4626ExceededMaxWithdraw(address owner, uint256 assets, uint256 max);

    /**
     * @dev Attempted to redeem more shares than the max amount for `receiver`.
     */
    error ERC4626ExceededMaxRedeem(address owner, uint256 shares, uint256 max);

    /**
     * @dev Set the underlying asset contract. This must be an ERC20-compatible contract (ERC20 or ERC777).
     */
    constructor(IERC20 asset_) {
        (bool success, uint8 assetDecimals) = _tryGetAssetDecimals(asset_);
        _underlyingDecimals = success ? assetDecimals : 18;
        _asset = asset_;
    }

    /**
     * @dev Attempts to fetch the asset decimals. A return value of false indicates that the attempt failed in some way.
     */
    function _tryGetAssetDecimals(IERC20 asset_) private view returns (bool, uint8) {
        (bool success, bytes memory encodedDecimals) = address(asset_).staticcall(
            abi.encodeCall(IERC20Metadata.decimals, ())
        );
        if (success && encodedDecimals.length >= 32) {
            uint256 returnedDecimals = abi.decode(encodedDecimals, (uint256));
            if (returnedDecimals <= type(uint8).max) {
                return (true, uint8(returnedDecimals));
            }
        }
        return (false, 0);
    }

    /**
     * @dev Decimals are computed by adding the decimal offset on top of the underlying asset's decimals. This
     * "original" value is cached during construction of the vault contract. If this read operation fails (e.g., the
     * asset has not been created yet), a default of 18 is used to represent the underlying asset's decimals.
     *
     * See {IERC20Metadata-decimals}.
     */
    function decimals() public view virtual override(IERC20Metadata, ERC20) returns (uint8) {
        return _underlyingDecimals + _decimalsOffset();
    }

    /** @dev See {IERC4626-asset}. */
    function asset() public view virtual returns (address) {
        return address(_asset);
    }

    /** @dev See {IERC4626-totalAssets}. */
    function totalAssets() public view virtual returns (uint256) {
        return _asset.balanceOf(address(this));
    }

    /** @dev See {IERC4626-convertToShares}. */
    function convertToShares(uint256 assets) public view virtual returns (uint256) {
        return _convertToShares(assets, Math.Rounding.Floor);
    }

    /** @dev See {IERC4626-convertToAssets}. */
    function convertToAssets(uint256 shares) public view virtual returns (uint256) {
        return _convertToAssets(shares, Math.Rounding.Floor);
    }

    /** @dev See {IERC4626-maxDeposit}. */
    function maxDeposit(address) public view virtual returns (uint256) {
        return type(uint256).max;
    }

    /** @dev See {IERC4626-maxMint}. */
    function maxMint(address) public view virtual returns (uint256) {
        return type(uint256).max;
    }

    /** @dev See {IERC4626-maxWithdraw}. */
    function maxWithdraw(address owner) public view virtual returns (uint256) {
        return _convertToAssets(balanceOf(owner), Math.Rounding.Floor);
    }

    /** @dev See {IERC4626-maxRedeem}. */
    function maxRedeem(address owner) public view virtual returns (uint256) {
        return balanceOf(owner);
    }

    /** @dev See {IERC4626-previewDeposit}. */
    function previewDeposit(uint256 assets) public view virtual returns (uint256) {
        return _convertToShares(assets, Math.Rounding.Floor);
    }

    /** @dev See {IERC4626-previewMint}. */
    function previewMint(uint256 shares) public view virtual returns (uint256) {
        return _convertToAssets(shares, Math.Rounding.Ceil);
    }

    /** @dev See {IERC4626-previewWithdraw}. */
    function previewWithdraw(uint256 assets) public view virtual returns (uint256) {
        return _convertToShares(assets, Math.Rounding.Ceil);
    }

    /** @dev See {IERC4626-previewRedeem}. */
    function previewRedeem(uint256 shares) public view virtual returns (uint256) {
        return _convertToAssets(shares, Math.Rounding.Floor);
    }

    /** @dev See {IERC4626-deposit}. */
    function deposit(uint256 assets, address receiver) public virtual returns (uint256) {
        uint256 maxAssets = maxDeposit(receiver);
        if (assets > maxAssets) {
            revert ERC4626ExceededMaxDeposit(receiver, assets, maxAssets);
        }

        uint256 shares = previewDeposit(assets);
        _deposit(_msgSender(), receiver, assets, shares);

        return shares;
    }

    /** @dev See {IERC4626-mint}.
     *
     * As opposed to {deposit}, minting is allowed even if the vault is in a state where the price of a share is zero.
     * In this case, the shares will be minted without requiring any assets to be deposited.
     */
    function mint(uint256 shares, address receiver) public virtual returns (uint256) {
        uint256 maxShares = maxMint(receiver);
        if (shares > maxShares) {
            revert ERC4626ExceededMaxMint(receiver, shares, maxShares);
        }

        uint256 assets = previewMint(shares);
        _deposit(_msgSender(), receiver, assets, shares);

        return assets;
    }

    /** @dev See {IERC4626-withdraw}. */
    function withdraw(uint256 assets, address receiver, address owner) public virtual returns (uint256) {
        uint256 maxAssets = maxWithdraw(owner);
        if (assets > maxAssets) {
            revert ERC4626ExceededMaxWithdraw(owner, assets, maxAssets);
        }

        uint256 shares = previewWithdraw(assets);
        _withdraw(_msgSender(), receiver, owner, assets, shares);

        return shares;
    }

    /** @dev See {IERC4626-redeem}. */
    function redeem(uint256 shares, address receiver, address owner) public virtual returns (uint256) {
        uint256 maxShares = maxRedeem(owner);
        if (shares > maxShares) {
            revert ERC4626ExceededMaxRedeem(owner, shares, maxShares);
        }

        uint256 assets = previewRedeem(shares);
        _withdraw(_msgSender(), receiver, owner, assets, shares);

        return assets;
    }

    /**
     * @dev Internal conversion function (from assets to shares) with support for rounding direction.
     */
    function _convertToShares(uint256 assets, Math.Rounding rounding) internal view virtual returns (uint256) {
        return assets.mulDiv(totalSupply() + 10 ** _decimalsOffset(), totalAssets() + 1, rounding);
    }

    /**
     * @dev Internal conversion function (from shares to assets) with support for rounding direction.
     */
    function _convertToAssets(uint256 shares, Math.Rounding rounding) internal view virtual returns (uint256) {
        return shares.mulDiv(totalAssets() + 1, totalSupply() + 10 ** _decimalsOffset(), rounding);
    }

    /**
     * @dev Deposit/mint common workflow.
     */
    function _deposit(address caller, address receiver, uint256 assets, uint256 shares) internal virtual {
        // If _asset is ERC777, `transferFrom` can trigger a reentrancy BEFORE the transfer happens through the
        // `tokensToSend` hook. On the other hand, the `tokenReceived` hook, that is triggered after the transfer,
        // calls the vault, which is assumed not malicious.
        //
        // Conclusion: we need to do the transfer before we mint so that any reentrancy would happen before the
        // assets are transferred and before the shares are minted, which is a valid state.
        // slither-disable-next-line reentrancy-no-eth
        SafeERC20.safeTransferFrom(_asset, caller, address(this), assets);
        _mint(receiver, shares);

        emit Deposit(caller, receiver, assets, shares);
    }

    /**
     * @dev Withdraw/redeem common workflow.
     */
    function _withdraw(
        address caller,
        address receiver,
        address owner,
        uint256 assets,
        uint256 shares
    ) internal virtual {
        if (caller != owner) {
            _spendAllowance(owner, caller, shares);
        }

        // If _asset is ERC777, `transfer` can trigger a reentrancy AFTER the transfer happens through the
        // `tokensReceived` hook. On the other hand, the `tokensToSend` hook, that is triggered before the transfer,
        // calls the vault, which is assumed not malicious.
        //
        // Conclusion: we need to do the transfer after the burn so that any reentrancy would happen after the
        // shares are burned and after the assets are transferred, which is a valid state.
        _burn(owner, shares);
        SafeERC20.safeTransfer(_asset, receiver, assets);

        emit Withdraw(caller, receiver, owner, assets, shares);
    }

    function _decimalsOffset() internal view virtual returns (uint8) {
        return 0;
    }
}

File 3 of 23 : ReentrancyGuard.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/ReentrancyGuard.sol)

pragma solidity ^0.8.20;

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

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

    uint256 private _status;

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

    constructor() {
        _status = NOT_ENTERED;
    }

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

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

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

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

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

File 4 of 23 : SafeERC20.sol
// 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 {IERC20Permit} from "../extensions/IERC20Permit.sol";
import {Address} from "../../../utils/Address.sol";

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

    /**
     * @dev An operation with an ERC20 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 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 5 of 23 : IERC20Metadata.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/extensions/IERC20Metadata.sol)

pragma solidity ^0.8.20;

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

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

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

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

File 6 of 23 : Math.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/math/Math.sol)

pragma solidity ^0.8.20;

/**
 * @dev Standard math utilities missing in the Solidity language.
 */
library Math {
    /**
     * @dev Muldiv operation overflow.
     */
    error MathOverflowedMulDiv();

    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 overflow flag.
     */
    function tryAdd(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        unchecked {
            uint256 c = a + b;
            if (c < a) return (false, 0);
            return (true, c);
        }
    }

    /**
     * @dev Returns the subtraction of two unsigned integers, with an overflow flag.
     */
    function trySub(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        unchecked {
            if (b > a) return (false, 0);
            return (true, a - b);
        }
    }

    /**
     * @dev Returns the multiplication of two unsigned integers, with an overflow flag.
     */
    function tryMul(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        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 division by zero flag.
     */
    function tryDiv(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        unchecked {
            if (b == 0) return (false, 0);
            return (true, a / b);
        }
    }

    /**
     * @dev Returns the remainder of dividing two unsigned integers, with a division by zero flag.
     */
    function tryMod(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        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.
            return a / b;
        }

        // (a + b - 1) / b can overflow on addition, so we distribute.
        return a == 0 ? 0 : (a - 1) / b + 1;
    }

    /**
     * @notice Calculates floor(x * y / denominator) with full precision. Throws if result overflows a uint256 or
     * denominator == 0.
     * @dev 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^256 and mod 2^256 - 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^256 + 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^256. Also prevents denominator == 0.
            if (denominator <= prod1) {
                revert MathOverflowedMulDiv();
            }

            ///////////////////////////////////////////////
            // 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^256 / 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^256. Now that denominator is an odd number, it has an inverse modulo 2^256 such
            // that denominator * inv = 1 mod 2^256. Compute the inverse by starting with a seed that is correct for
            // four bits. That is, denominator * inv = 1 mod 2^4.
            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^8
            inverse *= 2 - denominator * inverse; // inverse mod 2^16
            inverse *= 2 - denominator * inverse; // inverse mod 2^32
            inverse *= 2 - denominator * inverse; // inverse mod 2^64
            inverse *= 2 - denominator * inverse; // inverse mod 2^128
            inverse *= 2 - denominator * inverse; // inverse mod 2^256

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

    /**
     * @notice 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) {
        uint256 result = mulDiv(x, y, denominator);
        if (unsignedRoundsUp(rounding) && mulmod(x, y, denominator) > 0) {
            result += 1;
        }
        return result;
    }

    /**
     * @dev Returns the square root of a number. If the number is not a perfect square, the value is rounded
     * towards zero.
     *
     * Inspired by Henry S. Warren, Jr.'s "Hacker's Delight" (Chapter 11).
     */
    function sqrt(uint256 a) internal pure returns (uint256) {
        if (a == 0) {
            return 0;
        }

        // For our first guess, we get the biggest power of 2 which is smaller than the square root of the target.
        //
        // We know that the "msb" (most significant bit) of our target number `a` is a power of 2 such that we have
        // `msb(a) <= a < 2*msb(a)`. This value can be written `msb(a)=2**k` with `k=log2(a)`.
        //
        // This can be rewritten `2**log2(a) <= a < 2**(log2(a) + 1)`
        // → `sqrt(2**k) <= sqrt(a) < sqrt(2**(k+1))`
        // → `2**(k/2) <= sqrt(a) < 2**((k+1)/2) <= 2**(k/2 + 1)`
        //
        // Consequently, `2**(log2(a) / 2)` is a good first approximation of `sqrt(a)` with at least 1 correct bit.
        uint256 result = 1 << (log2(a) >> 1);

        // At this point `result` is an estimation with one bit of precision. We know the true value is a uint128,
        // since it is the square root of a uint256. Newton's method converges quadratically (precision doubles at
        // every iteration). We thus need at most 7 iteration to turn our partial result with one bit of precision
        // into the expected uint128 result.
        unchecked {
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            return min(result, a / result);
        }
    }

    /**
     * @notice 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 + (unsignedRoundsUp(rounding) && result * result < a ? 1 : 0);
        }
    }

    /**
     * @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;
        unchecked {
            if (value >> 128 > 0) {
                value >>= 128;
                result += 128;
            }
            if (value >> 64 > 0) {
                value >>= 64;
                result += 64;
            }
            if (value >> 32 > 0) {
                value >>= 32;
                result += 32;
            }
            if (value >> 16 > 0) {
                value >>= 16;
                result += 16;
            }
            if (value >> 8 > 0) {
                value >>= 8;
                result += 8;
            }
            if (value >> 4 > 0) {
                value >>= 4;
                result += 4;
            }
            if (value >> 2 > 0) {
                value >>= 2;
                result += 2;
            }
            if (value >> 1 > 0) {
                result += 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 + (unsignedRoundsUp(rounding) && 1 << result < value ? 1 : 0);
        }
    }

    /**
     * @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 + (unsignedRoundsUp(rounding) && 10 ** result < value ? 1 : 0);
        }
    }

    /**
     * @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;
        unchecked {
            if (value >> 128 > 0) {
                value >>= 128;
                result += 16;
            }
            if (value >> 64 > 0) {
                value >>= 64;
                result += 8;
            }
            if (value >> 32 > 0) {
                value >>= 32;
                result += 4;
            }
            if (value >> 16 > 0) {
                value >>= 16;
                result += 2;
            }
            if (value >> 8 > 0) {
                result += 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 + (unsignedRoundsUp(rounding) && 1 << (result << 3) < value ? 1 : 0);
        }
    }

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

File 7 of 23 : MultistrategyManageable.sol
// SPDX-License-Identifier: GNU AGPLv3

pragma solidity 0.8.27;

import { IERC4626 } from "@openzeppelin/contracts/interfaces/IERC4626.sol";
import { MultistrategyAdminable } from "src/abstracts/MultistrategyAdminable.sol";
import { IMultistrategyManageable } from "interfaces/infra/multistrategy/IMultistrategyManageable.sol";
import { IStrategyAdapter } from "interfaces/infra/multistrategy/IStrategyAdapter.sol";
import { MStrat } from "src/types/DataTypes.sol";
import { Errors } from "src/infra/libraries/Errors.sol";

abstract contract MultistrategyManageable is IMultistrategyManageable, MultistrategyAdminable {

    /// @dev Maximum amount of different strategies this contract can deposit into
    uint8 constant MAXIMUM_STRATEGIES = 10;

    /// @dev Maximum basis points (10_000 = 100%)
    uint256 constant MAX_BPS = 10_000;

    /// @dev Maximum performance fee that the owner can set is 20%
    uint256 constant MAX_PERFORMANCE_FEE = 2_000;
    
    /// @inheritdoc IMultistrategyManageable
    address public protocolFeeRecipient;

    /// @inheritdoc IMultistrategyManageable
    uint256 public performanceFee;

    /// @inheritdoc IMultistrategyManageable
    uint256 public depositLimit;

    /// @inheritdoc IMultistrategyManageable
    uint256 public debtRatio;

    /// @inheritdoc IMultistrategyManageable
    uint256 public totalDebt;

    /// @inheritdoc IMultistrategyManageable
    uint256 public slippageLimit;

    /// @inheritdoc IMultistrategyManageable
    uint8 public activeStrategies;

    /// @inheritdoc IMultistrategyManageable
    bool public retired;

    /*//////////////////////////////////////////////////////////////////////////
                                  PRIVATE STORAGE
    //////////////////////////////////////////////////////////////////////////*/

    /// @dev Strategy parameters mapped by the strategy address
    mapping(address strategyAddress => MStrat.StrategyParams strategyParameters) public strategies;

    /// @dev Order that `_withdraw()` uses to determine which strategy pull the funds from
    //       The first time a zero address is encountered, it stops withdrawing, so it is
    //       possible that there isn't enough to withdraw if the amount of strategies in
    //       `withdrawOrder` is smaller than the amount of active strategies.
    address[] public withdrawOrder;

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

    /// @dev Initial owner is the deployer of the multistrategy.
    /// @param _owner Address of the initial Multistrategy owner.
    /// @param _manager Address of the initial Multistrategy manager.
    /// @param _protocolFeeRecipient Address that will receive the performance fee.
    constructor(
        address _owner,
        address _manager,
        address _protocolFeeRecipient
    ) 
        MultistrategyAdminable(_owner, _manager) 
    {
        require(_protocolFeeRecipient != address(0), Errors.ZeroAddress());

        protocolFeeRecipient = _protocolFeeRecipient;
        withdrawOrder = new address[](MAXIMUM_STRATEGIES);
    }

    /*//////////////////////////////////////////////////////////////////////////
                                    MODIFIER
    //////////////////////////////////////////////////////////////////////////*/

    /// @dev Reverts if `_strategy` is not active.
    /// @param _strategy Address of the strategy to check if it is active. 
    modifier onlyActiveStrategy(address _strategy) {
        require(strategies[_strategy].activation > 0, Errors.StrategyNotActive(_strategy));
        _;
    }

    /// @dev Reverts if the multistrategy has been retired / eol.
    modifier whenNotRetired() {
        require(retired == false, Errors.Retired());
        _;
    }

    /*//////////////////////////////////////////////////////////////////////////
                        USER FACING CONSTANT FUNCTIONS
    //////////////////////////////////////////////////////////////////////////*/

    /// @inheritdoc IMultistrategyManageable
    function getWithdrawOrder() external view returns (address[] memory) {
        return withdrawOrder;
    }

    /// @inheritdoc IMultistrategyManageable
    function getStrategyParameters(address _strategy) external view returns (MStrat.StrategyParams memory) {
        return strategies[_strategy];
    }

    /*//////////////////////////////////////////////////////////////////////////
                        USER FACING NON-CONSTANT FUNCTIONS
    //////////////////////////////////////////////////////////////////////////*/

    /// @inheritdoc IMultistrategyManageable
    function setProtocolFeeRecipient(address _protocolFeeRecipient) external onlyOwner {
        require(_protocolFeeRecipient != address(0), Errors.ZeroAddress());

        protocolFeeRecipient = _protocolFeeRecipient;
        emit ProtocolFeeRecipientSet(protocolFeeRecipient);
    }

    /// @inheritdoc IMultistrategyManageable
    function setPerformanceFee(uint256 _performanceFee) external onlyOwner {
        require(_performanceFee <= MAX_PERFORMANCE_FEE, Errors.ExcessiveFee(_performanceFee));

        performanceFee = _performanceFee;
        emit PerformanceFeeSet(performanceFee);
    }

    /// @inheritdoc IMultistrategyManageable
    function setDepositLimit(uint256 _depositLimit) external onlyManager {
        depositLimit = _depositLimit;
        emit DepositLimitSet(depositLimit);
    }

    function setSlippageLimit(uint256 _slippageLimit) external onlyManager {
        require(_slippageLimit <= MAX_BPS, Errors.SlippageLimitExceeded(_slippageLimit));
        
        slippageLimit = _slippageLimit;
        emit SlippageLimitSet(slippageLimit);
    }

    /// @inheritdoc IMultistrategyManageable
    function setWithdrawOrder(address[] memory _strategies) external onlyManager {
        require(_validateStrategyOrder(_strategies), Errors.InvalidWithdrawOrder());

        withdrawOrder = _strategies;
        emit WithdrawOrderSet();
    }

    /// @inheritdoc IMultistrategyManageable
    function addStrategy(
        address _strategy,
        uint256 _debtRatio,
        uint256 _minDebtDelta,
        uint256 _maxDebtDelta
    ) external onlyOwner {
        require(activeStrategies < MAXIMUM_STRATEGIES, Errors.MaximumAmountStrategies());
        require(_strategy != address(0) && _strategy != address(this), Errors.InvalidAddress(_strategy));
        require(strategies[_strategy].activation == 0, Errors.StrategyAlreadyActive(_strategy));
        require(IERC4626(address(this)).asset() == IStrategyAdapter(_strategy).asset(), 
            Errors.AssetMismatch(IERC4626(address(this)).asset(), IStrategyAdapter(_strategy).asset()));
        require(debtRatio + _debtRatio <=  MAX_BPS, Errors.DebtRatioAboveMaximum(debtRatio + _debtRatio));
        require(_minDebtDelta <= _maxDebtDelta, Errors.InvalidDebtDelta());

        strategies[_strategy] = MStrat.StrategyParams({
            activation: block.timestamp,
            debtRatio: _debtRatio,
            lastReport: block.timestamp,
            minDebtDelta: _minDebtDelta,
            maxDebtDelta: _maxDebtDelta,
            totalDebt: 0,
            totalGain: 0,
            totalLoss: 0
        });

        debtRatio += _debtRatio;
        withdrawOrder[MAXIMUM_STRATEGIES - 1] = _strategy;
        ++activeStrategies;

        _organizeWithdrawOrder();

        emit StrategyAdded(_strategy);
    }

    /// @inheritdoc IMultistrategyManageable
    function retireStrategy(address _strategy) external onlyManager onlyActiveStrategy(_strategy) {
        debtRatio -= strategies[_strategy].debtRatio;
        strategies[_strategy].debtRatio = 0;

        emit StrategyRetired(_strategy);
    }

    /// @inheritdoc IMultistrategyManageable
    function removeStrategy(address _strategy) external onlyManager onlyActiveStrategy(_strategy) {
        require(strategies[_strategy].debtRatio == 0, Errors.StrategyNotRetired());
        require(strategies[_strategy].totalDebt == 0, Errors.StrategyWithOutstandingDebt());

        for(uint8 i = 0; i < MAXIMUM_STRATEGIES; ++i) {
            if(withdrawOrder[i] == _strategy) {
                withdrawOrder[i] = address(0);
                strategies[_strategy].activation = 0;
                --activeStrategies;
                _organizeWithdrawOrder();

                emit StrategyRemoved(_strategy);
                return;
            }
        }
    }

    /// @inheritdoc IMultistrategyManageable
    function setStrategyDebtRatio(address _strategy, uint256 _debtRatio) external onlyManager onlyActiveStrategy(_strategy) {
        uint256 newDebtRatio = debtRatio - strategies[_strategy].debtRatio + _debtRatio;
        require(newDebtRatio <= MAX_BPS, Errors.DebtRatioAboveMaximum(newDebtRatio));

        debtRatio = newDebtRatio;
        strategies[_strategy].debtRatio = _debtRatio;

        emit StrategyDebtRatioSet(_strategy, _debtRatio);
    }

    /// @inheritdoc IMultistrategyManageable
    function setStrategyMinDebtDelta(address _strategy, uint256 _minDebtDelta) external 
        onlyManager  
        onlyActiveStrategy(_strategy) 
    {
        require(strategies[_strategy].maxDebtDelta >= _minDebtDelta, Errors.InvalidDebtDelta());

        strategies[_strategy].minDebtDelta = _minDebtDelta;

        emit StrategyMinDebtDeltaSet(_strategy, _minDebtDelta);
    }

    /// @inheritdoc IMultistrategyManageable
    function setStrategyMaxDebtDelta(address _strategy, uint256 _maxDebtDelta) external 
        onlyManager 
        onlyActiveStrategy(_strategy) 
    {
        require(strategies[_strategy].minDebtDelta <= _maxDebtDelta, Errors.InvalidDebtDelta());

        strategies[_strategy].maxDebtDelta = _maxDebtDelta;

        emit StrategyMaxDebtDeltaSet(_strategy, _maxDebtDelta);
    }


    /// @inheritdoc IMultistrategyManageable
    function retire() external onlyGuardian {
        retired = true;
        emit MultistrategyRetired();
    }

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

    /// @notice Validates the order of strategies for withdrawals.
    /// 
    /// This function performs the following actions:
    /// - Ensures the length of the provided strategies array matches the maximum number of strategies.
    /// - Iterates through the provided strategies to validate each one:
    ///   - Checks that non-zero addresses correspond to active strategies.
    ///   - Ensures there are no duplicate strategies in the provided array.
    /// - If an address in the provided array is zero, it checks that all subsequent addresses are also zero.
    /// 
    /// @param _strategies The array of strategy addresses to validate.
    /// @return True if the order is valid. False if not valid.
    function _validateStrategyOrder(address[] memory _strategies) internal view returns (bool) {
        if(_strategies.length != MAXIMUM_STRATEGIES) return false;

        for(uint8 i = 0; i < MAXIMUM_STRATEGIES; ++i) {
            address strategy = _strategies[i];

            if(strategy != address(0)) {
                if(strategies[strategy].activation == 0) return false;
                // Start to check on the next strategy
                for(uint8 j = 0; j < MAXIMUM_STRATEGIES; ++j) {
                    // Check that the strategy isn't duplicate
                    if(i != j && strategy == _strategies[j]) return false;
                }
            } else {
                // Check that the rest of the addresses are address(0)
                for(uint8 j = i + 1; j < MAXIMUM_STRATEGIES; ++j) {
                    if(_strategies[j] != address(0)) return false;
                }
                return true;
            }
        }
        return true;
    }

    /// @notice Organizes the withdraw order by removing gaps and shifting strategies.
    /// 
    /// This function performs the following actions:
    /// - Iterates through the withdraw order array.
    /// - For each strategy, if it encounters an empty slot (address(0)), it shifts subsequent strategies up to fill the gap.
    /// - Ensures that any empty slots are moved to the end of the array.
    function _organizeWithdrawOrder() internal {
        uint8 position = 0;
        for(uint8 i = 0; i < MAXIMUM_STRATEGIES; ++i) {
            address strategy = withdrawOrder[i];
            if(strategy == address(0)) {
                ++position;
            } else if (position > 0) {
                withdrawOrder[i - position] = strategy;
                withdrawOrder[i] = address(0);
            }
        }
    }
}

File 8 of 23 : IMultistrategy.sol
// SPDX-License-Identifier: GNU AGPLv3

pragma solidity ^0.8.27;

import { IMultistrategyManageable } from "interfaces/infra/multistrategy/IMultistrategyManageable.sol";

interface IMultistrategy is IMultistrategyManageable {
    /// @notice Emitted when an account has made a deposit.
    /// @param amount Amount of asset that has been deposited.
    /// @param recipient Address that will receive the receipt tokens.
    event Deposit(uint256 amount, address indexed recipient);

    /// @notice Emitted when an account has made a withdraw.
    /// @param amount Amount of shares that have been withdrawn.
    event Withdraw(uint256 amount);

    /// @notice Emitted when a strategy has requested a credit.
    /// @param strategy Address of the strategy that requested the credit.
    /// @param amount Amount of credit that has been granted to the strategy.
    event CreditRequested(address indexed strategy, uint256 amount);

    /// @notice Emitted when a strategy has submitted a report.
    /// @param strategy Address of the strategy that has submitted the report.
    /// @param debtRepaid Amount of debt that has been repaid by the strategy.
    /// @param gain Amount of gain that the strategy has reported.
    /// @param loss Amount of loss that the strategy has reported.
    event StrategyReported(address indexed strategy, uint256 debtRepaid, uint256 gain, uint256 loss);

    /// @notice Timestamp of the last report made by a strategy.
    function lastReport() external view returns (uint256);

    /// @notice Amount of tokens that are locked as "locked profit" and can't be withdrawn.
    function lockedProfit() external view returns (uint256);

    /// @notice Rate at which the locked profit gets unlocked per second.
    function lockedProfitDegradation() external view returns (uint256);

    /// @notice Returns the value of a share in `asset` value.
    function pricePerShare() external view returns (uint256);

    /// @notice Returns the amount of tokens a strategy can borrow from this Multistrategy.
    /// @param strategy Address of the strategy we want to know the credit available for.
    function creditAvailable(address strategy) external view returns (uint256);

    /// @notice Returns the excess of debt a strategy currently holds.
    /// @param strategy Address of the strategy we want to know if it has any debt excess.
    function debtExcess(address strategy) external view returns (uint256);

    /// @notice Returns the total debt of `strategy`.
    /// @param strategy Address of the strategy we want to know the `totalDebt`.
    function strategyTotalDebt(address strategy) external view returns (uint256);

    /// @notice Returns the aggregate PnL of all strategies at max slippage.
    function currentPnL() external view returns (uint256, uint256);
    
    /// @notice Send the available credit of the caller to the caller.
    /// @dev Reverts if the caller is *NOT* an active strategy
    function requestCredit() external returns (uint256);

    /// @notice Report the profit or loss of a strategy along any debt the strategy is willing to pay back.
    /// @dev Can only be called by an active strategy.
    /// @param _debtRepayment Amount that the strategy will send back to the multistrategy as debt repayment.
    /// @param _gain Amount that the strategy has realized as a gain since the last report and will send it
    ///                to this Multistrategy as earnings. 
    /// @param _loss Amount that the strategy has realized as a loss since the last report. 
    function strategyReport(uint256 _debtRepayment, uint256 _gain, uint256 _loss) external;

    /// @notice Emergency function to rescue tokens not related to the Multistrategy sent to the contract by mistake.
    /// @param token Address of the token that will be rescued.
    /// @param recipient Address of who will receive the tokens.
    function rescueToken(address token, address recipient) external;
}

File 9 of 23 : IStrategyAdapter.sol
// SPDX-License-Identifier: GNU AGPLv3

pragma solidity ^0.8.27;

interface IStrategyAdapter {
    /// @notice Emitted when the slippage limit is set.
    /// @param slippageLimit The new slippage limit in basis points (BPS).
    event SlippageLimitSet(uint256 slippageLimit);

    /// @notice Returns the address of the multistrategy this Strategy belongs to.
    function multistrategy() external view returns (address);

    /// @notice Returns the address of the token used tby this strategy.
    /// @dev it should be the same as the token used by the multistrategy.
    function asset() external view returns (address);

    /// @notice Returns the identifier of this Strategy Adapter.
    function id() external view returns (string memory);

    /// @notice Returns the name of this Strategy Adapter.
    function name() external view returns (string memory);

    /// @notice Returns the current slippage limit in basis points (BPS).
    /// @dev The slippage limit is expressed in BPS, where 10,000 BPS equals 100%.
    /// @return The maximum allowable slippage in basis points.
    function slippageLimit() external view returns (uint256);

    /// @notice Sets the maximum allowable slippage limit for withdrawals.
    /// @dev Slippage limit is expressed in basis points (BPS), where 10,000 BPS equals 100%.
    /// This limit represents the tolerated difference between the expected withdrawal amount
    /// and the actual amount withdrawn from the strategy.
    /// @param _slippageLimit The maximum allowable slippage in basis points.
    function setSlippageLimit(uint256 _slippageLimit) external;

    /// @notice Requests a credit to the multistrategy. The multistrategy will send the
    /// maximum amount of credit available for this strategy.
    function requestCredit() external;

    /// @notice Sends a report to the Multistrategy of any gain or loss this strategy has
    ///         made along an amount to be withdrawn and be used for debt repayment.
    /// @dev Only the owner can call it
    /// @param _amountToWithdraw Amount that will be withdrawn from the strategy and will
    ///         be available for debt repayment.
    function sendReport(uint256 _amountToWithdraw) external;

    /// @notice Sends a report to the Multistrategy of any gain or loss this strategy has made.
    /// @dev This report wont withdraw any funds to repay debt to the Multistrategy.
    /// Only the multistrategy can call it
    function askReport() external;

    /// @notice Sends a report to the Multistrategy after this strategy has been panicked.
    ///         Reporting any gains or loss based on the balance the could be emergency withdrawn
    /// @dev This function should only be called after a strategy has been retired.
    function sendReportPanicked() external;

    /// @notice Withdraws `asset` from the strategy.
    /// @dev Only callable by the multistrategy.
    /// @param _amount Amount of tokens to withdraw from the strategy.
    function withdraw(uint256 _amount) external returns (uint256);

    /// @notice Returns the amount of `asset` this strategy holds.
    function totalAssets() external view returns (uint256);

    /// @notice Returns the gain or loss this strategy has made since the last report.
    /// @dev The returned values will account for max slippage.
    function currentPnL() external view returns (uint256, uint256);

    /// @notice Starts the panic process for this strategy.
    /// The panic process consists of:
    ///     - Withdraw as much funds as possible from the underlying strategy.
    ///     - Report back to the multistrategy with the available funds.
    ///     - Revoke the allowance that this adapter has given to the underlying strategy.
    ///     - Pauses this contract.
    function panic() external;

    /// @notice Pauses the smart contract.
    /// @dev Functions that implement the `paused` modifier will revert when called.
    /// Guardians and Owner can call this function
    function pause() external;

    /// @notice Unpauses the smart contract.
    /// @dev Functions that implement the `paused` won't revert when called.
    /// Only the Owner can call this function
    function unpause() external;
}

File 10 of 23 : Errors.sol
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.20;

/// @title Errors
/// @notice Library containing all custom errors the protocol may revert with.
library Errors {
    /*//////////////////////////////////////////////////////////////////////////
                                      GENERICS
    //////////////////////////////////////////////////////////////////////////*/

    /// @notice Thrown when `msg.sender` is not the manager.
    error CallerNotManager(address caller);

    /// @notice Thrown when `msg.sender` is not a guardian.
    error CallerNotGuardian(address caller);

    /// @notice Thrown when `amount` is zero.
    error ZeroAmount(uint256 amount);

    /// @notice Thrown when setting an address to the zero address.
    error ZeroAddress();

    /// @notice Thrown when `currentBalance` is lower than `amount`.
    error InsufficientBalance(uint256 currentBalance, uint256 amount);

    /// @notice Thrown when `addr` is an unexpected address.
    error InvalidAddress(address addr);

    /*//////////////////////////////////////////////////////////////////////////
                                    MULTISTRATEGY
    //////////////////////////////////////////////////////////////////////////*/

    /// @notice Thrown when performing an action on a non-active strategy.
    error StrategyNotActive(address strategy);

    /// @notice Thrown when performing an action on an active strategy.
    error StrategyAlreadyActive(address strategy);

    /// @notice Thrown when a strategy is reporting a gain and a loss simultaneously.
    error GainLossMismatch();

    /// @notice Thrown when a deposit would exceed the depositLimit
    error DepositLimit();

    /// @notice Thrown when the owner tries to set a fee above the maximum permitted fee.
    error ExcessiveFee(uint256 fee);

    /// @notice Thrown when the debtRatio of a strategy or a multistrategy is above 100%.
    error DebtRatioAboveMaximum(uint256 debtRatio);

    /// @notice Thrown when trying to remove a strategy from `withdrawOrder` that still has outstanding debt.
    error StrategyWithOutstandingDebt();

    /// @notice Thrown when minDebtDelta is above maxDebtDelta or maxDebtDelta is below minDebtDelta.
    error InvalidDebtDelta();

    /// @notice Thrown when a strategy is reporting a loss higher than its total debt.
    error InvalidStrategyLoss();

    /// @notice Thrown when there is non-Zero Address following a Zero Address in withdrawOrder.
    error InvalidWithdrawOrder();

    /// @notice Thrown when trying to add a new strategy to the multistrategy but it already reached the
    /// maximum amount of strategies.
    error MaximumAmountStrategies();

    /// @notice Thrown when trying to remove a strategy that has a `debtRatio` greater than 0.
    error StrategyNotRetired();

    /// @notice Thrown when there isn't enough liquidity to cover a withdraw
    /// @param assets The amount of assets requested.
    /// @param liquidity The current liquidity available in the contract.
    error InsufficientLiquidity(uint256 assets, uint256 liquidity);

    /// @notice Thrown when depositing / minting on a retired multistrategy.
    error Retired();

    /*//////////////////////////////////////////////////////////////////////////
                                STRATEGY ADAPTER
    //////////////////////////////////////////////////////////////////////////*/

    /// @notice Thrown when the caller is not the Multistrategy.
    error CallerNotMultistrategy(address caller);

    /// @notice Thrown when the `_asset` parameter on the constructor doesn't match 
    /// the `deposit` token on Multistrategy.
    error AssetMismatch(address multAsset, address stratAsset);

    /// @notice Thrown when the requested slippage limit exceeds the maximum permitted value.
    /// @param slippageLimit The slippage limit in basis points (BPS).
    error SlippageLimitExceeded(uint256 slippageLimit);

    /// @notice Thrown when the actual slippage exceeds the allowed slippage.
    /// @param amount0 The expected amount after accounting for allowed slippage.
    /// @param amount1 The actual amount obtained.
    error SlippageCheckFailed(uint256 amount0, uint256 amount1);

    /// @notice Thrown when the reward added is the base asset of the Strategy.
    error InvalidRewardToken(address rewardToken);

    /*//////////////////////////////////////////////////////////////////////////
                                    ERC-4626
    //////////////////////////////////////////////////////////////////////////*/

    /**
     * @dev Attempted to deposit more assets than the max amount for `receiver`.
     */
    error ERC4626ExceededMaxDeposit(address receiver, uint256 assets, uint256 max);

    /**
     * @dev Attempted to mint more shares than the max amount for `receiver`.
     */
    error ERC4626ExceededMaxMint(address receiver, uint256 shares, uint256 max);

    /**
     * @dev Attempted to withdraw more assets than the max amount for `receiver`.
     */
    error ERC4626ExceededMaxWithdraw(address owner, uint256 assets, uint256 max);

    /**
     * @dev Attempted to redeem more shares than the max amount for `receiver`.
     */
    error ERC4626ExceededMaxRedeem(address owner, uint256 shares, uint256 max);
}

File 11 of 23 : ERC20.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.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 ERC20
 * applications.
 *
 * Additionally, an {Approval} event is emitted on calls to {transferFrom}.
 * This allows applications to reconstruct the allowance for all accounts just
 * by listening to said events. Other implementations of the EIP may not emit
 * these events, as it isn't required by the specification.
 */
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}.
     *
     * All two of these values are immutable: they can only be set once during
     * construction.
     */
    constructor(string memory name_, string memory symbol_) {
        _name = name_;
        _symbol = symbol_;
    }

    /**
     * @dev Returns the name of the token.
     */
    function name() public view virtual 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}.
     *
     * Emits an {Approval} event indicating the updated allowance. This is not
     * required by the EIP. See the note at the beginning of {ERC20}.
     *
     * NOTE: Does not update the allowance if the current allowance
     * is the maximum `uint256`.
     *
     * Requirements:
     *
     * - `from` and `to` cannot be the zero address.
     * - `from` must have a balance of at least `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:
     * ```
     * 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);
            }
        }
    }
}

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

pragma solidity ^0.8.20;

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

pragma solidity ^0.8.20;

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

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

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

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

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

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

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

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

File 14 of 23 : IERC20Permit.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/extensions/IERC20Permit.sol)

pragma solidity ^0.8.20;

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

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

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

File 15 of 23 : Address.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/Address.sol)

pragma solidity ^0.8.20;

/**
 * @dev Collection of functions related to the address type
 */
library Address {
    /**
     * @dev The ETH balance of the account is not enough to perform the operation.
     */
    error AddressInsufficientBalance(address account);

    /**
     * @dev There's no code at `target` (it is not a contract).
     */
    error AddressEmptyCode(address target);

    /**
     * @dev A call to an address target failed. The target may have reverted.
     */
    error FailedInnerCall();

    /**
     * @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 AddressInsufficientBalance(address(this));
        }

        (bool success, ) = recipient.call{value: amount}("");
        if (!success) {
            revert FailedInnerCall();
        }
    }

    /**
     * @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
     * {FailedInnerCall} 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 AddressInsufficientBalance(address(this));
        }
        (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 {FailedInnerCall}) 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 {FailedInnerCall} 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 {FailedInnerCall}.
     */
    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 FailedInnerCall();
        }
    }
}

File 16 of 23 : MultistrategyAdminable.sol
// SPDX-License-Identifier: GNU AGPLv3

pragma solidity 0.8.27;

import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol";
import { Pausable } from "@openzeppelin/contracts/utils/Pausable.sol";
import { IMultistrategyAdminable } from "interfaces/infra/multistrategy/IMultistrategyAdminable.sol";
import { Errors } from "src/infra/libraries/Errors.sol";

abstract contract MultistrategyAdminable is IMultistrategyAdminable, Ownable, Pausable {
    /*//////////////////////////////////////////////////////////////////////////
                                       STORAGE
    //////////////////////////////////////////////////////////////////////////*/

    /// @inheritdoc IMultistrategyAdminable
    address public manager;

    /// @inheritdoc IMultistrategyAdminable
    mapping(address guardianAddress => bool isActive) public guardians;

    /// @notice Sets the Owner and Manager addresses.
    /// @param _owner The address of the initial owner.
    /// @param _manager The address of the initial manager.
    constructor(address _owner, address _manager) Ownable(_owner) {
        manager = _manager;
    }

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

    /// @notice Reverts if called by any account other than the owner or the manager.
    modifier onlyManager() {
        require(
            msg.sender == owner() || 
            msg.sender == manager, 
            Errors.CallerNotManager(msg.sender)
        );
        _;
    }

    /// @notice Reverts if called by any account other than the owner, the manager, or a guardian.
    modifier onlyGuardian() {
        require(
            msg.sender == owner() || 
            msg.sender == manager || 
            guardians[msg.sender], 
            Errors.CallerNotGuardian(msg.sender)
        );
        _;
    }

    /*//////////////////////////////////////////////////////////////////////////
                               USER-FACING NON-CONSTANT FUNCTIONS
    //////////////////////////////////////////////////////////////////////////*/

    /// @inheritdoc IMultistrategyAdminable
    function setManager(address _manager) external onlyOwner {
        require(_manager != address(0), Errors.ZeroAddress());

        manager = _manager;
        emit ManagerSet(_manager);
    }

    /// @inheritdoc IMultistrategyAdminable
    function enableGuardian(address _guardian) external onlyOwner {
        guardians[_guardian] = true;
        emit GuardianEnabled(_guardian);
    }

    /// @inheritdoc IMultistrategyAdminable
    function revokeGuardian(address _guardian) external onlyOwner {
        guardians[_guardian] = false;
        emit GuardianRevoked(_guardian);
    }

    /// @inheritdoc IMultistrategyAdminable
    function pause() external onlyGuardian {
        _pause();
    }

    /// @inheritdoc IMultistrategyAdminable
    function unpause() external onlyOwner {
        _unpause();
    }
}

File 17 of 23 : IMultistrategyManageable.sol
// SPDX-License-Identifier: GNU AGPLv3

pragma solidity ^0.8.27;

import { IMultistrategyAdminable } from "interfaces/infra/multistrategy/IMultistrategyAdminable.sol";
import { MStrat } from "src/types/DataTypes.sol";

interface IMultistrategyManageable is IMultistrategyAdminable {
    /// @notice Emitted when the protocol fee recipient is set.
    /// @param _protocolFeeRecipient The address that will receive the protocol fee.
    event ProtocolFeeRecipientSet(address indexed _protocolFeeRecipient);

    /// @notice Emitted when the performance fee is set.
    /// @param _performanceFee The new performance fee value.
    event PerformanceFeeSet(uint256 _performanceFee);

    /// @notice Emitted when the deposit limit is set.
    /// @param _depositLimit The new deposit limit value.
    event DepositLimitSet(uint256 _depositLimit);

    /// @notice Emitted when the slippage limit is set.
    /// @param _slippageLimit The new slippage limit value.
    event SlippageLimitSet(uint256 _slippageLimit);

    /// @notice Emitted when a new withdrawal order has been set.
    event WithdrawOrderSet();

    /// @notice Emitted when the debt ratio for a specific strategy is set.
    /// @param _strategy The address of the strategy whose debt ratio was updated.
    /// @param _debtRatio The new debt ratio value for the specified strategy.
    event StrategyDebtRatioSet(address indexed _strategy, uint256 _debtRatio);

    /// @notice Emitted when the minimum debt delta for a specific strategy is set.
    /// @param _strategy The address of the strategy whose minimum debt delta was updated.
    /// @param _minDebtDelta The new minimum debt delta value for the specified strategy.
    event StrategyMinDebtDeltaSet(address indexed _strategy, uint256 _minDebtDelta);

    /// @notice Emitted when the maximum debt delta for a specific strategy is set.
    /// @param _strategy The address of the strategy whose maximum debt delta was updated.
    /// @param _maxDebtDelta The new maximum debt delta value for the specified strategy.
    event StrategyMaxDebtDeltaSet(address indexed _strategy, uint256 _maxDebtDelta);

    /// @notice Emitted when a new strategy is added.
    /// @param _strategy The address of the newly added strategy.
    event StrategyAdded(address indexed _strategy);

    /// @notice Emitted when a strategy is retired.
    /// @param _strategy The address of the retired strategy.
    event StrategyRetired(address indexed _strategy);

    /// @notice Emitted when a strategy is removed.
    /// @param _strategy The address of the removed strategy.
    event StrategyRemoved(address indexed _strategy);

    /// @notice Emitted when the deposits into this multistrategy are paused.
    event MultistrategyRetired();

    /// @notice Address that will receive performance fee.
    function protocolFeeRecipient() external view returns (address);

    /// @notice Fee on the yield generated (in BPS).
    /// @dev Performance fee is taken on `strategyReport()` function on the Multistrategy contract.
    function performanceFee() external view returns (uint256);

    /// @notice Limit for total assets the multistrategy can hold.
    function depositLimit() external view returns (uint256);

    /// @notice Debt ratio of the multistrategy across all strategies (in BPS).
    /// @dev The debt ratio cannot exceed 10_000 BPS (100 %).
    function debtRatio() external view returns (uint256);

    /// @notice Amount of tokens that the strategies have borrowed in total.
    function totalDebt() external view returns (uint256);

    /// @notice Returns the current slippage limit in basis points (BPS).
    /// @dev The slippage limit is expressed in BPS, where 10,000 BPS equals 100%.
    function slippageLimit() external view returns (uint256);

    /// @notice Amount of active strategies.
    function activeStrategies() external view returns (uint8);

    /// @notice Returns true if multistrategy has been retired. 
    function retired() external view returns (bool);

    /// @notice Returns the withdraw order.
    function getWithdrawOrder() external view returns (address[] memory);

    /// @notice Returns the strategy params of `strategy`
    /// @param _strategy Address of the strategy the it will returns the parameters of.
    function getStrategyParameters(address _strategy) external view returns (MStrat.StrategyParams calldata);

    /// @notice Sets the recipient address of the performance fee.
    /// @dev Emits a `SetProtocolFeeRecipient` event.
    /// @param _protocolFeeRecipient Address that will receive the fees.
    function setProtocolFeeRecipient(address _protocolFeeRecipient) external;

    /// @notice Sets the performance fee in BPS.
    /// @dev Reverts if `performanceFee` is above MAX_PERFORMANCE_FEE
    /// @dev Emits a `SetPerformanceFee` event.
    /// @param _performanceFee New performance fee.
    function setPerformanceFee(uint256 _performanceFee) external;

    /// @notice Sets the deposit limit.
    /// @dev Emits a `SetDepositLimit` event.
    /// @param _depositLimit New deposit limit.
    function setDepositLimit(uint256 _depositLimit) external;

    /// @notice Sets the slippage limit of this Multistrategy.
    /// @dev The slippage limit is expressed in BPS, where 10,000 BPS equals 100%.
    /// @param _slippageLimit New slippage limit.
    function setSlippageLimit(uint256 _slippageLimit) external;

    /// @notice Sets the withdraw order. First position in the array will be the first strategy that it will get the funds withdrawn
    /// @dev It will revert if a strategy in the array is not active or if the array contains duplicate addresses.
    /// @param _strategies Array of strategy addresses
    function setWithdrawOrder(address[] memory _strategies) external;

    /// @notice Adds a strategy to the multistrategy.
    /// @dev The strategy will be appended to `withdrawOrder`.
    /// @param _strategy The address of the strategy.
    /// @param _debtRatio The share of total assets in the Multistrategy this strategy will have access to.
    /// @param _minDebtDelta Lower limit on the increase of debt.
    /// @param _maxDebtDelta Upper limit on the increase of debt.
    function addStrategy(
        address _strategy,
        uint256 _debtRatio,
        uint256 _minDebtDelta,
        uint256 _maxDebtDelta
    ) external;

    /// @notice Sets the strategy debtRatio to 0, which prevents any further deposits into the strategy.
    /// @dev Retiring a strategy will set the approval of `asset` to the retired strategy to 0.
    /// @param _strategy The address of the strategy that will be retired.
    function retireStrategy(address _strategy) external;

    /// @notice Removes a strategy from `withdrawOrder`.
    /// @param _strategy The address of the strategy that will be removed.
    function removeStrategy(address _strategy) external;

    /// @notice Change the debt ratio of a strategy.
    /// @param _strategy Address of the strategy.
    /// @param _debtRatio New debt ratio.
    function setStrategyDebtRatio(address _strategy, uint256 _debtRatio) external;

    /// @notice Change the minimum amount of debt a strategy can take.
    /// @dev Used to limit the minimum amount of debt a strategy should take. 
    ///      Taking a small credit wouldn't be optimal gas-wise.
    /// @param _strategy Address of the strategy.
    /// @param _minDebtDelta Lower limit of the change of debt.
    function setStrategyMinDebtDelta(address _strategy, uint256 _minDebtDelta) external;

    /// @notice Change the maximum amount of debt a strategy can take at once.
    /// @dev Used to protect large debt repayments or withdraws. Risks are IL, or low liquidity.
    /// @param _strategy Address of the strategy.
    /// @param _maxDebtDelta Upper limit of the change of debt.
    function setStrategyMaxDebtDelta(address _strategy, uint256 _maxDebtDelta) external;

    /// @notice Retires the Multistrategy. End of Life.
    function retire() external;
}

File 18 of 23 : DataTypes.sol
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.20;

/// @notice Namespace for the structs used in `Multistrategy`
library MStrat {
    /// @notice Struct that contains a strategy data
    /// @param activation Block.timestamp of when the strategy was activated. 0 means not active
    /// @param debtRatio Maximum amount the strategy can borrow from the Multistrategy (in BPS of total assets in a Multistrategy)
    /// @param lastReport 
    /// @param minDebtDelta Lower limit on the increase or decrease of debt since last harvest
    /// @param maxDebtDelta Upper limit on the increase or decrease of debt since last harvest
    /// @param totalDebt Total debt that this strategy has
    /// @param totalGain Total gains that this strategy has realized
    /// @param totalLoss Total losses that this strategy has realized
    struct StrategyParams {
        uint256 activation;
        uint256 debtRatio;
        uint256 lastReport;
        uint256 minDebtDelta;
        uint256 maxDebtDelta;
        uint256 totalDebt;
        uint256 totalGain;
        uint256 totalLoss;
    }
}

File 19 of 23 : Context.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;
    }
}

File 20 of 23 : draft-IERC6093.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (interfaces/draft-IERC6093.sol)
pragma solidity ^0.8.20;

/**
 * @dev Standard ERC20 Errors
 * Interface of the https://eips.ethereum.org/EIPS/eip-6093[ERC-6093] custom errors for ERC20 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 ERC721 Errors
 * Interface of the https://eips.ethereum.org/EIPS/eip-6093[ERC-6093] custom errors for ERC721 tokens.
 */
interface IERC721Errors {
    /**
     * @dev Indicates that an address can't be an owner. For example, `address(0)` is a forbidden owner in EIP-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 ERC1155 Errors
 * Interface of the https://eips.ethereum.org/EIPS/eip-6093[ERC-6093] custom errors for ERC1155 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);
}

File 21 of 23 : Ownable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (access/Ownable.sol)

pragma solidity ^0.8.20;

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

/**
 * @dev Contract module which provides a basic access control mechanism, where
 * there is an account (an owner) that can be granted exclusive access to
 * specific functions.
 *
 * The initial owner is set to the address provided by the deployer. This can
 * later be changed with {transferOwnership}.
 *
 * This module is used through inheritance. It will make available the modifier
 * `onlyOwner`, which can be applied to your functions to restrict their use to
 * the owner.
 */
abstract contract Ownable is Context {
    address private _owner;

    /**
     * @dev The caller account is not authorized to perform an operation.
     */
    error OwnableUnauthorizedAccount(address account);

    /**
     * @dev The owner is not a valid owner account. (eg. `address(0)`)
     */
    error OwnableInvalidOwner(address owner);

    event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);

    /**
     * @dev Initializes the contract setting the address provided by the deployer as the initial owner.
     */
    constructor(address initialOwner) {
        if (initialOwner == address(0)) {
            revert OwnableInvalidOwner(address(0));
        }
        _transferOwnership(initialOwner);
    }

    /**
     * @dev Throws if called by any account other than the owner.
     */
    modifier onlyOwner() {
        _checkOwner();
        _;
    }

    /**
     * @dev Returns the address of the current owner.
     */
    function owner() public view virtual returns (address) {
        return _owner;
    }

    /**
     * @dev Throws if the sender is not the owner.
     */
    function _checkOwner() internal view virtual {
        if (owner() != _msgSender()) {
            revert OwnableUnauthorizedAccount(_msgSender());
        }
    }

    /**
     * @dev Leaves the contract without owner. It will not be possible to call
     * `onlyOwner` functions. Can only be called by the current owner.
     *
     * NOTE: Renouncing ownership will leave the contract without an owner,
     * thereby disabling any functionality that is only available to the owner.
     */
    function renounceOwnership() public virtual onlyOwner {
        _transferOwnership(address(0));
    }

    /**
     * @dev Transfers ownership of the contract to a new account (`newOwner`).
     * Can only be called by the current owner.
     */
    function transferOwnership(address newOwner) public virtual onlyOwner {
        if (newOwner == address(0)) {
            revert OwnableInvalidOwner(address(0));
        }
        _transferOwnership(newOwner);
    }

    /**
     * @dev Transfers ownership of the contract to a new account (`newOwner`).
     * Internal function without access restriction.
     */
    function _transferOwnership(address newOwner) internal virtual {
        address oldOwner = _owner;
        _owner = newOwner;
        emit OwnershipTransferred(oldOwner, newOwner);
    }
}

File 22 of 23 : Pausable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/Pausable.sol)

pragma solidity ^0.8.20;

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

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

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

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

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

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

    /**
     * @dev Initializes the contract in unpaused state.
     */
    constructor() {
        _paused = false;
    }

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

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

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

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

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

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

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

File 23 of 23 : IMultistrategyAdminable.sol
// SPDX-License-Identifier: GNU AGPLv3

pragma solidity ^0.8.27;

/// @title IMultistrategyAdminable
/// @notice Contract module that provides a 3 level access control mechanism, with an owner, a manager
/// and a list of guardians that can be granted exclusive access to specific functions.
/// The inheriting contract must set the initial owner and the initial manager in the constructor.
interface IMultistrategyAdminable {
    /*//////////////////////////////////////////////////////////////////////////
                                       EVENTS
    //////////////////////////////////////////////////////////////////////////*/

    /// @notice Emitted when a new manager is set.
    /// @param _manager The address of the new manager.
    event ManagerSet(address indexed _manager);

    /// @notice Emitted when a new guardian has been granted access.
    /// @param _guardian The address of the guardian.
    event GuardianEnabled(address indexed _guardian);

    /// @notice Emitted when a the access of a guardian has been revoked.
    /// @param _guardian The address of the guardian.
    event GuardianRevoked(address indexed _guardian);

    /*//////////////////////////////////////////////////////////////////////////
                                 CONSTANT FUNCTIONS
    //////////////////////////////////////////////////////////////////////////*/

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

    /// @notice List of addresses enabled as guardian.
    /// @param _guardian The address to check if it is a guardian.
    function guardians(address _guardian) external view returns (bool);
    
    /*//////////////////////////////////////////////////////////////////////////
                               NON-CONSTANT FUNCTIONS
    //////////////////////////////////////////////////////////////////////////*/

    /// @notice Sets the manager address.
    /// @dev Doesn't revert if:
    /// - manager address is zero address.
    /// - manager address is the same as previous manager address.
    /// @param _manager Address of the new manager.
    function setManager(address _manager) external;

    /// @notice Enables an address to be a guardian.
    /// @dev Doesn't revert if:
    /// - guardian address is zero address.
    /// - guardian address is already enabled as guardian.
    /// @param _guardian The address of the guardian.
    function enableGuardian(address _guardian) external;

    /// @notice Revokes an address to be a guardian.
    /// @dev Doesn't revert if:
    /// - guardian address is zero address.
    /// - guardian address is already revoked.
    /// @param _guardian The address of the guardian.
    function revokeGuardian(address _guardian) external;

    /// @notice Pauses the smart contract.
    /// @dev Functions that implement the `paused` modifier will revert when called.
    /// Guardians, Manager and Owner can call this function
    function pause() external;

    /// @notice Unpauses the smart contract.
    /// @dev Functions that implement the `paused` won't revert when called.
    /// Guardians, Manager and Owner can call this function
    function unpause() external;
}

Settings
{
  "remappings": [
    "forge-std/=lib/forge-std/src/",
    "src/=src/",
    "interfaces/=src/interfaces/",
    "test-utils/=test/utils/",
    "createx/=lib/createx-forge/",
    "@openzeppelin/contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/contracts/",
    "@openzeppelin/contracts/=lib/openzeppelin-contracts-upgradeable/lib/openzeppelin-contracts/contracts/",
    "@openzeppelin-4/contracts/=lib/openzeppelin-contracts-4/contracts/",
    "@layerzero/=lib/solidity-examples/contracts/",
    "@aave/=lib/aave-v3-origin/src/",
    "@xerc20/=lib/xERC20/",
    "@uniswap/v3-periphery/=lib/v3-periphery/",
    "@uniswap/v3-core/=lib/v3-core/",
    "@addressbook/=address-book/",
    "@trust-security/trustlessPermit/=lib/trustlessPermit/",
    "@uniswapV3-periphery/=lib/v3-periphery/contracts/",
    "@prb/math/=lib/prb-math/",
    "@properties/=lib/properties/",
    "@prb/test/=lib/prb-math/node_modules/@prb/test/",
    "ERC4626/=lib/properties/lib/ERC4626/contracts/",
    "aave-v3-core/=lib/aave-v3-origin/src/core/",
    "aave-v3-origin/=lib/aave-v3-origin/",
    "aave-v3-periphery/=lib/aave-v3-origin/src/periphery/",
    "common/=lib/common/",
    "createx-forge/=lib/createx-forge/",
    "ds-test/=lib/solmate/lib/ds-test/src/",
    "erc4626-tests/=lib/erc4626-tests/",
    "openzeppelin-contracts-4/=lib/openzeppelin-contracts-4/",
    "openzeppelin-contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/",
    "openzeppelin-contracts/=lib/openzeppelin-contracts/",
    "openzeppelin-foundry-upgrades/=lib/openzeppelin-foundry-upgrades/src/",
    "openzeppelin/=lib/openzeppelin-contracts-4/contracts/",
    "prb-math/=lib/prb-math/src/",
    "properties/=lib/properties/contracts/",
    "solidity-examples/=lib/solidity-examples/contracts/",
    "solidity-stringutils/=lib/openzeppelin-foundry-upgrades/lib/solidity-stringutils/",
    "solidity-utils/=lib/aave-v3-origin/lib/solidity-utils/",
    "solmate/=lib/solmate/src/",
    "trustlessPermit/=lib/trustlessPermit/",
    "v3-core/=lib/v3-core/",
    "v3-periphery/=lib/v3-periphery/contracts/",
    "xERC20/=lib/xERC20/"
  ],
  "optimizer": {
    "enabled": true,
    "runs": 200
  },
  "metadata": {
    "useLiteralContent": false,
    "bytecodeHash": "ipfs",
    "appendCBOR": true
  },
  "outputSelection": {
    "*": {
      "*": [
        "evm.bytecode",
        "evm.deployedBytecode",
        "devdoc",
        "userdoc",
        "metadata",
        "abi"
      ]
    }
  },
  "evmVersion": "shanghai",
  "viaIR": false,
  "libraries": {}
}

Contract Security Audit

Contract ABI

[{"inputs":[{"internalType":"address","name":"_asset","type":"address"},{"internalType":"address","name":"_manager","type":"address"},{"internalType":"address","name":"_protocolFeeRecipient","type":"address"},{"internalType":"string","name":"_name","type":"string"},{"internalType":"string","name":"_symbol","type":"string"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[{"internalType":"address","name":"target","type":"address"}],"name":"AddressEmptyCode","type":"error"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"AddressInsufficientBalance","type":"error"},{"inputs":[{"internalType":"address","name":"multAsset","type":"address"},{"internalType":"address","name":"stratAsset","type":"address"}],"name":"AssetMismatch","type":"error"},{"inputs":[{"internalType":"address","name":"caller","type":"address"}],"name":"CallerNotGuardian","type":"error"},{"inputs":[{"internalType":"address","name":"caller","type":"address"}],"name":"CallerNotManager","type":"error"},{"inputs":[{"internalType":"uint256","name":"debtRatio","type":"uint256"}],"name":"DebtRatioAboveMaximum","type":"error"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"allowance","type":"uint256"},{"internalType":"uint256","name":"needed","type":"uint256"}],"name":"ERC20InsufficientAllowance","type":"error"},{"inputs":[{"internalType":"address","name":"sender","type":"address"},{"internalType":"uint256","name":"balance","type":"uint256"},{"internalType":"uint256","name":"needed","type":"uint256"}],"name":"ERC20InsufficientBalance","type":"error"},{"inputs":[{"internalType":"address","name":"approver","type":"address"}],"name":"ERC20InvalidApprover","type":"error"},{"inputs":[{"internalType":"address","name":"receiver","type":"address"}],"name":"ERC20InvalidReceiver","type":"error"},{"inputs":[{"internalType":"address","name":"sender","type":"address"}],"name":"ERC20InvalidSender","type":"error"},{"inputs":[{"internalType":"address","name":"spender","type":"address"}],"name":"ERC20InvalidSpender","type":"error"},{"inputs":[{"internalType":"address","name":"receiver","type":"address"},{"internalType":"uint256","name":"assets","type":"uint256"},{"internalType":"uint256","name":"max","type":"uint256"}],"name":"ERC4626ExceededMaxDeposit","type":"error"},{"inputs":[{"internalType":"address","name":"receiver","type":"address"},{"internalType":"uint256","name":"shares","type":"uint256"},{"internalType":"uint256","name":"max","type":"uint256"}],"name":"ERC4626ExceededMaxMint","type":"error"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"uint256","name":"shares","type":"uint256"},{"internalType":"uint256","name":"max","type":"uint256"}],"name":"ERC4626ExceededMaxRedeem","type":"error"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"uint256","name":"assets","type":"uint256"},{"internalType":"uint256","name":"max","type":"uint256"}],"name":"ERC4626ExceededMaxWithdraw","type":"error"},{"inputs":[],"name":"EnforcedPause","type":"error"},{"inputs":[{"internalType":"uint256","name":"fee","type":"uint256"}],"name":"ExcessiveFee","type":"error"},{"inputs":[],"name":"ExpectedPause","type":"error"},{"inputs":[],"name":"FailedInnerCall","type":"error"},{"inputs":[],"name":"GainLossMismatch","type":"error"},{"inputs":[{"internalType":"uint256","name":"currentBalance","type":"uint256"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"InsufficientBalance","type":"error"},{"inputs":[{"internalType":"uint256","name":"assets","type":"uint256"},{"internalType":"uint256","name":"liquidity","type":"uint256"}],"name":"InsufficientLiquidity","type":"error"},{"inputs":[{"internalType":"address","name":"addr","type":"address"}],"name":"InvalidAddress","type":"error"},{"inputs":[],"name":"InvalidDebtDelta","type":"error"},{"inputs":[],"name":"InvalidStrategyLoss","type":"error"},{"inputs":[],"name":"InvalidWithdrawOrder","type":"error"},{"inputs":[],"name":"MathOverflowedMulDiv","type":"error"},{"inputs":[],"name":"MaximumAmountStrategies","type":"error"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"OwnableInvalidOwner","type":"error"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"OwnableUnauthorizedAccount","type":"error"},{"inputs":[],"name":"ReentrancyGuardReentrantCall","type":"error"},{"inputs":[],"name":"Retired","type":"error"},{"inputs":[{"internalType":"address","name":"token","type":"address"}],"name":"SafeERC20FailedOperation","type":"error"},{"inputs":[{"internalType":"uint256","name":"amount0","type":"uint256"},{"internalType":"uint256","name":"amount1","type":"uint256"}],"name":"SlippageCheckFailed","type":"error"},{"inputs":[{"internalType":"uint256","name":"slippageLimit","type":"uint256"}],"name":"SlippageLimitExceeded","type":"error"},{"inputs":[{"internalType":"address","name":"strategy","type":"address"}],"name":"StrategyAlreadyActive","type":"error"},{"inputs":[{"internalType":"address","name":"strategy","type":"address"}],"name":"StrategyNotActive","type":"error"},{"inputs":[],"name":"StrategyNotRetired","type":"error"},{"inputs":[],"name":"StrategyWithOutstandingDebt","type":"error"},{"inputs":[],"name":"ZeroAddress","type":"error"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"ZeroAmount","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"spender","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"strategy","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"CreditRequested","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"sender","type":"address"},{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":false,"internalType":"uint256","name":"assets","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"shares","type":"uint256"}],"name":"Deposit","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":true,"internalType":"address","name":"recipient","type":"address"}],"name":"Deposit","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"_depositLimit","type":"uint256"}],"name":"DepositLimitSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"_guardian","type":"address"}],"name":"GuardianEnabled","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"_guardian","type":"address"}],"name":"GuardianRevoked","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"_manager","type":"address"}],"name":"ManagerSet","type":"event"},{"anonymous":false,"inputs":[],"name":"MultistrategyRetired","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"account","type":"address"}],"name":"Paused","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"_performanceFee","type":"uint256"}],"name":"PerformanceFeeSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"_protocolFeeRecipient","type":"address"}],"name":"ProtocolFeeRecipientSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"_slippageLimit","type":"uint256"}],"name":"SlippageLimitSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"_strategy","type":"address"}],"name":"StrategyAdded","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"_strategy","type":"address"},{"indexed":false,"internalType":"uint256","name":"_debtRatio","type":"uint256"}],"name":"StrategyDebtRatioSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"_strategy","type":"address"},{"indexed":false,"internalType":"uint256","name":"_maxDebtDelta","type":"uint256"}],"name":"StrategyMaxDebtDeltaSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"_strategy","type":"address"},{"indexed":false,"internalType":"uint256","name":"_minDebtDelta","type":"uint256"}],"name":"StrategyMinDebtDeltaSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"_strategy","type":"address"}],"name":"StrategyRemoved","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"strategy","type":"address"},{"indexed":false,"internalType":"uint256","name":"debtRepaid","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"gain","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"loss","type":"uint256"}],"name":"StrategyReported","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"_strategy","type":"address"}],"name":"StrategyRetired","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"Transfer","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"account","type":"address"}],"name":"Unpaused","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"sender","type":"address"},{"indexed":true,"internalType":"address","name":"receiver","type":"address"},{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":false,"internalType":"uint256","name":"assets","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"shares","type":"uint256"}],"name":"Withdraw","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"Withdraw","type":"event"},{"anonymous":false,"inputs":[],"name":"WithdrawOrderSet","type":"event"},{"inputs":[],"name":"DECIMALS_OFFSET","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"DEGRADATION_COEFFICIENT","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"PROFIT_UNLOCK_TIME","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"activeStrategies","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_strategy","type":"address"},{"internalType":"uint256","name":"_debtRatio","type":"uint256"},{"internalType":"uint256","name":"_minDebtDelta","type":"uint256"},{"internalType":"uint256","name":"_maxDebtDelta","type":"uint256"}],"name":"addStrategy","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"spender","type":"address"}],"name":"allowance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"}],"name":"approve","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"asset","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"shares","type":"uint256"}],"name":"convertToAssets","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"assets","type":"uint256"}],"name":"convertToShares","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_strategy","type":"address"}],"name":"creditAvailable","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"currentPnL","outputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_strategy","type":"address"}],"name":"debtExcess","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"debtRatio","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"decimals","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_assets","type":"uint256"},{"internalType":"address","name":"_receiver","type":"address"}],"name":"deposit","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"depositLimit","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_guardian","type":"address"}],"name":"enableGuardian","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_strategy","type":"address"}],"name":"getStrategyParameters","outputs":[{"components":[{"internalType":"uint256","name":"activation","type":"uint256"},{"internalType":"uint256","name":"debtRatio","type":"uint256"},{"internalType":"uint256","name":"lastReport","type":"uint256"},{"internalType":"uint256","name":"minDebtDelta","type":"uint256"},{"internalType":"uint256","name":"maxDebtDelta","type":"uint256"},{"internalType":"uint256","name":"totalDebt","type":"uint256"},{"internalType":"uint256","name":"totalGain","type":"uint256"},{"internalType":"uint256","name":"totalLoss","type":"uint256"}],"internalType":"struct MStrat.StrategyParams","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getWithdrawOrder","outputs":[{"internalType":"address[]","name":"","type":"address[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"guardianAddress","type":"address"}],"name":"guardians","outputs":[{"internalType":"bool","name":"isActive","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"lastReport","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"lockedProfit","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"lockedProfitDegradation","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"manager","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"maxDeposit","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_receiver","type":"address"}],"name":"maxMint","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"maxRedeem","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"maxWithdraw","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_shares","type":"uint256"},{"internalType":"address","name":"_receiver","type":"address"}],"name":"mint","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"pause","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"paused","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"performanceFee","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"assets","type":"uint256"}],"name":"previewDeposit","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"shares","type":"uint256"}],"name":"previewMint","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_shares","type":"uint256"}],"name":"previewRedeem","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_assets","type":"uint256"}],"name":"previewWithdraw","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"pricePerShare","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"protocolFeeRecipient","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_shares","type":"uint256"},{"internalType":"address","name":"_receiver","type":"address"},{"internalType":"address","name":"_owner","type":"address"}],"name":"redeem","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_strategy","type":"address"}],"name":"removeStrategy","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"requestCredit","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_token","type":"address"},{"internalType":"address","name":"_recipient","type":"address"}],"name":"rescueToken","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"retire","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_strategy","type":"address"}],"name":"retireStrategy","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"retired","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_guardian","type":"address"}],"name":"revokeGuardian","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_depositLimit","type":"uint256"}],"name":"setDepositLimit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_manager","type":"address"}],"name":"setManager","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_performanceFee","type":"uint256"}],"name":"setPerformanceFee","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_protocolFeeRecipient","type":"address"}],"name":"setProtocolFeeRecipient","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_slippageLimit","type":"uint256"}],"name":"setSlippageLimit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_strategy","type":"address"},{"internalType":"uint256","name":"_debtRatio","type":"uint256"}],"name":"setStrategyDebtRatio","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_strategy","type":"address"},{"internalType":"uint256","name":"_maxDebtDelta","type":"uint256"}],"name":"setStrategyMaxDebtDelta","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_strategy","type":"address"},{"internalType":"uint256","name":"_minDebtDelta","type":"uint256"}],"name":"setStrategyMinDebtDelta","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address[]","name":"_strategies","type":"address[]"}],"name":"setWithdrawOrder","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"slippageLimit","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"strategyAddress","type":"address"}],"name":"strategies","outputs":[{"internalType":"uint256","name":"activation","type":"uint256"},{"internalType":"uint256","name":"debtRatio","type":"uint256"},{"internalType":"uint256","name":"lastReport","type":"uint256"},{"internalType":"uint256","name":"minDebtDelta","type":"uint256"},{"internalType":"uint256","name":"maxDebtDelta","type":"uint256"},{"internalType":"uint256","name":"totalDebt","type":"uint256"},{"internalType":"uint256","name":"totalGain","type":"uint256"},{"internalType":"uint256","name":"totalLoss","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_debtRepayment","type":"uint256"},{"internalType":"uint256","name":"_gain","type":"uint256"},{"internalType":"uint256","name":"_loss","type":"uint256"}],"name":"strategyReport","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_strategy","type":"address"}],"name":"strategyTotalDebt","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalAssets","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalDebt","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"}],"name":"transfer","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"}],"name":"transferFrom","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"unpause","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_assets","type":"uint256"},{"internalType":"address","name":"_receiver","type":"address"},{"internalType":"address","name":"_owner","type":"address"}],"name":"withdraw","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"withdrawOrder","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"}]

610100604052348015610010575f5ffd5b506040516148bf3803806148bf83398101604081905261002f91610496565b8482823387878282818061005c57604051631e4fbdf760e01b81525f600482015260240160405180910390fd5b6100658161020a565b505f805460ff60a01b19169055600180546001600160a01b0319166001600160a01b03928316179055821690506100af5760405163d92e233d60e01b815260040160405180910390fd5b600380546001600160a01b0319166001600160a01b03831617905560408051600a8082526101608201909252906020820161014080368337505081516100fc92600b925060200190610348565b5050505081600f908161010f91906105b0565b50601061011c82826105b0565b5050505f5f6101308361025960201b60201c565b9150915081610140576012610142565b805b60ff1660a05250506001600160a01b0390811660805260016011556040805163313ce56760e01b815290516101d7925f929089169163313ce567916004808201926020929091908290030181865afa1580156101a0573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906101c4919061066a565b6101d29060ff166012610691565b61032f565b60ff1660c0526103e8600455426012556101fc6203f480670de0b6b3a76400006106b0565b60e052506107019350505050565b5f80546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b60408051600481526024810182526020810180516001600160e01b031663313ce56760e01b17905290515f918291829182916001600160a01b0387169161029f916106cf565b5f60405180830381855afa9150503d805f81146102d7576040519150601f19603f3d011682016040523d82523d5f602084013e6102dc565b606091505b50915091508180156102f057506020815110155b15610323575f8180602001905181019061030a91906106ea565b905060ff8111610321576001969095509350505050565b505b505f9485945092505050565b5f81831161033d578161033f565b825b90505b92915050565b828054828255905f5260205f2090810192821561039b579160200282015b8281111561039b57825182546001600160a01b0319166001600160a01b03909116178255602090920191600190910190610366565b506103a79291506103ab565b5090565b5b808211156103a7575f81556001016103ac565b80516001600160a01b03811681146103d5575f5ffd5b919050565b634e487b7160e01b5f52604160045260245ffd5b5f5b838110156104085781810151838201526020016103f0565b50505f910152565b5f82601f83011261041f575f5ffd5b81516001600160401b03811115610438576104386103da565b604051601f8201601f19908116603f011681016001600160401b0381118282101715610466576104666103da565b60405281815283820160200185101561047d575f5ffd5b61048e8260208301602087016103ee565b949350505050565b5f5f5f5f5f60a086880312156104aa575f5ffd5b6104b3866103bf565b94506104c1602087016103bf565b93506104cf604087016103bf565b60608701519093506001600160401b038111156104ea575f5ffd5b6104f688828901610410565b608088015190935090506001600160401b03811115610513575f5ffd5b61051f88828901610410565b9150509295509295909350565b600181811c9082168061054057607f821691505b60208210810361055e57634e487b7160e01b5f52602260045260245ffd5b50919050565b601f8211156105ab57805f5260205f20601f840160051c810160208510156105895750805b601f840160051c820191505b818110156105a8575f8155600101610595565b50505b505050565b81516001600160401b038111156105c9576105c96103da565b6105dd816105d7845461052c565b84610564565b6020601f82116001811461060f575f83156105f85750848201515b5f19600385901b1c1916600184901b1784556105a8565b5f84815260208120601f198516915b8281101561063e578785015182556020948501946001909201910161061e565b508482101561065b57868401515f19600387901b60f8161c191681555b50505050600190811b01905550565b5f6020828403121561067a575f5ffd5b815160ff8116811461068a575f5ffd5b9392505050565b8181038181111561034257634e487b7160e01b5f52601160045260245ffd5b5f826106ca57634e487b7160e01b5f52601260045260245ffd5b500490565b5f82516106e08184602087016103ee565b9190910192915050565b5f602082840312156106fa575f5ffd5b5051919050565b60805160a05160c05160e0516141376107885f395f8181610646015261386001525f81816108450152818161131201528181612314015261236601525f61133301525f8181610550015281816122710152818161262d0152818161291301528181612a9401528181612c8e01528181612ccb01528181613183015261326f01526141375ff3fe608060405234801561000f575f5ffd5b506004361061042d575f3560e01c8063877887821161022c578063c3535b5211610135578063dd62ed3e116100bf578063ef8b30f711610084578063ef8b30f714610907578063f10d3d5d146109fa578063f2fde38b14610a6d578063fb791b0b14610a80578063fc7b9c1814610a93575f5ffd5b8063dd62ed3e1461097e578063e2c23610146109b6578063e521cb92146109cb578063ec811718146109de578063ecf70858146109f1575f5ffd5b8063cea55f5711610105578063cea55f571461092d578063d0ebdbe714610936578063d250f08814610949578063d764801314610958578063d905777e1461096b575f5ffd5b8063c3535b52146108eb578063c63d75b6146108f4578063c6e6f59214610907578063ce96cb771461091a575f5ffd5b8063aea70acc116101b6578063b6b1fb3b11610186578063b6b1fb3b146108a0578063ba087652146108aa578063bdc8144b146108bd578063c031d2fb146108d0578063c285cbb5146108d8575f5ffd5b8063aea70acc14610840578063af648c3d14610867578063b3d7f6b91461087a578063b460af941461088d575f5ffd5b806399530b06116101fc57806399530b06146107f7578063a0c9d3fc146107ff578063a4874d7714610812578063a4c9b9611461081a578063a9059cbb1461082d575f5ffd5b806387788782146107c35780638da5cb5b146107cc57806394bf804d146107dc57806395d89b41146107ef575f5ffd5b8063422327161161033957806364df049e116102c3578063715018a611610288578063715018a61461078057806374f9479b146107885780637a9f35861461079b578063821a622f146107ae5780638456cb59146107bb575f5ffd5b806364df049e1461070c578063691083631461071f5780636e553f651461073257806370897b231461074557806370a0823114610758575f5ffd5b806349185ab81161030957806349185ab8146106975780634cdad506146106b45780635b07871a146106c75780635c975abb146106d057806361f488bf146106e1575f5ffd5b8063422327161461064157806344b81396146106685780634707d00014610671578063481c6a7514610684575f5ffd5b80631fe4ba17116103ba57806338d52e0f1161038a57806338d52e0f1461054e57806339ebf823146105885780633ea9e124146106135780633f4ba83a14610626578063402d267d1461062e575f5ffd5b80631fe4ba17146104fc57806323b872dd1461050f5780632eb38ae014610522578063313ce56714610534575f5ffd5b8063095ea7b311610400578063095ea7b3146104a65780630a28a477146104b95780630dd21b6c146104cc578063175188e8146104e157806318160ddd146104f4575f5ffd5b806301e1d114146104315780630633b14a1461044c57806306fdde031461047e57806307a2d13a14610493575b5f5ffd5b610439610a9c565b6040519081526020015b60405180910390f35b61046e61045a366004613af8565b60026020525f908152604090205460ff1681565b6040519015158152602001610443565b610486610ab7565b6040516104439190613b35565b6104396104a1366004613b67565b610b47565b61046e6104b4366004613b7e565b610b58565b6104396104c7366004613b67565b610b6f565b6104df6104da366004613ba8565b610bd3565b005b6104df6104ef366004613af8565b611022565b600e54610439565b6104df61050a366004613b67565b611237565b61046e61051d366004613be0565b6112e9565b60095461046e90610100900460ff1681565b61053c61130c565b60405160ff9091168152602001610443565b7f00000000000000000000000000000000000000000000000000000000000000005b6040516001600160a01b039091168152602001610443565b6105d8610596366004613af8565b600a6020525f90815260409020805460018201546002830154600384015460048501546005860154600687015460079097015495969495939492939192909188565b604080519889526020890197909752958701949094526060860192909252608085015260a084015260c083015260e082015261010001610443565b6104df610621366004613b7e565b611357565b6104df611482565b61043961063c366004613af8565b611494565b6104397f000000000000000000000000000000000000000000000000000000000000000081565b61043960135481565b6104df61067f366004613c1e565b6114c6565b600154610570906001600160a01b031681565b61069f611537565b60408051928352602083019190915201610443565b6104396106c2366004613b67565b611549565b61043960085481565b5f54600160a01b900460ff1661046e565b6104396106ef366004613af8565b6001600160a01b03165f908152600a602052604090206005015490565b600354610570906001600160a01b031681565b6104df61072d366004613af8565b611589565b610439610740366004613c55565b611697565b6104df610753366004613b67565b61172c565b610439610766366004613af8565b6001600160a01b03165f908152600c602052604090205490565b6104df611791565b610439610796366004613af8565b6117a2565b6104df6107a9366004613c78565b6117ac565b60095461053c9060ff1681565b6104df611800565b61043960045481565b5f546001600160a01b0316610570565b6104396107ea366004613c55565b61186f565b6104866118f7565b610439611906565b6104df61080d366004613af8565b611918565b6104df61196b565b6104df610828366004613cb5565b611a0b565b61046e61083b366004613b7e565b611abe565b61053c7f000000000000000000000000000000000000000000000000000000000000000081565b6104df610875366004613af8565b611acb565b610439610888366004613b67565b611b1b565b61043961089b366004613d80565b611b27565b6104396203f48081565b6104396108b8366004613d80565b611bca565b6104df6108cb366004613b67565b611c5c565b610439611cdf565b6104df6108e6366004613b7e565b611d32565b61043960125481565b610439610902366004613af8565b611e50565b610439610915366004613b67565b611e5d565b610439610928366004613af8565b611e68565b61043960065481565b6104df610944366004613af8565b611e8a565b610439670de0b6b3a764000081565b610439610966366004613af8565b611f02565b610439610979366004613af8565b611f0c565b61043961098c366004613c1e565b6001600160a01b039182165f908152600d6020908152604080832093909416825291909152205490565b6109be611f29565b6040516104439190613dbf565b6104df6109d9366004613af8565b611f88565b6104df6109ec366004613b7e565b612000565b61043960055481565b610a0d610a08366004613af8565b612153565b60405161044391905f61010082019050825182526020830151602083015260408301516040830152606083015160608301526080830151608083015260a083015160a083015260c083015160c083015260e083015160e083015292915050565b6104df610a7b366004613af8565b612209565b610570610a8e366004613b67565b612246565b61043960075481565b5f600754610aa861226e565b610ab29190613e1e565b905090565b6060600f8054610ac690613e31565b80601f0160208091040260200160405190810160405280929190818152602001828054610af290613e31565b8015610b3d5780601f10610b1457610100808354040283529160200191610b3d565b820191905f5260205f20905b815481529060010190602001808311610b2057829003601f168201915b5050505050905090565b5f610b52825f6122f8565b92915050565b5f33610b65818585612350565b5060019392505050565b5f5f610b7c83600161235d565b9050610b8661226e565b8311610b925792915050565b61271060085403610ba657505f1992915050565b610bc6612710600854612710610bbc9190613e63565b83919060016123ac565b9392505050565b50919050565b610bdb6123fb565b600954600a60ff90911610610c035760405163819a681f60e01b815260040160405180910390fd5b6001600160a01b03841615801590610c2457506001600160a01b0384163014155b8490610c5457604051634726455360e11b81526001600160a01b0390911660048201526024015b60405180910390fd5b506001600160a01b0384165f908152600a6020526040902054849015610c995760405163060f34a560e51b81526001600160a01b039091166004820152602401610c4b565b50836001600160a01b03166338d52e0f6040518163ffffffff1660e01b8152600401602060405180830381865afa158015610cd6573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610cfa9190613e76565b6001600160a01b0316306001600160a01b03166338d52e0f6040518163ffffffff1660e01b8152600401602060405180830381865afa158015610d3f573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610d639190613e76565b6001600160a01b031614306001600160a01b03166338d52e0f6040518163ffffffff1660e01b8152600401602060405180830381865afa158015610da9573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610dcd9190613e76565b856001600160a01b03166338d52e0f6040518163ffffffff1660e01b8152600401602060405180830381865afa158015610e09573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610e2d9190613e76565b9091610e5f57604051634e83a9b960e01b81526001600160a01b03928316600482015291166024820152604401610c4b565b505061271083600654610e729190613e1e565b111583600654610e829190613e1e565b90610ea357604051637698c0b760e11b8152600401610c4b91815260200190565b5080821115610ec5576040516328b7ed0d60e01b815260040160405180910390fd5b604080516101008101825242808252602080830187815283850192835260608401878152608085018781525f60a0870181815260c0880182815260e089018381526001600160a01b038f168452600a9097529882209751885593516001880155945160028701559051600386015551600485015551600584015592516006808401919091559251600790920191909155815485929190610f66908490613e1e565b90915550849050600b610f7b6001600a613e91565b60ff1681548110610f8e57610f8e613eaa565b5f918252602082200180546001600160a01b0319166001600160a01b03939093169290921790915560098054909190610fc99060ff16613ebe565b91906101000a81548160ff021916908360ff160217905550610fe9612427565b6040516001600160a01b038516907f3f008fd510eae7a9e7bee13513d7b83bef8003d488b5a3d0b0da4de71d6846f1905f90a250505050565b5f546001600160a01b031633148061104457506001546001600160a01b031633145b339061106f57604051633b2495f160e01b81526001600160a01b039091166004820152602401610c4b565b506001600160a01b0381165f908152600a6020526040902054819081906110b55760405163e5c0758760e01b81526001600160a01b039091166004820152602401610c4b565b506001600160a01b0382165f908152600a6020526040902060010154156110ef5760405163424ae5ab60e01b815260040160405180910390fd5b6001600160a01b0382165f908152600a6020526040902060050154156111285760405163a617d47160e01b815260040160405180910390fd5b5f5b600a60ff8216101561123157826001600160a01b0316600b8260ff168154811061115657611156613eaa565b5f918252602090912001546001600160a01b031603611229575f600b8260ff168154811061118657611186613eaa565b5f91825260208083209190910180546001600160a01b0319166001600160a01b039485161790559185168152600a90915260408120819055600980549091906111d19060ff16613edc565b91906101000a81548160ff021916908360ff1602179055506111f1612427565b6040516001600160a01b038416907f09a1db4b80c32706328728508c941a6b954f31eb5affd32f236c1fd405f8fea4905f90a2505050565b60010161112a565b505b5050565b5f546001600160a01b031633148061125957506001546001600160a01b031633145b339061128457604051633b2495f160e01b81526001600160a01b039091166004820152602401610c4b565b50806127108111156112ac57604051631920afa360e11b8152600401610c4b91815260200190565b5060088190556040518181527ff6c3c65ea16f3d6351596faef627640721d4bbe1c42e5f06ae3b40f5a399cfc6906020015b60405180910390a150565b5f336112f6858285612518565b61130185858561257a565b506001949350505050565b5f610ab27f00000000000000000000000000000000000000000000000000000000000000007f0000000000000000000000000000000000000000000000000000000000000000613ef7565b5f546001600160a01b031633148061137957506001546001600160a01b031633145b33906113a457604051633b2495f160e01b81526001600160a01b039091166004820152602401610c4b565b506001600160a01b0382165f908152600a6020526040902054829081906113ea5760405163e5c0758760e01b81526001600160a01b039091166004820152602401610c4b565b506001600160a01b0383165f908152600a6020526040902060030154821015611426576040516328b7ed0d60e01b815260040160405180910390fd5b6001600160a01b0383165f818152600a602052604090819020600401849055517fe6fd3fad502c8a253ce72fb45e7f218dde705007fa4d961e164e5117ff4e31f3906114759085815260200190565b60405180910390a2505050565b61148a6123fb565b6114926125d7565b565b5f6005546114a0610a9c565b106114ac57505f919050565b6114b4610a9c565b600554610b529190613e63565b919050565b5f546001600160a01b03163314806114e857506001546001600160a01b031633145b806115015750335f9081526002602052604090205460ff165b339061152c57604051631f851be960e31b81526001600160a01b039091166004820152602401610c4b565b50611233828261262b565b5f5f61154161270b565b915091509091565b5f5f611555835f6122f8565b905061155f61226e565b811161156b5792915050565b610bc660085461271061157e9190613e63565b82906127105f6123ac565b5f546001600160a01b03163314806115ab57506001546001600160a01b031633145b33906115d657604051633b2495f160e01b81526001600160a01b039091166004820152602401610c4b565b506001600160a01b0381165f908152600a60205260409020548190819061161c5760405163e5c0758760e01b81526001600160a01b039091166004820152602401610c4b565b506001600160a01b0382165f908152600a6020526040812060010154600680549192909161164b908490613e63565b90915550506001600160a01b0382165f818152600a6020526040808220600101829055517f43328b6eb7f0f6711a295cfc22368af8227bf60b67a6f6a5454f6168726d3e259190a25050565b5f6116a0612847565b600954610100900460ff16156116c957604051630c15f68f60e41b815260040160405180910390fd5b6116d1612871565b5f6116db83611494565b90508284828082111561170457604051633c8097d960e11b8152600401610c4b93929190613f10565b5050505f61171185611e5d565b905061171f3385878461289b565b915050610b526001601155565b6117346123fb565b806107d081111561175b57604051633de00b8560e01b8152600401610c4b91815260200190565b5060048190556040518181527fceb20f7f0b19335681096ee1eaa9bb2a6ef5a9a69ba48b6b488e7b7eff2ef04d906020016112de565b6117996123fb565b6114925f6129aa565b5f610b52826129f9565b6117b4612847565b335f818152600a602052604090205481906117ee5760405163e5c0758760e01b81526001600160a01b039091166004820152602401610c4b565b506117fa848484612a91565b50505050565b5f546001600160a01b031633148061182257506001546001600160a01b031633145b8061183b5750335f9081526002602052604090205460ff165b339061186657604051631f851be960e31b81526001600160a01b039091166004820152602401610c4b565b50611492612d48565b5f611878612847565b600954610100900460ff16156118a157604051630c15f68f60e41b815260040160405180910390fd5b6118a9612871565b5f6118b383611e50565b9050828482808211156118dc5760405163284ff66760e01b8152600401610c4b93929190613f10565b5050505f6118e985611b1b565b905061171f3385838861289b565b606060108054610ac690613e31565b5f610ab2670de0b6b3a7640000610b47565b6119206123fb565b6001600160a01b0381165f81815260026020526040808220805460ff19166001179055517fd0cd09d977e63f0acaf30ce79e6479c90d7ea56a41a0849ac60f5440ac608a159190a250565b5f546001600160a01b031633148061198d57506001546001600160a01b031633145b806119a65750335f9081526002602052604090205460ff165b33906119d157604051631f851be960e31b81526001600160a01b039091166004820152602401610c4b565b506009805461ff0019166101001790556040517f762e62217e4ec85388fb7cba32a604823803326c60ed9f962a2b86512ca78d8c905f90a1565b5f546001600160a01b0316331480611a2d57506001546001600160a01b031633145b3390611a5857604051633b2495f160e01b81526001600160a01b039091166004820152602401610c4b565b50611a6281612d8a565b611a7f5760405163660e947360e01b815260040160405180910390fd5b8051611a9290600b906020840190613a6b565b506040517f5c2915d8834f24b89a98a55fe6de71ad6dad79ef0b3052ff68231dbfcb011c88905f90a150565b5f33610b6581858561257a565b611ad36123fb565b6001600160a01b0381165f81815260026020526040808220805460ff19169055517f0c92d12d8037dd6d77aed8d12addd54d5eb2a6801541a1bf87c9822e78eea4219190a250565b5f610b528260016122f8565b5f611b30612847565b611b38612871565b5f611b4283611e68565b905082858280821115611b6b57604051633fa733bb60e21b8152600401610c4b93929190613f10565b5050505f611b7886610b6f565b90505f611b893387878a865f612eec565b91508290508181811115611bb957604051630a8f6c8d60e41b815260048101929092526024820152604401610c4b565b50909350505050610bc66001601155565b5f611bd3612847565b611bdb612871565b5f611be583611f0c565b905082858280821115611c0e57604051632e52afbb60e21b8152600401610c4b93929190613f10565b5050505f611c1b86611549565b90505f611c2d338787858b6001612eec565b509050818181811015611bb957604051630a8f6c8d60e41b815260048101929092526024820152604401610c4b565b5f546001600160a01b0316331480611c7e57506001546001600160a01b031633145b3390611ca957604051633b2495f160e01b81526001600160a01b039091166004820152602401610c4b565b5060058190556040518181527f5d2e73196f8ba1b44e887e2bcc9bcd1f3e657add341d4a0a632ffff00d6903f2906020016112de565b5f611ce8612847565b335f818152600a60205260409020548190611d225760405163e5c0758760e01b81526001600160a01b039091166004820152602401610c4b565b50611d2b613216565b91505b5090565b5f546001600160a01b0316331480611d5457506001546001600160a01b031633145b3390611d7f57604051633b2495f160e01b81526001600160a01b039091166004820152602401610c4b565b506001600160a01b0382165f908152600a602052604090205482908190611dc55760405163e5c0758760e01b81526001600160a01b039091166004820152602401610c4b565b506001600160a01b0383165f908152600a6020526040902060040154821115611e01576040516328b7ed0d60e01b815260040160405180910390fd5b6001600160a01b0383165f818152600a602052604090819020600301849055517fe0dccc38125c57629849ddb1eef292a3e47f8efdba994dc4efe9637c6b68010a906114759085815260200190565b5f610b5261091583611494565b5f610b52825f61235d565b6001600160a01b0381165f908152600c6020526040812054610b52905f6122f8565b611e926123fb565b6001600160a01b038116611eb95760405163d92e233d60e01b815260040160405180910390fd5b600180546001600160a01b0319166001600160a01b0383169081179091556040517f60a0f5b9f9e81e98216071b85826681c796256fe3d1354ecb675580fba64fa69905f90a250565b5f610b52826132cd565b6001600160a01b0381165f908152600c6020526040812054610b52565b6060600b805480602002602001604051908101604052809291908181526020018280548015610b3d57602002820191905f5260205f20905b81546001600160a01b03168152600190910190602001808311611f61575050505050905090565b611f906123fb565b6001600160a01b038116611fb75760405163d92e233d60e01b815260040160405180910390fd5b600380546001600160a01b0319166001600160a01b0383169081179091556040517f5034c7ac62cd0031ddf2f1dcde2858132dd886d61032c9b2d4a99f0490e80ee4905f90a250565b5f546001600160a01b031633148061202257506001546001600160a01b031633145b339061204d57604051633b2495f160e01b81526001600160a01b039091166004820152602401610c4b565b506001600160a01b0382165f908152600a6020526040902054829081906120935760405163e5c0758760e01b81526001600160a01b039091166004820152602401610c4b565b506001600160a01b0383165f908152600a602052604081206001015460065484916120bd91613e63565b6120c79190613e1e565b9050806127108111156120f057604051637698c0b760e11b8152600401610c4b91815260200190565b5060068190556001600160a01b0384165f818152600a602052604090819020600101859055517f64b9074a3c4edf892069f1a719f7b1de2f307df3b29626e0b381d819420419ef906121459086815260200190565b60405180910390a250505050565b6121936040518061010001604052805f81526020015f81526020015f81526020015f81526020015f81526020015f81526020015f81526020015f81525090565b506001600160a01b03165f908152600a6020908152604091829020825161010081018452815481526001820154928101929092526002810154928201929092526003820154606082015260048201546080820152600582015460a0820152600682015460c082015260079091015460e082015290565b6122116123fb565b6001600160a01b03811661223a57604051631e4fbdf760e01b81525f6004820152602401610c4b565b612243816129aa565b50565b600b8181548110612255575f80fd5b5f918252602090912001546001600160a01b0316905081565b5f7f00000000000000000000000000000000000000000000000000000000000000006040516370a0823160e01b81523060048201526001600160a01b0391909116906370a0823190602401602060405180830381865afa1580156122d4573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610ab29190613f31565b5f610bc66123046133c0565b61230f906001613e1e565b61233a7f0000000000000000000000000000000000000000000000000000000000000000600a61402b565b600e546123479190613e1e565b859190856123ac565b61123183838360016133db565b5f610bc661238c7f0000000000000000000000000000000000000000000000000000000000000000600a61402b565b600e546123999190613e1e565b6123a16133c0565b612347906001613e1e565b5f5f6123b986868661349f565b90506123c48361355e565b80156123df57505f84806123da576123da614039565b868809115b156123f2576123ef600182613e1e565b90505b95945050505050565b5f546001600160a01b031633146114925760405163118cdaa760e01b8152336004820152602401610c4b565b5f805b600a60ff82161015611233575f600b8260ff168154811061244d5761244d613eaa565b5f918252602090912001546001600160a01b03169050806124785761247183613ebe565b925061250f565b60ff83161561250f5780600b61248e8585613e91565b60ff16815481106124a1576124a1613eaa565b905f5260205f20015f6101000a8154816001600160a01b0302191690836001600160a01b031602179055505f600b8360ff16815481106124e3576124e3613eaa565b905f5260205f20015f6101000a8154816001600160a01b0302191690836001600160a01b031602179055505b5060010161242a565b6001600160a01b038381165f908152600d60209081526040808320938616835292905220545f1981146117fa578181101561256c57828183604051637dc7a0d960e11b8152600401610c4b93929190613f10565b6117fa84848484035f6133db565b6001600160a01b0383166125a357604051634b637e8f60e11b81525f6004820152602401610c4b565b6001600160a01b0382166125cc5760405163ec442f0560e01b81525f6004820152602401610c4b565b61123183838361358a565b6125df61369d565b5f805460ff60a01b191690557f5db9ee0a495bf2e6ff9c91a7834c1ba4fdd244a5e8aa4e537bd38aeae4b073aa335b6040516001600160a01b03909116815260200160405180910390a1565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316826001600160a01b03161415829061268c57604051634726455360e11b81526001600160a01b039091166004820152602401610c4b565b506040516370a0823160e01b81523060048201525f906001600160a01b038416906370a0823190602401602060405180830381865afa1580156126d1573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906126f59190613f31565b90506112316001600160a01b03841683836136c6565b6009545f90819060ff16810361272357505f91829150565b5f80805b60095460ff908116908216101561283d575f600b8260ff168154811061274f5761274f613eaa565b5f918252602090912001546001600160a01b0316905080612770575061283d565b6001600160a01b0381165f908152600a602052604081206005015490036127975750612835565b5f5f826001600160a01b03166349185ab86040518163ffffffff1660e01b81526004016040805180830381865afa1580156127d4573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906127f8919061404d565b9150915061281960045461271061280f9190613e63565b839061271061349f565b6128239087613e1e565b955061282f8186613e1e565b94505050505b600101612727565b5090939092509050565b5f54600160a01b900460ff16156114925760405163d93c066560e01b815260040160405180910390fd5b60026011540361289457604051633ee5aeb560e01b815260040160405180910390fd5b6002601155565b6001600160a01b038316158015906128bc57506001600160a01b0383163014155b83906128e757604051634726455360e11b81526001600160a01b039091166004820152602401610c4b565b50818061290a5760405163135ee08960e31b8152600401610c4b91815260200190565b506129448430847f00000000000000000000000000000000000000000000000000000000000000005b6001600160a01b0316929190613725565b61294e838261375e565b826001600160a01b0316846001600160a01b03167fdcbc1c05240f31ff3ad067ef1ee35ce4997762752e3a095284754544f4c709d7848460405161299c929190918252602082015260400190565b60405180910390a350505050565b5f80546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b5f6006545f03612a2157506001600160a01b03165f908152600a602052604090206005015490565b5f612a51612a2d610a9c565b6001600160a01b0385165f908152600a60205260409020600101549061271061349f565b6001600160a01b0384165f908152600a6020526040902060050154909150818111612a7f57505f9392505050565b612a898282613e63565b949350505050565b5f7f00000000000000000000000000000000000000000000000000000000000000006040516370a0823160e01b81523360048201526001600160a01b0391909116906370a0823190602401602060405180830381865afa158015612af7573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190612b1b9190613f31565b90505f83118015612b2b57505f82115b15612b4957604051635b1ba80560e11b815260040160405180910390fd5b612b538385613e1e565b81101581612b618587613e1e565b9091612b895760405163cf47918160e01b815260048101929092526024820152604401610c4b565b505f9050808315612b9e57612b9e3385613792565b8415612bc457600454612bb590869061271061349f565b9050612bc18186613e63565b91505b5f612bd787612bd2336129f9565b613847565b90508015612c1e57335f908152600a602052604081206005018054839290612c00908490613e63565b925050819055508060075f828254612c189190613e63565b90915550505b5f83612c2861385c565b612c329190613e1e565b905085811115612c4e57612c468682613e63565b601355612c53565b5f6013555b335f908152600a60205260408120426002909101819055601255612c778884613e1e565b1115612cb257612cb23330612c8c8a86613e1e565b7f0000000000000000000000000000000000000000000000000000000000000000612933565b8215612cfb57600354612cfb906001600160a01b0316847f00000000000000000000000000000000000000000000000000000000000000005b6001600160a01b031691906136c6565b604080518381526020810186905290810187905233907f6fbdf847c78f04eac7a7a77e1073af6eeec604ee0b495240579dccf57fcf2d889060600160405180910390a25050505050505050565b612d50612847565b5f805460ff60a01b1916600160a01b1790557f62e78cea01bee320cd4e420270b5ea74000d11b0c9f74754ebdbfc544b05a25861260e3390565b80515f90600a14612d9c57505f919050565b5f5b600a60ff82161015612ee3575f838260ff1681518110612dc057612dc0613eaa565b602002602001015190505f6001600160a01b0316816001600160a01b031614612e78576001600160a01b0381165f908152600a60205260408120549003612e0a57505f9392505050565b5f5b600a60ff82161015612e72578060ff168360ff1614158015612e5b5750848160ff1681518110612e3e57612e3e613eaa565b60200260200101516001600160a01b0316826001600160a01b0316145b15612e6a57505f949350505050565b600101612e0c565b50612eda565b5f612e84836001613ef7565b90505b600a60ff82161015611301575f6001600160a01b0316858260ff1681518110612eb257612eb2613eaa565b60200260200101516001600160a01b031614612ed257505f949350505050565b600101612e87565b50600101612d9e565b50600192915050565b5f808380612f105760405163135ee08960e31b8152600401610c4b91815260200190565b50856001600160a01b0316886001600160a01b031614612f3557612f35868986612518565b5f83612f415785612f4b565b612f4b855f6122f8565b9050612f5561226e565b811115613158575f5b600b5460ff821611613156575f600b8260ff1681548110612f8157612f81613eaa565b5f918252602090912001546001600160a01b0316905080151583612fa361226e565b9091612fcb5760405163a17e11d560e01b815260048101929092526024820152604401610c4b565b50505f613003612fd961226e565b612fe39086613e63565b6001600160a01b0384165f908152600a6020526040902060050154613847565b9050805f03613013575050613146565b604051632e1a7d4d60e01b8152600481018290525f906001600160a01b03841690632e1a7d4d906024016020604051808303815f875af1158015613059573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061307d9190613f31565b6001600160a01b0384165f908152600a60205260408120600501805492935083929091906130ac908490613e63565b925050819055508060075f8282546130c49190613e63565b92505081905550826001600160a01b031663c0275d5b6040518163ffffffff1660e01b81526004015f604051808303815f87803b158015613103575f5ffd5b505af1158015613115573d5f5f3e3d5ffd5b50505050871561312c57613129895f6122f8565b94505b61313461226e565b851161314257505050613156565b5050505b61314f81613ebe565b9050612f5e565b505b5f8461316e5761316982600161235d565b613170565b855b905061317c88826138d4565b6131a789837f0000000000000000000000000000000000000000000000000000000000000000612ceb565b876001600160a01b0316896001600160a01b03168b6001600160a01b03167ffbde797d201c681b91056529119e0b02407c7bb96a4a2c75c01fc9667232c8db85856040516131ff929190918252602082015260400190565b60405180910390a490999098509650505050505050565b5f5f613221336132cd565b905080156114c157335f908152600a60205260408120600501805483929061324a908490613e1e565b925050819055508060075f8282546132629190613e1e565b90915550613293905033827f0000000000000000000000000000000000000000000000000000000000000000612ceb565b60405181815233907f4bcdc8c4e44b31be744ac2494ab91b09234c21e24fa1cb0a1b4de3a85ef74d419060200160405180910390a2919050565b5f5f6132d7610a9c565b6006549091505f906132ec908361271061349f565b6007546001600160a01b0386165f908152600a6020526040812060010154929350909161331c908561271061349f565b6001600160a01b0387165f908152600a60205260409020600581015460038201546004909201549293509183831015806133565750858510155b1561336957505f98975050505050505050565b5f6133748486613e63565b90505f6133818789613e63565b905061338d8282613847565b9150838210156133a757505f9a9950505050505050505050565b6133b18284613847565b9b9a5050505050505050505050565b5f6133c961385c565b6133d1610a9c565b610ab29190613e63565b6001600160a01b0384166134045760405163e602df0560e01b81525f6004820152602401610c4b565b6001600160a01b03831661342d57604051634a1406b160e11b81525f6004820152602401610c4b565b6001600160a01b038085165f908152600d6020908152604080832093871683529290522082905580156117fa57826001600160a01b0316846001600160a01b03167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b9258460405161299c91815260200190565b5f838302815f1985870982811083820303915050805f036134d3578382816134c9576134c9614039565b0492505050610bc6565b8084116134f35760405163227bc15360e01b815260040160405180910390fd5b5f848688095f868103871696879004966002600389028118808a02820302808a02820302808a02820302808a02820302808a02820302808a02909103029181900381900460010186841190950394909402919094039290920491909117919091029150509392505050565b5f60028260038111156135735761357361406f565b61357d9190614083565b60ff166001149050919050565b6001600160a01b0383166135b45780600e5f8282546135a99190613e1e565b909155506136119050565b6001600160a01b0383165f908152600c6020526040902054818110156135f35783818360405163391434e360e21b8152600401610c4b93929190613f10565b6001600160a01b0384165f908152600c602052604090209082900390555b6001600160a01b03821661362d57600e8054829003905561364b565b6001600160a01b0382165f908152600c602052604090208054820190555b816001600160a01b0316836001600160a01b03167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef8360405161369091815260200190565b60405180910390a3505050565b5f54600160a01b900460ff1661149257604051638dfc202b60e01b815260040160405180910390fd5b6040516001600160a01b0383811660248301526044820183905261123191859182169063a9059cbb906064015b604051602081830303815290604052915060e01b6020820180516001600160e01b038381831617835250505050613908565b6040516001600160a01b0384811660248301528381166044830152606482018390526117fa9186918216906323b872dd906084016136f3565b6001600160a01b0382166137875760405163ec442f0560e01b81525f6004820152602401610c4b565b6112335f838361358a565b6001600160a01b0382165f908152600a60205260409020600501548111156137cd576040516345f8381360e11b815260040160405180910390fd5b6001600160a01b0382165f908152600a6020526040812060070180548392906137f7908490613e1e565b90915550506001600160a01b0382165f908152600a602052604081206005018054839290613826908490613e63565b925050819055508060075f82825461383e9190613e63565b90915550505050565b5f8183106138555781610bc6565b5090919050565b5f5f7f00000000000000000000000000000000000000000000000000000000000000006012544261388d9190613e63565b61389791906140b0565b9050670de0b6b3a76400008110156138cd576013546138c0908290670de0b6b3a764000061349f565b601354611d2b9190613e63565b5f91505090565b6001600160a01b0382166138fd57604051634b637e8f60e11b81525f6004820152602401610c4b565b611233825f8361358a565b5f61391c6001600160a01b03841683613969565b905080515f1415801561394057508080602001905181019061393e91906140c7565b155b1561123157604051635274afe760e01b81526001600160a01b0384166004820152602401610c4b565b6060610bc683835f845f5f856001600160a01b0316848660405161398d91906140e6565b5f6040518083038185875af1925050503d805f81146139c7576040519150601f19603f3d011682016040523d82523d5f602084013e6139cc565b606091505b50915091506139dc8683836139e6565b9695505050505050565b6060826139fb576139f682613a42565b610bc6565b8151158015613a1257506001600160a01b0384163b155b15613a3b57604051639996b31560e01b81526001600160a01b0385166004820152602401610c4b565b5080610bc6565b805115613a525780518082602001fd5b604051630a12f52160e11b815260040160405180910390fd5b828054828255905f5260205f20908101928215613abe579160200282015b82811115613abe57825182546001600160a01b0319166001600160a01b03909116178255602090920191600190910190613a89565b50611d2e9291505b80821115611d2e575f8155600101613ac6565b6001600160a01b0381168114612243575f5ffd5b80356114c181613ad9565b5f60208284031215613b08575f5ffd5b8135610bc681613ad9565b5f5b83811015613b2d578181015183820152602001613b15565b50505f910152565b602081525f8251806020840152613b53816040850160208701613b13565b601f01601f19169190910160400192915050565b5f60208284031215613b77575f5ffd5b5035919050565b5f5f60408385031215613b8f575f5ffd5b8235613b9a81613ad9565b946020939093013593505050565b5f5f5f5f60808587031215613bbb575f5ffd5b8435613bc681613ad9565b966020860135965060408601359560600135945092505050565b5f5f5f60608486031215613bf2575f5ffd5b8335613bfd81613ad9565b92506020840135613c0d81613ad9565b929592945050506040919091013590565b5f5f60408385031215613c2f575f5ffd5b8235613c3a81613ad9565b91506020830135613c4a81613ad9565b809150509250929050565b5f5f60408385031215613c66575f5ffd5b823591506020830135613c4a81613ad9565b5f5f5f60608486031215613c8a575f5ffd5b505081359360208301359350604090920135919050565b634e487b7160e01b5f52604160045260245ffd5b5f60208284031215613cc5575f5ffd5b813567ffffffffffffffff811115613cdb575f5ffd5b8201601f81018413613ceb575f5ffd5b803567ffffffffffffffff811115613d0557613d05613ca1565b8060051b604051601f19603f830116810181811067ffffffffffffffff82111715613d3257613d32613ca1565b604052918252602081840181019290810187841115613d4f575f5ffd5b6020850194505b83851015613d7557613d6785613aed565b815260209485019401613d56565b509695505050505050565b5f5f5f60608486031215613d92575f5ffd5b833592506020840135613da481613ad9565b91506040840135613db481613ad9565b809150509250925092565b602080825282518282018190525f918401906040840190835b81811015613dff5783516001600160a01b0316835260209384019390920191600101613dd8565b509095945050505050565b634e487b7160e01b5f52601160045260245ffd5b80820180821115610b5257610b52613e0a565b600181811c90821680613e4557607f821691505b602082108103610bcd57634e487b7160e01b5f52602260045260245ffd5b81810381811115610b5257610b52613e0a565b5f60208284031215613e86575f5ffd5b8151610bc681613ad9565b60ff8281168282160390811115610b5257610b52613e0a565b634e487b7160e01b5f52603260045260245ffd5b5f60ff821660ff8103613ed357613ed3613e0a565b60010192915050565b5f60ff821680613eee57613eee613e0a565b5f190192915050565b60ff8181168382160190811115610b5257610b52613e0a565b6001600160a01b039390931683526020830191909152604082015260600190565b5f60208284031215613f41575f5ffd5b5051919050565b6001815b6001841115613f8357808504811115613f6757613f67613e0a565b6001841615613f7557908102905b60019390931c928002613f4c565b935093915050565b5f82613f9957506001610b52565b81613fa557505f610b52565b8160018114613fbb5760028114613fc557613fe1565b6001915050610b52565b60ff841115613fd657613fd6613e0a565b50506001821b610b52565b5060208310610133831016604e8410600b8410161715614004575081810a610b52565b6140105f198484613f48565b805f190482111561402357614023613e0a565b029392505050565b5f610bc660ff841683613f8b565b634e487b7160e01b5f52601260045260245ffd5b5f5f6040838503121561405e575f5ffd5b505080516020909101519092909150565b634e487b7160e01b5f52602160045260245ffd5b5f60ff8316806140a157634e487b7160e01b5f52601260045260245ffd5b8060ff84160691505092915050565b8082028115828204841417610b5257610b52613e0a565b5f602082840312156140d7575f5ffd5b81518015158114610bc6575f5ffd5b5f82516140f7818460208701613b13565b919091019291505056fea26469706673582212204e695dd3a83dc12d398f9fcb85a9480fab4d5e83b9cbe84928b15bbb0fdc9c1664736f6c634300081b003300000000000000000000000029219dd400f2bf60e5a23d13be72b486d403889400000000000000000000000075cb5d555933fe86e0ac8975a623acb5cec13e28000000000000000000000000d132631a63af5c616e60606025c8e5871addf76f00000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000000135969656c642043686173696e672055534443650000000000000000000000000000000000000000000000000000000000000000000000000000000000000000077963555344436500000000000000000000000000000000000000000000000000

Deployed Bytecode

0x608060405234801561000f575f5ffd5b506004361061042d575f3560e01c8063877887821161022c578063c3535b5211610135578063dd62ed3e116100bf578063ef8b30f711610084578063ef8b30f714610907578063f10d3d5d146109fa578063f2fde38b14610a6d578063fb791b0b14610a80578063fc7b9c1814610a93575f5ffd5b8063dd62ed3e1461097e578063e2c23610146109b6578063e521cb92146109cb578063ec811718146109de578063ecf70858146109f1575f5ffd5b8063cea55f5711610105578063cea55f571461092d578063d0ebdbe714610936578063d250f08814610949578063d764801314610958578063d905777e1461096b575f5ffd5b8063c3535b52146108eb578063c63d75b6146108f4578063c6e6f59214610907578063ce96cb771461091a575f5ffd5b8063aea70acc116101b6578063b6b1fb3b11610186578063b6b1fb3b146108a0578063ba087652146108aa578063bdc8144b146108bd578063c031d2fb146108d0578063c285cbb5146108d8575f5ffd5b8063aea70acc14610840578063af648c3d14610867578063b3d7f6b91461087a578063b460af941461088d575f5ffd5b806399530b06116101fc57806399530b06146107f7578063a0c9d3fc146107ff578063a4874d7714610812578063a4c9b9611461081a578063a9059cbb1461082d575f5ffd5b806387788782146107c35780638da5cb5b146107cc57806394bf804d146107dc57806395d89b41146107ef575f5ffd5b8063422327161161033957806364df049e116102c3578063715018a611610288578063715018a61461078057806374f9479b146107885780637a9f35861461079b578063821a622f146107ae5780638456cb59146107bb575f5ffd5b806364df049e1461070c578063691083631461071f5780636e553f651461073257806370897b231461074557806370a0823114610758575f5ffd5b806349185ab81161030957806349185ab8146106975780634cdad506146106b45780635b07871a146106c75780635c975abb146106d057806361f488bf146106e1575f5ffd5b8063422327161461064157806344b81396146106685780634707d00014610671578063481c6a7514610684575f5ffd5b80631fe4ba17116103ba57806338d52e0f1161038a57806338d52e0f1461054e57806339ebf823146105885780633ea9e124146106135780633f4ba83a14610626578063402d267d1461062e575f5ffd5b80631fe4ba17146104fc57806323b872dd1461050f5780632eb38ae014610522578063313ce56714610534575f5ffd5b8063095ea7b311610400578063095ea7b3146104a65780630a28a477146104b95780630dd21b6c146104cc578063175188e8146104e157806318160ddd146104f4575f5ffd5b806301e1d114146104315780630633b14a1461044c57806306fdde031461047e57806307a2d13a14610493575b5f5ffd5b610439610a9c565b6040519081526020015b60405180910390f35b61046e61045a366004613af8565b60026020525f908152604090205460ff1681565b6040519015158152602001610443565b610486610ab7565b6040516104439190613b35565b6104396104a1366004613b67565b610b47565b61046e6104b4366004613b7e565b610b58565b6104396104c7366004613b67565b610b6f565b6104df6104da366004613ba8565b610bd3565b005b6104df6104ef366004613af8565b611022565b600e54610439565b6104df61050a366004613b67565b611237565b61046e61051d366004613be0565b6112e9565b60095461046e90610100900460ff1681565b61053c61130c565b60405160ff9091168152602001610443565b7f00000000000000000000000029219dd400f2bf60e5a23d13be72b486d40388945b6040516001600160a01b039091168152602001610443565b6105d8610596366004613af8565b600a6020525f90815260409020805460018201546002830154600384015460048501546005860154600687015460079097015495969495939492939192909188565b604080519889526020890197909752958701949094526060860192909252608085015260a084015260c083015260e082015261010001610443565b6104df610621366004613b7e565b611357565b6104df611482565b61043961063c366004613af8565b611494565b6104397f000000000000000000000000000000000000000000000000000003824430f69e81565b61043960135481565b6104df61067f366004613c1e565b6114c6565b600154610570906001600160a01b031681565b61069f611537565b60408051928352602083019190915201610443565b6104396106c2366004613b67565b611549565b61043960085481565b5f54600160a01b900460ff1661046e565b6104396106ef366004613af8565b6001600160a01b03165f908152600a602052604090206005015490565b600354610570906001600160a01b031681565b6104df61072d366004613af8565b611589565b610439610740366004613c55565b611697565b6104df610753366004613b67565b61172c565b610439610766366004613af8565b6001600160a01b03165f908152600c602052604090205490565b6104df611791565b610439610796366004613af8565b6117a2565b6104df6107a9366004613c78565b6117ac565b60095461053c9060ff1681565b6104df611800565b61043960045481565b5f546001600160a01b0316610570565b6104396107ea366004613c55565b61186f565b6104866118f7565b610439611906565b6104df61080d366004613af8565b611918565b6104df61196b565b6104df610828366004613cb5565b611a0b565b61046e61083b366004613b7e565b611abe565b61053c7f000000000000000000000000000000000000000000000000000000000000000c81565b6104df610875366004613af8565b611acb565b610439610888366004613b67565b611b1b565b61043961089b366004613d80565b611b27565b6104396203f48081565b6104396108b8366004613d80565b611bca565b6104df6108cb366004613b67565b611c5c565b610439611cdf565b6104df6108e6366004613b7e565b611d32565b61043960125481565b610439610902366004613af8565b611e50565b610439610915366004613b67565b611e5d565b610439610928366004613af8565b611e68565b61043960065481565b6104df610944366004613af8565b611e8a565b610439670de0b6b3a764000081565b610439610966366004613af8565b611f02565b610439610979366004613af8565b611f0c565b61043961098c366004613c1e565b6001600160a01b039182165f908152600d6020908152604080832093909416825291909152205490565b6109be611f29565b6040516104439190613dbf565b6104df6109d9366004613af8565b611f88565b6104df6109ec366004613b7e565b612000565b61043960055481565b610a0d610a08366004613af8565b612153565b60405161044391905f61010082019050825182526020830151602083015260408301516040830152606083015160608301526080830151608083015260a083015160a083015260c083015160c083015260e083015160e083015292915050565b6104df610a7b366004613af8565b612209565b610570610a8e366004613b67565b612246565b61043960075481565b5f600754610aa861226e565b610ab29190613e1e565b905090565b6060600f8054610ac690613e31565b80601f0160208091040260200160405190810160405280929190818152602001828054610af290613e31565b8015610b3d5780601f10610b1457610100808354040283529160200191610b3d565b820191905f5260205f20905b815481529060010190602001808311610b2057829003601f168201915b5050505050905090565b5f610b52825f6122f8565b92915050565b5f33610b65818585612350565b5060019392505050565b5f5f610b7c83600161235d565b9050610b8661226e565b8311610b925792915050565b61271060085403610ba657505f1992915050565b610bc6612710600854612710610bbc9190613e63565b83919060016123ac565b9392505050565b50919050565b610bdb6123fb565b600954600a60ff90911610610c035760405163819a681f60e01b815260040160405180910390fd5b6001600160a01b03841615801590610c2457506001600160a01b0384163014155b8490610c5457604051634726455360e11b81526001600160a01b0390911660048201526024015b60405180910390fd5b506001600160a01b0384165f908152600a6020526040902054849015610c995760405163060f34a560e51b81526001600160a01b039091166004820152602401610c4b565b50836001600160a01b03166338d52e0f6040518163ffffffff1660e01b8152600401602060405180830381865afa158015610cd6573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610cfa9190613e76565b6001600160a01b0316306001600160a01b03166338d52e0f6040518163ffffffff1660e01b8152600401602060405180830381865afa158015610d3f573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610d639190613e76565b6001600160a01b031614306001600160a01b03166338d52e0f6040518163ffffffff1660e01b8152600401602060405180830381865afa158015610da9573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610dcd9190613e76565b856001600160a01b03166338d52e0f6040518163ffffffff1660e01b8152600401602060405180830381865afa158015610e09573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610e2d9190613e76565b9091610e5f57604051634e83a9b960e01b81526001600160a01b03928316600482015291166024820152604401610c4b565b505061271083600654610e729190613e1e565b111583600654610e829190613e1e565b90610ea357604051637698c0b760e11b8152600401610c4b91815260200190565b5080821115610ec5576040516328b7ed0d60e01b815260040160405180910390fd5b604080516101008101825242808252602080830187815283850192835260608401878152608085018781525f60a0870181815260c0880182815260e089018381526001600160a01b038f168452600a9097529882209751885593516001880155945160028701559051600386015551600485015551600584015592516006808401919091559251600790920191909155815485929190610f66908490613e1e565b90915550849050600b610f7b6001600a613e91565b60ff1681548110610f8e57610f8e613eaa565b5f918252602082200180546001600160a01b0319166001600160a01b03939093169290921790915560098054909190610fc99060ff16613ebe565b91906101000a81548160ff021916908360ff160217905550610fe9612427565b6040516001600160a01b038516907f3f008fd510eae7a9e7bee13513d7b83bef8003d488b5a3d0b0da4de71d6846f1905f90a250505050565b5f546001600160a01b031633148061104457506001546001600160a01b031633145b339061106f57604051633b2495f160e01b81526001600160a01b039091166004820152602401610c4b565b506001600160a01b0381165f908152600a6020526040902054819081906110b55760405163e5c0758760e01b81526001600160a01b039091166004820152602401610c4b565b506001600160a01b0382165f908152600a6020526040902060010154156110ef5760405163424ae5ab60e01b815260040160405180910390fd5b6001600160a01b0382165f908152600a6020526040902060050154156111285760405163a617d47160e01b815260040160405180910390fd5b5f5b600a60ff8216101561123157826001600160a01b0316600b8260ff168154811061115657611156613eaa565b5f918252602090912001546001600160a01b031603611229575f600b8260ff168154811061118657611186613eaa565b5f91825260208083209190910180546001600160a01b0319166001600160a01b039485161790559185168152600a90915260408120819055600980549091906111d19060ff16613edc565b91906101000a81548160ff021916908360ff1602179055506111f1612427565b6040516001600160a01b038416907f09a1db4b80c32706328728508c941a6b954f31eb5affd32f236c1fd405f8fea4905f90a2505050565b60010161112a565b505b5050565b5f546001600160a01b031633148061125957506001546001600160a01b031633145b339061128457604051633b2495f160e01b81526001600160a01b039091166004820152602401610c4b565b50806127108111156112ac57604051631920afa360e11b8152600401610c4b91815260200190565b5060088190556040518181527ff6c3c65ea16f3d6351596faef627640721d4bbe1c42e5f06ae3b40f5a399cfc6906020015b60405180910390a150565b5f336112f6858285612518565b61130185858561257a565b506001949350505050565b5f610ab27f000000000000000000000000000000000000000000000000000000000000000c7f0000000000000000000000000000000000000000000000000000000000000006613ef7565b5f546001600160a01b031633148061137957506001546001600160a01b031633145b33906113a457604051633b2495f160e01b81526001600160a01b039091166004820152602401610c4b565b506001600160a01b0382165f908152600a6020526040902054829081906113ea5760405163e5c0758760e01b81526001600160a01b039091166004820152602401610c4b565b506001600160a01b0383165f908152600a6020526040902060030154821015611426576040516328b7ed0d60e01b815260040160405180910390fd5b6001600160a01b0383165f818152600a602052604090819020600401849055517fe6fd3fad502c8a253ce72fb45e7f218dde705007fa4d961e164e5117ff4e31f3906114759085815260200190565b60405180910390a2505050565b61148a6123fb565b6114926125d7565b565b5f6005546114a0610a9c565b106114ac57505f919050565b6114b4610a9c565b600554610b529190613e63565b919050565b5f546001600160a01b03163314806114e857506001546001600160a01b031633145b806115015750335f9081526002602052604090205460ff165b339061152c57604051631f851be960e31b81526001600160a01b039091166004820152602401610c4b565b50611233828261262b565b5f5f61154161270b565b915091509091565b5f5f611555835f6122f8565b905061155f61226e565b811161156b5792915050565b610bc660085461271061157e9190613e63565b82906127105f6123ac565b5f546001600160a01b03163314806115ab57506001546001600160a01b031633145b33906115d657604051633b2495f160e01b81526001600160a01b039091166004820152602401610c4b565b506001600160a01b0381165f908152600a60205260409020548190819061161c5760405163e5c0758760e01b81526001600160a01b039091166004820152602401610c4b565b506001600160a01b0382165f908152600a6020526040812060010154600680549192909161164b908490613e63565b90915550506001600160a01b0382165f818152600a6020526040808220600101829055517f43328b6eb7f0f6711a295cfc22368af8227bf60b67a6f6a5454f6168726d3e259190a25050565b5f6116a0612847565b600954610100900460ff16156116c957604051630c15f68f60e41b815260040160405180910390fd5b6116d1612871565b5f6116db83611494565b90508284828082111561170457604051633c8097d960e11b8152600401610c4b93929190613f10565b5050505f61171185611e5d565b905061171f3385878461289b565b915050610b526001601155565b6117346123fb565b806107d081111561175b57604051633de00b8560e01b8152600401610c4b91815260200190565b5060048190556040518181527fceb20f7f0b19335681096ee1eaa9bb2a6ef5a9a69ba48b6b488e7b7eff2ef04d906020016112de565b6117996123fb565b6114925f6129aa565b5f610b52826129f9565b6117b4612847565b335f818152600a602052604090205481906117ee5760405163e5c0758760e01b81526001600160a01b039091166004820152602401610c4b565b506117fa848484612a91565b50505050565b5f546001600160a01b031633148061182257506001546001600160a01b031633145b8061183b5750335f9081526002602052604090205460ff165b339061186657604051631f851be960e31b81526001600160a01b039091166004820152602401610c4b565b50611492612d48565b5f611878612847565b600954610100900460ff16156118a157604051630c15f68f60e41b815260040160405180910390fd5b6118a9612871565b5f6118b383611e50565b9050828482808211156118dc5760405163284ff66760e01b8152600401610c4b93929190613f10565b5050505f6118e985611b1b565b905061171f3385838861289b565b606060108054610ac690613e31565b5f610ab2670de0b6b3a7640000610b47565b6119206123fb565b6001600160a01b0381165f81815260026020526040808220805460ff19166001179055517fd0cd09d977e63f0acaf30ce79e6479c90d7ea56a41a0849ac60f5440ac608a159190a250565b5f546001600160a01b031633148061198d57506001546001600160a01b031633145b806119a65750335f9081526002602052604090205460ff165b33906119d157604051631f851be960e31b81526001600160a01b039091166004820152602401610c4b565b506009805461ff0019166101001790556040517f762e62217e4ec85388fb7cba32a604823803326c60ed9f962a2b86512ca78d8c905f90a1565b5f546001600160a01b0316331480611a2d57506001546001600160a01b031633145b3390611a5857604051633b2495f160e01b81526001600160a01b039091166004820152602401610c4b565b50611a6281612d8a565b611a7f5760405163660e947360e01b815260040160405180910390fd5b8051611a9290600b906020840190613a6b565b506040517f5c2915d8834f24b89a98a55fe6de71ad6dad79ef0b3052ff68231dbfcb011c88905f90a150565b5f33610b6581858561257a565b611ad36123fb565b6001600160a01b0381165f81815260026020526040808220805460ff19169055517f0c92d12d8037dd6d77aed8d12addd54d5eb2a6801541a1bf87c9822e78eea4219190a250565b5f610b528260016122f8565b5f611b30612847565b611b38612871565b5f611b4283611e68565b905082858280821115611b6b57604051633fa733bb60e21b8152600401610c4b93929190613f10565b5050505f611b7886610b6f565b90505f611b893387878a865f612eec565b91508290508181811115611bb957604051630a8f6c8d60e41b815260048101929092526024820152604401610c4b565b50909350505050610bc66001601155565b5f611bd3612847565b611bdb612871565b5f611be583611f0c565b905082858280821115611c0e57604051632e52afbb60e21b8152600401610c4b93929190613f10565b5050505f611c1b86611549565b90505f611c2d338787858b6001612eec565b509050818181811015611bb957604051630a8f6c8d60e41b815260048101929092526024820152604401610c4b565b5f546001600160a01b0316331480611c7e57506001546001600160a01b031633145b3390611ca957604051633b2495f160e01b81526001600160a01b039091166004820152602401610c4b565b5060058190556040518181527f5d2e73196f8ba1b44e887e2bcc9bcd1f3e657add341d4a0a632ffff00d6903f2906020016112de565b5f611ce8612847565b335f818152600a60205260409020548190611d225760405163e5c0758760e01b81526001600160a01b039091166004820152602401610c4b565b50611d2b613216565b91505b5090565b5f546001600160a01b0316331480611d5457506001546001600160a01b031633145b3390611d7f57604051633b2495f160e01b81526001600160a01b039091166004820152602401610c4b565b506001600160a01b0382165f908152600a602052604090205482908190611dc55760405163e5c0758760e01b81526001600160a01b039091166004820152602401610c4b565b506001600160a01b0383165f908152600a6020526040902060040154821115611e01576040516328b7ed0d60e01b815260040160405180910390fd5b6001600160a01b0383165f818152600a602052604090819020600301849055517fe0dccc38125c57629849ddb1eef292a3e47f8efdba994dc4efe9637c6b68010a906114759085815260200190565b5f610b5261091583611494565b5f610b52825f61235d565b6001600160a01b0381165f908152600c6020526040812054610b52905f6122f8565b611e926123fb565b6001600160a01b038116611eb95760405163d92e233d60e01b815260040160405180910390fd5b600180546001600160a01b0319166001600160a01b0383169081179091556040517f60a0f5b9f9e81e98216071b85826681c796256fe3d1354ecb675580fba64fa69905f90a250565b5f610b52826132cd565b6001600160a01b0381165f908152600c6020526040812054610b52565b6060600b805480602002602001604051908101604052809291908181526020018280548015610b3d57602002820191905f5260205f20905b81546001600160a01b03168152600190910190602001808311611f61575050505050905090565b611f906123fb565b6001600160a01b038116611fb75760405163d92e233d60e01b815260040160405180910390fd5b600380546001600160a01b0319166001600160a01b0383169081179091556040517f5034c7ac62cd0031ddf2f1dcde2858132dd886d61032c9b2d4a99f0490e80ee4905f90a250565b5f546001600160a01b031633148061202257506001546001600160a01b031633145b339061204d57604051633b2495f160e01b81526001600160a01b039091166004820152602401610c4b565b506001600160a01b0382165f908152600a6020526040902054829081906120935760405163e5c0758760e01b81526001600160a01b039091166004820152602401610c4b565b506001600160a01b0383165f908152600a602052604081206001015460065484916120bd91613e63565b6120c79190613e1e565b9050806127108111156120f057604051637698c0b760e11b8152600401610c4b91815260200190565b5060068190556001600160a01b0384165f818152600a602052604090819020600101859055517f64b9074a3c4edf892069f1a719f7b1de2f307df3b29626e0b381d819420419ef906121459086815260200190565b60405180910390a250505050565b6121936040518061010001604052805f81526020015f81526020015f81526020015f81526020015f81526020015f81526020015f81526020015f81525090565b506001600160a01b03165f908152600a6020908152604091829020825161010081018452815481526001820154928101929092526002810154928201929092526003820154606082015260048201546080820152600582015460a0820152600682015460c082015260079091015460e082015290565b6122116123fb565b6001600160a01b03811661223a57604051631e4fbdf760e01b81525f6004820152602401610c4b565b612243816129aa565b50565b600b8181548110612255575f80fd5b5f918252602090912001546001600160a01b0316905081565b5f7f00000000000000000000000029219dd400f2bf60e5a23d13be72b486d40388946040516370a0823160e01b81523060048201526001600160a01b0391909116906370a0823190602401602060405180830381865afa1580156122d4573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610ab29190613f31565b5f610bc66123046133c0565b61230f906001613e1e565b61233a7f000000000000000000000000000000000000000000000000000000000000000c600a61402b565b600e546123479190613e1e565b859190856123ac565b61123183838360016133db565b5f610bc661238c7f000000000000000000000000000000000000000000000000000000000000000c600a61402b565b600e546123999190613e1e565b6123a16133c0565b612347906001613e1e565b5f5f6123b986868661349f565b90506123c48361355e565b80156123df57505f84806123da576123da614039565b868809115b156123f2576123ef600182613e1e565b90505b95945050505050565b5f546001600160a01b031633146114925760405163118cdaa760e01b8152336004820152602401610c4b565b5f805b600a60ff82161015611233575f600b8260ff168154811061244d5761244d613eaa565b5f918252602090912001546001600160a01b03169050806124785761247183613ebe565b925061250f565b60ff83161561250f5780600b61248e8585613e91565b60ff16815481106124a1576124a1613eaa565b905f5260205f20015f6101000a8154816001600160a01b0302191690836001600160a01b031602179055505f600b8360ff16815481106124e3576124e3613eaa565b905f5260205f20015f6101000a8154816001600160a01b0302191690836001600160a01b031602179055505b5060010161242a565b6001600160a01b038381165f908152600d60209081526040808320938616835292905220545f1981146117fa578181101561256c57828183604051637dc7a0d960e11b8152600401610c4b93929190613f10565b6117fa84848484035f6133db565b6001600160a01b0383166125a357604051634b637e8f60e11b81525f6004820152602401610c4b565b6001600160a01b0382166125cc5760405163ec442f0560e01b81525f6004820152602401610c4b565b61123183838361358a565b6125df61369d565b5f805460ff60a01b191690557f5db9ee0a495bf2e6ff9c91a7834c1ba4fdd244a5e8aa4e537bd38aeae4b073aa335b6040516001600160a01b03909116815260200160405180910390a1565b7f00000000000000000000000029219dd400f2bf60e5a23d13be72b486d40388946001600160a01b0316826001600160a01b03161415829061268c57604051634726455360e11b81526001600160a01b039091166004820152602401610c4b565b506040516370a0823160e01b81523060048201525f906001600160a01b038416906370a0823190602401602060405180830381865afa1580156126d1573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906126f59190613f31565b90506112316001600160a01b03841683836136c6565b6009545f90819060ff16810361272357505f91829150565b5f80805b60095460ff908116908216101561283d575f600b8260ff168154811061274f5761274f613eaa565b5f918252602090912001546001600160a01b0316905080612770575061283d565b6001600160a01b0381165f908152600a602052604081206005015490036127975750612835565b5f5f826001600160a01b03166349185ab86040518163ffffffff1660e01b81526004016040805180830381865afa1580156127d4573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906127f8919061404d565b9150915061281960045461271061280f9190613e63565b839061271061349f565b6128239087613e1e565b955061282f8186613e1e565b94505050505b600101612727565b5090939092509050565b5f54600160a01b900460ff16156114925760405163d93c066560e01b815260040160405180910390fd5b60026011540361289457604051633ee5aeb560e01b815260040160405180910390fd5b6002601155565b6001600160a01b038316158015906128bc57506001600160a01b0383163014155b83906128e757604051634726455360e11b81526001600160a01b039091166004820152602401610c4b565b50818061290a5760405163135ee08960e31b8152600401610c4b91815260200190565b506129448430847f00000000000000000000000029219dd400f2bf60e5a23d13be72b486d40388945b6001600160a01b0316929190613725565b61294e838261375e565b826001600160a01b0316846001600160a01b03167fdcbc1c05240f31ff3ad067ef1ee35ce4997762752e3a095284754544f4c709d7848460405161299c929190918252602082015260400190565b60405180910390a350505050565b5f80546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b5f6006545f03612a2157506001600160a01b03165f908152600a602052604090206005015490565b5f612a51612a2d610a9c565b6001600160a01b0385165f908152600a60205260409020600101549061271061349f565b6001600160a01b0384165f908152600a6020526040902060050154909150818111612a7f57505f9392505050565b612a898282613e63565b949350505050565b5f7f00000000000000000000000029219dd400f2bf60e5a23d13be72b486d40388946040516370a0823160e01b81523360048201526001600160a01b0391909116906370a0823190602401602060405180830381865afa158015612af7573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190612b1b9190613f31565b90505f83118015612b2b57505f82115b15612b4957604051635b1ba80560e11b815260040160405180910390fd5b612b538385613e1e565b81101581612b618587613e1e565b9091612b895760405163cf47918160e01b815260048101929092526024820152604401610c4b565b505f9050808315612b9e57612b9e3385613792565b8415612bc457600454612bb590869061271061349f565b9050612bc18186613e63565b91505b5f612bd787612bd2336129f9565b613847565b90508015612c1e57335f908152600a602052604081206005018054839290612c00908490613e63565b925050819055508060075f828254612c189190613e63565b90915550505b5f83612c2861385c565b612c329190613e1e565b905085811115612c4e57612c468682613e63565b601355612c53565b5f6013555b335f908152600a60205260408120426002909101819055601255612c778884613e1e565b1115612cb257612cb23330612c8c8a86613e1e565b7f00000000000000000000000029219dd400f2bf60e5a23d13be72b486d4038894612933565b8215612cfb57600354612cfb906001600160a01b0316847f00000000000000000000000029219dd400f2bf60e5a23d13be72b486d40388945b6001600160a01b031691906136c6565b604080518381526020810186905290810187905233907f6fbdf847c78f04eac7a7a77e1073af6eeec604ee0b495240579dccf57fcf2d889060600160405180910390a25050505050505050565b612d50612847565b5f805460ff60a01b1916600160a01b1790557f62e78cea01bee320cd4e420270b5ea74000d11b0c9f74754ebdbfc544b05a25861260e3390565b80515f90600a14612d9c57505f919050565b5f5b600a60ff82161015612ee3575f838260ff1681518110612dc057612dc0613eaa565b602002602001015190505f6001600160a01b0316816001600160a01b031614612e78576001600160a01b0381165f908152600a60205260408120549003612e0a57505f9392505050565b5f5b600a60ff82161015612e72578060ff168360ff1614158015612e5b5750848160ff1681518110612e3e57612e3e613eaa565b60200260200101516001600160a01b0316826001600160a01b0316145b15612e6a57505f949350505050565b600101612e0c565b50612eda565b5f612e84836001613ef7565b90505b600a60ff82161015611301575f6001600160a01b0316858260ff1681518110612eb257612eb2613eaa565b60200260200101516001600160a01b031614612ed257505f949350505050565b600101612e87565b50600101612d9e565b50600192915050565b5f808380612f105760405163135ee08960e31b8152600401610c4b91815260200190565b50856001600160a01b0316886001600160a01b031614612f3557612f35868986612518565b5f83612f415785612f4b565b612f4b855f6122f8565b9050612f5561226e565b811115613158575f5b600b5460ff821611613156575f600b8260ff1681548110612f8157612f81613eaa565b5f918252602090912001546001600160a01b0316905080151583612fa361226e565b9091612fcb5760405163a17e11d560e01b815260048101929092526024820152604401610c4b565b50505f613003612fd961226e565b612fe39086613e63565b6001600160a01b0384165f908152600a6020526040902060050154613847565b9050805f03613013575050613146565b604051632e1a7d4d60e01b8152600481018290525f906001600160a01b03841690632e1a7d4d906024016020604051808303815f875af1158015613059573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061307d9190613f31565b6001600160a01b0384165f908152600a60205260408120600501805492935083929091906130ac908490613e63565b925050819055508060075f8282546130c49190613e63565b92505081905550826001600160a01b031663c0275d5b6040518163ffffffff1660e01b81526004015f604051808303815f87803b158015613103575f5ffd5b505af1158015613115573d5f5f3e3d5ffd5b50505050871561312c57613129895f6122f8565b94505b61313461226e565b851161314257505050613156565b5050505b61314f81613ebe565b9050612f5e565b505b5f8461316e5761316982600161235d565b613170565b855b905061317c88826138d4565b6131a789837f00000000000000000000000029219dd400f2bf60e5a23d13be72b486d4038894612ceb565b876001600160a01b0316896001600160a01b03168b6001600160a01b03167ffbde797d201c681b91056529119e0b02407c7bb96a4a2c75c01fc9667232c8db85856040516131ff929190918252602082015260400190565b60405180910390a490999098509650505050505050565b5f5f613221336132cd565b905080156114c157335f908152600a60205260408120600501805483929061324a908490613e1e565b925050819055508060075f8282546132629190613e1e565b90915550613293905033827f00000000000000000000000029219dd400f2bf60e5a23d13be72b486d4038894612ceb565b60405181815233907f4bcdc8c4e44b31be744ac2494ab91b09234c21e24fa1cb0a1b4de3a85ef74d419060200160405180910390a2919050565b5f5f6132d7610a9c565b6006549091505f906132ec908361271061349f565b6007546001600160a01b0386165f908152600a6020526040812060010154929350909161331c908561271061349f565b6001600160a01b0387165f908152600a60205260409020600581015460038201546004909201549293509183831015806133565750858510155b1561336957505f98975050505050505050565b5f6133748486613e63565b90505f6133818789613e63565b905061338d8282613847565b9150838210156133a757505f9a9950505050505050505050565b6133b18284613847565b9b9a5050505050505050505050565b5f6133c961385c565b6133d1610a9c565b610ab29190613e63565b6001600160a01b0384166134045760405163e602df0560e01b81525f6004820152602401610c4b565b6001600160a01b03831661342d57604051634a1406b160e11b81525f6004820152602401610c4b565b6001600160a01b038085165f908152600d6020908152604080832093871683529290522082905580156117fa57826001600160a01b0316846001600160a01b03167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b9258460405161299c91815260200190565b5f838302815f1985870982811083820303915050805f036134d3578382816134c9576134c9614039565b0492505050610bc6565b8084116134f35760405163227bc15360e01b815260040160405180910390fd5b5f848688095f868103871696879004966002600389028118808a02820302808a02820302808a02820302808a02820302808a02820302808a02909103029181900381900460010186841190950394909402919094039290920491909117919091029150509392505050565b5f60028260038111156135735761357361406f565b61357d9190614083565b60ff166001149050919050565b6001600160a01b0383166135b45780600e5f8282546135a99190613e1e565b909155506136119050565b6001600160a01b0383165f908152600c6020526040902054818110156135f35783818360405163391434e360e21b8152600401610c4b93929190613f10565b6001600160a01b0384165f908152600c602052604090209082900390555b6001600160a01b03821661362d57600e8054829003905561364b565b6001600160a01b0382165f908152600c602052604090208054820190555b816001600160a01b0316836001600160a01b03167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef8360405161369091815260200190565b60405180910390a3505050565b5f54600160a01b900460ff1661149257604051638dfc202b60e01b815260040160405180910390fd5b6040516001600160a01b0383811660248301526044820183905261123191859182169063a9059cbb906064015b604051602081830303815290604052915060e01b6020820180516001600160e01b038381831617835250505050613908565b6040516001600160a01b0384811660248301528381166044830152606482018390526117fa9186918216906323b872dd906084016136f3565b6001600160a01b0382166137875760405163ec442f0560e01b81525f6004820152602401610c4b565b6112335f838361358a565b6001600160a01b0382165f908152600a60205260409020600501548111156137cd576040516345f8381360e11b815260040160405180910390fd5b6001600160a01b0382165f908152600a6020526040812060070180548392906137f7908490613e1e565b90915550506001600160a01b0382165f908152600a602052604081206005018054839290613826908490613e63565b925050819055508060075f82825461383e9190613e63565b90915550505050565b5f8183106138555781610bc6565b5090919050565b5f5f7f000000000000000000000000000000000000000000000000000003824430f69e6012544261388d9190613e63565b61389791906140b0565b9050670de0b6b3a76400008110156138cd576013546138c0908290670de0b6b3a764000061349f565b601354611d2b9190613e63565b5f91505090565b6001600160a01b0382166138fd57604051634b637e8f60e11b81525f6004820152602401610c4b565b611233825f8361358a565b5f61391c6001600160a01b03841683613969565b905080515f1415801561394057508080602001905181019061393e91906140c7565b155b1561123157604051635274afe760e01b81526001600160a01b0384166004820152602401610c4b565b6060610bc683835f845f5f856001600160a01b0316848660405161398d91906140e6565b5f6040518083038185875af1925050503d805f81146139c7576040519150601f19603f3d011682016040523d82523d5f602084013e6139cc565b606091505b50915091506139dc8683836139e6565b9695505050505050565b6060826139fb576139f682613a42565b610bc6565b8151158015613a1257506001600160a01b0384163b155b15613a3b57604051639996b31560e01b81526001600160a01b0385166004820152602401610c4b565b5080610bc6565b805115613a525780518082602001fd5b604051630a12f52160e11b815260040160405180910390fd5b828054828255905f5260205f20908101928215613abe579160200282015b82811115613abe57825182546001600160a01b0319166001600160a01b03909116178255602090920191600190910190613a89565b50611d2e9291505b80821115611d2e575f8155600101613ac6565b6001600160a01b0381168114612243575f5ffd5b80356114c181613ad9565b5f60208284031215613b08575f5ffd5b8135610bc681613ad9565b5f5b83811015613b2d578181015183820152602001613b15565b50505f910152565b602081525f8251806020840152613b53816040850160208701613b13565b601f01601f19169190910160400192915050565b5f60208284031215613b77575f5ffd5b5035919050565b5f5f60408385031215613b8f575f5ffd5b8235613b9a81613ad9565b946020939093013593505050565b5f5f5f5f60808587031215613bbb575f5ffd5b8435613bc681613ad9565b966020860135965060408601359560600135945092505050565b5f5f5f60608486031215613bf2575f5ffd5b8335613bfd81613ad9565b92506020840135613c0d81613ad9565b929592945050506040919091013590565b5f5f60408385031215613c2f575f5ffd5b8235613c3a81613ad9565b91506020830135613c4a81613ad9565b809150509250929050565b5f5f60408385031215613c66575f5ffd5b823591506020830135613c4a81613ad9565b5f5f5f60608486031215613c8a575f5ffd5b505081359360208301359350604090920135919050565b634e487b7160e01b5f52604160045260245ffd5b5f60208284031215613cc5575f5ffd5b813567ffffffffffffffff811115613cdb575f5ffd5b8201601f81018413613ceb575f5ffd5b803567ffffffffffffffff811115613d0557613d05613ca1565b8060051b604051601f19603f830116810181811067ffffffffffffffff82111715613d3257613d32613ca1565b604052918252602081840181019290810187841115613d4f575f5ffd5b6020850194505b83851015613d7557613d6785613aed565b815260209485019401613d56565b509695505050505050565b5f5f5f60608486031215613d92575f5ffd5b833592506020840135613da481613ad9565b91506040840135613db481613ad9565b809150509250925092565b602080825282518282018190525f918401906040840190835b81811015613dff5783516001600160a01b0316835260209384019390920191600101613dd8565b509095945050505050565b634e487b7160e01b5f52601160045260245ffd5b80820180821115610b5257610b52613e0a565b600181811c90821680613e4557607f821691505b602082108103610bcd57634e487b7160e01b5f52602260045260245ffd5b81810381811115610b5257610b52613e0a565b5f60208284031215613e86575f5ffd5b8151610bc681613ad9565b60ff8281168282160390811115610b5257610b52613e0a565b634e487b7160e01b5f52603260045260245ffd5b5f60ff821660ff8103613ed357613ed3613e0a565b60010192915050565b5f60ff821680613eee57613eee613e0a565b5f190192915050565b60ff8181168382160190811115610b5257610b52613e0a565b6001600160a01b039390931683526020830191909152604082015260600190565b5f60208284031215613f41575f5ffd5b5051919050565b6001815b6001841115613f8357808504811115613f6757613f67613e0a565b6001841615613f7557908102905b60019390931c928002613f4c565b935093915050565b5f82613f9957506001610b52565b81613fa557505f610b52565b8160018114613fbb5760028114613fc557613fe1565b6001915050610b52565b60ff841115613fd657613fd6613e0a565b50506001821b610b52565b5060208310610133831016604e8410600b8410161715614004575081810a610b52565b6140105f198484613f48565b805f190482111561402357614023613e0a565b029392505050565b5f610bc660ff841683613f8b565b634e487b7160e01b5f52601260045260245ffd5b5f5f6040838503121561405e575f5ffd5b505080516020909101519092909150565b634e487b7160e01b5f52602160045260245ffd5b5f60ff8316806140a157634e487b7160e01b5f52601260045260245ffd5b8060ff84160691505092915050565b8082028115828204841417610b5257610b52613e0a565b5f602082840312156140d7575f5ffd5b81518015158114610bc6575f5ffd5b5f82516140f7818460208701613b13565b919091019291505056fea26469706673582212204e695dd3a83dc12d398f9fcb85a9480fab4d5e83b9cbe84928b15bbb0fdc9c1664736f6c634300081b0033

Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)

00000000000000000000000029219dd400f2bf60e5a23d13be72b486d403889400000000000000000000000075cb5d555933fe86e0ac8975a623acb5cec13e28000000000000000000000000d132631a63af5c616e60606025c8e5871addf76f00000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000000135969656c642043686173696e672055534443650000000000000000000000000000000000000000000000000000000000000000000000000000000000000000077963555344436500000000000000000000000000000000000000000000000000

-----Decoded View---------------
Arg [0] : _asset (address): 0x29219dd400f2Bf60E5a23d13Be72B486D4038894
Arg [1] : _manager (address): 0x75cb5d555933fe86E0ac8975A623aCb5CEC13E28
Arg [2] : _protocolFeeRecipient (address): 0xd132631A63af5C616e60606025C8e5871ADdF76f
Arg [3] : _name (string): Yield Chasing USDCe
Arg [4] : _symbol (string): ycUSDCe

-----Encoded View---------------
9 Constructor Arguments found :
Arg [0] : 00000000000000000000000029219dd400f2bf60e5a23d13be72b486d4038894
Arg [1] : 00000000000000000000000075cb5d555933fe86e0ac8975a623acb5cec13e28
Arg [2] : 000000000000000000000000d132631a63af5c616e60606025c8e5871addf76f
Arg [3] : 00000000000000000000000000000000000000000000000000000000000000a0
Arg [4] : 00000000000000000000000000000000000000000000000000000000000000e0
Arg [5] : 0000000000000000000000000000000000000000000000000000000000000013
Arg [6] : 5969656c642043686173696e6720555344436500000000000000000000000000
Arg [7] : 0000000000000000000000000000000000000000000000000000000000000007
Arg [8] : 7963555344436500000000000000000000000000000000000000000000000000


[ Download: CSV Export  ]
[ Download: CSV Export  ]

A token is a representation of an on-chain or off-chain asset. The token page shows information such as price, total supply, holders, transfers and social links. Learn more about this page in our Knowledge Base.