ETH Price: $2,673.47 (-2.62%)

Contract

0x443C30ed77862966c1fA2aeb7978481f96c5357d
 

Overview

ETH Balance

0 ETH

Eth Value

$0.00

Multichain Info

No addresses found
Transaction Hash
Method
Block
From
To
0x60806040154635082022-09-03 5:21:59723 days ago1662182519IN
 Create: Vault
0 ETH0.0612982212

View more zero value Internal Transactions in Advanced View mode

Advanced mode:
Loading...
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
Vault

Compiler Version
v0.8.3+commit.8d00100c

Optimization Enabled:
Yes with 200 runs

Other Settings:
default evmVersion
File 1 of 23 : Vault.sol
// SPDX-License-Identifier: AGPL-3.0-or-later

pragma solidity ^0.8.0;
/**
 * @title  Vault Contract
 * @notice The Vault contract defines the storage for the Vault contracts
 * @author BankOfChain Protocol Inc
 */
import "@openzeppelin/contracts-upgradeable/token/ERC20/utils/SafeERC20Upgradeable.sol";
import "@openzeppelin/contracts/utils/math/Math.sol";

import "./VaultStorage.sol";
import "../exchanges/IExchangeAggregator.sol";

contract Vault is VaultStorage {
    using StableMath for uint256;
    using SafeERC20Upgradeable for IERC20Upgradeable;
    using EnumerableSet for EnumerableSet.AddressSet;
    using IterableIntMap for IterableIntMap.AddressToIntMap;

    function initialize(
        address _accessControlProxy,
        address _treasury,
        address _exchangeManager,
        address _valueInterpreter
    ) public initializer {
        _initAccessControl(_accessControlProxy);

        treasury = _treasury;
        exchangeManager = _exchangeManager;
        valueInterpreter = _valueInterpreter;

        rebasePaused = false;
        // Initial redeem fee of 0 basis points
        redeemFeeBps = 0;
        // 1 / 1000e4
        rebaseThreshold = 1;
        // one week
        maxTimestampBetweenTwoReported = 604800;
        underlyingUnitsPerShare = 1e18;
        minCheckedStrategyTotalDebt = 1000e18;
    }

    modifier whenNotEmergency() {
        require(!emergencyShutdown, "ES");
        _;
    }

    modifier whenNotAdjustPosition() {
        require(!adjustPositionPeriod, "AD");
        _;
    }

    /**
     * @dev Verifies that the rebasing is not paused.
     */
    modifier whenNotRebasePaused() {
        require(!rebasePaused, "RP");
        _;
    }

    modifier isActiveStrategy(address _strategy) {
        checkActiveStrategy(_strategy);
        _;
    }

    /// @notice Version of vault
    function getVersion() external pure returns (string memory) {
        return "1.1.0";
    }

    /// @notice Minting USDi supported assets
    function getSupportAssets() external view returns (address[] memory) {
        return assetSet.values();
    }

    /// @notice Check '_asset' is supported or not
    function checkIsSupportAsset(address _asset) public view {
        require(assetSet.contains(_asset), "The asset not support");
    }

    /// @notice Assets held by Vault
    function getTrackedAssets() external view returns (address[] memory) {
        return _getTrackedAssets();
    }

    /// @notice Vault holds asset value directly in USD (1e18)
    function valueOfTrackedTokens() external view returns (uint256) {
        return _totalValueInVault();
    }

    /// @notice Vault and vault buffer holds asset value directly in USD
    function valueOfTrackedTokensIncludeVaultBuffer() external view returns (uint256) {
        return _totalAssetInVaultAndVaultBuffer();
    }

    /// @notice Vault total asset in USD(1e18)
    function totalAssets() external view returns (uint256) {
        return _totalValueInVault() + totalDebt;
    }

    /// @notice Vault and vault buffer total asset in USD
    function totalAssetsIncludeVaultBuffer() external view returns (uint256) {
        return _totalAssetInVaultAndVaultBuffer() + totalDebt;
    }

    /// @notice Vault total value(by chainlink price) in USD(1e18)
    function totalValue() external view returns (uint256) {
        return _totalValueInVault() + totalValueInStrategies();
    }

    /**
     * @dev Internal to calculate total value of all assets held in Vault.
     * @return Total value(by chainlink price) in USD (1e18)
     */
    function totalValueInVault() external view returns (uint256) {
        return _totalValueInVault();
    }

    /**
     * @dev Internal to calculate total value of all assets held in Strategies.
     * @return _value Total value(by chainlink price) in USD (1e18)
     */
    function totalValueInStrategies() public view returns (uint256 _value) {
        uint256 _strategyLength = strategySet.length();
        for (uint256 i = 0; i < _strategyLength; i++) {
            uint256 estimatedTotalAssets = IStrategy(strategySet.at(i)).estimatedTotalAssets();
            if (estimatedTotalAssets > 0) {
                _value = _value + estimatedTotalAssets;
            }
        }
    }

    /**
     * @notice Get pegToken price in USD
     * @return  price in USD (1e18)
     */
    function getPegTokenPrice() external view returns (uint256) {
        uint256 _totalSupply = IPegToken(pegTokenAddress).totalSupply();
        uint256 _pegTokenPrice = 1e18;
        if (_totalSupply > 0) {
            address[] memory _trackedAssets = _getTrackedAssets();
            uint256 _trackedAssetsLength = _trackedAssets.length;
            uint256[] memory _assetPrices = new uint256[](_trackedAssetsLength);
            uint256[] memory _assetDecimals = new uint256[](_trackedAssetsLength);
            uint256 _totalValueInVault = 0;
            uint256 _totalTransferValue = 0;
            for (uint256 i = 0; i < _trackedAssetsLength; i++) {
                address _trackedAsset = _trackedAssets[i];
                uint256 _balance = _balanceOfToken(_trackedAsset, address(this));
                if (_balance > 0) {
                    _totalValueInVault =
                    _totalValueInVault +
                    _calculateAssetValue(_assetPrices, _assetDecimals, i, _trackedAsset, _balance);
                }
                _balance = transferFromVaultBufferAssetsMap[_trackedAsset];
                if (_balance > 0) {
                    _totalTransferValue =
                    _totalTransferValue +
                    _calculateAssetValue(_assetPrices, _assetDecimals, i, _trackedAsset, _balance);
                }
            }
            _pegTokenPrice = ((_totalValueInVault + totalDebt - _totalTransferValue) * 1e18) / _totalSupply;
        }
        return _pegTokenPrice;
    }

    /// @notice All strategies
    function getStrategies() external view returns (address[] memory) {
        return strategySet.values();
    }

    /// @notice Check '_strategy' is active or not
    function checkActiveStrategy(address _strategy) public view {
        require(strategySet.contains(_strategy), "strategy not exist");
    }

    /// @notice estimate Minting pending share
    /// @param _assets Address of the asset being deposited
    /// @param _amounts Amount of the asset being deposited
    /// @dev Support single asset or multi-assets
    /// @return _pending Share Amount
    function estimateMint(address[] memory _assets, uint256[] memory _amounts)
        public
        view
        returns (uint256)
    {
        return _estimateMint(_assets, _amounts);
    }

    /// @notice Minting share with stablecoins
    /// @param _assets Address of the asset being deposited
    /// @param _amounts Amount of the asset being deposited
    /// @dev Support single asset or multi-assets
    /// @return The amount of share minted
    function mint(
        address[] memory _assets,
        uint256[] memory _amounts,
        uint256 _minimumAmount
    ) external whenNotEmergency whenNotAdjustPosition nonReentrant returns (uint256) {
        uint256 _shareAmount = _estimateMint(_assets, _amounts);
        if (_minimumAmount > 0) {
            require(_shareAmount >= _minimumAmount, "Mint amount lower than minimum");
        }

        for (uint256 i = 0; i < _assets.length; i++) {
            // Transfer the deposited coins to the vault
            IERC20Upgradeable(_assets[i]).safeTransferFrom(msg.sender, vaultBufferAddress, _amounts[i]);
        }
        IVaultBuffer(vaultBufferAddress).mint(msg.sender, _shareAmount);

        emit Mint(msg.sender, _assets, _amounts, _shareAmount);
        return _shareAmount;
    }

    /// @notice burn USDi,return stablecoins
    /// @param _amount Amount of USDi to burn
    /// @param _minimumAmount Minimum usd to receive in return
    function burn(uint256 _amount, uint256 _minimumAmount)
        external
        whenNotEmergency
        whenNotAdjustPosition
        nonReentrant
        returns (address[] memory _assets, uint256[] memory _amounts)
    {
        uint256 _accountBalance = IPegToken(pegTokenAddress).balanceOf(msg.sender);
        require(_amount > 0 && _amount <= _accountBalance, "USDi not enough");

        address[] memory _trackedAssets = _getTrackedAssets();
        uint256[] memory _assetPrices = new uint256[](_trackedAssets.length);
        uint256[] memory _assetDecimals = new uint256[](_trackedAssets.length);
        (uint256 _sharesAmount, uint256 _actualAsset) = _repayToVault(
            _amount,
            _accountBalance,
            _trackedAssets,
            _assetPrices,
            _assetDecimals
        );

        uint256 _actuallyReceivedAmount = 0;
        (_assets, _amounts, _actuallyReceivedAmount) = _calculateAndTransfer(
            _actualAsset,
            _trackedAssets,
            _assetPrices,
            _assetDecimals
        );

        if (_minimumAmount > 0) {
            require(_actuallyReceivedAmount >= _minimumAmount, "amount lower than minimum");
        }
        _burnRebaseAndEmit(
            _amount,
            _actuallyReceivedAmount,
            _sharesAmount,
            _assets,
            _amounts,
            _trackedAssets,
            _assetPrices,
            _assetDecimals
        );
    }

    /// @notice redeem the funds from specified strategy.
    function redeem(
        address _strategy,
        uint256 _amount,
        uint256 _outputCode
    ) external isKeeper isActiveStrategy(_strategy) nonReentrant {
        uint256 _strategyAssetValue = strategies[_strategy].totalDebt;
        require(_amount <= _strategyAssetValue);

        (address[] memory _assets, uint256[] memory _amounts) = IStrategy(_strategy).repay(
            _amount,
            _strategyAssetValue,
            _outputCode
        );
        if (adjustPositionPeriod) {
            uint256 _assetsLength = _assets.length;
            for (uint256 i = 0; i < _assetsLength; i++) {
                uint256 _amount = _amounts[i];
                if (_amount > 0) {
                    redeemAssetsMap[_assets[i]] += _amount;
                }
            }
        }
        uint256 _nowStrategyTotalDebt = strategies[_strategy].totalDebt;
        uint256 _thisWithdrawValue = (_nowStrategyTotalDebt * _amount) / _strategyAssetValue;
        strategies[_strategy].totalDebt = _nowStrategyTotalDebt - _thisWithdrawValue;
        totalDebt -= _thisWithdrawValue;
        emit Redeem(_strategy, _amount, _assets, _amounts);
    }

    /// @notice Allocate funds in Vault to strategies.
    function lend(address _strategyAddr, IExchangeAggregator.ExchangeToken[] calldata _exchangeTokens)
        external
        isKeeper
        whenNotEmergency
        isActiveStrategy(_strategyAddr)
        nonReentrant
    {
        (
            address[] memory _wants,
            uint256[] memory _ratios,
            uint256[] memory _toAmounts
        ) = _checkAndExchange(_strategyAddr, _exchangeTokens);
        //Definition rule 0 means unconstrained, currencies that do not participate are not in the returned wants
        uint256 _minProductIndex = 0;
        bool _isWantRatioIgnorable = IStrategy(_strategyAddr).isWantRatioIgnorable();
        if (!_isWantRatioIgnorable && _ratios.length > 1) {
            for (uint256 i = 1; i < _ratios.length; i++) {
                if (_ratios[i] == 0) {
                    //0 is free
                    continue;
                } else if (_ratios[_minProductIndex] == 0) {
                    //minProductIndex is assigned to the first index whose proportion is not 0
                    _minProductIndex = i;
                } else if (
                    _toAmounts[_minProductIndex] * _ratios[i] > _toAmounts[i] * _ratios[_minProductIndex]
                ) {
                    _minProductIndex = i;
                }
            }
        }

        uint256 _minAmount = _toAmounts[_minProductIndex];
        uint256 _minAspect = _ratios[_minProductIndex];
        uint256 _lendValue;
        for (uint256 i = 0; i < _toAmounts.length; i++) {
            uint256 _actualAmount = _toAmounts[i];
            if (_actualAmount > 0) {
                address _want = _wants[i];

                if (!_isWantRatioIgnorable && _ratios[i] > 0) {
                    _actualAmount = (_ratios[i] * _minAmount) / _minAspect;
                    _toAmounts[i] = _actualAmount;
                }
                _lendValue =
                    _lendValue +
                    IValueInterpreter(valueInterpreter).calcCanonicalAssetValueInUsd(
                        _want,
                        _actualAmount
                    );
                IERC20Upgradeable(_want).safeTransfer(_strategyAddr, _actualAmount);
            }
        }
        IStrategy _strategy = IStrategy(_strategyAddr);
        _strategy.borrow(_wants, _toAmounts);
        address[] memory _rewardTokens;
        uint256[] memory _claimAmounts;
        _report(_strategyAddr, _rewardTokens, _claimAmounts, _lendValue);
        emit LendToStrategy(_strategyAddr, _wants, _toAmounts, _lendValue);
    }

    function exchange(
        address _fromToken,
        address _toToken,
        uint256 _amount,
        IExchangeAggregator.ExchangeParam memory _exchangeParam
    ) external isKeeper nonReentrant returns (uint256) {
        return _exchange(_fromToken, _toToken, _amount, _exchangeParam);
    }

    /// @notice Change USDi supply with Vault total assets.
    function rebase() external whenNotEmergency whenNotAdjustPosition whenNotRebasePaused nonReentrant {
        uint256 _totalAssets = _totalValueInVault() + totalDebt;
        _rebase(_totalAssets);
    }

    function report(address[] memory _rewardTokens, uint256[] memory _claimAmounts)
        external
        isActiveStrategy(msg.sender)
    {
        _report(msg.sender, _rewardTokens, _claimAmounts, 0);
    }

    /// @notice start  Adjust  Position
    function startAdjustPosition() external isKeeper whenNotAdjustPosition whenNotEmergency nonReentrant {
        adjustPositionPeriod = true;
        address[] memory _trackedAssets = _getTrackedAssets();

        (
            uint256[] memory _vaultAmounts,
            uint256[] memory _transferAmounts,
            bool _vaultBufferAboveZero
        ) = _calculateVault(_trackedAssets, true);
        uint256 _totalDebt = totalDebt;
        if (_vaultBufferAboveZero) {
            uint256 _trackedAssetsLength = _trackedAssets.length;
            uint256[] memory _assetPrices = new uint256[](_trackedAssetsLength);
            uint256[] memory _assetDecimals = new uint256[](_trackedAssetsLength);
            uint256 _totalValueInVault = 0;
            for (uint256 i = 0; i < _trackedAssetsLength; i++) {
                address _trackedAsset = _trackedAssets[i];
                uint256 _amount = _vaultAmounts[i];
                if (_amount > 0) {
                    _totalValueInVault =
                        _totalValueInVault +
                        _calculateAssetValue(_assetPrices, _assetDecimals, i, _trackedAsset, _amount);
                }
            }
            uint256 _totalAssets = _totalValueInVault + _totalDebt;
            uint256 _totalShares = IPegToken(pegTokenAddress).totalShares();
            if (!rebasePaused) {
                _rebase(_totalAssets, _totalShares);
            }
            IVaultBuffer(vaultBufferAddress).transferCashToVault(_trackedAssets, _transferAmounts);
        }
        uint256 _totalDebtOfBeforeAdjustPosition = _totalDebt;
        totalDebtOfBeforeAdjustPosition = _totalDebtOfBeforeAdjustPosition;
        emit StartAdjustPosition(
            _totalDebtOfBeforeAdjustPosition,
            _trackedAssets,
            _vaultAmounts,
            _transferAmounts
        );
    }

    /// @notice end  Adjust Position
    function endAdjustPosition() external isKeeper nonReentrant {
        require(adjustPositionPeriod, "AD OVER");
        address[] memory _trackedAssets = _getTrackedAssets();
        uint256 _trackedAssetsLength = _trackedAssets.length;
        uint256[] memory _assetPrices = new uint256[](_trackedAssetsLength);
        uint256[] memory _assetDecimals = new uint256[](_trackedAssetsLength);

        (uint256[] memory _vaultAmounts, , ) = _calculateVault(_trackedAssets, false);

        uint256 _transferValue = 0;
        uint256 _redeemValue = 0;
        uint256 _vaultValueOfNow = 0;
        uint256 _vaultValueOfBefore = 0;
        for (uint256 i = 0; i < _trackedAssetsLength; i++) {
            address _trackedAsset = _trackedAssets[i];
            _transferValue =
                _transferValue +
                _calculateAssetValue(
                    _assetPrices,
                    _assetDecimals,
                    i,
                    _trackedAsset,
                    transferFromVaultBufferAssetsMap[_trackedAsset]
                );
            _redeemValue =
                _redeemValue +
                _calculateAssetValue(
                    _assetPrices,
                    _assetDecimals,
                    i,
                    _trackedAsset,
                    redeemAssetsMap[_trackedAsset]
                );
            _vaultValueOfNow =
                _vaultValueOfNow +
                _calculateAssetValue(_assetPrices, _assetDecimals, i, _trackedAsset, _vaultAmounts[i]);
            _vaultValueOfBefore =
                _vaultValueOfBefore +
                _calculateAssetValue(
                    _assetPrices,
                    _assetDecimals,
                    i,
                    _trackedAsset,
                    beforeAdjustPositionAssetsMap[_trackedAsset]
                );
        }

        uint256 _totalDebtOfBefore = totalDebtOfBeforeAdjustPosition;
        uint256 _totalDebtOfNow = totalDebt;

        uint256 _totalValueOfNow = _totalDebtOfNow + _vaultValueOfNow;
        uint256 _totalValueOfBefore = _totalDebtOfBefore + _vaultValueOfBefore;

        {
            uint256 _transferAssets = 0;
            uint256 _old2LendAssets = 0;
            if (_vaultValueOfNow + _transferValue < _vaultValueOfBefore) {
                _old2LendAssets = _vaultValueOfBefore - _vaultValueOfNow - _transferValue;
            }
            if (_redeemValue + _old2LendAssets > _totalValueOfBefore - _transferValue) {
                _redeemValue = _totalValueOfBefore - _transferValue - _old2LendAssets;
            }
            if (_totalValueOfNow > _totalValueOfBefore) {
                uint256 _gain = _totalValueOfNow - _totalValueOfBefore;
                if (_transferValue > 0) {
                    _transferAssets =
                        _transferValue +
                        (_gain * _transferValue) /
                        (_transferValue + _redeemValue + _old2LendAssets);
                }
            } else {
                uint256 _loss = _totalValueOfBefore - _totalValueOfNow;
                if (_transferValue > 0) {
                    _transferAssets =
                        _transferValue -
                        (_loss * _transferValue) /
                        (_transferValue + _redeemValue + _old2LendAssets);
                }
            }
            uint256 _totalShares = IPegToken(pegTokenAddress).totalShares();
            if (!rebasePaused) {
                _totalShares = _rebase(_totalValueOfNow - _transferAssets, _totalShares);
            }
            if (_transferAssets > 0) {
                uint256 _sharesAmount = _calculateShare(
                    _transferAssets,
                    _totalValueOfNow - _transferAssets,
                    _totalShares
                );
                if (_sharesAmount > 0) {
                    IPegToken(pegTokenAddress).mintShares(vaultBufferAddress, _sharesAmount);
                }
            }
        }

        {
            totalDebtOfBeforeAdjustPosition = 0;
            for (uint256 i = 0; i < _trackedAssetsLength; i++) {
                address _trackedAsset = _trackedAssets[i];
                redeemAssetsMap[_trackedAsset] = 0;
                beforeAdjustPositionAssetsMap[_trackedAsset] = 0;
                transferFromVaultBufferAssetsMap[_trackedAsset] = 0;
            }
            if (!IVaultBuffer(vaultBufferAddress).isDistributing()) {
                IVaultBuffer(vaultBufferAddress).openDistribute();
            }
            adjustPositionPeriod = false;
        }

        emit EndAdjustPosition(
            _transferValue,
            _redeemValue,
            _totalDebtOfNow,
            _totalValueOfNow,
            _totalValueOfBefore
        );
    }

    function _calculateVault(address[] memory _trackedAssets, bool _dealVaultBuffer)
        internal
        returns (
            uint256[] memory,
            uint256[] memory,
            bool
        )
    {
        uint256 _trackedAssetsLength = _trackedAssets.length;
        uint256[] memory _transferAmounts = new uint256[](_trackedAssetsLength);
        uint256[] memory _vaultAmounts = new uint256[](_trackedAssetsLength);
        bool _vaultBufferAboveZero = false;
        for (uint256 i = 0; i < _trackedAssetsLength; i++) {
            address _trackedAsset = _trackedAssets[i];
            uint256 _balance = 0;
            if (_dealVaultBuffer && assetSet.contains(_trackedAsset)) {
                _balance = _balanceOfToken(_trackedAsset, vaultBufferAddress);
                if (_balance > 0) {
                    _transferAmounts[i] = _balance;
                    _vaultBufferAboveZero = true;
                    transferFromVaultBufferAssetsMap[_trackedAsset] = _balance;
                }
            }
            uint256 _vaultAmount = _balanceOfToken(_trackedAsset, address(this));
            if (_vaultAmount > 0) {
                _vaultAmounts[i] = _vaultAmount;
            }
            if (_dealVaultBuffer && _vaultAmount + _balance > 0) {
                beforeAdjustPositionAssetsMap[_trackedAsset] = _vaultAmount + _balance;
            }
        }
        return (_vaultAmounts, _transferAmounts, _vaultBufferAboveZero);
    }

    /// @notice Assets held by Vault
    function _getTrackedAssets() internal view returns (address[] memory) {
        return trackedAssetsMap._inner._keys.values();
    }

    function _totalValueInVault() internal view returns (uint256) {
        address[] memory _trackedAssets = _getTrackedAssets();
        uint256 _trackedAssetsLength = _trackedAssets.length;
        uint256[] memory _assetPrices = new uint256[](_trackedAssetsLength);
        uint256[] memory _assetDecimals = new uint256[](_trackedAssetsLength);
        uint256 _totalValueInVault = _totalValueInVault(_trackedAssets, _assetPrices, _assetDecimals);
        return _totalValueInVault;
    }

    function _totalValueInVault(
        address[] memory _trackedAssets,
        uint256[] memory _assetPrices,
        uint256[] memory _assetDecimals
    ) internal view returns (uint256) {
        uint256 _totalValueInVault;
        uint256 _trackedAssetsLength = _trackedAssets.length;
        for (uint256 i = 0; i < _trackedAssetsLength; i++) {
            address _trackedAsset = _trackedAssets[i];
            uint256 _balance = _balanceOfToken(_trackedAsset, address(this));
            if (_balance > 0) {
                _totalValueInVault =
                    _totalValueInVault +
                    _calculateAssetValue(_assetPrices, _assetDecimals, i, _trackedAsset, _balance);
            }
        }
        return _totalValueInVault;
    }

    function _totalAssetInVaultAndVaultBuffer() internal view returns (uint256) {
        address[] memory _trackedAssets = _getTrackedAssets();
        uint256 _totalAssetInVaultAndVaultBuffer = 0;
        //price in vault
        for (uint256 i = 0; i < _trackedAssets.length; i++) {
            address _trackedAsset = _trackedAssets[i];
            uint256 _assetBalancesInVault = _balanceOfToken(_trackedAsset, address(this));
            uint256 _assetBalancesInVaultBuffer = _balanceOfToken(_trackedAsset, vaultBufferAddress);
            uint256 _balance = _assetBalancesInVault + _assetBalancesInVaultBuffer;
            if (_balance > 0) {
                uint256 _price = _priceUSD(_trackedAsset);
                uint256 _decimal = trackedAssetDecimalsMap[_trackedAsset];
                uint256 _value = _balance.mulTruncateScale(_price, 10**_decimal);
                _totalAssetInVaultAndVaultBuffer = _totalAssetInVaultAndVaultBuffer + _value;
            }
        }
        return _totalAssetInVaultAndVaultBuffer;
    }

    function _estimateMint(address[] memory _assets, uint256[] memory _amounts)
        private
        view
        returns (uint256)
    {
        _checkMintAssets(_assets, _amounts);
        uint256 _mintAmount = 0;
        for (uint256 i = 0; i < _assets.length; i++) {
            address _asset = _assets[i];
            uint256 _assetPrice = IValueInterpreter(valueInterpreter).price(_asset);
            uint256 _assetDecimal = trackedAssetDecimalsMap[_asset];
            _mintAmount += _amounts[i].mulTruncateScale(_assetPrice, 10**_assetDecimal);
        }
        uint256 _minimumInvestmentAmount = minimumInvestmentAmount;
        if (_minimumInvestmentAmount > 0) {
            require(
                _mintAmount >= _minimumInvestmentAmount,
                "Amount must be gt minimum Investment Amount"
            );
        }
        return _mintAmount;
    }

    /// @notice withdraw from strategy queue
    function _repayFromWithdrawQueue(uint256 _needWithdrawValue) internal {
        uint256 _totalWithdrawValue;
        for (uint256 i = 0; i < withdrawQueue.length; i++) {
            address _strategy = withdrawQueue[i];
            if (_strategy == ZERO_ADDRESS) break;

            uint256 _strategyTotalValue = strategies[_strategy].totalDebt;
            if (_strategyTotalValue <= 0) {
                continue;
            }

            uint256 _strategyWithdrawValue;
            if (_needWithdrawValue > _strategyTotalValue) {
                _strategyWithdrawValue = _strategyTotalValue;
                _needWithdrawValue -= _strategyWithdrawValue;
            } else {
                //If there is less than 1u left, then all redemption
                if (_needWithdrawValue + 1e18 >= _strategyTotalValue) {
                    _strategyWithdrawValue = _strategyTotalValue;
                } else {
                    _strategyWithdrawValue = _needWithdrawValue;
                }
                _needWithdrawValue = 0;
            }

            (address[] memory _assets, uint256[] memory _amounts) = IStrategy(_strategy).repay(
                _strategyWithdrawValue,
                _strategyTotalValue,
                0
            );
            emit RepayFromStrategy(
                _strategy,
                _strategyWithdrawValue,
                _strategyTotalValue,
                _assets,
                _amounts
            );
            uint256 _nowStrategyTotalDebt = strategies[_strategy].totalDebt;
            uint256 _thisWithdrawValue = (_nowStrategyTotalDebt * _strategyWithdrawValue) /
                _strategyTotalValue;
            strategies[_strategy].totalDebt = _nowStrategyTotalDebt - _thisWithdrawValue;
            _totalWithdrawValue += _thisWithdrawValue;

            if (_needWithdrawValue <= 0) {
                break;
            }
        }
        totalDebt -= _totalWithdrawValue;
    }

    /// @notice withdraw from vault buffer
    function _repayFromVaultBuffer(
        uint256 _needTransferValue,
        address[] memory _trackedAssets,
        uint256[] memory _assetPrices,
        uint256[] memory _assetDecimals,
        uint256 _totalAssets,
        uint256 _totalShares
    ) internal returns (uint256) {
        address[] memory _transferAssets = _trackedAssets;
        uint256 _transferAssetsLength = _transferAssets.length;
        uint256[] memory _amounts = new uint256[](_transferAssetsLength);
        uint256 _totalTransferValue;
        //price in vault
        for (uint256 i = 0; i < _transferAssetsLength; i++) {
            address _trackedAsset = _transferAssets[i];
            if (assetSet.contains(_trackedAsset)) {
                uint256 _assetBalancesInVaultBuffer = _balanceOfToken(_trackedAsset, vaultBufferAddress);
                if (_assetBalancesInVaultBuffer > 0) {
                    uint256 _value = _calculateAssetValue(
                        _assetPrices,
                        _assetDecimals,
                        i,
                        _trackedAsset,
                        _assetBalancesInVaultBuffer
                    );

                    if (_needTransferValue > _value) {
                        _totalTransferValue = _totalTransferValue + _value;
                        _needTransferValue = _needTransferValue - _value;
                        _amounts[i] = _assetBalancesInVaultBuffer;
                    } else {
                        _totalTransferValue = _totalTransferValue + _needTransferValue;
                        _amounts[i] = (_assetBalancesInVaultBuffer * _needTransferValue) / _value;
                        _needTransferValue = 0;
                        break;
                    }
                }
            }
        }
        if (_totalTransferValue > 0) {
            IVaultBuffer(vaultBufferAddress).transferCashToVault(_transferAssets, _amounts);

            uint256 _totalTransferShares = _calculateShare(
                _totalTransferValue,
                _totalAssets,
                _totalShares
            );
            IPegToken(pegTokenAddress).mintShares(vaultBufferAddress, _totalTransferShares);

            emit PegTokenSwapCash(_totalTransferValue, _transferAssets, _amounts);
        }
        return _totalTransferValue;
    }

    function _calculateShare(
        uint256 _amount,
        uint256 _totalAssets,
        uint256 _totalShares
    ) internal view returns (uint256) {
        uint256 _shareAmount = 0;
        if (_totalAssets > 0 && _totalShares > 0) {
            _shareAmount = (_amount * _totalShares) / _totalAssets;
        }
        if (_shareAmount == 0) {
            uint256 _underlyingUnitsPerShare = underlyingUnitsPerShare;
            if (_underlyingUnitsPerShare > 0) {
                _shareAmount = _amount.divPreciselyScale(_underlyingUnitsPerShare, 1e27);
            } else {
                _shareAmount = _amount * 1e9;
            }
        }
        return _shareAmount;
    }

    /// @notice calculate need transfer amount from vault ,set to outputs
    function _calculateOutputs(
        uint256 _needTransferAmount,
        address[] memory _trackedAssets,
        uint256[] memory _assetPrices,
        uint256[] memory _assetDecimals
    ) internal view returns (uint256[] memory) {
        uint256 _trackedAssetsLength = _trackedAssets.length;
        uint256[] memory _outputs = new uint256[](_trackedAssetsLength);

        for (uint256 i = 0; i < _trackedAssetsLength; i++) {
            address _trackedAsset = _trackedAssets[i];
            uint256 _balance = _balanceOfToken(_trackedAsset, address(this));
            if (_balance > 0) {
                uint256 _value = _calculateAssetValue(
                    _assetPrices,
                    _assetDecimals,
                    i,
                    _trackedAsset,
                    _balance
                );

                if (_value >= _needTransferAmount) {
                    _outputs[i] = (_balance * _needTransferAmount) / _value;
                    break;
                } else {
                    _outputs[i] = _balance;
                    _needTransferAmount = _needTransferAmount - _value;
                }
            }
        }
        return _outputs;
    }

    /// @notice calculate Asset value in usd by oracle price
    /// @param _assetPrices array of asset price
    /// @param _assetDecimals array of asset decimal
    /// @param _assetIndex index of the asset in trackedAssets array
    /// @param _trackedAsset address of the asset
    /// @return shareAmount
    function _calculateAssetValue(
        uint256[] memory _assetPrices,
        uint256[] memory _assetDecimals,
        uint256 _assetIndex,
        address _trackedAsset,
        uint256 _balance
    ) private view returns (uint256) {
        uint256 _assetPrice = _getAssetPrice(_assetPrices, _assetIndex, _trackedAsset);
        uint256 _assetDecimal = _getAssetDecimals(_assetDecimals, _assetIndex, _trackedAsset);

        uint256 _value = _balance.mulTruncateScale(_assetPrice, 10**_assetDecimal);
        return _value;
    }

    // @notice without exchange token and transfer form vault to user
    function _transfer(
        uint256[] memory _outputs,
        address[] memory _trackedAssets,
        uint256[] memory _assetPrices,
        uint256[] memory _assetDecimals
    ) internal returns (uint256) {
        uint256 _actualAmount;
        uint256 _trackedAssetsLength = _trackedAssets.length;
        for (uint256 i = 0; i < _trackedAssetsLength; i++) {
            uint256 _amount = _outputs[i];
            if (_amount > 0) {
                address _trackedAsset = _trackedAssets[i];
                uint256 _value = _calculateAssetValue(
                    _assetPrices,
                    _assetDecimals,
                    i,
                    _trackedAsset,
                    _amount
                );
                _actualAmount = _actualAmount + _value;
                IERC20Upgradeable(_trackedAsset).safeTransfer(msg.sender, _amount);
            }
        }
        return _actualAmount;
    }

    function _repayToVault(
        uint256 _amount,
        uint256 _accountBalance,
        address[] memory _trackedAssets,
        uint256[] memory _assetPrices,
        uint256[] memory _assetDecimals
    ) internal returns (uint256 _sharesAmount, uint256 _actualAsset) {
        uint256 _totalAssetInVault = _totalValueInVault(_trackedAssets, _assetPrices, _assetDecimals);
        uint256 _actualAmount = _amount;
        uint256 _currentTotalAssets = _totalAssetInVault + totalDebt;
        uint256 _currentTotalShares = IPegToken(pegTokenAddress).totalShares();
        {
            uint256 _underlyingUnitsPerShare = underlyingUnitsPerShare;
            if (_accountBalance == _actualAmount) {
                _sharesAmount = IPegToken(pegTokenAddress).sharesOf(msg.sender);
            } else {
                _sharesAmount = _actualAmount.divPreciselyScale(_underlyingUnitsPerShare, 1e27);
            }
            // Calculate redeem fee
            if (redeemFeeBps > 0) {
                _actualAmount = _actualAmount - (_actualAmount * redeemFeeBps) / MAX_BPS;
            }
            uint256 _currentTotalSupply = _currentTotalShares.mulTruncateScale(
                _underlyingUnitsPerShare,
                1e27
            );
            _actualAsset = (_actualAmount * _currentTotalAssets) / _currentTotalSupply;
        }

        // vault not enough,withdraw from vault buffer
        if (_totalAssetInVault < _actualAsset) {
            _totalAssetInVault =
                _totalAssetInVault +
                _repayFromVaultBuffer(
                    _actualAsset - _totalAssetInVault,
                    _trackedAssets,
                    _assetPrices,
                    _assetDecimals,
                    _currentTotalAssets,
                    _currentTotalShares
                );
        }

        // vault not enough,withdraw from withdraw queue strategy
        if (_totalAssetInVault < _actualAsset) {
            _repayFromWithdrawQueue(_actualAsset - _totalAssetInVault);
        }
    }

    function _calculateAndTransfer(
        uint256 _actualAsset,
        address[] memory _trackedAssets,
        uint256[] memory _assetPrices,
        uint256[] memory _assetDecimals
    )
        internal
        returns (
            address[] memory,
            uint256[] memory,
            uint256
        )
    {
        // calculate need transfer amount from vault ,set to outputs
        uint256[] memory _outputs = _calculateOutputs(
            _actualAsset,
            _trackedAssets,
            _assetPrices,
            _assetDecimals
        );
        uint256 _actuallyReceivedAmount = _transfer(
            _outputs,
            _trackedAssets,
            _assetPrices,
            _assetDecimals
        );
        return (_trackedAssets, _outputs, _actuallyReceivedAmount);
    }

    // @notice burn usdi and check rebase
    function _burnRebaseAndEmit(
        uint256 _amount,
        uint256 _actualAmount,
        uint256 _shareAmount,
        address[] memory _assets,
        uint256[] memory _amounts,
        address[] memory _trackedAssets,
        uint256[] memory _assetPrices,
        uint256[] memory _assetDecimals
    ) internal {
        IPegToken(pegTokenAddress).burnShares(msg.sender, _shareAmount);

        // Until we can prove that we won't affect the prices of our assets
        // by withdrawing them, this should be here.
        // It's possible that a strategy was off on its asset total, perhaps
        // a reward token sold for more or for less than anticipated.
        if (!rebasePaused) {
            uint256 _totalValueInVault = _totalValueInVault(_trackedAssets, _assetPrices, _assetDecimals);
            _rebase(_totalValueInVault + totalDebt);
        }
        emit Burn(msg.sender, _amount, _actualAmount, _shareAmount, _assets, _amounts);
    }

    /**
     * @dev Calculate the total value of assets held by the Vault and all
     *      strategies and update the supply of USDI, optionally sending a
     *      portion of the yield to the trustee.
     */
    function _rebase(uint256 _totalAssets) internal {
        uint256 _totalShares = IPegToken(pegTokenAddress).totalShares();
        _rebase(_totalAssets, _totalShares);
    }

    function _rebase(uint256 _totalAssets, uint256 _totalShares) internal returns (uint256) {
        if (_totalShares == 0) {
            return _totalShares;
        }

        uint256 _underlyingUnitsPerShare = underlyingUnitsPerShare;
        uint256 _totalSupply = _totalShares.mulTruncateScale(_underlyingUnitsPerShare, 1e27);

        // Final check should use latest value
        if (
            _totalAssets > _totalSupply &&
            (_totalAssets - _totalSupply) * TEN_MILLION_BPS > _totalSupply * rebaseThreshold
        ) {
            // Yield fee collection
            address _treasuryAddress = treasury;
            uint256 _trusteeFeeBps = trusteeFeeBps;
            if (_trusteeFeeBps > 0 && _treasuryAddress != address(0)) {
                uint256 _yield = _totalAssets - _totalSupply;
                uint256 _fee = (_yield * _trusteeFeeBps) / MAX_BPS;
                require(_yield > _fee, "Fee must not be greater than yield");
                if (_fee > 0) {
                    uint256 _sharesAmount = (_fee * _totalShares) / (_totalAssets - _fee);
                    if (_sharesAmount > 0) {
                        IPegToken(pegTokenAddress).mintShares(_treasuryAddress, _sharesAmount);
                        _totalShares = _totalShares + _sharesAmount;
                    }
                }
            }
            uint256 _newUnderlyingUnitsPerShare = _totalAssets.divPreciselyScale(_totalShares, 1e27);
            if (_newUnderlyingUnitsPerShare != _underlyingUnitsPerShare) {
                underlyingUnitsPerShare = _newUnderlyingUnitsPerShare;
                emit Rebase(_totalShares, _totalAssets, _newUnderlyingUnitsPerShare);
            }
        }
        return _totalShares;
    }

    /// @notice check valid and exchange to want token
    function _checkAndExchange(
        address _strategy,
        IExchangeAggregator.ExchangeToken[] calldata _exchangeTokens
    )
        internal
        returns (
            address[] memory _wants,
            uint256[] memory _ratios,
            uint256[] memory toAmounts
        )
    {
        (_wants, _ratios) = IStrategy(_strategy).getWantsInfo();
        uint256 _wantsLength = _wants.length;
        toAmounts = new uint256[](_wantsLength);
        uint256 _exchangeTokensLength = _exchangeTokens.length;
        for (uint256 i = 0; i < _exchangeTokensLength; i++) {
            bool _findToToken = false;
            for (uint256 j = 0; j < _wantsLength; j++) {
                if (_exchangeTokens[i].toToken == _wants[j]) {
                    _findToToken = true;
                    break;
                }
            }
            require(_findToToken, "toToken invalid");
        }

        for (uint256 j = 0; j < _wantsLength; j++) {
            for (uint256 i = 0; i < _exchangeTokensLength; i++) {
                IExchangeAggregator.ExchangeToken memory _exchangeToken = _exchangeTokens[i];

                // not strategy need token,skip
                if (_wants[j] != _exchangeToken.toToken) continue;

                uint256 _toAmount;
                if (_exchangeToken.fromToken == _exchangeToken.toToken) {
                    _toAmount = _exchangeToken.fromAmount;
                } else {
                    if (_exchangeToken.fromAmount > 0) {
                        _toAmount = _exchange(
                            _exchangeToken.fromToken,
                            _exchangeToken.toToken,
                            _exchangeToken.fromAmount,
                            _exchangeToken.exchangeParam
                        );
                    }
                }

                toAmounts[j] = _toAmount;
                break;
            }
        }
    }

    function _exchange(
        address _fromToken,
        address _toToken,
        uint256 _amount,
        IExchangeAggregator.ExchangeParam memory _exchangeParam
    ) internal returns (uint256 _exchangeAmount) {
        require(trackedAssetsMap.contains(_toToken), "!T");

        IExchangeAdapter.SwapDescription memory _swapDescription = IExchangeAdapter.SwapDescription({
            amount: _amount,
            srcToken: _fromToken,
            dstToken: _toToken,
            receiver: address(this)
        });
        IERC20Upgradeable(_fromToken).safeApprove(exchangeManager, 0);
        IERC20Upgradeable(_fromToken).safeApprove(exchangeManager, _amount);
        _exchangeAmount = IExchangeAggregator(exchangeManager).swap(
            _exchangeParam.platform,
            _exchangeParam.method,
            _exchangeParam.encodeExchangeArgs,
            _swapDescription
        );
        uint256 oracleExpectedAmount = IValueInterpreter(valueInterpreter).calcCanonicalAssetValue(
            _fromToken,
            _amount,
            _toToken
        );
        require(
            _exchangeAmount >=
                (oracleExpectedAmount *
                    (MAX_BPS - _exchangeParam.slippage - _exchangeParam.oracleAdditionalSlippage)) /
                    MAX_BPS,
            "OL"
        );
        emit Exchange(_exchangeParam.platform, _fromToken, _amount, _toToken, _exchangeAmount);
    }

    function _report(
        address _strategy,
        address[] memory _rewardTokens,
        uint256[] memory _claimAmounts,
        uint256 _lendValue
    ) private {
        StrategyParams memory _strategyParam = strategies[_strategy];
        uint256 _lastStrategyTotalDebt = _strategyParam.totalDebt + _lendValue;
        uint256 _nowStrategyTotalDebt = IStrategy(_strategy).estimatedTotalAssets();
        uint256 _gain = 0;
        uint256 _loss = 0;

        if (_nowStrategyTotalDebt > _lastStrategyTotalDebt) {
            _gain = _nowStrategyTotalDebt - _lastStrategyTotalDebt;
        } else if (_nowStrategyTotalDebt < _lastStrategyTotalDebt) {
            _loss = _lastStrategyTotalDebt - _nowStrategyTotalDebt;
        }

        if (_strategyParam.enforceChangeLimit) {
            if (
                block.timestamp - strategies[_strategy].lastReport < maxTimestampBetweenTwoReported &&
                (_lastStrategyTotalDebt > minCheckedStrategyTotalDebt ||
                    _nowStrategyTotalDebt > minCheckedStrategyTotalDebt)
            ) {
                if (_gain > 0) {
                    require(
                        _gain <= ((_lastStrategyTotalDebt * _strategyParam.profitLimitRatio) / MAX_BPS),
                        "GL"
                    );
                } else if (_loss > 0) {
                    require(
                        _loss <= ((_lastStrategyTotalDebt * _strategyParam.lossLimitRatio) / MAX_BPS),
                        "LL"
                    );
                }
            }
        } else {
            strategies[_strategy].enforceChangeLimit = true;
            // The check is turned off only once and turned back on.
        }

        strategies[_strategy].totalDebt = _nowStrategyTotalDebt;
        totalDebt = totalDebt + _nowStrategyTotalDebt + _lendValue - _lastStrategyTotalDebt;

        strategies[_strategy].lastReport = block.timestamp;
        uint256 _type = 0;
        if (_lendValue > 0) {
            _type = 1;
        }
        emit StrategyReported(
            _strategy,
            _gain,
            _loss,
            _lastStrategyTotalDebt,
            _nowStrategyTotalDebt,
            _rewardTokens,
            _claimAmounts,
            _type
        );
    }

    function _balanceOfToken(address _trackedAsset, address _owner) internal view returns (uint256) {
        return IERC20Upgradeable(_trackedAsset).balanceOf(_owner);
    }

    /**
     * @notice Get the supported asset Decimal
     * @return _assetDecimal asset Decimals
     */
    function _getAssetDecimals(
        uint256[] memory _assetDecimals,
        uint256 _assetIndex,
        address _asset
    ) internal view returns (uint256) {
        uint256 _decimal = _assetDecimals[_assetIndex];
        if (_decimal == 0) {
            _decimal = trackedAssetDecimalsMap[_asset];
            _assetDecimals[_assetIndex] = _decimal;
        }
        return _decimal;
    }

    /**
     * @notice Get an array of the supported asset prices in USD
     * @return  prices in USD (1e18)
     */
    function _getAssetPrice(
        uint256[] memory _assetPrices,
        uint256 _assetIndex,
        address _asset
    ) internal view returns (uint256) {
        uint256 _price = _assetPrices[_assetIndex];
        if (_price == 0) {
            _price = _priceUSD(_asset);
            _assetPrices[_assetIndex] = _price;
        }
        return _price;
    }

    /**
     * @dev Returns the total price in 18 digit USD for a given asset
     * @param _asset Address of the asset
     * @return _price USD price of 1 of the asset, in 18 decimal fixed
     */
    function _priceUSD(address _asset) internal view returns (uint256 _price) {
        _price = IValueInterpreter(valueInterpreter).price(_asset);
    }

    function _checkMintAssets(address[] memory _assets, uint256[] memory _amounts) private view {
        require(!(IVaultBuffer(vaultBufferAddress).isDistributing()), "is distributing");
        uint256 _assetsLength = _assets.length;
        uint256 _amountsLength = _amounts.length;
        require(
            _assetsLength > 0 && _assetsLength == _amountsLength,
            "Assets and amounts must be equal in length and not empty"
        );

        for (uint256 i = 0; i < _assetsLength; i++) {
            checkIsSupportAsset(_assets[i]);
            require(_amounts[i] > 0, "Amount must be gt 0");
        }
    }

    /**
     * @dev Falldown to the admin implementation
     * @notice This is a catch all for all functions not declared in core
     */
    fallback() external payable {
        bytes32 _slot = ADMIN_IMPL_POSITION;

        assembly {
            // Copy msg.data. We take full control of memory in this inline assembly
            // block because it will not return to Solidity code. We overwrite the
            // Solidity scratch pad at memory position 0.
            calldatacopy(0, 0, calldatasize())

            // Call the implementation.
            // out and outsize are 0 because we don't know the size yet.
            let result := delegatecall(gas(), sload(_slot), 0, calldatasize(), 0, 0)

            // Copy the returned data.
            returndatacopy(0, 0, returndatasize())

            switch result
            // delegatecall returns 0 on error.
            case 0 {
                revert(0, returndatasize())
            }
            default {
                return(0, returndatasize())
            }
        }
    }
}

File 2 of 23 : SafeERC20Upgradeable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (token/ERC20/utils/SafeERC20.sol)

pragma solidity ^0.8.0;

import "../IERC20Upgradeable.sol";
import "../../../utils/AddressUpgradeable.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 SafeERC20Upgradeable {
    using AddressUpgradeable for address;

    function safeTransfer(
        IERC20Upgradeable token,
        address to,
        uint256 value
    ) internal {
        _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));
    }

    function safeTransferFrom(
        IERC20Upgradeable token,
        address from,
        address to,
        uint256 value
    ) internal {
        _callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));
    }

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

    function safeIncreaseAllowance(
        IERC20Upgradeable token,
        address spender,
        uint256 value
    ) internal {
        uint256 newAllowance = token.allowance(address(this), spender) + value;
        _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
    }

    function safeDecreaseAllowance(
        IERC20Upgradeable token,
        address spender,
        uint256 value
    ) internal {
        unchecked {
            uint256 oldAllowance = token.allowance(address(this), spender);
            require(oldAllowance >= value, "SafeERC20: decreased allowance below zero");
            uint256 newAllowance = oldAllowance - value;
            _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
        }
    }

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

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

File 3 of 23 : Math.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.0) (utils/math/Math.sol)

pragma solidity ^0.8.0;

/**
 * @dev Standard math utilities missing in the Solidity language.
 */
library Math {
    enum Rounding {
        Down, // Toward negative infinity
        Up, // Toward infinity
        Zero // Toward zero
    }

    /**
     * @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 up instead
     * of rounding down.
     */
    function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {
        // (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; // Least significant 256 bits of the product
            uint256 prod1; // Most significant 256 bits of the product
            assembly {
                let mm := mulmod(x, y, not(0))
                prod0 := mul(x, y)
                prod1 := sub(sub(mm, prod0), lt(mm, prod0))
            }

            // Handle non-overflow cases, 256 by 256 division.
            if (prod1 == 0) {
                return prod0 / denominator;
            }

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

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

            // Make division exact by subtracting the remainder from [prod1 prod0].
            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.

            // Does not overflow because the denominator cannot be zero at this stage in the function.
            uint256 twos = denominator & (~denominator + 1);
            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 (rounding == Rounding.Up && mulmod(x, y, denominator) > 0) {
            result += 1;
        }
        return result;
    }

    /**
     * @dev Returns the square root of a number. It the number is not a perfect square, the value is rounded down.
     *
     * 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)`.
        // We also know that `k`, the position of the most significant bit, is such that `msb(a) = 2**k`.
        // This gives `2**k < a <= 2**(k+1)` → `2**(k/2) <= sqrt(a) < 2 ** (k/2+1)`.
        // Using an algorithm similar to the msb conmputation, we are able to compute `result = 2**(k/2)` which is a
        // good first aproximation of `sqrt(a)` with at least 1 correct bit.
        uint256 result = 1;
        uint256 x = a;
        if (x >> 128 > 0) {
            x >>= 128;
            result <<= 64;
        }
        if (x >> 64 > 0) {
            x >>= 64;
            result <<= 32;
        }
        if (x >> 32 > 0) {
            x >>= 32;
            result <<= 16;
        }
        if (x >> 16 > 0) {
            x >>= 16;
            result <<= 8;
        }
        if (x >> 8 > 0) {
            x >>= 8;
            result <<= 4;
        }
        if (x >> 4 > 0) {
            x >>= 4;
            result <<= 2;
        }
        if (x >> 2 > 0) {
            result <<= 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) {
        uint256 result = sqrt(a);
        if (rounding == Rounding.Up && result * result < a) {
            result += 1;
        }
        return result;
    }
}

File 4 of 23 : VaultStorage.sol
// SPDX-License-Identifier: AGPL-3.0-or-later

pragma solidity ^0.8.0;
import "../access-control/AccessControlMixin.sol";
import "../library/IterableIntMap.sol";
import "../library/StableMath.sol";
import "../token/IPegToken.sol";
import "./IVaultBuffer.sol";
import "../library/BocRoles.sol";
import "../strategy/IStrategy.sol";
import "../price-feeds/IValueInterpreter.sol";

import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
import "@openzeppelin/contracts-upgradeable/security/ReentrancyGuardUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/utils/AddressUpgradeable.sol";
import "@openzeppelin/contracts/utils/structs/EnumerableSet.sol";

contract VaultStorage is Initializable, ReentrancyGuardUpgradeable, AccessControlMixin {
    using StableMath for uint256;
    using EnumerableSet for EnumerableSet.AddressSet;
    using IterableIntMap for IterableIntMap.AddressToIntMap;

    /// @param lastReport The last report timestamp
    /// @param totalDebt The total asset of this strategy
    /// @param profitLimitRatio The limited ratio of profit
    /// @param lossLimitRatio The limited ratio for loss
    /// @param enforceChangeLimit The switch of enforce change Limit
    struct StrategyParams {
        uint256 lastReport;
        uint256 totalDebt;
        uint256 profitLimitRatio;
        uint256 lossLimitRatio;
        bool enforceChangeLimit;
    }

    /// @param strategy The new strategy to add
    /// @param profitLimitRatio The limited ratio of profit
    /// @param lossLimitRatio The limited ratio for loss
    struct StrategyAdd {
        address strategy;
        uint256 profitLimitRatio;
        uint256 lossLimitRatio;
    }

    event AddAsset(address _asset);
    event RemoveAsset(address _asset);
    event AddStrategies(address[] _strategies);
    event RemoveStrategies(address[] _strategies);
    event RemoveStrategyByForce(address _strategy);
    event Mint(address _account, address[] _assets, uint256[] _amounts, uint256 _mintAmount);
    event Burn(
        address _account,
        uint256 _amount,
        uint256 _actualAmount,
        uint256 _shareAmount,
        address[] _assets,
        uint256[] _amounts
    );
    event Exchange(
        address _platform,
        address _srcAsset,
        uint256 _srcAmount,
        address _distAsset,
        uint256 _distAmount
    );
    event Redeem(address _strategy, uint256 _debtChangeAmount, address[] _assets, uint256[] _amounts);
    event LendToStrategy(
        address indexed _strategy,
        address[] _wants,
        uint256[] _amounts,
        uint256 _lendValue
    );
    event RepayFromStrategy(
        address indexed _strategy,
        uint256 _strategyWithdrawValue,
        uint256 _strategyTotalValue,
        address[] _assets,
        uint256[] _amounts
    );
    event StrategyReported(
        address indexed _strategy,
        uint256 _gain,
        uint256 _loss,
        uint256 _lastStrategyTotalDebt,
        uint256 _nowStrategyTotalDebt,
        address[] _rewardTokens,
        uint256[] _claimAmounts,
        uint256 _type
    );
    event RemoveStrategyFromQueue(address[] _strategies);
    event SetEmergencyShutdown(bool _shutdown);
    event RebasePaused();
    event RebaseUnpaused();
    event RebaseThresholdUpdated(uint256 _threshold);
    event TrusteeFeeBpsChanged(uint256 _basis);
    event MaxTimestampBetweenTwoReportedChanged(uint256 _maxTimestampBetweenTwoReported);
    event MinCheckedStrategyTotalDebtChanged(uint256 _minCheckedStrategyTotalDebt);
    event MinimumInvestmentAmountChanged(uint256 _minimumInvestmentAmount);
    event TreasuryAddressChanged(address _address);
    event ExchangeManagerAddressChanged(address _address);
    event SetAdjustPositionPeriod(bool _adjustPositionPeriod);
    event RedeemFeeUpdated(uint256 _redeemFeeBps);
    event SetWithdrawalQueue(address[] _queues);
    event Rebase(uint256 _totalShares, uint256 _totalValue, uint256 _newUnderlyingUnitsPerShare);
    event StartAdjustPosition(
        uint256 _totalDebtOfBeforeAdjustPosition,
        address[] _trackedAssets,
        uint256[] _vaultCashDetatil,
        uint256[] _vaultBufferCashDetail
    );
    event EndAdjustPosition(
        uint256 _transferValue,
        uint256 _redeemValue,
        uint256 _totalDebt,
        uint256 _totalValueOfAfterAdjustPosition,
        uint256 _totalValueOfBeforeAdjustPosition
    );
    event PegTokenSwapCash(uint256 _pegTokenAmount, address[] _assets, uint256[] _amounts);

    address internal constant ZERO_ADDRESS = address(0);

    //max percentage 100%
    uint256 internal constant MAX_BPS = 10000;

    // all strategy
    EnumerableSet.AddressSet internal strategySet;
    // Assets supported by the Vault, i.e. Stablecoins
    EnumerableSet.AddressSet internal assetSet;
    // Assets held by Vault
    IterableIntMap.AddressToIntMap internal trackedAssetsMap;
    // Decimals of the assets held by Vault
    mapping(address => uint256) internal trackedAssetDecimalsMap;

    //adjust Position Period
    bool public adjustPositionPeriod;
    // emergency shutdown
    bool public emergencyShutdown;
    // Pausing bools
    bool public rebasePaused;
    // over this difference ratio automatically rebase. rebaseThreshold is the numerator and the denominator is 10000000 x/10000000.
    uint256 public rebaseThreshold;
    // Deprecated
    uint256 public maxSupplyDiff;
    // Amount of yield collected in basis points
    uint256 public trusteeFeeBps;
    // Redemption fee in basis points
    uint256 public redeemFeeBps;
    //all strategy asset
    uint256 public totalDebt;
    // treasury contract that can collect a percentage of yield
    address public treasury;
    //valueInterpreter
    address public valueInterpreter;
    //exchangeManager
    address public exchangeManager;
    // strategy info
    mapping(address => StrategyParams) public strategies;

    //withdraw strategy set
    address[] public withdrawQueue;
    //keccak256("USDi.vault.governor.admin.impl");
    bytes32 internal constant ADMIN_IMPL_POSITION =
        0x3d78d3961e16fde088e2e26c1cfa163f5f8bb870709088dd68c87eb4091137e2;

    //vault Buffer Address
    address public vaultBufferAddress;
    // usdi PegToken address
    address public pegTokenAddress;
    // Assets held in Vault from vault buffer
    mapping(address => uint256) internal transferFromVaultBufferAssetsMap;
    // redeem Assets where ad
    mapping(address => uint256) internal redeemAssetsMap;
    // Assets held in Vault and buffer before Adjust Position
    mapping(address => uint256) internal beforeAdjustPositionAssetsMap;
    // totalDebt before Adjust Position
    uint256 internal totalDebtOfBeforeAdjustPosition;
    // totalAsset/totalShare
    uint256 public underlyingUnitsPerShare;
    //Maximum timestamp between two reported
    uint256 public maxTimestampBetweenTwoReported;
    //Minimum strategy total debt that will be checked for the strategy reporting
    uint256 public minCheckedStrategyTotalDebt;
    //Minimum investment amount
    uint256 public minimumInvestmentAmount;

    //max percentage 10000000/10000000
    uint256 internal constant TEN_MILLION_BPS = 10000000;

    /**
     * @dev set the implementation for the admin, this needs to be in a base class else we cannot set it
     * @param _newImpl address of the implementation
     */
    function setAdminImpl(address _newImpl) external onlyGovOrDelegate {
        require(AddressUpgradeable.isContract(_newImpl), "new implementation is not a contract");
        bytes32 _position = ADMIN_IMPL_POSITION;
        assembly {
            sstore(_position, _newImpl)
        }
    }
}

File 5 of 23 : IExchangeAggregator.sol
// SPDX-License-Identifier: AGPL-3.0-or-later

pragma solidity ^0.8.0;

import "./IExchangeAdapter.sol";

interface IExchangeAggregator {
    /**
     * @param platform Called exchange platforms
     * @param method The method of the exchange platform
     * @param encodeExchangeArgs The encoded parameters to call
     * @param slippage The slippage when exchange
     * @param oracleAdditionalSlippage The additional slippage for oracle estimated
     */
    struct ExchangeParam {
        address platform;
        uint8 method;
        bytes encodeExchangeArgs;
        uint256 slippage;
        uint256 oracleAdditionalSlippage;
    }

    /**
     * @param platform Called exchange platforms
     * @param method The method of the exchange platform
     * @param data The encoded parameters to call
     * @param swapDescription swap info
     */
    struct SwapParam {
        address platform;
        uint8 method;
        bytes data;
        IExchangeAdapter.SwapDescription swapDescription;
    }

    /**
     * @param srcToken The token swap from
     * @param dstToken The token swap to
     * @param amount The amount to swap
     * @param exchangeParam The struct of ExchangeParam
     */
    struct ExchangeToken {
        address fromToken;
        address toToken;
        uint256 fromAmount;
        ExchangeParam exchangeParam;
    }

    event ExchangeAdapterAdded(address[] _exchangeAdapters);

    event ExchangeAdapterRemoved(address[] _exchangeAdapters);

    event Swap(
        address _platform,
        uint256 _amount,
        address _srcToken,
        address _dstToken,
        uint256 _exchangeAmount,
        address indexed _receiver,
        address _sender
    );

    function swap(
        address _platform,
        uint8 _method,
        bytes calldata _data,
        IExchangeAdapter.SwapDescription calldata _sd
    ) external payable returns (uint256);

    function batchSwap(SwapParam[] calldata _swapParams) external payable returns (uint256[] memory);

    function getExchangeAdapters()
        external
        view
        returns (address[] memory _exchangeAdapters, string[] memory _identifiers);

    function addExchangeAdapters(address[] calldata _exchangeAdapters) external;

    function removeExchangeAdapters(address[] calldata _exchangeAdapters) external;
}

File 6 of 23 : IERC20Upgradeable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.6.0) (token/ERC20/IERC20.sol)

pragma solidity ^0.8.0;

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

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

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

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

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

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

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

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

File 7 of 23 : AddressUpgradeable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.5.0) (utils/Address.sol)

pragma solidity ^0.8.1;

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

        return account.code.length > 0;
    }

    /**
     * @dev Replacement for Solidity's `transfer`: sends `amount` wei to
     * `recipient`, forwarding all available gas and reverting on errors.
     *
     * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
     * of certain opcodes, possibly making contracts go over the 2300 gas limit
     * imposed by `transfer`, making them unable to receive funds via
     * `transfer`. {sendValue} removes this limitation.
     *
     * https://diligence.consensys.net/posts/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.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
     */
    function sendValue(address payable recipient, uint256 amount) internal {
        require(address(this).balance >= amount, "Address: insufficient balance");

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

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

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

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

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

        (bool success, bytes memory returndata) = target.call{value: value}(data);
        return verifyCallResult(success, returndata, errorMessage);
    }

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

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
     * but performing a static call.
     *
     * _Available since v3.3._
     */
    function functionStaticCall(
        address target,
        bytes memory data,
        string memory errorMessage
    ) internal view returns (bytes memory) {
        require(isContract(target), "Address: static call to non-contract");

        (bool success, bytes memory returndata) = target.staticcall(data);
        return verifyCallResult(success, returndata, errorMessage);
    }

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

                assembly {
                    let returndata_size := mload(returndata)
                    revert(add(32, returndata), returndata_size)
                }
            } else {
                revert(errorMessage);
            }
        }
    }
}

File 8 of 23 : AccessControlMixin.sol
// SPDX-License-Identifier: AGPL-3.0-or-later

pragma solidity >=0.6.0 <0.9.0;

import "./IAccessControlProxy.sol";

abstract contract AccessControlMixin {
    IAccessControlProxy public accessControlProxy;

    function _initAccessControl(address _accessControlProxy) internal {
        accessControlProxy = IAccessControlProxy(_accessControlProxy);
    }

    modifier hasRole(bytes32 _role, address _account) {
        accessControlProxy.checkRole(_role, _account);
        _;
    }

    modifier onlyRole(bytes32 _role) {
        accessControlProxy.checkRole(_role, msg.sender);
        _;
    }

    modifier onlyGovOrDelegate() {
        accessControlProxy.checkGovOrDelegate(msg.sender);
        _;
    }

    modifier isVaultManager() {
        accessControlProxy.checkVaultOrGov(msg.sender);
        _;
    }

    modifier isKeeper() {
        accessControlProxy.checkKeeperOrVaultOrGov(msg.sender);
        _;
    }
}

File 9 of 23 : IterableIntMap.sol
// SPDX-License-Identifier: AGPL-3.0-or-later

pragma solidity ^0.8.0;

import '@openzeppelin/contracts/utils/structs/EnumerableSet.sol';

library IterableIntMap {

    using EnumerableSet for EnumerableSet.AddressSet;

    struct Map {
        // Storage of keys
        EnumerableSet.AddressSet _keys;

        mapping (address => int256) _values;
    }

    /**
    * @dev Adds a key-value pair to a map, or updates the value for an existing
    * key. O(1).
    *
    * Returns true if the key was added to the map, that is if it was not
    * already present.
    */
    function _set(Map storage map, address key, int256 value) private returns (bool) {
        map._values[key] = value;
        return map._keys.add(key);
    }

    /**
    * @dev plus a key‘s value pair in a map
    * key. O(1).
    *
    * Returns true if the key was added to the map, that is if it was not
    * already present.
    */
    function _plus(Map storage map, address key, int256 value) private {
        map._values[key] += value;
        map._keys.add(key);
    }

    /**
    * @dev minus a key‘s value pair in a map
    * key. O(1).
    *
    * Returns true if the key was added to the map, that is if it was not
    * already present.
    */
    function _minus(Map storage map, address key, int256 value) private {
        map._values[key] -= value;
        map._keys.add(key);
    }

    /**
     * @dev Removes a key-value pair from a map. O(1).
     *
     * Returns true if the key was removed from the map, that is if it was present.
     */
    function _remove(Map storage map, address key) private returns (bool) {
        delete map._values[key];
        return map._keys.remove(key);
    }

    /**
     * @dev Returns true if the key is in the map. O(1).
     */
    function _contains(Map storage map, address key) private view returns (bool) {
        return map._keys.contains(key);
    }

    /**
     * @dev Returns the number of key-value pairs in the map. O(1).
     */
    function _length(Map storage map) private view returns (uint256) {
        return map._keys.length();
    }

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

    /**
     * @dev Returns the value associated with `key`.  O(1).
     *
     * Requirements:
     *
     * - `key` must be in the map.
     */
    function _get(Map storage map, address key) private view returns (int256) {
        int256 value = map._values[key];
        return value;
    }

    struct AddressToIntMap {
        Map _inner;
    }

    /**
     * @dev Adds a key-value pair to a map, or updates the value for an existing
     * key. O(1).
     *
     * Returns true if the key was added to the map, that is if it was not
     * already present.
     */
    function set(AddressToIntMap storage map, address key, int256 value) internal returns (bool) {
        return _set(map._inner, key, value);
    }

    /**
    * @dev plus a key‘s value pair in a map
    * key. O(1).
    *
    * Returns true if the key was added to the map, that is if it was not
    * already present.
    */
    function plus(AddressToIntMap storage map, address key, int256 value) internal {
        return _plus(map._inner, key, value);
    }

    /**
    * @dev minus a key‘s value pair in a map
    * key. O(1).
    *
    * Returns true if the key was added to the map, that is if it was not
    * already present.
    */
    function minus(AddressToIntMap storage map, address key, int256 value) internal {
        return _minus(map._inner, key, value);
    }

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

    /**
     * @dev Returns true if the key is in the map. O(1).
     */
    function contains(AddressToIntMap storage map, address key) internal view returns (bool) {
        return _contains(map._inner, key);
    }

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

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

    /**
     * @dev Returns the value associated with `key`.  O(1).
     *
     * Requirements:
     *
     * - `key` must be in the map.
     */
    function get(AddressToIntMap storage map, address key) internal view returns (int256) {
        return _get(map._inner, key);
    }
}

File 10 of 23 : StableMath.sol
// SPDX-License-Identifier: AGPL-3.0-or-later

pragma solidity ^0.8.0;

import {SafeMath} from "@openzeppelin/contracts/utils/math/SafeMath.sol";

// Based on StableMath from Stability Labs Pty. Ltd.
// https://github.com/mstable/mStable-contracts/blob/master/contracts/shared/StableMath.sol

library StableMath {

    /**
     * @dev Scaling unit for use in specific calculations,
     * where 1 * 10**18, or 1e18 represents a unit '1'
     */
    uint256 private constant FULL_SCALE = 1e18;

    /***************************************
                    Helpers
    ****************************************/

    /**
     * @dev Adjust the scale of an integer
     * @param to Decimals to scale to
     * @param from Decimals to scale from
     */
    function scaleBy(
        uint256 x,
        uint256 to,
        uint256 from
    ) internal pure returns (uint256) {
        if (to > from) {
            x = x * (10 ** (to - from));
        } else if (to < from) {
            x = x / (10 ** (from - to));
        }
        return x;
    }

    /***************************************
               Precise Arithmetic
    ****************************************/

    /**
     * @dev Multiplies two precise units, and then truncates by the full scale
     * @param x Left hand input to multiplication
     * @param y Right hand input to multiplication
     * @return Result after multiplying the two inputs and then dividing by the shared
     *         scale unit
     */
    function mulTruncate(uint256 x, uint256 y) internal pure returns (uint256) {
        return mulTruncateScale(x, y, FULL_SCALE);
    }

    /**
     * @dev Multiplies two precise units, and then truncates by the given scale. For example,
     * when calculating 90% of 10e18, (10e18 * 9e17) / 1e18 = (9e36) / 1e18 = 9e18
     * @param x Left hand input to multiplication
     * @param y Right hand input to multiplication
     * @param scale Scale unit
     * @return Result after multiplying the two inputs and then dividing by the shared
     *         scale unit
     */
    function mulTruncateScale(
        uint256 x,
        uint256 y,
        uint256 scale
    ) internal pure returns (uint256) {
        // e.g. assume scale = fullScale
        // z = 10e18 * 9e17 = 9e36
        uint256 z = x * y;
        // return 9e36 / 1e18 = 9e18
        return z / scale;
    }

    /**
     * @dev Multiplies two precise units, and then truncates by the full scale, rounding up the result
     * @param x Left hand input to multiplication
     * @param y Right hand input to multiplication
     * @return Result after multiplying the two inputs and then dividing by the shared
     *          scale unit, rounded up to the closest base unit.
     */
    function mulTruncateCeil(uint256 x, uint256 y)
    internal
    pure
    returns (uint256)
    {
        // e.g. 8e17 * 17268172638 = 138145381104e17
        uint256 scaled = x * y;
        // e.g. 138145381104e17 + 9.99...e17 = 138145381113.99...e17
        uint256 ceil = scaled + (FULL_SCALE - 1);
        // e.g. 13814538111.399...e18 / 1e18 = 13814538111
        return ceil / FULL_SCALE;
    }

    /**
     * @dev Precisely divides two units, by first scaling the left hand operand. Useful
     *      for finding percentage weightings, i.e. 8e18/10e18 = 80% (or 8e17)
     * @param x Left hand input to division
     * @param y Right hand input to division
     * @return Result after multiplying the left operand by the scale, and
     *         executing the division on the right hand input.
     */
    function divPrecisely(uint256 x, uint256 y)
    internal
    pure
    returns (uint256)
    {
        // e.g. 8e18 * 1e18 = 8e36
        uint256 z = x * FULL_SCALE;
        // e.g. 8e36 / 10e18 = 8e17
        return z / y;
    }

    /**
     * @dev Precisely divides two units, by first scaling the left hand operand. Useful
     *      for finding percentage weightings, i.e. 8e18/10e18 = 80% (or 8e17)
     * @param x Left hand input to division
     * @param y Right hand input to division
     * @return Result after multiplying the left operand by the scale, and
     *         executing the division on the right hand input.
     */
    function divPreciselyScale(uint256 x, uint256 y, uint256 scale)
    internal
    pure
    returns (uint256)
    {
        // e.g. 8e18 * 1e18 = 8e36
        uint256 z = x * scale;
        // e.g. 8e36 / 10e18 = 8e17
        return z / y;
    }
}

File 11 of 23 : IPegToken.sol
// SPDX-License-Identifier: AGPL-3.0-or-later

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

interface IPegToken is IERC20 {

    /**
     * @return the total shares minted.
     */
    function totalShares() external view returns (uint256);

    /**
     * @return the shares of specified address.
     */
    function sharesOf(address _account) external view returns (uint256);

    /**
     * @dev query the value that can be returned for a specified number of shares.
     * @return underlying units etc usd/eth.
     */
    function getUnderlyingUnitsByShares(uint256 _sharesAmount) external view returns (uint256);

    /**
     * @dev query the shares that can be returned for a specified number of underlying uints.
     * @return the shares.
     */
    function getSharesByUnderlyingUnits(uint256 _underlyingUnits) external view returns (uint256);
    
    /**
     * @dev change the pause state.
     * @param _isPaused.
     */
    function changePauseState(bool _isPaused) external;

    /**
     * @notice Creates `_sharesAmount` shares and assigns them to `_recipient`, increasing the total amount of shares.
     * @dev This doesn't increase the token total supply.
     *
     * Requirements:
     *
     * - `_recipient` cannot be the zero address.
     * - the contract must not be paused.
     */
    function mintShares(address _recipient, uint256 _sharesAmount) external;

    /**
     * @notice Destroys `_sharesAmount` shares from `_account`'s holdings, decreasing the total amount of shares.
     * @dev This doesn't decrease the token total supply.
     *
     * Requirements:
     *
     * - `_account` cannot be the zero address.
     * - `_account` must hold at least `_sharesAmount` shares.
     * - the contract must not be paused.
     */
    function burnShares(address _account, uint256 _sharesAmount) external;

}

File 12 of 23 : IVaultBuffer.sol
// SPDX-License-Identifier: AGPL-3.0-or-later

pragma solidity ^0.8.0;

interface IVaultBuffer {
    event OpenDistribute();
    event CloseDistribute();

    /// @notice mint pending shares
    /// @param _sender user account address
    /// @param _amount mint amount
    function mint(address _sender, uint256 _amount) external payable;

    /// @notice transfer cash to vault
    /// @param _assets transfer token
    /// @param _amounts transfer token amount
    function transferCashToVault(address[] memory _assets, uint256[] memory _amounts) external;

    function openDistribute() external;

    function distributeWhenDistributing() external returns (bool);

    function distributeOnce() external returns (bool);

    function isDistributing() external view returns (bool);

    function getDistributeLimit() external view returns (uint256);

    function setDistributeLimit(uint256 _limit) external;
}

File 13 of 23 : BocRoles.sol
// SPDX-License-Identifier: AGPL-3.0-or-later

pragma solidity >=0.6.0 <0.9.0;

library BocRoles {
    bytes32 internal constant GOV_ROLE = 0x00;

    bytes32 internal constant DELEGATE_ROLE = keccak256("DELEGATE_ROLE");

    bytes32 internal constant VAULT_ROLE = keccak256("VAULT_ROLE");

    bytes32 internal constant KEEPER_ROLE = keccak256("KEEPER_ROLE");
}

File 14 of 23 : IStrategy.sol
// SPDX-License-Identifier: AGPL-3.0-or-later

pragma solidity ^0.8.0;

import "../vault/IVault.sol";

interface IStrategy {
    struct OutputInfo {
        uint256 outputCode; //0:default path,Greater than 0:specify output path
        address[] outputTokens; //output tokens
    }

    event Borrow(address[] _assets, uint256[] _amounts);

    event Repay(uint256 _withdrawShares, uint256 _totalShares, address[] _assets, uint256[] _amounts);

    event SetIsWantRatioIgnorable(bool _oldValue, bool _newValue);

    /// @notice Version of strategy
    function getVersion() external pure returns (string memory);

    /// @notice Name of strategy
    function name() external view returns (string memory);

    /// @notice ID of strategy
    function protocol() external view returns (uint16);

    /// @notice Vault address
    function vault() external view returns (IVault);

    /// @notice Harvester address
    function harvester() external view returns (address);

    /// @notice Provide the strategy need underlying token and ratio
    function getWantsInfo() external view returns (address[] memory _assets, uint256[] memory _ratios);

    /// @notice Provide the strategy need underlying token
    function getWants() external view returns (address[] memory _wants);

    // @notice Provide the strategy output path when withdraw.
    function getOutputsInfo() external view returns (OutputInfo[] memory _outputsInfo);

    /// @notice True means that can ignore ratios given by wants info
    function setIsWantRatioIgnorable(bool _isWantRatioIgnorable) external;

    /// @notice Returns the position details of the strategy.
    function getPositionDetail()
        external
        view
        returns (
            address[] memory _tokens,
            uint256[] memory _amounts,
            bool _isUsd,
            uint256 _usdValue
        );

    /// @notice Total assets of strategy in USD.
    function estimatedTotalAssets() external view returns (uint256);

    /// @notice 3rd protocol's pool total assets in USD.
    function get3rdPoolAssets() external view returns (uint256);

    /// @notice Harvests the Strategy, recognizing any profits or losses and adjusting the Strategy's position.
    function harvest() external returns (address[] memory _rewardsTokens, uint256[] memory _claimAmounts);

    /// @notice Strategy borrow funds from vault
    /// @param _assets borrow token address
    /// @param _amounts borrow token amount
    function borrow(address[] memory _assets, uint256[] memory _amounts) external;

    /// @notice Strategy repay the funds to vault
    /// @param _withdrawShares Numerator
    /// @param _totalShares Denominator
    function repay(
        uint256 _withdrawShares,
        uint256 _totalShares,
        uint256 _outputCode
    ) external returns (address[] memory _assets, uint256[] memory _amounts);

    /// @notice getter isWantRatioIgnorable
    function isWantRatioIgnorable() external view returns (bool);

    /// @notice Investable amount of strategy in USD
    function poolQuota() external view returns (uint256);
}

File 15 of 23 : IValueInterpreter.sol
// SPDX-License-Identifier: AGPL-3.0-or-later

pragma solidity ^0.8.0;

interface IValueInterpreter {

    /// @notice Calculates the value of a given amount of one asset in terms of another asset
    /// @param _baseAsset The asset from which to convert
    /// @param _amount The amount of the _baseAsset to convert
    /// @param _quoteAsset The asset to which to convert
    /// @return _value The equivalent quantity in the _quoteAsset
    /// @dev Does not alter protocol state,
    /// but not a view because calls to price feeds can potentially update third party state
    function calcCanonicalAssetValue(
        address _baseAsset,
        uint256 _amount,
        address _quoteAsset
    ) external view returns (uint256);

    /// @notice Calculates the total value of given amounts of assets in a single quote asset
    /// @param _baseAssets The assets to convert
    /// @param _amounts The amounts of the _baseAssets to convert
    /// @param _quoteAsset The asset to which to convert
    /// @return _value The sum value of _baseAssets, denominated in the _quoteAsset
    /// @dev Does not alter protocol state,
    /// but not a view because calls to price feeds can potentially update third party state.
    /// Does not handle a derivative quote asset.
    function calcCanonicalAssetsTotalValue(
        address[] calldata _baseAssets,
        uint256[] calldata _amounts,
        address _quoteAsset
    ) external view returns (uint256);

    
    /// @dev Calculate the usd value of a specified number of assets
    /// @param _baseAsset Source token address
    /// @param _amount The amount of source token
    /// @return usd(1e18)
    function calcCanonicalAssetValueInUsd(
        address _baseAsset,
        uint256 _amount
    ) external view returns (uint256);

     
    /// @dev Calculate the usd value of baseUnit volume assets
    /// @param _baseAsset The ssset token address
    /// @return usd(1e18)
    function price(address _baseAsset) external view returns (uint256);
}

File 16 of 23 : Initializable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.6.0) (proxy/utils/Initializable.sol)

pragma solidity ^0.8.2;

import "../../utils/AddressUpgradeable.sol";

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

    /**
     * @dev Indicates that the contract is in the process of being initialized.
     */
    bool private _initializing;

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

    /**
     * @dev A modifier that defines a protected initializer function that can be invoked at most once. In its scope,
     * `onlyInitializing` functions can be used to initialize parent contracts. Equivalent to `reinitializer(1)`.
     */
    modifier initializer() {
        bool isTopLevelCall = _setInitializedVersion(1);
        if (isTopLevelCall) {
            _initializing = true;
        }
        _;
        if (isTopLevelCall) {
            _initializing = false;
            emit Initialized(1);
        }
    }

    /**
     * @dev A modifier that defines a protected reinitializer function that can be invoked at most once, and only if the
     * contract hasn't been initialized to a greater version before. In its scope, `onlyInitializing` functions can be
     * used to initialize parent contracts.
     *
     * `initializer` is equivalent to `reinitializer(1)`, so a reinitializer may be used after the original
     * initialization step. This is essential to configure modules that are added through upgrades and that require
     * initialization.
     *
     * Note that versions can jump in increments greater than 1; this implies that if multiple reinitializers coexist in
     * a contract, executing them in the right order is up to the developer or operator.
     */
    modifier reinitializer(uint8 version) {
        bool isTopLevelCall = _setInitializedVersion(version);
        if (isTopLevelCall) {
            _initializing = true;
        }
        _;
        if (isTopLevelCall) {
            _initializing = false;
            emit Initialized(version);
        }
    }

    /**
     * @dev Modifier to protect an initialization function so that it can only be invoked by functions with the
     * {initializer} and {reinitializer} modifiers, directly or indirectly.
     */
    modifier onlyInitializing() {
        require(_initializing, "Initializable: contract is not initializing");
        _;
    }

    /**
     * @dev Locks the contract, preventing any future reinitialization. This cannot be part of an initializer call.
     * Calling this in the constructor of a contract will prevent that contract from being initialized or reinitialized
     * to any version. It is recommended to use this to lock implementation contracts that are designed to be called
     * through proxies.
     */
    function _disableInitializers() internal virtual {
        _setInitializedVersion(type(uint8).max);
    }

    function _setInitializedVersion(uint8 version) private returns (bool) {
        // If the contract is initializing we ignore whether _initialized is set in order to support multiple
        // inheritance patterns, but we only do this in the context of a constructor, and for the lowest level
        // of initializers, because in other contexts the contract may have been reentered.
        if (_initializing) {
            require(
                version == 1 && !AddressUpgradeable.isContract(address(this)),
                "Initializable: contract is already initialized"
            );
            return false;
        } else {
            require(_initialized < version, "Initializable: contract is already initialized");
            _initialized = version;
            return true;
        }
    }
}

File 17 of 23 : ReentrancyGuardUpgradeable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (security/ReentrancyGuard.sol)

pragma solidity ^0.8.0;
import "../proxy/utils/Initializable.sol";

/**
 * @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 ReentrancyGuardUpgradeable is Initializable {
    // 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;

    function __ReentrancyGuard_init() internal onlyInitializing {
        __ReentrancyGuard_init_unchained();
    }

    function __ReentrancyGuard_init_unchained() internal onlyInitializing {
        _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() {
        // On the first call to nonReentrant, _notEntered will be true
        require(_status != _ENTERED, "ReentrancyGuard: reentrant call");

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

        _;

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

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

File 18 of 23 : EnumerableSet.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.0) (utils/structs/EnumerableSet.sol)

pragma solidity ^0.8.0;

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

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

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

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

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

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

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

                // Move the last value to the index where the value to delete is
                set._values[toDeleteIndex] = lastValue;
                // Update the index for the moved value
                set._indexes[lastValue] = valueIndex; // Replace lastValue's index to valueIndex
            }

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

            // Delete the index for the deleted slot
            delete set._indexes[value];

            return true;
        } else {
            return false;
        }
    }

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

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

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

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

    // Bytes32Set

    struct Bytes32Set {
        Set _inner;
    }

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

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

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

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

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

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

    // AddressSet

    struct AddressSet {
        Set _inner;
    }

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

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

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

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

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

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

        /// @solidity memory-safe-assembly
        assembly {
            result := store
        }

        return result;
    }

    // UintSet

    struct UintSet {
        Set _inner;
    }

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

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

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

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

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

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

        /// @solidity memory-safe-assembly
        assembly {
            result := store
        }

        return result;
    }
}

File 19 of 23 : IAccessControlProxy.sol
// SPDX-License-Identifier: AGPL-3.0-or-later

pragma solidity >=0.6.0 <0.9.0;

interface IAccessControlProxy {
    function isGovOrDelegate(address _account) external view returns (bool);

    function isVaultOrGov(address _account) external view returns (bool);

    function isKeeperOrVaultOrGov(address _account) external view returns (bool);

    function hasRole(bytes32 _role, address _account) external view returns (bool);

    function checkRole(bytes32 _role, address _account) external view;

    function checkGovOrDelegate(address _account) external view;

    function checkVaultOrGov(address _account) external view;

    function checkKeeperOrVaultOrGov(address _account) external;
}

File 20 of 23 : SafeMath.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.6.0) (utils/math/SafeMath.sol)

pragma solidity ^0.8.0;

// CAUTION
// This version of SafeMath should only be used with Solidity 0.8 or later,
// because it relies on the compiler's built in overflow checks.

/**
 * @dev Wrappers over Solidity's arithmetic operations.
 *
 * NOTE: `SafeMath` is generally not needed starting with Solidity 0.8, since the compiler
 * now has built in overflow checking.
 */
library SafeMath {
    /**
     * @dev Returns the addition of two unsigned integers, with an overflow flag.
     *
     * _Available since v3.4._
     */
    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.
     *
     * _Available since v3.4._
     */
    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.
     *
     * _Available since v3.4._
     */
    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.
     *
     * _Available since v3.4._
     */
    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.
     *
     * _Available since v3.4._
     */
    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 addition of two unsigned integers, reverting on
     * overflow.
     *
     * Counterpart to Solidity's `+` operator.
     *
     * Requirements:
     *
     * - Addition cannot overflow.
     */
    function add(uint256 a, uint256 b) internal pure returns (uint256) {
        return a + b;
    }

    /**
     * @dev Returns the subtraction of two unsigned integers, reverting on
     * overflow (when the result is negative).
     *
     * Counterpart to Solidity's `-` operator.
     *
     * Requirements:
     *
     * - Subtraction cannot overflow.
     */
    function sub(uint256 a, uint256 b) internal pure returns (uint256) {
        return a - b;
    }

    /**
     * @dev Returns the multiplication of two unsigned integers, reverting on
     * overflow.
     *
     * Counterpart to Solidity's `*` operator.
     *
     * Requirements:
     *
     * - Multiplication cannot overflow.
     */
    function mul(uint256 a, uint256 b) internal pure returns (uint256) {
        return a * b;
    }

    /**
     * @dev Returns the integer division of two unsigned integers, reverting on
     * division by zero. The result is rounded towards zero.
     *
     * Counterpart to Solidity's `/` operator.
     *
     * Requirements:
     *
     * - The divisor cannot be zero.
     */
    function div(uint256 a, uint256 b) internal pure returns (uint256) {
        return a / b;
    }

    /**
     * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
     * reverting when dividing by zero.
     *
     * Counterpart to Solidity's `%` operator. This function uses a `revert`
     * opcode (which leaves remaining gas untouched) while Solidity uses an
     * invalid opcode to revert (consuming all remaining gas).
     *
     * Requirements:
     *
     * - The divisor cannot be zero.
     */
    function mod(uint256 a, uint256 b) internal pure returns (uint256) {
        return a % b;
    }

    /**
     * @dev Returns the subtraction of two unsigned integers, reverting with custom message on
     * overflow (when the result is negative).
     *
     * CAUTION: This function is deprecated because it requires allocating memory for the error
     * message unnecessarily. For custom revert reasons use {trySub}.
     *
     * Counterpart to Solidity's `-` operator.
     *
     * Requirements:
     *
     * - Subtraction cannot overflow.
     */
    function sub(
        uint256 a,
        uint256 b,
        string memory errorMessage
    ) internal pure returns (uint256) {
        unchecked {
            require(b <= a, errorMessage);
            return a - b;
        }
    }

    /**
     * @dev Returns the integer division of two unsigned integers, reverting with custom message on
     * division by zero. The result is rounded towards zero.
     *
     * Counterpart to Solidity's `/` operator. Note: this function uses a
     * `revert` opcode (which leaves remaining gas untouched) while Solidity
     * uses an invalid opcode to revert (consuming all remaining gas).
     *
     * Requirements:
     *
     * - The divisor cannot be zero.
     */
    function div(
        uint256 a,
        uint256 b,
        string memory errorMessage
    ) internal pure returns (uint256) {
        unchecked {
            require(b > 0, errorMessage);
            return a / b;
        }
    }

    /**
     * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
     * reverting with custom message when dividing by zero.
     *
     * CAUTION: This function is deprecated because it requires allocating memory for the error
     * message unnecessarily. For custom revert reasons use {tryMod}.
     *
     * Counterpart to Solidity's `%` operator. This function uses a `revert`
     * opcode (which leaves remaining gas untouched) while Solidity uses an
     * invalid opcode to revert (consuming all remaining gas).
     *
     * Requirements:
     *
     * - The divisor cannot be zero.
     */
    function mod(
        uint256 a,
        uint256 b,
        string memory errorMessage
    ) internal pure returns (uint256) {
        unchecked {
            require(b > 0, errorMessage);
            return a % b;
        }
    }
}

File 21 of 23 : IERC20.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.6.0) (token/ERC20/IERC20.sol)

pragma solidity ^0.8.0;

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

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

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

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

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

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

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

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

File 22 of 23 : IVault.sol
// SPDX-License-Identifier: AGPL-3.0-or-later

pragma solidity ^0.8.0;
pragma experimental ABIEncoderV2;

import "../exchanges/IExchangeAggregator.sol";

interface IVault {
    /// @param lastReport The last report timestamp
    /// @param totalDebt The total asset of this strategy
    /// @param profitLimitRatio The limited ratio of profit
    /// @param lossLimitRatio The limited ratio for loss
    /// @param enforceChangeLimit The switch of enforce change Limit
    struct StrategyParams {
        uint256 lastReport;
        uint256 totalDebt;
        uint256 profitLimitRatio;
        uint256 lossLimitRatio;
        bool enforceChangeLimit;
    }

    /// @param strategy The new strategy to add
    /// @param profitLimitRatio The limited ratio of profit
    /// @param lossLimitRatio The limited ratio for loss
    struct StrategyAdd {
        address strategy;
        uint256 profitLimitRatio;
        uint256 lossLimitRatio;
    }

    event AddAsset(address _asset);
    event RemoveAsset(address _asset);
    event AddStrategies(address[] _strategies);
    event RemoveStrategies(address[] _strategies);
    event RemoveStrategyByForce(address _strategy);
    event Mint(address _account, address[] _assets, uint256[] _amounts, uint256 _mintAmount);
    event Burn(
        address _account,
        uint256 _amount,
        uint256 _actualAmount,
        uint256 _shareAmount,
        address[] _assets,
        uint256[] _amounts
    );
    event Exchange(
        address _platform,
        address _srcAsset,
        uint256 _srcAmount,
        address _distAsset,
        uint256 _distAmount
    );
    event Redeem(address _strategy, uint256 _debtChangeAmount, address[] _assets, uint256[] _amounts);
    event LendToStrategy(
        address indexed _strategy,
        address[] _wants,
        uint256[] _amounts,
        uint256 _lendValue
    );
    event RepayFromStrategy(
        address indexed _strategy,
        uint256 _strategyWithdrawValue,
        uint256 _strategyTotalValue,
        address[] _assets,
        uint256[] _amounts
    );
    event RemoveStrategyFromQueue(address[] _strategies);
    event SetEmergencyShutdown(bool _shutdown);
    event RebasePaused();
    event RebaseUnpaused();
    event RebaseThresholdUpdated(uint256 _threshold);
    event TrusteeFeeBpsChanged(uint256 _basis);
    event MaxTimestampBetweenTwoReportedChanged(uint256 _maxTimestampBetweenTwoReported);
    event MinCheckedStrategyTotalDebtChanged(uint256 _minCheckedStrategyTotalDebt);
    event MinimumInvestmentAmountChanged(uint256 _minimumInvestmentAmount);
    event TreasuryAddressChanged(address _address);
    event ExchangeManagerAddressChanged(address _address);
    event SetAdjustPositionPeriod(bool _adjustPositionPeriod);
    event RedeemFeeUpdated(uint256 _redeemFeeBps);
    event SetWithdrawalQueue(address[] _queues);
    event Rebase(uint256 _totalShares, uint256 _totalValue, uint256 _newUnderlyingUnitsPerShare);
    event StrategyReported(
        address indexed _strategy,
        uint256 _gain,
        uint256 _loss,
        uint256 _lastStrategyTotalDebt,
        uint256 _nowStrategyTotalDebt,
        address[] _rewardTokens,
        uint256[] _claimAmounts,
        uint256 _type
    );
    event StartAdjustPosition(
        uint256 _totalDebtOfBeforeAdjustPosition,
        address[] _trackedAssets,
        uint256[] _vaultCashDetatil,
        uint256[] _vaultBufferCashDetail
    );
    event EndAdjustPosition(
        uint256 _transferValue,
        uint256 _redeemValue,
        uint256 _totalDebt,
        uint256 _totalValueOfAfterAdjustPosition,
        uint256 _totalValueOfBeforeAdjustPosition
    );
    event PegTokenSwapCash(uint256 _pegTokenAmount, address[] _assets, uint256[] _amounts);

    /// @notice Version of vault
    function getVersion() external pure returns (string memory);

    /// @notice Minting USDi supported assets
    function getSupportAssets() external view returns (address[] memory _assets);

    /// @notice Check '_asset' is supported or not
    function checkIsSupportAsset(address _asset) external view;

    /// @notice Assets held by Vault
    function getTrackedAssets() external view returns (address[] memory _assets);

    /// @notice Vault holds asset value directly in USD
    function valueOfTrackedTokens() external view returns (uint256 _totalValue);

    /// @notice Vault and vault buffer holds asset value directly in USD
    function valueOfTrackedTokensIncludeVaultBuffer() external view returns (uint256 _totalValue);

    /// @notice Vault total asset in USD
    function totalAssets() external view returns (uint256);

    /// @notice Vault and vault buffer total asset in USD
    function totalAssetsIncludeVaultBuffer() external view returns (uint256);

    /// @notice Vault total value(by chainlink price) in USD(1e18)
    function totalValue() external view returns (uint256);

    /// @notice Start adjust position
    function startAdjustPosition() external;

    /// @notice End adjust position
    function endAdjustPosition() external;

    /// @notice Return underlying token per share token
    function underlyingUnitsPerShare() external view returns (uint256);

    /// @notice Get pegToken price in USD(1e18)
    function getPegTokenPrice() external view returns (uint256);

    /**
     * @dev Internal to calculate total value of all assets held in Vault.
     * @return _value Total value(by chainlink price) in USD (1e18)
     */
    function totalValueInVault() external view returns (uint256 _value);

    /**
     * @dev Internal to calculate total value of all assets held in Strategies.
     * @return _value Total value(by chainlink price) in USD (1e18)
     */
    function totalValueInStrategies() external view returns (uint256 _value);

    /// @notice Return all strategy addresses
    function getStrategies() external view returns (address[] memory _strategies);

    /// @notice Check '_strategy' is active or not
    function checkActiveStrategy(address _strategy) external view;

    /// @notice estimate Minting share with stablecoins
    /// @param _assets Address of the asset being deposited
    /// @param _amounts Amount of the asset being deposited
    /// @dev Support single asset or multi-assets
    /// @return _shareAmount
    function estimateMint(address[] memory _assets, uint256[] memory _amounts)
        external
        view
        returns (uint256 _shareAmount);

    /// @notice Minting share with stablecoins
    /// @param _assets Address of the asset being deposited
    /// @param _amounts Amount of the asset being deposited
    /// @dev Support single asset or multi-assets
    /// @return _shareAmount
    function mint(
        address[] memory _assets,
        uint256[] memory _amounts,
        uint256 _minimumAmount
    ) external returns (uint256 _shareAmount);

    /// @notice burn USDi,return stablecoins
    /// @param _amount Amount of USDi to burn
    /// @param _minimumAmount Minimum usd to receive in return
    function burn(uint256 _amount, uint256 _minimumAmount)
        external
        returns (address[] memory _assets, uint256[] memory _amounts);

    /// @notice Change USDi supply with Vault total assets.
    function rebase() external;

    /// @notice Allocate funds in Vault to strategies.
    function lend(address _strategy, IExchangeAggregator.ExchangeToken[] calldata _exchangeTokens)
        external;

    /// @notice Withdraw the funds from specified strategy.
    function redeem(
        address _strategy,
        uint256 _amount,
        uint256 _outputCode
    ) external;

    /**
     * @dev Exchange from '_fromToken' to '_toToken'
     * @param _fromToken The token swap from
     * @param _toToken The token swap to
     * @param _amount The amount to swap
     * @param _exchangeParam The struct of ExchangeParam, see {ExchangeParam} struct
     * @return _exchangeAmount The real amount to exchange
     * Emits a {Exchange} event.
     */
    function exchange(
        address _fromToken,
        address _toToken,
        uint256 _amount,
        IExchangeAggregator.ExchangeParam memory _exchangeParam
    ) external returns (uint256);

    /**
     * @dev Report the current asset of strategy caller
     * @param _rewardTokens The reward token list
     * @param _claimAmounts The claim amount list
     * Emits a {StrategyReported} event.
     */
    function report(address[] memory _rewardTokens, uint256[] memory _claimAmounts) external;

    /// @notice Shutdown the vault when an emergency occurs, cannot mint/burn.
    function setEmergencyShutdown(bool _active) external;

    /// @notice set adjustPositionPeriod true when adjust position occurs, cannot remove add asset/strategy and cannot mint/burn.
    function setAdjustPositionPeriod(bool _adjustPositionPeriod) external;

    /**
     * @dev Set a minimum difference ratio automatically rebase.
     * rebase
     * @param _threshold _threshold is the numerator and the denominator is 10000000 (x/10000000).
     */
    function setRebaseThreshold(uint256 _threshold) external;

    /**
     * @dev Set a fee in basis points to be charged for a redeem.
     * @param _redeemFeeBps Basis point fee to be charged
     */
    function setRedeemFeeBps(uint256 _redeemFeeBps) external;

    /**
     * @dev Sets the treasuryAddress that can receive a portion of yield.
     *      Setting to the zero address disables this feature.
     */
    function setTreasuryAddress(address _address) external;

    /**
     * @dev Sets the exchangeManagerAddress that can receive a portion of yield.
     */
    function setExchangeManagerAddress(address _exchangeManagerAddress) external;

    /**
     * @dev Sets the TrusteeFeeBps to the percentage of yield that should be
     *      received in basis points.
     */
    function setTrusteeFeeBps(uint256 _basis) external;

    /// @notice set '_queues' as advance withdrawal queue
    function setWithdrawalQueue(address[] memory _queues) external;

    function setStrategyEnforceChangeLimit(address _strategy, bool _enabled) external;

    function setStrategySetLimitRatio(
        address _strategy,
        uint256 _lossRatioLimit,
        uint256 _profitLimitRatio
    ) external;

    /**
     * @dev Set the deposit paused flag to true to prevent rebasing.
     */
    function pauseRebase() external;

    /**
     * @dev Set the deposit paused flag to true to allow rebasing.
     */
    function unpauseRebase() external;

    /// @notice Added support for specific asset.
    function addAsset(address _asset) external;

    /// @notice Remove support for specific asset.
    function removeAsset(address _asset) external;

    /// @notice Add strategy to strategy list
    /// @dev The strategy added to the strategy list,
    ///      Vault may invest funds into the strategy,
    ///      and the strategy will invest the funds in the 3rd protocol
    function addStrategy(StrategyAdd[] memory _strategyAdds) external;

    /// @notice Remove strategy from strategy list
    /// @dev The removed policy withdraws funds from the 3rd protocol and returns to the Vault
    function removeStrategy(address[] memory _strategies) external;

    function forceRemoveStrategy(address _strategy) external;

    /***************************************
                     WithdrawalQueue
     ****************************************/
    function getWithdrawalQueue() external view returns (address[] memory);

    function removeStrategyFromQueue(address[] memory _strategies) external;

    /// @notice Return the period of adjust position
    function adjustPositionPeriod() external view returns (bool);

    /// @notice Return the status of emergency shutdown switch
    function emergencyShutdown() external view returns (bool);

    /// @notice Return the status of rebase paused switch
    function rebasePaused() external view returns (bool);

    /// @notice Return the rebaseThreshold value,
    /// over this difference ratio automatically rebase.
    /// rebaseThreshold is the numerator and the denominator is 10000000 x/10000000.
    function rebaseThreshold() external view returns (uint256);

    /// @notice Return the Amount of yield collected in basis points
    function trusteeFeeBps() external view returns (uint256);

    /// @notice Return the redemption fee in basis points
    function redeemFeeBps() external view returns (uint256);

    /// @notice Return the total asset of all strategy
    function totalDebt() external view returns (uint256);

    /// @notice Return the exchange manager address
    function exchangeManager() external view returns (address);

    /// @notice Return all info of '_strategy'
    function strategies(address _strategy) external view returns (StrategyParams memory);

    /// @notice Return withdraw strategy address list
    function withdrawQueue() external view returns (address[] memory);

    /// @notice Return the address of treasury
    function treasury() external view returns (address);

    /// @notice Return the address of price oracle
    function valueInterpreter() external view returns (address);

    /// @notice Return the address of access control proxy contract
    function accessControlProxy() external view returns (address);

    /// @notice Set the minimum strategy total debt that will be checked for the strategy reporting
    function setMinCheckedStrategyTotalDebt(uint256 _minCheckedStrategyTotalDebt) external;

    /// @notice Return the minimum strategy total debt that will be checked for the strategy reporting
    function minCheckedStrategyTotalDebt() external view returns (uint256);

    /// @notice Set the maximum timestamp between two reported
    function setMaxTimestampBetweenTwoReported(uint256 _maxTimestampBetweenTwoReported) external;

    /// @notice The maximum timestamp between two reported
    function maxTimestampBetweenTwoReported() external view returns (uint256);

    /// @notice Set the minimum investment amount
    function setMinimumInvestmentAmount(uint256 _minimumInvestmentAmount) external;

    /// @notice Return the minimum investment amount
    function minimumInvestmentAmount() external view returns (uint256);

    /// @notice Set the address of vault buffer contract
    function setVaultBufferAddress(address _address) external;

    /// @notice Return the address of vault buffer contract
    function vaultBufferAddress() external view returns (address);

    /// @notice Set the address of PegToken contract
    function setPegTokenAddress(address _address) external;

    /// @notice Return the address of PegToken contract
    function pegTokenAddress() external view returns (address);

    /// @notice Set the new implement contract address
    function setAdminImpl(address _newImpl) external;
}

File 23 of 23 : IExchangeAdapter.sol
// SPDX-License-Identifier: AGPL-3.0-or-later

pragma solidity >=0.6.0 <0.9.0;
pragma experimental ABIEncoderV2;

interface IExchangeAdapter {
    /**
     * @param amount The amount to swap
     * @param srcToken The token swap from
     * @param dstToken The token swap to
     * @param receiver The user to receive `dstToken`
     */
    struct SwapDescription {
        uint256 amount;
        address srcToken;
        address dstToken;
        address receiver;
    }

    /// @notice The identifier of this exchange adapter
    function identifier() external pure returns (string memory _identifier);

    /**
     * @notice Swap with `_sd` data by using `_method` and `_data` on `_platform`.
     * @param _method The method of the exchange platform
     * @param _encodedCallArgs The encoded parameters to call
     * @param _sd The description info of this swap
     * @return The expected amountIn to swap
     */
    function swap(
        uint8 _method,
        bytes calldata _encodedCallArgs,
        SwapDescription calldata _sd
    ) external payable returns (uint256);
}

Settings
{
  "optimizer": {
    "enabled": true,
    "runs": 200,
    "details": {
      "yul": true
    }
  },
  "outputSelection": {
    "*": {
      "*": [
        "evm.bytecode",
        "evm.deployedBytecode",
        "devdoc",
        "userdoc",
        "metadata",
        "abi"
      ]
    }
  },
  "libraries": {}
}

Contract Security Audit

Contract ABI

[{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"_asset","type":"address"}],"name":"AddAsset","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address[]","name":"_strategies","type":"address[]"}],"name":"AddStrategies","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"_account","type":"address"},{"indexed":false,"internalType":"uint256","name":"_amount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"_actualAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"_shareAmount","type":"uint256"},{"indexed":false,"internalType":"address[]","name":"_assets","type":"address[]"},{"indexed":false,"internalType":"uint256[]","name":"_amounts","type":"uint256[]"}],"name":"Burn","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"_transferValue","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"_redeemValue","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"_totalDebt","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"_totalValueOfAfterAdjustPosition","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"_totalValueOfBeforeAdjustPosition","type":"uint256"}],"name":"EndAdjustPosition","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"_platform","type":"address"},{"indexed":false,"internalType":"address","name":"_srcAsset","type":"address"},{"indexed":false,"internalType":"uint256","name":"_srcAmount","type":"uint256"},{"indexed":false,"internalType":"address","name":"_distAsset","type":"address"},{"indexed":false,"internalType":"uint256","name":"_distAmount","type":"uint256"}],"name":"Exchange","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"_address","type":"address"}],"name":"ExchangeManagerAddressChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint8","name":"version","type":"uint8"}],"name":"Initialized","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"_strategy","type":"address"},{"indexed":false,"internalType":"address[]","name":"_wants","type":"address[]"},{"indexed":false,"internalType":"uint256[]","name":"_amounts","type":"uint256[]"},{"indexed":false,"internalType":"uint256","name":"_lendValue","type":"uint256"}],"name":"LendToStrategy","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"_maxTimestampBetweenTwoReported","type":"uint256"}],"name":"MaxTimestampBetweenTwoReportedChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"_minCheckedStrategyTotalDebt","type":"uint256"}],"name":"MinCheckedStrategyTotalDebtChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"_minimumInvestmentAmount","type":"uint256"}],"name":"MinimumInvestmentAmountChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"_account","type":"address"},{"indexed":false,"internalType":"address[]","name":"_assets","type":"address[]"},{"indexed":false,"internalType":"uint256[]","name":"_amounts","type":"uint256[]"},{"indexed":false,"internalType":"uint256","name":"_mintAmount","type":"uint256"}],"name":"Mint","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"_pegTokenAmount","type":"uint256"},{"indexed":false,"internalType":"address[]","name":"_assets","type":"address[]"},{"indexed":false,"internalType":"uint256[]","name":"_amounts","type":"uint256[]"}],"name":"PegTokenSwapCash","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"_totalShares","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"_totalValue","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"_newUnderlyingUnitsPerShare","type":"uint256"}],"name":"Rebase","type":"event"},{"anonymous":false,"inputs":[],"name":"RebasePaused","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"_threshold","type":"uint256"}],"name":"RebaseThresholdUpdated","type":"event"},{"anonymous":false,"inputs":[],"name":"RebaseUnpaused","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"_strategy","type":"address"},{"indexed":false,"internalType":"uint256","name":"_debtChangeAmount","type":"uint256"},{"indexed":false,"internalType":"address[]","name":"_assets","type":"address[]"},{"indexed":false,"internalType":"uint256[]","name":"_amounts","type":"uint256[]"}],"name":"Redeem","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"_redeemFeeBps","type":"uint256"}],"name":"RedeemFeeUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"_asset","type":"address"}],"name":"RemoveAsset","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address[]","name":"_strategies","type":"address[]"}],"name":"RemoveStrategies","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"_strategy","type":"address"}],"name":"RemoveStrategyByForce","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address[]","name":"_strategies","type":"address[]"}],"name":"RemoveStrategyFromQueue","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"_strategy","type":"address"},{"indexed":false,"internalType":"uint256","name":"_strategyWithdrawValue","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"_strategyTotalValue","type":"uint256"},{"indexed":false,"internalType":"address[]","name":"_assets","type":"address[]"},{"indexed":false,"internalType":"uint256[]","name":"_amounts","type":"uint256[]"}],"name":"RepayFromStrategy","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"bool","name":"_adjustPositionPeriod","type":"bool"}],"name":"SetAdjustPositionPeriod","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"bool","name":"_shutdown","type":"bool"}],"name":"SetEmergencyShutdown","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address[]","name":"_queues","type":"address[]"}],"name":"SetWithdrawalQueue","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"_totalDebtOfBeforeAdjustPosition","type":"uint256"},{"indexed":false,"internalType":"address[]","name":"_trackedAssets","type":"address[]"},{"indexed":false,"internalType":"uint256[]","name":"_vaultCashDetatil","type":"uint256[]"},{"indexed":false,"internalType":"uint256[]","name":"_vaultBufferCashDetail","type":"uint256[]"}],"name":"StartAdjustPosition","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"_strategy","type":"address"},{"indexed":false,"internalType":"uint256","name":"_gain","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"_loss","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"_lastStrategyTotalDebt","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"_nowStrategyTotalDebt","type":"uint256"},{"indexed":false,"internalType":"address[]","name":"_rewardTokens","type":"address[]"},{"indexed":false,"internalType":"uint256[]","name":"_claimAmounts","type":"uint256[]"},{"indexed":false,"internalType":"uint256","name":"_type","type":"uint256"}],"name":"StrategyReported","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"_address","type":"address"}],"name":"TreasuryAddressChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"_basis","type":"uint256"}],"name":"TrusteeFeeBpsChanged","type":"event"},{"stateMutability":"payable","type":"fallback"},{"inputs":[],"name":"accessControlProxy","outputs":[{"internalType":"contract IAccessControlProxy","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"adjustPositionPeriod","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_amount","type":"uint256"},{"internalType":"uint256","name":"_minimumAmount","type":"uint256"}],"name":"burn","outputs":[{"internalType":"address[]","name":"_assets","type":"address[]"},{"internalType":"uint256[]","name":"_amounts","type":"uint256[]"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_strategy","type":"address"}],"name":"checkActiveStrategy","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_asset","type":"address"}],"name":"checkIsSupportAsset","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[],"name":"emergencyShutdown","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"endAdjustPosition","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address[]","name":"_assets","type":"address[]"},{"internalType":"uint256[]","name":"_amounts","type":"uint256[]"}],"name":"estimateMint","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_fromToken","type":"address"},{"internalType":"address","name":"_toToken","type":"address"},{"internalType":"uint256","name":"_amount","type":"uint256"},{"components":[{"internalType":"address","name":"platform","type":"address"},{"internalType":"uint8","name":"method","type":"uint8"},{"internalType":"bytes","name":"encodeExchangeArgs","type":"bytes"},{"internalType":"uint256","name":"slippage","type":"uint256"},{"internalType":"uint256","name":"oracleAdditionalSlippage","type":"uint256"}],"internalType":"struct IExchangeAggregator.ExchangeParam","name":"_exchangeParam","type":"tuple"}],"name":"exchange","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"exchangeManager","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getPegTokenPrice","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getStrategies","outputs":[{"internalType":"address[]","name":"","type":"address[]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getSupportAssets","outputs":[{"internalType":"address[]","name":"","type":"address[]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getTrackedAssets","outputs":[{"internalType":"address[]","name":"","type":"address[]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getVersion","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"_accessControlProxy","type":"address"},{"internalType":"address","name":"_treasury","type":"address"},{"internalType":"address","name":"_exchangeManager","type":"address"},{"internalType":"address","name":"_valueInterpreter","type":"address"}],"name":"initialize","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_strategyAddr","type":"address"},{"components":[{"internalType":"address","name":"fromToken","type":"address"},{"internalType":"address","name":"toToken","type":"address"},{"internalType":"uint256","name":"fromAmount","type":"uint256"},{"components":[{"internalType":"address","name":"platform","type":"address"},{"internalType":"uint8","name":"method","type":"uint8"},{"internalType":"bytes","name":"encodeExchangeArgs","type":"bytes"},{"internalType":"uint256","name":"slippage","type":"uint256"},{"internalType":"uint256","name":"oracleAdditionalSlippage","type":"uint256"}],"internalType":"struct IExchangeAggregator.ExchangeParam","name":"exchangeParam","type":"tuple"}],"internalType":"struct IExchangeAggregator.ExchangeToken[]","name":"_exchangeTokens","type":"tuple[]"}],"name":"lend","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"maxSupplyDiff","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"maxTimestampBetweenTwoReported","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"minCheckedStrategyTotalDebt","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"minimumInvestmentAmount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address[]","name":"_assets","type":"address[]"},{"internalType":"uint256[]","name":"_amounts","type":"uint256[]"},{"internalType":"uint256","name":"_minimumAmount","type":"uint256"}],"name":"mint","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"pegTokenAddress","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"rebase","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"rebasePaused","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"rebaseThreshold","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_strategy","type":"address"},{"internalType":"uint256","name":"_amount","type":"uint256"},{"internalType":"uint256","name":"_outputCode","type":"uint256"}],"name":"redeem","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"redeemFeeBps","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address[]","name":"_rewardTokens","type":"address[]"},{"internalType":"uint256[]","name":"_claimAmounts","type":"uint256[]"}],"name":"report","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_newImpl","type":"address"}],"name":"setAdminImpl","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"startAdjustPosition","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"strategies","outputs":[{"internalType":"uint256","name":"lastReport","type":"uint256"},{"internalType":"uint256","name":"totalDebt","type":"uint256"},{"internalType":"uint256","name":"profitLimitRatio","type":"uint256"},{"internalType":"uint256","name":"lossLimitRatio","type":"uint256"},{"internalType":"bool","name":"enforceChangeLimit","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalAssets","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalAssetsIncludeVaultBuffer","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":"totalValue","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalValueInStrategies","outputs":[{"internalType":"uint256","name":"_value","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalValueInVault","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"treasury","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"trusteeFeeBps","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"underlyingUnitsPerShare","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"valueInterpreter","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"valueOfTrackedTokens","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"valueOfTrackedTokensIncludeVaultBuffer","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"vaultBufferAddress","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"withdrawQueue","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"}]

608060405234801561001057600080fd5b50615b7880620000216000396000f3fe60806040526004361061027d5760003560e01c80637af690971161014f578063b390c0ab116100c1578063d4c3eea01161007a578063d4c3eea01461078b578063e5f36762146107a0578063e6f2e598146107c0578063f8c8765e146107e0578063fc0cfeee14610800578063fc7b9c18146108205761027d565b8063b390c0ab146106db578063b49a60bb14610709578063bca9a47e1461072b578063c4b9737014610740578063cd5a7e9e14610755578063ce7f8cff1461076b5761027d565b80639fb0fbc5116101135780639fb0fbc51461063a578063a688b5d114610650578063a819e6ef14610670578063af14052c14610690578063afddbb58146106a5578063b1d061ab146106c55761027d565b80637af69097146105d55780637e428dac146105c05780638e510b52146105f557806391b9a4181461060b5780639c311dbb146106255761027d565b806338582a3f116101f35780635e2a0023116101ac5780635e2a00231461052a57806361d027b31461054a57806362518ddf1461056a5780636618231f1461058a57806367715935146105a05780636c3e2e0a146105c05761027d565b806338582a3f1461043e57806339ebf8231461045357806352d38e5d146104ca57806353ca9f24146104e057806354f1a073146105005780635646d0ec146105155761027d565b80631abb0a4a116102455780631abb0a4a146103825780631e8de77b14610399578063207134b0146103b95780632b83cccd146103cf5780632ed60f2c146103ef5780633403c2fc1461040f5761027d565b806301e1d114146102c357806309f6442c146102eb5780630d8e6e2c14610301578063119e3c7014610335578063178522641461036d575b7f3d78d3961e16fde088e2e26c1cfa163f5f8bb870709088dd68c87eb4091137e2366000803760008036600084545af43d6000803e8080156102be573d6000f35b3d6000fd5b3480156102cf57600080fd5b506102d8610836565b6040519081526020015b60405180910390f35b3480156102f757600080fd5b506102d860405481565b34801561030d57600080fd5b5060408051808201825260058152640312e312e360dc1b602082015290516102e291906156a6565b34801561034157600080fd5b50604854610355906001600160a01b031681565b6040516001600160a01b0390911681526020016102e2565b34801561037957600080fd5b506102d8610852565b34801561038e57600080fd5b50610397610916565b005b3480156103a557600080fd5b506103976103b4366004615261565b61102f565b3480156103c557600080fd5b506102d8603f5481565b3480156103db57600080fd5b506103976103ea36600461522d565b61104b565b3480156103fb57600080fd5b506102d861040a366004615261565b61131f565b34801561041b57600080fd5b50603c5461042e90610100900460ff1681565b60405190151581526020016102e2565b34801561044a57600080fd5b506102d8611334565b34801561045f57600080fd5b506104a061046e3660046150cb565b604560205260009081526040902080546001820154600283015460038401546004909401549293919290919060ff1685565b6040805195865260208601949094529284019190915260608301521515608082015260a0016102e2565b3480156104d657600080fd5b506102d8603d5481565b3480156104ec57600080fd5b50603c5461042e9062010000900460ff1681565b34801561050c57600080fd5b506102d8611341565b34801561052157600080fd5b5061039761134b565b34801561053657600080fd5b506102d861054536600461537c565b611711565b34801561055657600080fd5b50604254610355906001600160a01b031681565b34801561057657600080fd5b50610355610585366004615405565b61193a565b34801561059657600080fd5b506102d860505481565b3480156105ac57600080fd5b50603354610355906001600160a01b031681565b3480156105cc57600080fd5b506102d8611964565b3480156105e157600080fd5b50604754610355906001600160a01b031681565b34801561060157600080fd5b506102d8603e5481565b34801561061757600080fd5b50603c5461042e9060ff1681565b34801561063157600080fd5b506102d861196e565b34801561064657600080fd5b506102d8604d5481565b34801561065c57600080fd5b50604454610355906001600160a01b031681565b34801561067c57600080fd5b5061039761068b3660046151ab565b611bbc565b34801561069c57600080fd5b50610397612160565b3480156106b157600080fd5b506102d86106c0366004615142565b61223b565b3480156106d157600080fd5b506102d8604f5481565b3480156106e757600080fd5b506106fb6106f6366004615435565b6122da565b6040516102e292919061564b565b34801561071557600080fd5b5061071e612578565b6040516102e29190615638565b34801561073757600080fd5b5061071e612584565b34801561074c57600080fd5b5061071e612590565b34801561076157600080fd5b506102d8604e5481565b34801561077757600080fd5b506103976107863660046150cb565b61259a565b34801561079757600080fd5b506102d86125e9565b3480156107ac57600080fd5b506103976107bb3660046150cb565b6125fb565b3480156107cc57600080fd5b50604354610355906001600160a01b031681565b3480156107ec57600080fd5b506103976107fb3660046150e7565b61264a565b34801561080c57600080fd5b5061039761081b3660046150cb565b612738565b34801561082c57600080fd5b506102d860415481565b600060415461084361281a565b61084d91906158c3565b905090565b60008061085f60346128e9565b905060005b818110156109115760006108796034836128fb565b6001600160a01b031663efbb5cb06040518163ffffffff1660e01b815260040160206040518083038186803b1580156108b157600080fd5b505afa1580156108c5573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906108e9919061541d565b905080156108fe576108fb81856158c3565b93505b508061090981615ae6565b915050610864565b505090565b6033546040516311aec98d60e01b81523360048201526001600160a01b03909116906311aec98d90602401600060405180830381600087803b15801561095b57600080fd5b505af115801561096f573d6000803e3d6000fd5b505050506002600154141561099f5760405162461bcd60e51b81526004016109969061573f565b60405180910390fd5b6002600155603c5460ff166109e05760405162461bcd60e51b815260206004820152600760248201526620a21027ab22a960c91b6044820152606401610996565b60006109ea612907565b80519091506000816001600160401b03811115610a1757634e487b7160e01b600052604160045260246000fd5b604051908082528060200260200182016040528015610a40578160200160208202803683370190505b5090506000826001600160401b03811115610a6b57634e487b7160e01b600052604160045260246000fd5b604051908082528060200260200182016040528015610a94578160200160208202803683370190505b5090506000610aa4856000612913565b5050905060008060008060005b88811015610bf35760008a8281518110610adb57634e487b7160e01b600052603260045260246000fd5b60200260200101519050610b178989848460496000876001600160a01b03166001600160a01b0316815260200190815260200160002054612b2c565b610b2190876158c3565b9550610b5589898484604a6000876001600160a01b03166001600160a01b0316815260200190815260200160002054612b2c565b610b5f90866158c3565b9450610b95898984848b8781518110610b8857634e487b7160e01b600052603260045260246000fd5b6020026020010151612b2c565b610b9f90856158c3565b9350610bd389898484604b6000876001600160a01b03166001600160a01b0316815260200190815260200160002054612b2c565b610bdd90846158c3565b9250508080610beb90615ae6565b915050610ab1565b50604c546041546000610c0685836158c3565b90506000610c1485856158c3565b905060008086610c248b8a6158c3565b1015610c425789610c358989615a2e565b610c3f9190615a2e565b90505b610c4c8a84615a2e565b610c56828b6158c3565b1115610c745780610c678b85615a2e565b610c719190615a2e565b98505b82841115610ccc576000610c888486615a2e565b90508a15610cc65781610c9b8b8d6158c3565b610ca591906158c3565b610caf8c83615a0f565b610cb991906158db565b610cc3908c6158c3565b92505b50610d18565b6000610cd88585615a2e565b90508a15610d165781610ceb8b8d6158c3565b610cf591906158c3565b610cff8c83615a0f565b610d0991906158db565b610d13908c615a2e565b92505b505b60485460408051633a98ef3960e01b815290516000926001600160a01b031691633a98ef39916004808301926020929190829003018186803b158015610d5d57600080fd5b505afa158015610d71573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610d95919061541d565b603c5490915062010000900460ff16610dbe57610dbb610db58487615a2e565b82612b71565b90505b8215610e4d576000610dda84610dd48189615a2e565b84612da2565b90508015610e4b576048546047546040516329460cc560e11b81526001600160a01b0391821660048201526024810184905291169063528c198a90604401600060405180830381600087803b158015610e3257600080fd5b505af1158015610e46573d6000803e3d6000fd5b505050505b505b50506000604c81905590505b8c811015610ecf5760008e8281518110610e8357634e487b7160e01b600052603260045260246000fd5b6020908102919091018101516001600160a01b03166000908152604a82526040808220829055604b83528082208290556049909252908120555080610ec781615ae6565b915050610e59565b50604760009054906101000a90046001600160a01b03166001600160a01b03166345153af86040518163ffffffff1660e01b815260040160206040518083038186803b158015610f1e57600080fd5b505afa158015610f32573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610f5691906153e5565b610fc357604760009054906101000a90046001600160a01b03166001600160a01b0316636b5b53716040518163ffffffff1660e01b8152600401600060405180830381600087803b158015610faa57600080fd5b505af1158015610fbe573d6000803e3d6000fd5b505050505b603c805460ff19169055604080518981526020810189905290810184905260608101839052608081018290527fcba28a6e335abfe9107150a5b5d2cce58719830735abd62327b9dbc8055fa9ba9060a00160405180910390a15050600180555050505050505050505050565b336110398161259a565b6110463384846000612e1a565b505050565b6033546040516311aec98d60e01b81523360048201526001600160a01b03909116906311aec98d90602401600060405180830381600087803b15801561109057600080fd5b505af11580156110a4573d6000803e3d6000fd5b50505050826110b28161259a565b600260015414156110d55760405162461bcd60e51b81526004016109969061573f565b600260019081556001600160a01b038516600090815260456020526040902001548084111561110357600080fd5b60405163895a748560e01b815260048101859052602481018290526044810184905260009081906001600160a01b0388169063895a748590606401600060405180830381600087803b15801561115857600080fd5b505af115801561116c573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f1916820160405261119491908101906152c1565b603c54919350915060ff161561125e57815160005b8181101561125b5760008382815181106111d357634e487b7160e01b600052603260045260246000fd5b6020026020010151905060008111156112485780604a600087858151811061120b57634e487b7160e01b600052603260045260246000fd5b60200260200101516001600160a01b03166001600160a01b03168152602001908152602001600020600082825461124291906158c3565b90915550505b508061125381615ae6565b9150506111a9565b50505b6001600160a01b03871660009081526045602052604081206001015490846112868984615a0f565b61129091906158db565b905061129c8183615a2e565b6001600160a01b038a16600090815260456020526040812060010191909155604180548392906112cd908490615a2e565b90915550506040517f72dfda45f3c6c752a1cbef9d63f47877b3c19dbc227919bcfbbc26673bc96e1090611308908b908b9088908890615558565b60405180910390a150506001805550505050505050565b600061132b8383613117565b90505b92915050565b60006041546108436132da565b600061084d6132da565b6033546040516311aec98d60e01b81523360048201526001600160a01b03909116906311aec98d90602401600060405180830381600087803b15801561139057600080fd5b505af11580156113a4573d6000803e3d6000fd5b5050603c5460ff161591506113cd90505760405162461bcd60e51b8152600401610996906156b9565b603c54610100900460ff16156113f55760405162461bcd60e51b8152600401610996906156d5565b600260015414156114185760405162461bcd60e51b81526004016109969061573f565b60026001908155603c805460ff191690911790556000611436612907565b90506000806000611448846001612913565b604154929550909350915081156116c05784516000816001600160401b0381111561148357634e487b7160e01b600052604160045260246000fd5b6040519080825280602002602001820160405280156114ac578160200160208202803683370190505b5090506000826001600160401b038111156114d757634e487b7160e01b600052604160045260246000fd5b604051908082528060200260200182016040528015611500578160200160208202803683370190505b5090506000805b8481101561159f5760008a828151811061153157634e487b7160e01b600052603260045260246000fd5b6020026020010151905060008a838151811061155d57634e487b7160e01b600052603260045260246000fd5b60200260200101519050600081111561158a5761157d8686858585612b2c565b61158790856158c3565b93505b5050808061159790615ae6565b915050611507565b5060006115ac86836158c3565b90506000604860009054906101000a90046001600160a01b03166001600160a01b0316633a98ef396040518163ffffffff1660e01b815260040160206040518083038186803b1580156115fe57600080fd5b505afa158015611612573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611636919061541d565b603c5490915062010000900460ff16611655576116538282612b71565b505b604754604051637615eab160e11b81526001600160a01b039091169063ec2bd56290611687908e908d9060040161564b565b600060405180830381600087803b1580156116a157600080fd5b505af11580156116b5573d6000803e3d6000fd5b505050505050505050505b604c81905560405181907fbe4b7098b96c8f167582de3b9de443834380e1265a9a4453dbcf620e5ae63cac906116fd9083908990899089906157ab565b60405180910390a150506001805550505050565b603c54600090610100900460ff161561173c5760405162461bcd60e51b8152600401610996906156d5565b603c5460ff161561175f5760405162461bcd60e51b8152600401610996906156b9565b600260015414156117825760405162461bcd60e51b81526004016109969061573f565b600260015560006117938585613117565b905082156117eb57828110156117eb5760405162461bcd60e51b815260206004820152601e60248201527f4d696e7420616d6f756e74206c6f776572207468616e206d696e696d756d00006044820152606401610996565b60005b855181101561188c5761187a33604760009054906101000a90046001600160a01b031687848151811061183157634e487b7160e01b600052603260045260246000fd5b602002602001015189858151811061185957634e487b7160e01b600052603260045260246000fd5b60200260200101516001600160a01b03166133cd909392919063ffffffff16565b8061188481615ae6565b9150506117ee565b506047546040516340c10f1960e01b8152336004820152602481018390526001600160a01b03909116906340c10f1990604401600060405180830381600087803b1580156118d957600080fd5b505af11580156118ed573d6000803e3d6000fd5b505050507f62a0e51e012d92909587a2f2aa7f4016592ddf30135d505e2e0038b7f446a5bd338686846040516119269493929190615510565b60405180910390a160018055949350505050565b6046818154811061194a57600080fd5b6000918252602090912001546001600160a01b0316905081565b600061084d61281a565b600080604860009054906101000a90046001600160a01b03166001600160a01b03166318160ddd6040518163ffffffff1660e01b815260040160206040518083038186803b1580156119bf57600080fd5b505afa1580156119d3573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906119f7919061541d565b9050670de0b6b3a76400008115611bb6576000611a12612907565b80519091506000816001600160401b03811115611a3f57634e487b7160e01b600052604160045260246000fd5b604051908082528060200260200182016040528015611a68578160200160208202803683370190505b5090506000826001600160401b03811115611a9357634e487b7160e01b600052604160045260246000fd5b604051908082528060200260200182016040528015611abc578160200160208202803683370190505b50905060008060005b85811015611b76576000878281518110611aef57634e487b7160e01b600052603260045260246000fd5b602002602001015190506000611b05823061343e565b90508015611b2757611b1a8787858585612b2c565b611b2490866158c3565b94505b506001600160a01b0381166000908152604960205260409020548015611b6157611b548787858585612b2c565b611b5e90856158c3565b93505b50508080611b6e90615ae6565b915050611ac5565b50878160415484611b8791906158c3565b611b919190615a2e565b611ba390670de0b6b3a7640000615a0f565b611bad91906158db565b96505050505050505b91505090565b6033546040516311aec98d60e01b81523360048201526001600160a01b03909116906311aec98d90602401600060405180830381600087803b158015611c0157600080fd5b505af1158015611c15573d6000803e3d6000fd5b5050603c54610100900460ff16159150611c4390505760405162461bcd60e51b8152600401610996906156d5565b82611c4d8161259a565b60026001541415611c705760405162461bcd60e51b81526004016109969061573f565b600260015560008080611c848787876134bb565b925092509250600080886001600160a01b031663970d44ee6040518163ffffffff1660e01b815260040160206040518083038186803b158015611cc657600080fd5b505afa158015611cda573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611cfe91906153e5565b905080158015611d0f575060018451115b15611e5f5760015b8451811015611e5d57848181518110611d4057634e487b7160e01b600052603260045260246000fd5b602002602001015160001415611d5557611e4b565b848381518110611d7557634e487b7160e01b600052603260045260246000fd5b602002602001015160001415611d8d57809250611e4b565b848381518110611dad57634e487b7160e01b600052603260045260246000fd5b6020026020010151848281518110611dd557634e487b7160e01b600052603260045260246000fd5b6020026020010151611de79190615a0f565b858281518110611e0757634e487b7160e01b600052603260045260246000fd5b6020026020010151858581518110611e2f57634e487b7160e01b600052603260045260246000fd5b6020026020010151611e419190615a0f565b1115611e4b578092505b80611e5581615ae6565b915050611d17565b505b6000838381518110611e8157634e487b7160e01b600052603260045260246000fd5b602002602001015190506000858481518110611ead57634e487b7160e01b600052603260045260246000fd5b60200260200101519050600080600090505b8651811015612094576000878281518110611eea57634e487b7160e01b600052603260045260246000fd5b6020026020010151905060008111156120815760008a8381518110611f1f57634e487b7160e01b600052603260045260246000fd5b6020026020010151905086158015611f5e575060008a8481518110611f5457634e487b7160e01b600052603260045260246000fd5b6020026020010151115b15611fd15784868b8581518110611f8557634e487b7160e01b600052603260045260246000fd5b6020026020010151611f979190615a0f565b611fa191906158db565b915081898481518110611fc457634e487b7160e01b600052603260045260246000fd5b6020026020010181815250505b60435460405163ebd6d95560e01b81526001600160a01b038381166004830152602482018590529091169063ebd6d9559060440160206040518083038186803b15801561201d57600080fd5b505afa158015612031573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612055919061541d565b61205f90856158c3565b935061207f8f83836001600160a01b03166137f49092919063ffffffff16565b505b508061208c81615ae6565b915050611ebf565b50604051630e7949d360e21b81528c906001600160a01b038216906339e5274c906120c5908c908b9060040161564b565b600060405180830381600087803b1580156120df57600080fd5b505af11580156120f3573d6000803e3d6000fd5b505050506060806121068f838387612e1a565b8e6001600160a01b03167fa320f26f9e050a2dbc872470ba0413f8dbb2134c86680cfb6e911c68bb6f911b8c8b8760405161214393929190615670565b60405180910390a250506001805550505050505050505050505050565b603c54610100900460ff16156121885760405162461bcd60e51b8152600401610996906156d5565b603c5460ff16156121ab5760405162461bcd60e51b8152600401610996906156b9565b603c5462010000900460ff16156121e95760405162461bcd60e51b8152602060048201526002602482015261052560f41b6044820152606401610996565b6002600154141561220c5760405162461bcd60e51b81526004016109969061573f565b600260015560415460009061221f61281a565b61222991906158c3565b905061223481613824565b5060018055565b6033546040516311aec98d60e01b81523360048201526000916001600160a01b0316906311aec98d90602401600060405180830381600087803b15801561228157600080fd5b505af1158015612295573d6000803e3d6000fd5b50505050600260015414156122bc5760405162461bcd60e51b81526004016109969061573f565b60026001556122cd858585856138ad565b6001805595945050505050565b603c546060908190610100900460ff16156123075760405162461bcd60e51b8152600401610996906156d5565b603c5460ff161561232a5760405162461bcd60e51b8152600401610996906156b9565b6002600154141561234d5760405162461bcd60e51b81526004016109969061573f565b60026001556048546040516370a0823160e01b81523360048201526000916001600160a01b0316906370a082319060240160206040518083038186803b15801561239657600080fd5b505afa1580156123aa573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906123ce919061541d565b90506000851180156123e05750808511155b61241e5760405162461bcd60e51b815260206004820152600f60248201526e0aaa688d240dcdee840cadcdeeaced608b1b6044820152606401610996565b6000612428612907565b9050600081516001600160401b0381111561245357634e487b7160e01b600052604160045260246000fd5b60405190808252806020026020018201604052801561247c578160200160208202803683370190505b509050600082516001600160401b038111156124a857634e487b7160e01b600052604160045260246000fd5b6040519080825280602002602001820160405280156124d1578160200160208202803683370190505b5090506000806124e48a87878787613b3e565b9150915060006124f682878787613d4f565b919a5098509050891561255357898110156125535760405162461bcd60e51b815260206004820152601960248201527f616d6f756e74206c6f776572207468616e206d696e696d756d000000000000006044820152606401610996565b6125638b82858c8c8b8b8b613d80565b50505050505050600180819055509250929050565b606061084d6034613e68565b606061084d6036613e68565b606061084d612907565b6125a5603482613e75565b6125e65760405162461bcd60e51b81526020600482015260126024820152711cdd1c985d1959de481b9bdd08195e1a5cdd60721b6044820152606401610996565b50565b60006125f3610852565b61084361281a565b612606603682613e75565b6125e65760405162461bcd60e51b8152602060048201526015602482015274151a1948185cdcd95d081b9bdd081cdd5c1c1bdc9d605a1b6044820152606401610996565b60006126566001613e97565b9050801561266e576000805461ff0019166101001790555b603380546001600160a01b038781166001600160a01b03199283161790925560428054821687841617905560448054821686841617905560438054909116918416919091179055603c805462ff00001916905560006040556001603d5562093a80604e55670de0b6b3a7640000604d55683635c9adc5dea00000604f558015612731576000805461ff0019169055604051600181527f7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb38474024989060200160405180910390a15b5050505050565b603354604051630881b00560e41b81523360048201526001600160a01b039091169063881b00509060240160006040518083038186803b15801561277b57600080fd5b505afa15801561278f573d6000803e3d6000fd5b505050506001600160a01b0381163b6127f65760405162461bcd60e51b8152602060048201526024808201527f6e657720696d706c656d656e746174696f6e206973206e6f74206120636f6e746044820152631c9858dd60e21b6064820152608401610996565b7f3d78d3961e16fde088e2e26c1cfa163f5f8bb870709088dd68c87eb4091137e255565b600080612825612907565b80519091506000816001600160401b0381111561285257634e487b7160e01b600052604160045260246000fd5b60405190808252806020026020018201604052801561287b578160200160208202803683370190505b5090506000826001600160401b038111156128a657634e487b7160e01b600052604160045260246000fd5b6040519080825280602002602001820160405280156128cf578160200160208202803683370190505b50905060006128df858484613f1c565b9550505050505090565b60006128f3825490565b90505b919050565b600061132b8383613fa7565b606061084d6038613e68565b606080600080855190506000816001600160401b0381111561294557634e487b7160e01b600052604160045260246000fd5b60405190808252806020026020018201604052801561296e578160200160208202803683370190505b5090506000826001600160401b0381111561299957634e487b7160e01b600052604160045260246000fd5b6040519080825280602002602001820160405280156129c2578160200160208202803683370190505b5090506000805b84811015612b1d5760008a82815181106129f357634e487b7160e01b600052603260045260246000fd5b6020026020010151905060008a8015612a125750612a12603683613e75565b15612a8657604754612a2e9083906001600160a01b031661343e565b90508015612a865780868481518110612a5757634e487b7160e01b600052603260045260246000fd5b6020908102919091018101919091526001600160a01b0383166000908152604990915260409020819055600193505b6000612a92833061343e565b90508015612ac85780868581518110612abb57634e487b7160e01b600052603260045260246000fd5b6020026020010181815250505b8b8015612ade57506000612adc83836158c3565b115b15612b0757612aed82826158c3565b6001600160a01b0384166000908152604b60205260409020555b5050508080612b1590615ae6565b9150506129c9565b50909891975095509350505050565b600080612b3a878686613fdf565b90506000612b49878787614054565b90506000612b6483612b5c84600a615941565b8791906140c8565b9998505050505050505050565b600081612b7f57508061132e565b604d546000612b9b84836b033b2e3c9fd0803ce80000006140c8565b90508085118015612bce5750603d54612bb49082615a0f565b62989680612bc28388615a2e565b612bcc9190615a0f565b115b15612d9957604254603f546001600160a01b03909116908015801590612bfc57506001600160a01b03821615155b15612d2c576000612c0d8489615a2e565b90506000612710612c1e8484615a0f565b612c2891906158db565b9050808211612c845760405162461bcd60e51b815260206004820152602260248201527f466565206d757374206e6f742062652067726561746572207468616e207969656044820152611b1960f21b6064820152608401610996565b8015612d29576000612c96828b615a2e565b612ca08a84615a0f565b612caa91906158db565b90508015612d27576048546040516329460cc560e11b81526001600160a01b038781166004830152602482018490529091169063528c198a90604401600060405180830381600087803b158015612d0057600080fd5b505af1158015612d14573d6000803e3d6000fd5b505050508089612d2491906158c3565b98505b505b50505b6000612d4588886b033b2e3c9fd0803ce80000006140ea565b9050848114612d9557604d81905560408051888152602081018a90529081018290527fc6642d24d84e7f3d36ca39f5cce10e75639d9b158d5193aa350e2f900653e4c09060600160405180910390a15b5050505b50919392505050565b6000808315801590612db45750600083115b15612dd15783612dc48487615a0f565b612dce91906158db565b90505b80612e1057604d548015612dfd57612df686826b033b2e3c9fd0803ce80000006140ea565b9150612e0e565b612e0b86633b9aca00615a0f565b91505b505b90505b9392505050565b6001600160a01b0384166000908152604560209081526040808320815160a081018352815481526001820154938101849052600282015492810192909252600381015460608301526004015460ff16151560808201529190612e7d9084906158c3565b90506000866001600160a01b031663efbb5cb06040518163ffffffff1660e01b815260040160206040518083038186803b158015612eba57600080fd5b505afa158015612ece573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612ef2919061541d565b905060008083831115612f1057612f098484615a2e565b9150612f25565b83831015612f2557612f228385615a2e565b90505b84608001511561302b57604e546001600160a01b038a16600090815260456020526040902054612f559042615a2e565b108015612f6e5750604f54841180612f6e5750604f5483115b15613026578115612fcf57612710856040015185612f8c9190615a0f565b612f9691906158db565b821115612fca5760405162461bcd60e51b815260206004820152600260248201526111d360f21b6044820152606401610996565b613026565b801561302657612710856060015185612fe89190615a0f565b612ff291906158db565b8111156130265760405162461bcd60e51b8152602060048201526002602482015261131360f21b6044820152606401610996565b613052565b6001600160a01b0389166000908152604560205260409020600401805460ff191660011790555b6001600160a01b0389166000908152604560205260409020600101839055604154849087906130829086906158c3565b61308c91906158c3565b6130969190615a2e565b6041556001600160a01b038916600090815260456020526040812042905586156130be575060015b896001600160a01b03167f6d35366477ec3dbdcc668ff1cf0df6df18998d6bbfbea924257ccbdfca3b1f35848488888e8e886040516131039796959493929190615809565b60405180910390a250505050505050505050565b60006131238383614103565b6000805b845181101561326457600085828151811061315257634e487b7160e01b600052603260045260246000fd5b60209081029190910101516043546040516315d5220f60e31b81526001600160a01b0380841660048301529293506000929091169063aea910789060240160206040518083038186803b1580156131a857600080fd5b505afa1580156131bc573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906131e0919061541d565b6001600160a01b0383166000908152603b60205260409020549091506132428261320b83600a615941565b89878151811061322b57634e487b7160e01b600052603260045260246000fd5b60200260200101516140c89092919063ffffffff16565b61324c90866158c3565b9450505050808061325c90615ae6565b915050613127565b5060505480156132d257808210156132d25760405162461bcd60e51b815260206004820152602b60248201527f416d6f756e74206d757374206265206774206d696e696d756d20496e7665737460448201526a1b595b9d08105b5bdd5b9d60aa1b6064820152608401610996565b509392505050565b6000806132e5612907565b90506000805b82518110156133c657600083828151811061331657634e487b7160e01b600052603260045260246000fd5b60200260200101519050600061332c823061343e565b6047549091506000906133499084906001600160a01b031661343e565b9050600061335782846158c3565b905080156133af57600061336a85614306565b6001600160a01b0386166000908152603b602052604081205491925061339d8361339584600a615941565b8691906140c8565b90506133a9818a6158c3565b98505050505b5050505080806133be90615ae6565b9150506132eb565b5091505090565b6040516001600160a01b03808516602483015283166044820152606481018290526134389085906323b872dd60e01b906084015b60408051601f198184030181529190526020810180516001600160e01b03166001600160e01b031990931692909217909152614384565b50505050565b6040516370a0823160e01b81526001600160a01b038281166004830152600091908416906370a082319060240160206040518083038186803b15801561348357600080fd5b505afa158015613497573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061132b919061541d565b6060806060856001600160a01b031663adc58dd96040518163ffffffff1660e01b815260040160006040518083038186803b1580156134f957600080fd5b505afa15801561350d573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f1916820160405261353591908101906152c1565b81519194509250806001600160401b0381111561356257634e487b7160e01b600052604160045260246000fd5b60405190808252806020026020018201604052801561358b578160200160208202803683370190505b5091508460005b81811015613697576000805b84811015613644578781815181106135c657634e487b7160e01b600052603260045260246000fd5b60200260200101516001600160a01b03168a8a858181106135f757634e487b7160e01b600052603260045260246000fd5b9050602002810190613609919061585b565b61361a9060408101906020016150cb565b6001600160a01b031614156136325760019150613644565b8061363c81615ae6565b91505061359e565b50806136845760405162461bcd60e51b815260206004820152600f60248201526e1d1bd51bdad95b881a5b9d985b1a59608a1b6044820152606401610996565b508061368f81615ae6565b915050613592565b5060005b828110156137e85760005b828110156137d55760008989838181106136d057634e487b7160e01b600052603260045260246000fd5b90506020028101906136e2919061585b565b6136eb90615a45565b905080602001516001600160a01b031688848151811061371b57634e487b7160e01b600052603260045260246000fd5b60200260200101516001600160a01b03161461373757506137c3565b600081602001516001600160a01b031682600001516001600160a01b031614156137665750604081015161378f565b60408201511561378f5761378c82600001518360200151846040015185606001516138ad565b90505b808785815181106137b057634e487b7160e01b600052603260045260246000fd5b60200260200101818152505050506137d5565b806137cd81615ae6565b9150506136a6565b50806137e081615ae6565b91505061369b565b50505093509350939050565b6040516001600160a01b03831660248201526044810182905261104690849063a9059cbb60e01b90606401613401565b60485460408051633a98ef3960e01b815290516000926001600160a01b031691633a98ef39916004808301926020929190829003018186803b15801561386957600080fd5b505afa15801561387d573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906138a1919061541d565b90506110468282612b71565b60006138ba603885614456565b6138eb5760405162461bcd60e51b8152602060048201526002602482015261085560f21b6044820152606401610996565b604080516080810182528481526001600160a01b038088166020830181905287821693830193909352306060830152604454919261392d929091166000614462565b604454613947906001600160a01b03888116911686614462565b60445483516020850151604080870151905163538d5ab960e11b81526001600160a01b039094169363a71ab57293613987939092909187906004016155d6565b602060405180830381600087803b1580156139a157600080fd5b505af11580156139b5573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906139d9919061541d565b604354604051632633f08360e11b81526001600160a01b03898116600483015260248201889052888116604483015292945060009290911690634c67e1069060640160206040518083038186803b158015613a3357600080fd5b505afa158015613a47573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613a6b919061541d565b905061271084608001518560600151612710613a879190615a2e565b613a919190615a2e565b613a9b9083615a0f565b613aa591906158db565b831015613ad95760405162461bcd60e51b815260206004820152600260248201526113d360f21b6044820152606401610996565b8351604080516001600160a01b0392831681528983166020820152808201889052918816606083015260808201859052517fec43bc4dddba4c8ae8d4f92f53b39989eaf64022bec70471d105647e57c7891c9181900360a00190a15050949350505050565b6000806000613b4e868686613f1c565b6041549091508890600090613b6390846158c3565b90506000604860009054906101000a90046001600160a01b03166001600160a01b0316633a98ef396040518163ffffffff1660e01b815260040160206040518083038186803b158015613bb557600080fd5b505afa158015613bc9573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613bed919061541d565b604d549091508a841415613c7d57604854604051633d7ad0b760e21b81523360048201526001600160a01b039091169063f5eb42dc9060240160206040518083038186803b158015613c3e57600080fd5b505afa158015613c52573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613c76919061541d565b9650613c97565b613c9484826b033b2e3c9fd0803ce80000006140ea565b96505b60405415613cc75761271060405485613cb09190615a0f565b613cba91906158db565b613cc49085615a2e565b93505b6000613ce083836b033b2e3c9fd0803ce80000006140c8565b905080613ced8587615a0f565b613cf791906158db565b9650505084841015613d2757613d1a613d108587615a2e565b8a8a8a8686614586565b613d2490856158c3565b93505b84841015613d4157613d41613d3c8587615a2e565b614857565b505050509550959350505050565b606080600080613d6188888888614aa2565b90506000613d7182898989614c01565b97999198509095505050505050565b604854604051633b9e9f0160e21b8152336004820152602481018890526001600160a01b039091169063ee7a7c0490604401600060405180830381600087803b158015613dcc57600080fd5b505af1158015613de0573d6000803e3d6000fd5b5050603c5462010000900460ff169150613e1d9050576000613e03848484613f1c565b9050613e1b60415482613e1691906158c3565b613824565b505b7f41d90e25634e59cdc487695ce90710379a58f86f34cd95ae7e104a526aac04b9338989898989604051613e5696959493929190615591565b60405180910390a15050505050505050565b60606000612e1383614cc6565b6001600160a01b0381166000908152600183016020526040812054151561132b565b60008054610100900460ff1615613ede578160ff166001148015613eba5750303b155b613ed65760405162461bcd60e51b8152600401610996906156f1565b5060006128f6565b60005460ff808416911610613f055760405162461bcd60e51b8152600401610996906156f1565b506000805460ff191660ff831617905560016128f6565b82516000908190815b81811015613f9c576000878281518110613f4f57634e487b7160e01b600052603260045260246000fd5b602002602001015190506000613f65823061343e565b90508015613f8757613f7a8888858585612b2c565b613f8490866158c3565b94505b50508080613f9490615ae6565b915050613f25565b509095945050505050565b6000826000018281548110613fcc57634e487b7160e01b600052603260045260246000fd5b9060005260206000200154905092915050565b60008084848151811061400257634e487b7160e01b600052603260045260246000fd5b602002602001015190508060001415612e105761401e83614306565b90508085858151811061404157634e487b7160e01b600052603260045260246000fd5b6020908102919091010152949350505050565b60008084848151811061407757634e487b7160e01b600052603260045260246000fd5b602002602001015190508060001415612e1057506001600160a01b0382166000908152603b60205260409020548451819086908690811061404157634e487b7160e01b600052603260045260246000fd5b6000806140d58486615a0f565b90506140e183826158db565b95945050505050565b6000806140f78386615a0f565b90506140e184826158db565b604760009054906101000a90046001600160a01b03166001600160a01b03166345153af86040518163ffffffff1660e01b815260040160206040518083038186803b15801561415157600080fd5b505afa158015614165573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061418991906153e5565b156141c85760405162461bcd60e51b815260206004820152600f60248201526e697320646973747269627574696e6760881b6044820152606401610996565b8151815181158015906141da57508082145b61424c5760405162461bcd60e51b815260206004820152603860248201527f41737365747320616e6420616d6f756e7473206d75737420626520657175616c60448201527f20696e206c656e67746820616e64206e6f7420656d70747900000000000000006064820152608401610996565b60005b828110156127315761428785828151811061427a57634e487b7160e01b600052603260045260246000fd5b60200260200101516125fb565b60008482815181106142a957634e487b7160e01b600052603260045260246000fd5b6020026020010151116142f45760405162461bcd60e51b81526020600482015260136024820152720416d6f756e74206d757374206265206774203606c1b6044820152606401610996565b806142fe81615ae6565b91505061424f565b6043546040516315d5220f60e31b81526001600160a01b038381166004830152600092169063aea910789060240160206040518083038186803b15801561434c57600080fd5b505afa158015614360573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906128f3919061541d565b60006143d9826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b0316614d229092919063ffffffff16565b80519091501561104657808060200190518101906143f791906153e5565b6110465760405162461bcd60e51b815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e6044820152691bdd081cdd58d8d9595960b21b6064820152608401610996565b600061132b8383614d31565b8015806144eb5750604051636eb1769f60e11b81523060048201526001600160a01b03838116602483015284169063dd62ed3e9060440160206040518083038186803b1580156144b157600080fd5b505afa1580156144c5573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906144e9919061541d565b155b6145565760405162461bcd60e51b815260206004820152603660248201527f5361666545524332303a20617070726f76652066726f6d206e6f6e2d7a65726f60448201527520746f206e6f6e2d7a65726f20616c6c6f77616e636560501b6064820152608401610996565b6040516001600160a01b03831660248201526044810182905261104690849063095ea7b360e01b90606401613401565b8451600090869082816001600160401b038111156145b457634e487b7160e01b600052604160045260246000fd5b6040519080825280602002602001820160405280156145dd578160200160208202803683370190505b5090506000805b8381101561472a57600085828151811061460e57634e487b7160e01b600052603260045260246000fd5b6020026020010151905061462c816036613e7590919063ffffffff16565b156147175760475460009061464b9083906001600160a01b031661343e565b905080156147155760006146628d8d868686612b2c565b9050808f11156146b85761467681866158c3565b9450808f6146849190615a2e565b9e50818685815181106146a757634e487b7160e01b600052603260045260246000fd5b602002602001018181525050614713565b6146c28f866158c3565b9450808f836146d19190615a0f565b6146db91906158db565b8685815181106146fb57634e487b7160e01b600052603260045260246000fd5b60200260200101818152505060009e5050505061472a565b505b505b508061472281615ae6565b9150506145e4565b50801561484957604754604051637615eab160e11b81526001600160a01b039091169063ec2bd56290614763908790869060040161564b565b600060405180830381600087803b15801561477d57600080fd5b505af1158015614791573d6000803e3d6000fd5b5050505060006147a2828989612da2565b6048546047546040516329460cc560e11b81526001600160a01b03918216600482015260248101849052929350169063528c198a90604401600060405180830381600087803b1580156147f457600080fd5b505af1158015614808573d6000803e3d6000fd5b505050507f07afc15fb70b2f450b68377bcdb1f517c3b24d4f8f10e18f02635e8771281ac182868560405161483f93929190615776565b60405180910390a1505b9a9950505050505050505050565b6000805b604654811015614a865760006046828154811061488857634e487b7160e01b600052603260045260246000fd5b6000918252602090912001546001600160a01b03169050806148aa5750614a86565b6001600160a01b038116600090815260456020526040902060010154806148d2575050614a74565b6000818611156148ef5750806148e88187615a2e565b9550614916565b8161490287670de0b6b3a76400006158c3565b1061490e575080614911565b50845b600095505b60405163895a748560e01b815260048101829052602481018390526000604482018190529081906001600160a01b0386169063895a748590606401600060405180830381600087803b15801561496b57600080fd5b505af115801561497f573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526149a791908101906152c1565b91509150846001600160a01b03167f8784f8bc43bb3cadc89640d1bde1d352aaf5dbb424e50a2bd4c642111409bf7d848685856040516149ea94939291906157ea565b60405180910390a26001600160a01b0385166000908152604560205260408120600101549085614a1a8684615a0f565b614a2491906158db565b9050614a308183615a2e565b6001600160a01b038816600090815260456020526040902060010155614a56818a6158c3565b985060008a11614a6c5750505050505050614a86565b505050505050505b80614a7e81615ae6565b91505061485b565b508060416000828254614a999190615a2e565b90915550505050565b82516060906000816001600160401b03811115614acf57634e487b7160e01b600052604160045260246000fd5b604051908082528060200260200182016040528015614af8578160200160208202803683370190505b50905060005b82811015614bf6576000878281518110614b2857634e487b7160e01b600052603260045260246000fd5b602002602001015190506000614b3e823061343e565b90508015614be1576000614b558989868686612b2c565b90508a8110614ba75780614b698c84615a0f565b614b7391906158db565b858581518110614b9357634e487b7160e01b600052603260045260246000fd5b602002602001018181525050505050614bf6565b81858581518110614bc857634e487b7160e01b600052603260045260246000fd5b6020908102919091010152614bdd818c615a2e565b9a50505b50508080614bee90615ae6565b915050614afe565b509695505050505050565b82516000908190815b81811015614cba576000888281518110614c3457634e487b7160e01b600052603260045260246000fd5b602002602001015190506000811115614ca7576000888381518110614c6957634e487b7160e01b600052603260045260246000fd5b602002602001015190506000614c828989868587612b2c565b9050614c8e81876158c3565b9550614ca46001600160a01b03831633856137f4565b50505b5080614cb281615ae6565b915050614c0a565b50909695505050505050565b606081600001805480602002602001604051908101604052809291908181526020018280548015614d1657602002820191906000526020600020905b815481526020019060010190808311614d02575b50505050509050919050565b6060612e108484600085614d3d565b600061132b8383613e75565b606082471015614d9e5760405162461bcd60e51b815260206004820152602660248201527f416464726573733a20696e73756666696369656e742062616c616e636520666f6044820152651c8818d85b1b60d21b6064820152608401610996565b6001600160a01b0385163b614df55760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152606401610996565b600080866001600160a01b03168587604051614e1191906154f4565b60006040518083038185875af1925050503d8060008114614e4e576040519150601f19603f3d011682016040523d82523d6000602084013e614e53565b606091505b5091509150614e63828286614e6e565b979650505050505050565b60608315614e7d575081612e13565b825115614e8d5782518084602001fd5b8160405162461bcd60e51b815260040161099691906156a6565b600082601f830112614eb7578081fd5b81356020614ecc614ec7836158a0565b615870565b80838252828201915082860187848660051b8901011115614eeb578586fd5b855b85811015614f12578135614f0081615b2d565b84529284019290840190600101614eed565b5090979650505050505050565b600082601f830112614f2f578081fd5b81356020614f3f614ec7836158a0565b80838252828201915082860187848660051b8901011115614f5e578586fd5b855b85811015614f1257813584529284019290840190600101614f60565b600082601f830112614f8c578081fd5b81516020614f9c614ec7836158a0565b80838252828201915082860187848660051b8901011115614fbb578586fd5b855b85811015614f1257815184529284019290840190600101614fbd565b600082601f830112614fe9578081fd5b81356001600160401b0381111561500257615002615b17565b615015601f8201601f1916602001615870565b818152846020838601011115615029578283fd5b816020850160208301379081016020019190915292915050565b600060a08284031215615054578081fd5b61505e60a0615870565b9050813561506b81615b2d565b8152602082013560ff8116811461508157600080fd5b602082015260408201356001600160401b0381111561509f57600080fd5b6150ab84828501614fd9565b604083015250606082013560608201526080820135608082015292915050565b6000602082840312156150dc578081fd5b8135612e1381615b2d565b600080600080608085870312156150fc578283fd5b843561510781615b2d565b9350602085013561511781615b2d565b9250604085013561512781615b2d565b9150606085013561513781615b2d565b939692955090935050565b60008060008060808587031215615157578384fd5b843561516281615b2d565b9350602085013561517281615b2d565b92506040850135915060608501356001600160401b03811115615193578182fd5b61519f87828801615043565b91505092959194509250565b6000806000604084860312156151bf578081fd5b83356151ca81615b2d565b925060208401356001600160401b03808211156151e5578283fd5b818601915086601f8301126151f8578283fd5b813581811115615206578384fd5b8760208260051b850101111561521a578384fd5b6020830194508093505050509250925092565b600080600060608486031215615241578081fd5b833561524c81615b2d565b95602085013595506040909401359392505050565b60008060408385031215615273578182fd5b82356001600160401b0380821115615289578384fd5b61529586838701614ea7565b935060208501359150808211156152aa578283fd5b506152b785828601614f1f565b9150509250929050565b600080604083850312156152d3578182fd5b82516001600160401b03808211156152e9578384fd5b818501915085601f8301126152fc578384fd5b8151602061530c614ec7836158a0565b8083825282820191508286018a848660051b890101111561532b578889fd5b8896505b8487101561535657805161534281615b2d565b83526001969096019591830191830161532f565b509188015191965090935050508082111561536f578283fd5b506152b785828601614f7c565b600080600060608486031215615390578081fd5b83356001600160401b03808211156153a6578283fd5b6153b287838801614ea7565b945060208601359150808211156153c7578283fd5b506153d486828701614f1f565b925050604084013590509250925092565b6000602082840312156153f6578081fd5b81518015158114612e13578182fd5b600060208284031215615416578081fd5b5035919050565b60006020828403121561542e578081fd5b5051919050565b60008060408385031215615447578182fd5b50508035926020909101359150565b6000815180845260208085019450808401835b8381101561548e5781516001600160a01b031687529582019590820190600101615469565b509495945050505050565b6000815180845260208085019450808401835b8381101561548e578151875295820195908201906001016154ac565b600081518084526154e0816020860160208601615aba565b601f01601f19169290920160200192915050565b60008251615506818460208701615aba565b9190910192915050565b6001600160a01b038516815260806020820181905260009061553490830186615456565b82810360408401526155468186615499565b91505082606083015295945050505050565b600060018060a01b03861682528460208301526080604083015261557f6080830185615456565b8281036060840152614e638185615499565b600060018060a01b038816825286602083015285604083015284606083015260c060808301526155c460c0830185615456565b82810360a0840152612b648185615499565b600060018060a01b03808716835260ff8616602084015260e0604084015261560160e08401866154c8565b9150835160608401528060208501511660808401528060408501511660a08401528060608501511660c08401525095945050505050565b60006020825261132b6020830184615456565b60006040825261565e6040830185615456565b82810360208401526140e18185615499565b6000606082526156836060830186615456565b82810360208401526156958186615499565b915050826040830152949350505050565b60006020825261132b60208301846154c8565b602080825260029082015261105160f21b604082015260600190565b602080825260029082015261455360f01b604082015260600190565b6020808252602e908201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160408201526d191e481a5b9a5d1a585b1a5e995960921b606082015260800190565b6020808252601f908201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c00604082015260600190565b60008482526060602083015261578f6060830185615456565b82810360408401526157a18185615499565b9695505050505050565b6000858252608060208301526157c46080830186615456565b82810360408401526157d68186615499565b90508281036060840152614e638185615499565b60008582528460208301526080604083015261557f6080830185615456565b600088825287602083015286604083015285606083015260e0608083015261583460e0830186615456565b82810360a08401526158468186615499565b9150508260c083015298975050505050505050565b60008235607e19833603018112615506578182fd5b604051601f8201601f191681016001600160401b038111828210171561589857615898615b17565b604052919050565b60006001600160401b038211156158b9576158b9615b17565b5060051b60200190565b600082198211156158d6576158d6615b01565b500190565b6000826158f657634e487b7160e01b81526012600452602481fd5b500490565b80825b600180861161590d5750615938565b81870482111561591f5761591f615b01565b8086161561592c57918102915b9490941c9380026158fe565b94509492505050565b600061132b600019848460008261595a57506001612e13565b8161596757506000612e13565b816001811461597d5760028114615987576159b4565b6001915050612e13565b60ff84111561599857615998615b01565b6001841b9150848211156159ae576159ae615b01565b50612e13565b5060208310610133831016604e8410600b84101617156159e7575081810a838111156159e2576159e2615b01565b612e13565b6159f484848460016158fb565b808604821115615a0657615a06615b01565b02949350505050565b6000816000190483118215151615615a2957615a29615b01565b500290565b600082821015615a4057615a40615b01565b500390565b600060808236031215615a56578081fd5b615a606080615870565b8235615a6b81615b2d565b81526020830135615a7b81615b2d565b60208201526040838101359082015260608301356001600160401b03811115615aa2578283fd5b615aae36828601615043565b60608301525092915050565b60005b83811015615ad5578181015183820152602001615abd565b838111156134385750506000910152565b6000600019821415615afa57615afa615b01565b5060010190565b634e487b7160e01b600052601160045260246000fd5b634e487b7160e01b600052604160045260246000fd5b6001600160a01b03811681146125e657600080fdfea2646970667358221220aa1cd7ac66af40f91b156c90beed3b8f85c70703e2ce1670baa42cdc7d53409e64736f6c63430008030033

Deployed Bytecode

0x60806040526004361061027d5760003560e01c80637af690971161014f578063b390c0ab116100c1578063d4c3eea01161007a578063d4c3eea01461078b578063e5f36762146107a0578063e6f2e598146107c0578063f8c8765e146107e0578063fc0cfeee14610800578063fc7b9c18146108205761027d565b8063b390c0ab146106db578063b49a60bb14610709578063bca9a47e1461072b578063c4b9737014610740578063cd5a7e9e14610755578063ce7f8cff1461076b5761027d565b80639fb0fbc5116101135780639fb0fbc51461063a578063a688b5d114610650578063a819e6ef14610670578063af14052c14610690578063afddbb58146106a5578063b1d061ab146106c55761027d565b80637af69097146105d55780637e428dac146105c05780638e510b52146105f557806391b9a4181461060b5780639c311dbb146106255761027d565b806338582a3f116101f35780635e2a0023116101ac5780635e2a00231461052a57806361d027b31461054a57806362518ddf1461056a5780636618231f1461058a57806367715935146105a05780636c3e2e0a146105c05761027d565b806338582a3f1461043e57806339ebf8231461045357806352d38e5d146104ca57806353ca9f24146104e057806354f1a073146105005780635646d0ec146105155761027d565b80631abb0a4a116102455780631abb0a4a146103825780631e8de77b14610399578063207134b0146103b95780632b83cccd146103cf5780632ed60f2c146103ef5780633403c2fc1461040f5761027d565b806301e1d114146102c357806309f6442c146102eb5780630d8e6e2c14610301578063119e3c7014610335578063178522641461036d575b7f3d78d3961e16fde088e2e26c1cfa163f5f8bb870709088dd68c87eb4091137e2366000803760008036600084545af43d6000803e8080156102be573d6000f35b3d6000fd5b3480156102cf57600080fd5b506102d8610836565b6040519081526020015b60405180910390f35b3480156102f757600080fd5b506102d860405481565b34801561030d57600080fd5b5060408051808201825260058152640312e312e360dc1b602082015290516102e291906156a6565b34801561034157600080fd5b50604854610355906001600160a01b031681565b6040516001600160a01b0390911681526020016102e2565b34801561037957600080fd5b506102d8610852565b34801561038e57600080fd5b50610397610916565b005b3480156103a557600080fd5b506103976103b4366004615261565b61102f565b3480156103c557600080fd5b506102d8603f5481565b3480156103db57600080fd5b506103976103ea36600461522d565b61104b565b3480156103fb57600080fd5b506102d861040a366004615261565b61131f565b34801561041b57600080fd5b50603c5461042e90610100900460ff1681565b60405190151581526020016102e2565b34801561044a57600080fd5b506102d8611334565b34801561045f57600080fd5b506104a061046e3660046150cb565b604560205260009081526040902080546001820154600283015460038401546004909401549293919290919060ff1685565b6040805195865260208601949094529284019190915260608301521515608082015260a0016102e2565b3480156104d657600080fd5b506102d8603d5481565b3480156104ec57600080fd5b50603c5461042e9062010000900460ff1681565b34801561050c57600080fd5b506102d8611341565b34801561052157600080fd5b5061039761134b565b34801561053657600080fd5b506102d861054536600461537c565b611711565b34801561055657600080fd5b50604254610355906001600160a01b031681565b34801561057657600080fd5b50610355610585366004615405565b61193a565b34801561059657600080fd5b506102d860505481565b3480156105ac57600080fd5b50603354610355906001600160a01b031681565b3480156105cc57600080fd5b506102d8611964565b3480156105e157600080fd5b50604754610355906001600160a01b031681565b34801561060157600080fd5b506102d8603e5481565b34801561061757600080fd5b50603c5461042e9060ff1681565b34801561063157600080fd5b506102d861196e565b34801561064657600080fd5b506102d8604d5481565b34801561065c57600080fd5b50604454610355906001600160a01b031681565b34801561067c57600080fd5b5061039761068b3660046151ab565b611bbc565b34801561069c57600080fd5b50610397612160565b3480156106b157600080fd5b506102d86106c0366004615142565b61223b565b3480156106d157600080fd5b506102d8604f5481565b3480156106e757600080fd5b506106fb6106f6366004615435565b6122da565b6040516102e292919061564b565b34801561071557600080fd5b5061071e612578565b6040516102e29190615638565b34801561073757600080fd5b5061071e612584565b34801561074c57600080fd5b5061071e612590565b34801561076157600080fd5b506102d8604e5481565b34801561077757600080fd5b506103976107863660046150cb565b61259a565b34801561079757600080fd5b506102d86125e9565b3480156107ac57600080fd5b506103976107bb3660046150cb565b6125fb565b3480156107cc57600080fd5b50604354610355906001600160a01b031681565b3480156107ec57600080fd5b506103976107fb3660046150e7565b61264a565b34801561080c57600080fd5b5061039761081b3660046150cb565b612738565b34801561082c57600080fd5b506102d860415481565b600060415461084361281a565b61084d91906158c3565b905090565b60008061085f60346128e9565b905060005b818110156109115760006108796034836128fb565b6001600160a01b031663efbb5cb06040518163ffffffff1660e01b815260040160206040518083038186803b1580156108b157600080fd5b505afa1580156108c5573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906108e9919061541d565b905080156108fe576108fb81856158c3565b93505b508061090981615ae6565b915050610864565b505090565b6033546040516311aec98d60e01b81523360048201526001600160a01b03909116906311aec98d90602401600060405180830381600087803b15801561095b57600080fd5b505af115801561096f573d6000803e3d6000fd5b505050506002600154141561099f5760405162461bcd60e51b81526004016109969061573f565b60405180910390fd5b6002600155603c5460ff166109e05760405162461bcd60e51b815260206004820152600760248201526620a21027ab22a960c91b6044820152606401610996565b60006109ea612907565b80519091506000816001600160401b03811115610a1757634e487b7160e01b600052604160045260246000fd5b604051908082528060200260200182016040528015610a40578160200160208202803683370190505b5090506000826001600160401b03811115610a6b57634e487b7160e01b600052604160045260246000fd5b604051908082528060200260200182016040528015610a94578160200160208202803683370190505b5090506000610aa4856000612913565b5050905060008060008060005b88811015610bf35760008a8281518110610adb57634e487b7160e01b600052603260045260246000fd5b60200260200101519050610b178989848460496000876001600160a01b03166001600160a01b0316815260200190815260200160002054612b2c565b610b2190876158c3565b9550610b5589898484604a6000876001600160a01b03166001600160a01b0316815260200190815260200160002054612b2c565b610b5f90866158c3565b9450610b95898984848b8781518110610b8857634e487b7160e01b600052603260045260246000fd5b6020026020010151612b2c565b610b9f90856158c3565b9350610bd389898484604b6000876001600160a01b03166001600160a01b0316815260200190815260200160002054612b2c565b610bdd90846158c3565b9250508080610beb90615ae6565b915050610ab1565b50604c546041546000610c0685836158c3565b90506000610c1485856158c3565b905060008086610c248b8a6158c3565b1015610c425789610c358989615a2e565b610c3f9190615a2e565b90505b610c4c8a84615a2e565b610c56828b6158c3565b1115610c745780610c678b85615a2e565b610c719190615a2e565b98505b82841115610ccc576000610c888486615a2e565b90508a15610cc65781610c9b8b8d6158c3565b610ca591906158c3565b610caf8c83615a0f565b610cb991906158db565b610cc3908c6158c3565b92505b50610d18565b6000610cd88585615a2e565b90508a15610d165781610ceb8b8d6158c3565b610cf591906158c3565b610cff8c83615a0f565b610d0991906158db565b610d13908c615a2e565b92505b505b60485460408051633a98ef3960e01b815290516000926001600160a01b031691633a98ef39916004808301926020929190829003018186803b158015610d5d57600080fd5b505afa158015610d71573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610d95919061541d565b603c5490915062010000900460ff16610dbe57610dbb610db58487615a2e565b82612b71565b90505b8215610e4d576000610dda84610dd48189615a2e565b84612da2565b90508015610e4b576048546047546040516329460cc560e11b81526001600160a01b0391821660048201526024810184905291169063528c198a90604401600060405180830381600087803b158015610e3257600080fd5b505af1158015610e46573d6000803e3d6000fd5b505050505b505b50506000604c81905590505b8c811015610ecf5760008e8281518110610e8357634e487b7160e01b600052603260045260246000fd5b6020908102919091018101516001600160a01b03166000908152604a82526040808220829055604b83528082208290556049909252908120555080610ec781615ae6565b915050610e59565b50604760009054906101000a90046001600160a01b03166001600160a01b03166345153af86040518163ffffffff1660e01b815260040160206040518083038186803b158015610f1e57600080fd5b505afa158015610f32573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610f5691906153e5565b610fc357604760009054906101000a90046001600160a01b03166001600160a01b0316636b5b53716040518163ffffffff1660e01b8152600401600060405180830381600087803b158015610faa57600080fd5b505af1158015610fbe573d6000803e3d6000fd5b505050505b603c805460ff19169055604080518981526020810189905290810184905260608101839052608081018290527fcba28a6e335abfe9107150a5b5d2cce58719830735abd62327b9dbc8055fa9ba9060a00160405180910390a15050600180555050505050505050505050565b336110398161259a565b6110463384846000612e1a565b505050565b6033546040516311aec98d60e01b81523360048201526001600160a01b03909116906311aec98d90602401600060405180830381600087803b15801561109057600080fd5b505af11580156110a4573d6000803e3d6000fd5b50505050826110b28161259a565b600260015414156110d55760405162461bcd60e51b81526004016109969061573f565b600260019081556001600160a01b038516600090815260456020526040902001548084111561110357600080fd5b60405163895a748560e01b815260048101859052602481018290526044810184905260009081906001600160a01b0388169063895a748590606401600060405180830381600087803b15801561115857600080fd5b505af115801561116c573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f1916820160405261119491908101906152c1565b603c54919350915060ff161561125e57815160005b8181101561125b5760008382815181106111d357634e487b7160e01b600052603260045260246000fd5b6020026020010151905060008111156112485780604a600087858151811061120b57634e487b7160e01b600052603260045260246000fd5b60200260200101516001600160a01b03166001600160a01b03168152602001908152602001600020600082825461124291906158c3565b90915550505b508061125381615ae6565b9150506111a9565b50505b6001600160a01b03871660009081526045602052604081206001015490846112868984615a0f565b61129091906158db565b905061129c8183615a2e565b6001600160a01b038a16600090815260456020526040812060010191909155604180548392906112cd908490615a2e565b90915550506040517f72dfda45f3c6c752a1cbef9d63f47877b3c19dbc227919bcfbbc26673bc96e1090611308908b908b9088908890615558565b60405180910390a150506001805550505050505050565b600061132b8383613117565b90505b92915050565b60006041546108436132da565b600061084d6132da565b6033546040516311aec98d60e01b81523360048201526001600160a01b03909116906311aec98d90602401600060405180830381600087803b15801561139057600080fd5b505af11580156113a4573d6000803e3d6000fd5b5050603c5460ff161591506113cd90505760405162461bcd60e51b8152600401610996906156b9565b603c54610100900460ff16156113f55760405162461bcd60e51b8152600401610996906156d5565b600260015414156114185760405162461bcd60e51b81526004016109969061573f565b60026001908155603c805460ff191690911790556000611436612907565b90506000806000611448846001612913565b604154929550909350915081156116c05784516000816001600160401b0381111561148357634e487b7160e01b600052604160045260246000fd5b6040519080825280602002602001820160405280156114ac578160200160208202803683370190505b5090506000826001600160401b038111156114d757634e487b7160e01b600052604160045260246000fd5b604051908082528060200260200182016040528015611500578160200160208202803683370190505b5090506000805b8481101561159f5760008a828151811061153157634e487b7160e01b600052603260045260246000fd5b6020026020010151905060008a838151811061155d57634e487b7160e01b600052603260045260246000fd5b60200260200101519050600081111561158a5761157d8686858585612b2c565b61158790856158c3565b93505b5050808061159790615ae6565b915050611507565b5060006115ac86836158c3565b90506000604860009054906101000a90046001600160a01b03166001600160a01b0316633a98ef396040518163ffffffff1660e01b815260040160206040518083038186803b1580156115fe57600080fd5b505afa158015611612573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611636919061541d565b603c5490915062010000900460ff16611655576116538282612b71565b505b604754604051637615eab160e11b81526001600160a01b039091169063ec2bd56290611687908e908d9060040161564b565b600060405180830381600087803b1580156116a157600080fd5b505af11580156116b5573d6000803e3d6000fd5b505050505050505050505b604c81905560405181907fbe4b7098b96c8f167582de3b9de443834380e1265a9a4453dbcf620e5ae63cac906116fd9083908990899089906157ab565b60405180910390a150506001805550505050565b603c54600090610100900460ff161561173c5760405162461bcd60e51b8152600401610996906156d5565b603c5460ff161561175f5760405162461bcd60e51b8152600401610996906156b9565b600260015414156117825760405162461bcd60e51b81526004016109969061573f565b600260015560006117938585613117565b905082156117eb57828110156117eb5760405162461bcd60e51b815260206004820152601e60248201527f4d696e7420616d6f756e74206c6f776572207468616e206d696e696d756d00006044820152606401610996565b60005b855181101561188c5761187a33604760009054906101000a90046001600160a01b031687848151811061183157634e487b7160e01b600052603260045260246000fd5b602002602001015189858151811061185957634e487b7160e01b600052603260045260246000fd5b60200260200101516001600160a01b03166133cd909392919063ffffffff16565b8061188481615ae6565b9150506117ee565b506047546040516340c10f1960e01b8152336004820152602481018390526001600160a01b03909116906340c10f1990604401600060405180830381600087803b1580156118d957600080fd5b505af11580156118ed573d6000803e3d6000fd5b505050507f62a0e51e012d92909587a2f2aa7f4016592ddf30135d505e2e0038b7f446a5bd338686846040516119269493929190615510565b60405180910390a160018055949350505050565b6046818154811061194a57600080fd5b6000918252602090912001546001600160a01b0316905081565b600061084d61281a565b600080604860009054906101000a90046001600160a01b03166001600160a01b03166318160ddd6040518163ffffffff1660e01b815260040160206040518083038186803b1580156119bf57600080fd5b505afa1580156119d3573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906119f7919061541d565b9050670de0b6b3a76400008115611bb6576000611a12612907565b80519091506000816001600160401b03811115611a3f57634e487b7160e01b600052604160045260246000fd5b604051908082528060200260200182016040528015611a68578160200160208202803683370190505b5090506000826001600160401b03811115611a9357634e487b7160e01b600052604160045260246000fd5b604051908082528060200260200182016040528015611abc578160200160208202803683370190505b50905060008060005b85811015611b76576000878281518110611aef57634e487b7160e01b600052603260045260246000fd5b602002602001015190506000611b05823061343e565b90508015611b2757611b1a8787858585612b2c565b611b2490866158c3565b94505b506001600160a01b0381166000908152604960205260409020548015611b6157611b548787858585612b2c565b611b5e90856158c3565b93505b50508080611b6e90615ae6565b915050611ac5565b50878160415484611b8791906158c3565b611b919190615a2e565b611ba390670de0b6b3a7640000615a0f565b611bad91906158db565b96505050505050505b91505090565b6033546040516311aec98d60e01b81523360048201526001600160a01b03909116906311aec98d90602401600060405180830381600087803b158015611c0157600080fd5b505af1158015611c15573d6000803e3d6000fd5b5050603c54610100900460ff16159150611c4390505760405162461bcd60e51b8152600401610996906156d5565b82611c4d8161259a565b60026001541415611c705760405162461bcd60e51b81526004016109969061573f565b600260015560008080611c848787876134bb565b925092509250600080886001600160a01b031663970d44ee6040518163ffffffff1660e01b815260040160206040518083038186803b158015611cc657600080fd5b505afa158015611cda573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611cfe91906153e5565b905080158015611d0f575060018451115b15611e5f5760015b8451811015611e5d57848181518110611d4057634e487b7160e01b600052603260045260246000fd5b602002602001015160001415611d5557611e4b565b848381518110611d7557634e487b7160e01b600052603260045260246000fd5b602002602001015160001415611d8d57809250611e4b565b848381518110611dad57634e487b7160e01b600052603260045260246000fd5b6020026020010151848281518110611dd557634e487b7160e01b600052603260045260246000fd5b6020026020010151611de79190615a0f565b858281518110611e0757634e487b7160e01b600052603260045260246000fd5b6020026020010151858581518110611e2f57634e487b7160e01b600052603260045260246000fd5b6020026020010151611e419190615a0f565b1115611e4b578092505b80611e5581615ae6565b915050611d17565b505b6000838381518110611e8157634e487b7160e01b600052603260045260246000fd5b602002602001015190506000858481518110611ead57634e487b7160e01b600052603260045260246000fd5b60200260200101519050600080600090505b8651811015612094576000878281518110611eea57634e487b7160e01b600052603260045260246000fd5b6020026020010151905060008111156120815760008a8381518110611f1f57634e487b7160e01b600052603260045260246000fd5b6020026020010151905086158015611f5e575060008a8481518110611f5457634e487b7160e01b600052603260045260246000fd5b6020026020010151115b15611fd15784868b8581518110611f8557634e487b7160e01b600052603260045260246000fd5b6020026020010151611f979190615a0f565b611fa191906158db565b915081898481518110611fc457634e487b7160e01b600052603260045260246000fd5b6020026020010181815250505b60435460405163ebd6d95560e01b81526001600160a01b038381166004830152602482018590529091169063ebd6d9559060440160206040518083038186803b15801561201d57600080fd5b505afa158015612031573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612055919061541d565b61205f90856158c3565b935061207f8f83836001600160a01b03166137f49092919063ffffffff16565b505b508061208c81615ae6565b915050611ebf565b50604051630e7949d360e21b81528c906001600160a01b038216906339e5274c906120c5908c908b9060040161564b565b600060405180830381600087803b1580156120df57600080fd5b505af11580156120f3573d6000803e3d6000fd5b505050506060806121068f838387612e1a565b8e6001600160a01b03167fa320f26f9e050a2dbc872470ba0413f8dbb2134c86680cfb6e911c68bb6f911b8c8b8760405161214393929190615670565b60405180910390a250506001805550505050505050505050505050565b603c54610100900460ff16156121885760405162461bcd60e51b8152600401610996906156d5565b603c5460ff16156121ab5760405162461bcd60e51b8152600401610996906156b9565b603c5462010000900460ff16156121e95760405162461bcd60e51b8152602060048201526002602482015261052560f41b6044820152606401610996565b6002600154141561220c5760405162461bcd60e51b81526004016109969061573f565b600260015560415460009061221f61281a565b61222991906158c3565b905061223481613824565b5060018055565b6033546040516311aec98d60e01b81523360048201526000916001600160a01b0316906311aec98d90602401600060405180830381600087803b15801561228157600080fd5b505af1158015612295573d6000803e3d6000fd5b50505050600260015414156122bc5760405162461bcd60e51b81526004016109969061573f565b60026001556122cd858585856138ad565b6001805595945050505050565b603c546060908190610100900460ff16156123075760405162461bcd60e51b8152600401610996906156d5565b603c5460ff161561232a5760405162461bcd60e51b8152600401610996906156b9565b6002600154141561234d5760405162461bcd60e51b81526004016109969061573f565b60026001556048546040516370a0823160e01b81523360048201526000916001600160a01b0316906370a082319060240160206040518083038186803b15801561239657600080fd5b505afa1580156123aa573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906123ce919061541d565b90506000851180156123e05750808511155b61241e5760405162461bcd60e51b815260206004820152600f60248201526e0aaa688d240dcdee840cadcdeeaced608b1b6044820152606401610996565b6000612428612907565b9050600081516001600160401b0381111561245357634e487b7160e01b600052604160045260246000fd5b60405190808252806020026020018201604052801561247c578160200160208202803683370190505b509050600082516001600160401b038111156124a857634e487b7160e01b600052604160045260246000fd5b6040519080825280602002602001820160405280156124d1578160200160208202803683370190505b5090506000806124e48a87878787613b3e565b9150915060006124f682878787613d4f565b919a5098509050891561255357898110156125535760405162461bcd60e51b815260206004820152601960248201527f616d6f756e74206c6f776572207468616e206d696e696d756d000000000000006044820152606401610996565b6125638b82858c8c8b8b8b613d80565b50505050505050600180819055509250929050565b606061084d6034613e68565b606061084d6036613e68565b606061084d612907565b6125a5603482613e75565b6125e65760405162461bcd60e51b81526020600482015260126024820152711cdd1c985d1959de481b9bdd08195e1a5cdd60721b6044820152606401610996565b50565b60006125f3610852565b61084361281a565b612606603682613e75565b6125e65760405162461bcd60e51b8152602060048201526015602482015274151a1948185cdcd95d081b9bdd081cdd5c1c1bdc9d605a1b6044820152606401610996565b60006126566001613e97565b9050801561266e576000805461ff0019166101001790555b603380546001600160a01b038781166001600160a01b03199283161790925560428054821687841617905560448054821686841617905560438054909116918416919091179055603c805462ff00001916905560006040556001603d5562093a80604e55670de0b6b3a7640000604d55683635c9adc5dea00000604f558015612731576000805461ff0019169055604051600181527f7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb38474024989060200160405180910390a15b5050505050565b603354604051630881b00560e41b81523360048201526001600160a01b039091169063881b00509060240160006040518083038186803b15801561277b57600080fd5b505afa15801561278f573d6000803e3d6000fd5b505050506001600160a01b0381163b6127f65760405162461bcd60e51b8152602060048201526024808201527f6e657720696d706c656d656e746174696f6e206973206e6f74206120636f6e746044820152631c9858dd60e21b6064820152608401610996565b7f3d78d3961e16fde088e2e26c1cfa163f5f8bb870709088dd68c87eb4091137e255565b600080612825612907565b80519091506000816001600160401b0381111561285257634e487b7160e01b600052604160045260246000fd5b60405190808252806020026020018201604052801561287b578160200160208202803683370190505b5090506000826001600160401b038111156128a657634e487b7160e01b600052604160045260246000fd5b6040519080825280602002602001820160405280156128cf578160200160208202803683370190505b50905060006128df858484613f1c565b9550505050505090565b60006128f3825490565b90505b919050565b600061132b8383613fa7565b606061084d6038613e68565b606080600080855190506000816001600160401b0381111561294557634e487b7160e01b600052604160045260246000fd5b60405190808252806020026020018201604052801561296e578160200160208202803683370190505b5090506000826001600160401b0381111561299957634e487b7160e01b600052604160045260246000fd5b6040519080825280602002602001820160405280156129c2578160200160208202803683370190505b5090506000805b84811015612b1d5760008a82815181106129f357634e487b7160e01b600052603260045260246000fd5b6020026020010151905060008a8015612a125750612a12603683613e75565b15612a8657604754612a2e9083906001600160a01b031661343e565b90508015612a865780868481518110612a5757634e487b7160e01b600052603260045260246000fd5b6020908102919091018101919091526001600160a01b0383166000908152604990915260409020819055600193505b6000612a92833061343e565b90508015612ac85780868581518110612abb57634e487b7160e01b600052603260045260246000fd5b6020026020010181815250505b8b8015612ade57506000612adc83836158c3565b115b15612b0757612aed82826158c3565b6001600160a01b0384166000908152604b60205260409020555b5050508080612b1590615ae6565b9150506129c9565b50909891975095509350505050565b600080612b3a878686613fdf565b90506000612b49878787614054565b90506000612b6483612b5c84600a615941565b8791906140c8565b9998505050505050505050565b600081612b7f57508061132e565b604d546000612b9b84836b033b2e3c9fd0803ce80000006140c8565b90508085118015612bce5750603d54612bb49082615a0f565b62989680612bc28388615a2e565b612bcc9190615a0f565b115b15612d9957604254603f546001600160a01b03909116908015801590612bfc57506001600160a01b03821615155b15612d2c576000612c0d8489615a2e565b90506000612710612c1e8484615a0f565b612c2891906158db565b9050808211612c845760405162461bcd60e51b815260206004820152602260248201527f466565206d757374206e6f742062652067726561746572207468616e207969656044820152611b1960f21b6064820152608401610996565b8015612d29576000612c96828b615a2e565b612ca08a84615a0f565b612caa91906158db565b90508015612d27576048546040516329460cc560e11b81526001600160a01b038781166004830152602482018490529091169063528c198a90604401600060405180830381600087803b158015612d0057600080fd5b505af1158015612d14573d6000803e3d6000fd5b505050508089612d2491906158c3565b98505b505b50505b6000612d4588886b033b2e3c9fd0803ce80000006140ea565b9050848114612d9557604d81905560408051888152602081018a90529081018290527fc6642d24d84e7f3d36ca39f5cce10e75639d9b158d5193aa350e2f900653e4c09060600160405180910390a15b5050505b50919392505050565b6000808315801590612db45750600083115b15612dd15783612dc48487615a0f565b612dce91906158db565b90505b80612e1057604d548015612dfd57612df686826b033b2e3c9fd0803ce80000006140ea565b9150612e0e565b612e0b86633b9aca00615a0f565b91505b505b90505b9392505050565b6001600160a01b0384166000908152604560209081526040808320815160a081018352815481526001820154938101849052600282015492810192909252600381015460608301526004015460ff16151560808201529190612e7d9084906158c3565b90506000866001600160a01b031663efbb5cb06040518163ffffffff1660e01b815260040160206040518083038186803b158015612eba57600080fd5b505afa158015612ece573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612ef2919061541d565b905060008083831115612f1057612f098484615a2e565b9150612f25565b83831015612f2557612f228385615a2e565b90505b84608001511561302b57604e546001600160a01b038a16600090815260456020526040902054612f559042615a2e565b108015612f6e5750604f54841180612f6e5750604f5483115b15613026578115612fcf57612710856040015185612f8c9190615a0f565b612f9691906158db565b821115612fca5760405162461bcd60e51b815260206004820152600260248201526111d360f21b6044820152606401610996565b613026565b801561302657612710856060015185612fe89190615a0f565b612ff291906158db565b8111156130265760405162461bcd60e51b8152602060048201526002602482015261131360f21b6044820152606401610996565b613052565b6001600160a01b0389166000908152604560205260409020600401805460ff191660011790555b6001600160a01b0389166000908152604560205260409020600101839055604154849087906130829086906158c3565b61308c91906158c3565b6130969190615a2e565b6041556001600160a01b038916600090815260456020526040812042905586156130be575060015b896001600160a01b03167f6d35366477ec3dbdcc668ff1cf0df6df18998d6bbfbea924257ccbdfca3b1f35848488888e8e886040516131039796959493929190615809565b60405180910390a250505050505050505050565b60006131238383614103565b6000805b845181101561326457600085828151811061315257634e487b7160e01b600052603260045260246000fd5b60209081029190910101516043546040516315d5220f60e31b81526001600160a01b0380841660048301529293506000929091169063aea910789060240160206040518083038186803b1580156131a857600080fd5b505afa1580156131bc573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906131e0919061541d565b6001600160a01b0383166000908152603b60205260409020549091506132428261320b83600a615941565b89878151811061322b57634e487b7160e01b600052603260045260246000fd5b60200260200101516140c89092919063ffffffff16565b61324c90866158c3565b9450505050808061325c90615ae6565b915050613127565b5060505480156132d257808210156132d25760405162461bcd60e51b815260206004820152602b60248201527f416d6f756e74206d757374206265206774206d696e696d756d20496e7665737460448201526a1b595b9d08105b5bdd5b9d60aa1b6064820152608401610996565b509392505050565b6000806132e5612907565b90506000805b82518110156133c657600083828151811061331657634e487b7160e01b600052603260045260246000fd5b60200260200101519050600061332c823061343e565b6047549091506000906133499084906001600160a01b031661343e565b9050600061335782846158c3565b905080156133af57600061336a85614306565b6001600160a01b0386166000908152603b602052604081205491925061339d8361339584600a615941565b8691906140c8565b90506133a9818a6158c3565b98505050505b5050505080806133be90615ae6565b9150506132eb565b5091505090565b6040516001600160a01b03808516602483015283166044820152606481018290526134389085906323b872dd60e01b906084015b60408051601f198184030181529190526020810180516001600160e01b03166001600160e01b031990931692909217909152614384565b50505050565b6040516370a0823160e01b81526001600160a01b038281166004830152600091908416906370a082319060240160206040518083038186803b15801561348357600080fd5b505afa158015613497573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061132b919061541d565b6060806060856001600160a01b031663adc58dd96040518163ffffffff1660e01b815260040160006040518083038186803b1580156134f957600080fd5b505afa15801561350d573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f1916820160405261353591908101906152c1565b81519194509250806001600160401b0381111561356257634e487b7160e01b600052604160045260246000fd5b60405190808252806020026020018201604052801561358b578160200160208202803683370190505b5091508460005b81811015613697576000805b84811015613644578781815181106135c657634e487b7160e01b600052603260045260246000fd5b60200260200101516001600160a01b03168a8a858181106135f757634e487b7160e01b600052603260045260246000fd5b9050602002810190613609919061585b565b61361a9060408101906020016150cb565b6001600160a01b031614156136325760019150613644565b8061363c81615ae6565b91505061359e565b50806136845760405162461bcd60e51b815260206004820152600f60248201526e1d1bd51bdad95b881a5b9d985b1a59608a1b6044820152606401610996565b508061368f81615ae6565b915050613592565b5060005b828110156137e85760005b828110156137d55760008989838181106136d057634e487b7160e01b600052603260045260246000fd5b90506020028101906136e2919061585b565b6136eb90615a45565b905080602001516001600160a01b031688848151811061371b57634e487b7160e01b600052603260045260246000fd5b60200260200101516001600160a01b03161461373757506137c3565b600081602001516001600160a01b031682600001516001600160a01b031614156137665750604081015161378f565b60408201511561378f5761378c82600001518360200151846040015185606001516138ad565b90505b808785815181106137b057634e487b7160e01b600052603260045260246000fd5b60200260200101818152505050506137d5565b806137cd81615ae6565b9150506136a6565b50806137e081615ae6565b91505061369b565b50505093509350939050565b6040516001600160a01b03831660248201526044810182905261104690849063a9059cbb60e01b90606401613401565b60485460408051633a98ef3960e01b815290516000926001600160a01b031691633a98ef39916004808301926020929190829003018186803b15801561386957600080fd5b505afa15801561387d573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906138a1919061541d565b90506110468282612b71565b60006138ba603885614456565b6138eb5760405162461bcd60e51b8152602060048201526002602482015261085560f21b6044820152606401610996565b604080516080810182528481526001600160a01b038088166020830181905287821693830193909352306060830152604454919261392d929091166000614462565b604454613947906001600160a01b03888116911686614462565b60445483516020850151604080870151905163538d5ab960e11b81526001600160a01b039094169363a71ab57293613987939092909187906004016155d6565b602060405180830381600087803b1580156139a157600080fd5b505af11580156139b5573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906139d9919061541d565b604354604051632633f08360e11b81526001600160a01b03898116600483015260248201889052888116604483015292945060009290911690634c67e1069060640160206040518083038186803b158015613a3357600080fd5b505afa158015613a47573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613a6b919061541d565b905061271084608001518560600151612710613a879190615a2e565b613a919190615a2e565b613a9b9083615a0f565b613aa591906158db565b831015613ad95760405162461bcd60e51b815260206004820152600260248201526113d360f21b6044820152606401610996565b8351604080516001600160a01b0392831681528983166020820152808201889052918816606083015260808201859052517fec43bc4dddba4c8ae8d4f92f53b39989eaf64022bec70471d105647e57c7891c9181900360a00190a15050949350505050565b6000806000613b4e868686613f1c565b6041549091508890600090613b6390846158c3565b90506000604860009054906101000a90046001600160a01b03166001600160a01b0316633a98ef396040518163ffffffff1660e01b815260040160206040518083038186803b158015613bb557600080fd5b505afa158015613bc9573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613bed919061541d565b604d549091508a841415613c7d57604854604051633d7ad0b760e21b81523360048201526001600160a01b039091169063f5eb42dc9060240160206040518083038186803b158015613c3e57600080fd5b505afa158015613c52573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613c76919061541d565b9650613c97565b613c9484826b033b2e3c9fd0803ce80000006140ea565b96505b60405415613cc75761271060405485613cb09190615a0f565b613cba91906158db565b613cc49085615a2e565b93505b6000613ce083836b033b2e3c9fd0803ce80000006140c8565b905080613ced8587615a0f565b613cf791906158db565b9650505084841015613d2757613d1a613d108587615a2e565b8a8a8a8686614586565b613d2490856158c3565b93505b84841015613d4157613d41613d3c8587615a2e565b614857565b505050509550959350505050565b606080600080613d6188888888614aa2565b90506000613d7182898989614c01565b97999198509095505050505050565b604854604051633b9e9f0160e21b8152336004820152602481018890526001600160a01b039091169063ee7a7c0490604401600060405180830381600087803b158015613dcc57600080fd5b505af1158015613de0573d6000803e3d6000fd5b5050603c5462010000900460ff169150613e1d9050576000613e03848484613f1c565b9050613e1b60415482613e1691906158c3565b613824565b505b7f41d90e25634e59cdc487695ce90710379a58f86f34cd95ae7e104a526aac04b9338989898989604051613e5696959493929190615591565b60405180910390a15050505050505050565b60606000612e1383614cc6565b6001600160a01b0381166000908152600183016020526040812054151561132b565b60008054610100900460ff1615613ede578160ff166001148015613eba5750303b155b613ed65760405162461bcd60e51b8152600401610996906156f1565b5060006128f6565b60005460ff808416911610613f055760405162461bcd60e51b8152600401610996906156f1565b506000805460ff191660ff831617905560016128f6565b82516000908190815b81811015613f9c576000878281518110613f4f57634e487b7160e01b600052603260045260246000fd5b602002602001015190506000613f65823061343e565b90508015613f8757613f7a8888858585612b2c565b613f8490866158c3565b94505b50508080613f9490615ae6565b915050613f25565b509095945050505050565b6000826000018281548110613fcc57634e487b7160e01b600052603260045260246000fd5b9060005260206000200154905092915050565b60008084848151811061400257634e487b7160e01b600052603260045260246000fd5b602002602001015190508060001415612e105761401e83614306565b90508085858151811061404157634e487b7160e01b600052603260045260246000fd5b6020908102919091010152949350505050565b60008084848151811061407757634e487b7160e01b600052603260045260246000fd5b602002602001015190508060001415612e1057506001600160a01b0382166000908152603b60205260409020548451819086908690811061404157634e487b7160e01b600052603260045260246000fd5b6000806140d58486615a0f565b90506140e183826158db565b95945050505050565b6000806140f78386615a0f565b90506140e184826158db565b604760009054906101000a90046001600160a01b03166001600160a01b03166345153af86040518163ffffffff1660e01b815260040160206040518083038186803b15801561415157600080fd5b505afa158015614165573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061418991906153e5565b156141c85760405162461bcd60e51b815260206004820152600f60248201526e697320646973747269627574696e6760881b6044820152606401610996565b8151815181158015906141da57508082145b61424c5760405162461bcd60e51b815260206004820152603860248201527f41737365747320616e6420616d6f756e7473206d75737420626520657175616c60448201527f20696e206c656e67746820616e64206e6f7420656d70747900000000000000006064820152608401610996565b60005b828110156127315761428785828151811061427a57634e487b7160e01b600052603260045260246000fd5b60200260200101516125fb565b60008482815181106142a957634e487b7160e01b600052603260045260246000fd5b6020026020010151116142f45760405162461bcd60e51b81526020600482015260136024820152720416d6f756e74206d757374206265206774203606c1b6044820152606401610996565b806142fe81615ae6565b91505061424f565b6043546040516315d5220f60e31b81526001600160a01b038381166004830152600092169063aea910789060240160206040518083038186803b15801561434c57600080fd5b505afa158015614360573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906128f3919061541d565b60006143d9826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b0316614d229092919063ffffffff16565b80519091501561104657808060200190518101906143f791906153e5565b6110465760405162461bcd60e51b815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e6044820152691bdd081cdd58d8d9595960b21b6064820152608401610996565b600061132b8383614d31565b8015806144eb5750604051636eb1769f60e11b81523060048201526001600160a01b03838116602483015284169063dd62ed3e9060440160206040518083038186803b1580156144b157600080fd5b505afa1580156144c5573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906144e9919061541d565b155b6145565760405162461bcd60e51b815260206004820152603660248201527f5361666545524332303a20617070726f76652066726f6d206e6f6e2d7a65726f60448201527520746f206e6f6e2d7a65726f20616c6c6f77616e636560501b6064820152608401610996565b6040516001600160a01b03831660248201526044810182905261104690849063095ea7b360e01b90606401613401565b8451600090869082816001600160401b038111156145b457634e487b7160e01b600052604160045260246000fd5b6040519080825280602002602001820160405280156145dd578160200160208202803683370190505b5090506000805b8381101561472a57600085828151811061460e57634e487b7160e01b600052603260045260246000fd5b6020026020010151905061462c816036613e7590919063ffffffff16565b156147175760475460009061464b9083906001600160a01b031661343e565b905080156147155760006146628d8d868686612b2c565b9050808f11156146b85761467681866158c3565b9450808f6146849190615a2e565b9e50818685815181106146a757634e487b7160e01b600052603260045260246000fd5b602002602001018181525050614713565b6146c28f866158c3565b9450808f836146d19190615a0f565b6146db91906158db565b8685815181106146fb57634e487b7160e01b600052603260045260246000fd5b60200260200101818152505060009e5050505061472a565b505b505b508061472281615ae6565b9150506145e4565b50801561484957604754604051637615eab160e11b81526001600160a01b039091169063ec2bd56290614763908790869060040161564b565b600060405180830381600087803b15801561477d57600080fd5b505af1158015614791573d6000803e3d6000fd5b5050505060006147a2828989612da2565b6048546047546040516329460cc560e11b81526001600160a01b03918216600482015260248101849052929350169063528c198a90604401600060405180830381600087803b1580156147f457600080fd5b505af1158015614808573d6000803e3d6000fd5b505050507f07afc15fb70b2f450b68377bcdb1f517c3b24d4f8f10e18f02635e8771281ac182868560405161483f93929190615776565b60405180910390a1505b9a9950505050505050505050565b6000805b604654811015614a865760006046828154811061488857634e487b7160e01b600052603260045260246000fd5b6000918252602090912001546001600160a01b03169050806148aa5750614a86565b6001600160a01b038116600090815260456020526040902060010154806148d2575050614a74565b6000818611156148ef5750806148e88187615a2e565b9550614916565b8161490287670de0b6b3a76400006158c3565b1061490e575080614911565b50845b600095505b60405163895a748560e01b815260048101829052602481018390526000604482018190529081906001600160a01b0386169063895a748590606401600060405180830381600087803b15801561496b57600080fd5b505af115801561497f573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526149a791908101906152c1565b91509150846001600160a01b03167f8784f8bc43bb3cadc89640d1bde1d352aaf5dbb424e50a2bd4c642111409bf7d848685856040516149ea94939291906157ea565b60405180910390a26001600160a01b0385166000908152604560205260408120600101549085614a1a8684615a0f565b614a2491906158db565b9050614a308183615a2e565b6001600160a01b038816600090815260456020526040902060010155614a56818a6158c3565b985060008a11614a6c5750505050505050614a86565b505050505050505b80614a7e81615ae6565b91505061485b565b508060416000828254614a999190615a2e565b90915550505050565b82516060906000816001600160401b03811115614acf57634e487b7160e01b600052604160045260246000fd5b604051908082528060200260200182016040528015614af8578160200160208202803683370190505b50905060005b82811015614bf6576000878281518110614b2857634e487b7160e01b600052603260045260246000fd5b602002602001015190506000614b3e823061343e565b90508015614be1576000614b558989868686612b2c565b90508a8110614ba75780614b698c84615a0f565b614b7391906158db565b858581518110614b9357634e487b7160e01b600052603260045260246000fd5b602002602001018181525050505050614bf6565b81858581518110614bc857634e487b7160e01b600052603260045260246000fd5b6020908102919091010152614bdd818c615a2e565b9a50505b50508080614bee90615ae6565b915050614afe565b509695505050505050565b82516000908190815b81811015614cba576000888281518110614c3457634e487b7160e01b600052603260045260246000fd5b602002602001015190506000811115614ca7576000888381518110614c6957634e487b7160e01b600052603260045260246000fd5b602002602001015190506000614c828989868587612b2c565b9050614c8e81876158c3565b9550614ca46001600160a01b03831633856137f4565b50505b5080614cb281615ae6565b915050614c0a565b50909695505050505050565b606081600001805480602002602001604051908101604052809291908181526020018280548015614d1657602002820191906000526020600020905b815481526020019060010190808311614d02575b50505050509050919050565b6060612e108484600085614d3d565b600061132b8383613e75565b606082471015614d9e5760405162461bcd60e51b815260206004820152602660248201527f416464726573733a20696e73756666696369656e742062616c616e636520666f6044820152651c8818d85b1b60d21b6064820152608401610996565b6001600160a01b0385163b614df55760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152606401610996565b600080866001600160a01b03168587604051614e1191906154f4565b60006040518083038185875af1925050503d8060008114614e4e576040519150601f19603f3d011682016040523d82523d6000602084013e614e53565b606091505b5091509150614e63828286614e6e565b979650505050505050565b60608315614e7d575081612e13565b825115614e8d5782518084602001fd5b8160405162461bcd60e51b815260040161099691906156a6565b600082601f830112614eb7578081fd5b81356020614ecc614ec7836158a0565b615870565b80838252828201915082860187848660051b8901011115614eeb578586fd5b855b85811015614f12578135614f0081615b2d565b84529284019290840190600101614eed565b5090979650505050505050565b600082601f830112614f2f578081fd5b81356020614f3f614ec7836158a0565b80838252828201915082860187848660051b8901011115614f5e578586fd5b855b85811015614f1257813584529284019290840190600101614f60565b600082601f830112614f8c578081fd5b81516020614f9c614ec7836158a0565b80838252828201915082860187848660051b8901011115614fbb578586fd5b855b85811015614f1257815184529284019290840190600101614fbd565b600082601f830112614fe9578081fd5b81356001600160401b0381111561500257615002615b17565b615015601f8201601f1916602001615870565b818152846020838601011115615029578283fd5b816020850160208301379081016020019190915292915050565b600060a08284031215615054578081fd5b61505e60a0615870565b9050813561506b81615b2d565b8152602082013560ff8116811461508157600080fd5b602082015260408201356001600160401b0381111561509f57600080fd5b6150ab84828501614fd9565b604083015250606082013560608201526080820135608082015292915050565b6000602082840312156150dc578081fd5b8135612e1381615b2d565b600080600080608085870312156150fc578283fd5b843561510781615b2d565b9350602085013561511781615b2d565b9250604085013561512781615b2d565b9150606085013561513781615b2d565b939692955090935050565b60008060008060808587031215615157578384fd5b843561516281615b2d565b9350602085013561517281615b2d565b92506040850135915060608501356001600160401b03811115615193578182fd5b61519f87828801615043565b91505092959194509250565b6000806000604084860312156151bf578081fd5b83356151ca81615b2d565b925060208401356001600160401b03808211156151e5578283fd5b818601915086601f8301126151f8578283fd5b813581811115615206578384fd5b8760208260051b850101111561521a578384fd5b6020830194508093505050509250925092565b600080600060608486031215615241578081fd5b833561524c81615b2d565b95602085013595506040909401359392505050565b60008060408385031215615273578182fd5b82356001600160401b0380821115615289578384fd5b61529586838701614ea7565b935060208501359150808211156152aa578283fd5b506152b785828601614f1f565b9150509250929050565b600080604083850312156152d3578182fd5b82516001600160401b03808211156152e9578384fd5b818501915085601f8301126152fc578384fd5b8151602061530c614ec7836158a0565b8083825282820191508286018a848660051b890101111561532b578889fd5b8896505b8487101561535657805161534281615b2d565b83526001969096019591830191830161532f565b509188015191965090935050508082111561536f578283fd5b506152b785828601614f7c565b600080600060608486031215615390578081fd5b83356001600160401b03808211156153a6578283fd5b6153b287838801614ea7565b945060208601359150808211156153c7578283fd5b506153d486828701614f1f565b925050604084013590509250925092565b6000602082840312156153f6578081fd5b81518015158114612e13578182fd5b600060208284031215615416578081fd5b5035919050565b60006020828403121561542e578081fd5b5051919050565b60008060408385031215615447578182fd5b50508035926020909101359150565b6000815180845260208085019450808401835b8381101561548e5781516001600160a01b031687529582019590820190600101615469565b509495945050505050565b6000815180845260208085019450808401835b8381101561548e578151875295820195908201906001016154ac565b600081518084526154e0816020860160208601615aba565b601f01601f19169290920160200192915050565b60008251615506818460208701615aba565b9190910192915050565b6001600160a01b038516815260806020820181905260009061553490830186615456565b82810360408401526155468186615499565b91505082606083015295945050505050565b600060018060a01b03861682528460208301526080604083015261557f6080830185615456565b8281036060840152614e638185615499565b600060018060a01b038816825286602083015285604083015284606083015260c060808301526155c460c0830185615456565b82810360a0840152612b648185615499565b600060018060a01b03808716835260ff8616602084015260e0604084015261560160e08401866154c8565b9150835160608401528060208501511660808401528060408501511660a08401528060608501511660c08401525095945050505050565b60006020825261132b6020830184615456565b60006040825261565e6040830185615456565b82810360208401526140e18185615499565b6000606082526156836060830186615456565b82810360208401526156958186615499565b915050826040830152949350505050565b60006020825261132b60208301846154c8565b602080825260029082015261105160f21b604082015260600190565b602080825260029082015261455360f01b604082015260600190565b6020808252602e908201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160408201526d191e481a5b9a5d1a585b1a5e995960921b606082015260800190565b6020808252601f908201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c00604082015260600190565b60008482526060602083015261578f6060830185615456565b82810360408401526157a18185615499565b9695505050505050565b6000858252608060208301526157c46080830186615456565b82810360408401526157d68186615499565b90508281036060840152614e638185615499565b60008582528460208301526080604083015261557f6080830185615456565b600088825287602083015286604083015285606083015260e0608083015261583460e0830186615456565b82810360a08401526158468186615499565b9150508260c083015298975050505050505050565b60008235607e19833603018112615506578182fd5b604051601f8201601f191681016001600160401b038111828210171561589857615898615b17565b604052919050565b60006001600160401b038211156158b9576158b9615b17565b5060051b60200190565b600082198211156158d6576158d6615b01565b500190565b6000826158f657634e487b7160e01b81526012600452602481fd5b500490565b80825b600180861161590d5750615938565b81870482111561591f5761591f615b01565b8086161561592c57918102915b9490941c9380026158fe565b94509492505050565b600061132b600019848460008261595a57506001612e13565b8161596757506000612e13565b816001811461597d5760028114615987576159b4565b6001915050612e13565b60ff84111561599857615998615b01565b6001841b9150848211156159ae576159ae615b01565b50612e13565b5060208310610133831016604e8410600b84101617156159e7575081810a838111156159e2576159e2615b01565b612e13565b6159f484848460016158fb565b808604821115615a0657615a06615b01565b02949350505050565b6000816000190483118215151615615a2957615a29615b01565b500290565b600082821015615a4057615a40615b01565b500390565b600060808236031215615a56578081fd5b615a606080615870565b8235615a6b81615b2d565b81526020830135615a7b81615b2d565b60208201526040838101359082015260608301356001600160401b03811115615aa2578283fd5b615aae36828601615043565b60608301525092915050565b60005b83811015615ad5578181015183820152602001615abd565b838111156134385750506000910152565b6000600019821415615afa57615afa615b01565b5060010190565b634e487b7160e01b600052601160045260246000fd5b634e487b7160e01b600052604160045260246000fd5b6001600160a01b03811681146125e657600080fdfea2646970667358221220aa1cd7ac66af40f91b156c90beed3b8f85c70703e2ce1670baa42cdc7d53409e64736f6c63430008030033

Block Transaction Difficulty Gas Used Reward
View All Blocks Produced

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

Validator Index Block Amount
View All Withdrawals

Transaction Hash Block Value Eth2 PubKey Valid
View All Deposits
Loading...
Loading
[ Download: CSV Export  ]

A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.