ETH Price: $2,121.52 (+2.45%)

Contract

0x165Ce764e2511bc5bbd1e239dB4FDB63234c8BA3
 

Overview

ETH Balance

0 ETH

Eth Value

$0.00

More Info

Private Name Tags

Multichain Info

No addresses found
Transaction Hash
Method
Block
From
To

There are no matching entries

Please try again later

View more zero value Internal Transactions in Advanced View mode

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

Block Transaction Difficulty Gas Used Reward
View All Blocks Produced

Validator Index Block Amount
View All Withdrawals

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

Contract Source Code Verified (Exact Match)

Contract Name:
Vault

Compiler Version
v0.8.11+commit.d7f03943

Optimization Enabled:
Yes with 148 runs

Other Settings:
default evmVersion, BSL 1.1 license
// SPDX-License-Identifier: BUSL-1.1

pragma solidity 0.8.11;

import "./vault/VaultRestricted.sol";

/**
 * @notice Implementation of the {IVault} interface.
 *
 * @dev
 * All vault instances are meant to be deployed via the Controller
 * as a proxy and will not be recognizable by the Spool if they are
 * not done so.
 *
 * The vault contract is capable of supporting a single currency underlying
 * asset and deposit to multiple strategies at once, including dual-collateral
 * ones.
 *
 * The vault also supports the additional distribution of extra reward tokens as
 * an incentivization mechanism proportionate to each user's deposit amount within
 * the vhe vault.
 *
 * Vault implementation consists of following contracts:
 * 1. VaultImmutable: reads vault specific immutable variable from vault proxy contract
 * 2. VaultBase: holds vault state variables and provides some of the common vault functions
 * 3. RewardDrip: distributes vault incentivized rewards to users participating in the vault
 * 4. VaultIndexActions: implements functions to synchronize the vault with central Spool contract
 * 5. VaultRestricted: exposes functions restricted for other Spool specific contracts
 * 6. Vault: exposes unrestricted functons to interact with the core vault functionality (deposit/withdraw/claim)
 */
contract Vault is VaultRestricted {
    using SafeERC20 for IERC20;
    using Bitwise for uint256;

    /* ========== CONSTRUCTOR ========== */

    /**
     * @notice Sets the initial immutable values of the contract.
     *
     * @dev
     * All values have been sanitized by the controller contract, meaning
     * that no additional checks need to be applied here.
     *
     * @param _spool the spool implemenation
     * @param _controller the controller implemenation
     * @param _fastWithdraw fast withdraw implementation
     * @param _feeHandler fee handler implementation
     * @param _spoolOwner spool owner contract
     */
    constructor(
        ISpool _spool,
        IController _controller,
        IFastWithdraw _fastWithdraw,
        IFeeHandler _feeHandler,
        ISpoolOwner _spoolOwner
    )
        VaultBase(
            _spool,
            _controller,
            _fastWithdraw,
            _feeHandler
        )
        SpoolOwnable(_spoolOwner)
    {}

    /* ========== DEPOSIT ========== */

    /**
     * @notice Allows a user to perform a particular deposit to the vault.
     *
     * @dev
     * Emits a {Deposit} event indicating the amount newly deposited for index.
     *
     * Perform redeem if possible:
     * - Vault: Index has been completed (sync deposits/withdrawals)
     * - User: Claim deposit shares or withdrawn amount
     * 
     * Requirements:
     *
     * - the provided strategies must be valid
     * - the caller must have pre-approved the contract for the token amount deposited
     * - the caller cannot deposit zero value
     * - the system should not be paused
     *
     * @param vaultStrategies strategies of this vault (verified internally)
     * @param amount amount to deposit
     * @param transferFromVault if the transfer should occur from the funds transfer(controller) address
     */
    function deposit(address[] memory vaultStrategies, uint128 amount, bool transferFromVault)
        external
        verifyStrategies(vaultStrategies)
        hasStrategies(vaultStrategies)
        redeemVaultStrategiesModifier(vaultStrategies)
        redeemUserModifier
        updateRewards
    {
        require(amount > 0, "NDP");

        // get next possible index to deposit
        uint24 activeGlobalIndex = _getActiveGlobalIndex();

        // Mark user deposited amount for active index
        vaultIndexAction[activeGlobalIndex].depositAmount += amount;
        userIndexAction[msg.sender][activeGlobalIndex].depositAmount += amount;

        // Mark vault strategies to deposit at index
        _distributeInStrats(vaultStrategies, amount, activeGlobalIndex);

        // mark that vault and user have interacted at this global index
        _updateInteractedIndex(activeGlobalIndex);
        _updateUserInteractedIndex(activeGlobalIndex);

        // transfer user deposit to Spool
        _transferDepositToSpool(amount, transferFromVault);

        // store user deposit amount
        _addInstantDeposit(amount);

        emit Deposit(msg.sender, activeGlobalIndex, amount);
    }

    /**
     * @notice Distributes a deposit to the various strategies based on the allocations of the vault.
     */
    function _distributeInStrats(
        address[] memory vaultStrategies,
        uint128 amount,
        uint256 activeGlobalIndex
    ) private {
        uint128 amountLeft = amount;
        uint256 lastElement = vaultStrategies.length - 1;
        uint256 _proportions = proportions;

        for (uint256 i; i < lastElement; i++) {
            uint128 proportionateAmount = _getStrategyDepositAmount(_proportions, i, amount);
            if (proportionateAmount > 0) {
                spool.deposit(vaultStrategies[i], proportionateAmount, activeGlobalIndex);
                amountLeft -= proportionateAmount;
            }
        }

        if (amountLeft > 0) {
            spool.deposit(vaultStrategies[lastElement], amountLeft, activeGlobalIndex);
        }
    }

    /* ========== WITHDRAW ========== */

    /**
     * @notice Allows a user to withdraw their deposited funds from the vault at next possible index.
     * The withdrawal is queued for when do hard work for index is completed.
     * 
     * @dev
     * Perform redeem if possible:
     * - Vault: Index has been completed (sync deposits/withdrawals)
     * - User: Claim deposit shares or withdrawn amount
     *
     * Emits a {Withdrawal} event indicating the shares burned, index of the withdraw and the amount of funds withdrawn.
     *
     * Requirements:
     *
     * - vault must not be reallocating
     * - the provided strategies must be valid
     * - the caller must have a non-zero amount of shares to withdraw
     * - the caller must have enough shares to withdraw the specified share amount
     * - the system should not be paused
     *
     * @param vaultStrategies strategies of this vault (verified internally)
     * @param sharesToWithdraw shares amount to withdraw
     * @param withdrawAll if all shares should be removed
     */
    function withdraw(
        address[] memory vaultStrategies,
        uint128 sharesToWithdraw,
        bool withdrawAll
    )
        external
        verifyStrategies(vaultStrategies)
        redeemVaultStrategiesModifier(vaultStrategies)
        noReallocation
        redeemUserModifier
        updateRewards
    {
        sharesToWithdraw = _withdrawShares(sharesToWithdraw, withdrawAll);
        
        // get next possible index to withdraw
        uint24 activeGlobalIndex = _getActiveGlobalIndex();

        // mark user withdrawn shares amount for active index
        userIndexAction[msg.sender][activeGlobalIndex].withdrawShares += sharesToWithdraw;
        vaultIndexAction[activeGlobalIndex].withdrawShares += sharesToWithdraw;

        // mark strategies in the spool contract to be withdrawn at next possible index
        _withdrawFromStrats(vaultStrategies, sharesToWithdraw, activeGlobalIndex);

        // mark that vault and user interacted at this global index
        _updateInteractedIndex(activeGlobalIndex);
        _updateUserInteractedIndex(activeGlobalIndex);

        emit Withdraw(msg.sender, activeGlobalIndex, sharesToWithdraw);
    }

    /* ========== FAST WITHDRAW ========== */

    /**
     * @notice Allows a user to withdraw their deposited funds right away.
     *
     * @dev
     * @dev
     * User can execute the withdrawal of his shares from the vault at any time without
     * waiting for the DHW to process it. This is done independently of other events (e.g. DHW)
     * and the gas cost is paid entirely by the user.
     * Shares belonging to the user and are sent back to the FastWithdraw contract
     * where an actual withdrawal can be peformed, where user recieves the underlying tokens
     * right away.
     *
     * Requirements:
     *
     * - vault must not be reallocating
     * - the spool system must not be mid reallocation
     *   (started DHW and not finished, at index the reallocation was initiated)
     * - the provided strategies must be valid
     * - the sistem must not be in the middle of the reallocation
     * - the system should not be paused
     *
     * @param vaultStrategies strategies of this vault
     * @param sharesToWithdraw shares amount to withdraw
     * @param withdrawAll if all shares should be removed
     * @param fastWithdrawParams extra parameters to perform fast withdraw
     */
    function withdrawFast(
        address[] memory vaultStrategies,
        uint128 sharesToWithdraw,
        bool withdrawAll,
        FastWithdrawParams memory fastWithdrawParams
    )
        external
        noMidReallocation
        verifyStrategies(vaultStrategies)
        redeemVaultStrategiesModifier(vaultStrategies)
        noReallocation
        redeemUserModifier
        updateRewards
    {
        sharesToWithdraw = _withdrawShares(sharesToWithdraw, withdrawAll);

        uint256 vaultShareProportion = _getVaultShareProportion(sharesToWithdraw);
        totalShares -= sharesToWithdraw;

        uint128[] memory strategyRemovedShares = spool.removeShares(vaultStrategies, vaultShareProportion);

        uint256 proportionateDeposit = _getUserProportionateDeposit(sharesToWithdraw);

        // transfer removed shares to fast withdraw contract
        fastWithdraw.transferShares(
            vaultStrategies,
            strategyRemovedShares,
            proportionateDeposit,
            msg.sender,
            fastWithdrawParams
        );

        emit WithdrawFast(msg.sender, sharesToWithdraw);
    }

    /**
     * @dev Updates storage values according to shares withdrawn.
     *      If `withdrawAll` is true, all shares are removed from the users
     * @param sharesToWithdraw Amount of shares to withdraw
     * @param withdrawAll Withdraw all user shares
     */
    function _withdrawShares(uint128 sharesToWithdraw, bool withdrawAll) private returns(uint128) {
        User storage user = users[msg.sender];
        uint128 userShares = user.shares;

        uint128 userActiveInstantDeposit = user.instantDeposit;

        // Substract the not processed instant deposit
        // This way we don't consider the deposit that was not yet processed by the DHW
        // when calculating amount of it withdrawn
        LastIndexInteracted memory userIndexInteracted = userLastInteractions[msg.sender];
        if (userIndexInteracted.index1 > 0) {
            userActiveInstantDeposit -= userIndexAction[msg.sender][userIndexInteracted.index1].depositAmount;
            // also check if user second index has pending actions
            if (userIndexInteracted.index2 > 0) {
                userActiveInstantDeposit -= userIndexAction[msg.sender][userIndexInteracted.index2].depositAmount;
            }
        }
        
        // check if withdraw all flag was set or user requested
        // withdraw of all shares in `sharesToWithdraw`
        if (withdrawAll || (userShares > 0 && userShares == sharesToWithdraw)) {
            sharesToWithdraw = userShares;
            // set user shares to 0
            user.shares = 0;

            // substract all the users instant deposit processed till now
            // substract the same amount from vault total instand deposit value
            totalInstantDeposit -= userActiveInstantDeposit;
            user.instantDeposit -= userActiveInstantDeposit;
        } else {
            require(
                userShares >= sharesToWithdraw &&
                sharesToWithdraw > 0, 
                "WSH"
            );

            // if we didnt withdraw all calculate the proportion of
            // the instant deposit to substract it from the user and vault amounts
            uint128 instantDepositWithdrawn = _getProportion128(userActiveInstantDeposit, sharesToWithdraw, userShares);

            totalInstantDeposit -= instantDepositWithdrawn;
            user.instantDeposit -= instantDepositWithdrawn;

            // susrtact withdrawn shares from the user
            // NOTE: vault shares will be substracted when the at the redeem
            // for the current active index is processed. This way we substract it
            // only once for all the users.
            unchecked {
                user.shares = userShares - sharesToWithdraw;
            }
        }
        
        return sharesToWithdraw;
    }

    /**
     * @notice Calculates user proportionate deposit when withdrawing and updated user deposit storage
     * @dev Checks user index action to see if user already has some withdrawn shares
     *      pending to be processed.
     *      Called when performing the fast withdraw
     *
     * @param sharesToWithdraw shares amount to withdraw
     *
     * @return User deposit amount proportionate to the amount of shares being withdrawn
     */
    function _getUserProportionateDeposit(uint128 sharesToWithdraw) private returns(uint256) {
        User storage user = users[msg.sender];
        LastIndexInteracted memory userIndexInteracted = userLastInteractions[msg.sender];

        uint128 proportionateDeposit;
        uint128 sharesAtWithdrawal = user.shares + sharesToWithdraw;

        if (userIndexInteracted.index1 > 0) {
            sharesAtWithdrawal += userIndexAction[msg.sender][userIndexInteracted.index1].withdrawShares;

            if (userIndexInteracted.index2 > 0) {
                sharesAtWithdrawal += userIndexAction[msg.sender][userIndexInteracted.index2].withdrawShares;
            }
        }

        if (sharesAtWithdrawal > sharesToWithdraw) {
            uint128 userTotalDeposit = user.activeDeposit;
            proportionateDeposit = _getProportion128(userTotalDeposit, sharesToWithdraw, sharesAtWithdrawal);
            user.activeDeposit = userTotalDeposit - proportionateDeposit;
        } else {
            proportionateDeposit = user.activeDeposit;
            user.activeDeposit = 0;
        }

        return proportionateDeposit;
    }

    function _withdrawFromStrats(address[] memory vaultStrategies, uint128 totalSharesToWithdraw, uint256 activeGlobalIndex) private {
        uint256 vaultShareProportion = _getVaultShareProportion(totalSharesToWithdraw);
        for (uint256 i; i < vaultStrategies.length; i++) {
            spool.withdraw(vaultStrategies[i], vaultShareProportion, activeGlobalIndex);
        }
    }

    /* ========== CLAIM ========== */

    /**
     * @notice Allows a user to claim their debt from the vault after withdrawn shares were processed.
     *
     * @dev
     * Fee is taken from the profit
     * Perform redeem on user demand
     *
     * Emits a {DebtClaim} event indicating the debt the user claimed.
     *
     * Requirements:
     *
     * - if `doRedeemVault` is true, the provided strategies must be valid
     * - the caller must have a non-zero debt owed
     * - the system should not be paused (if doRedeemVault)
     *
     * @param doRedeemVault flag, to execute redeem for the vault (synchronize deposit/withdrawals with the system)
     * @param vaultStrategies vault stratigies
     * @param doRedeemUser flag, to execute redeem for the caller
     *
     * @return claimAmount amount of underlying asset, claimed by the caller
     */
    function claim(
        bool doRedeemVault,
        address[] memory vaultStrategies,
        bool doRedeemUser
    ) external returns (uint128 claimAmount) {
        User storage user = users[msg.sender];

        if (doRedeemVault) {
            _verifyStrategies(vaultStrategies);
            _redeemVaultStrategies(vaultStrategies);
        }

        if (doRedeemUser) {
            _redeemUser();
        }

        claimAmount = user.owed;
        require(claimAmount > 0, "CA0");

        user.owed = 0;

        // Calculate profit and take fees
        uint128 userWithdrawnDeposits = user.withdrawnDeposits;
        if (claimAmount > userWithdrawnDeposits) {
            user.withdrawnDeposits = 0;
            uint128 profit = claimAmount - userWithdrawnDeposits;

            uint128 feesPaid = _payFeesAndTransfer(profit);

            // Substract fees paid from claim amount
            claimAmount -= feesPaid;
        } else {
            user.withdrawnDeposits = userWithdrawnDeposits - claimAmount;
        }

        _underlying().safeTransfer(msg.sender, claimAmount);

        emit Claimed(msg.sender, claimAmount);
    }

    /* ========== REDEEM ========== */

    /**
     * @notice Redeem vault and user deposit and withdrawals
     *
     * Requirements:
     *
     * - the provided strategies must be valid
     *
     * @param vaultStrategies vault stratigies
     */
    function redeemVaultAndUser(address[] memory vaultStrategies)
        external
        verifyStrategies(vaultStrategies)
        redeemVaultStrategiesModifier(vaultStrategies)
        redeemUserModifier
    {}

    /**
     * @notice Redeem vault and user and return the user state
     * @dev Intended to be called as a static call for view purposes
     *
     * Requirements:
     *
     * - the provided strategies must be valid
     *
     * @param vaultStrategies vault strategies
     *
     * @return userShares current user shares
     * @return activeDeposit user active deposit (already processed by the DHW)
     * @return userOwed user total unclaimed amount
     * @return userWithdrawnDeposits unclaimed withdrawn deposit amount
     * @return userTotalUnderlying current user total underlying
     * @return pendingDeposit1 pending user deposit for the next index 
     * @return pendingWithdrawalShares1 pending user withdrawal shares for the next index 
     * @return pendingDeposit2 pending user deposit for the index after the next one 
     * @return pendingWithdrawalShares2 pending user withdrawal shares for the after the next one 
     */
    function getUpdatedUser(address[] memory vaultStrategies)
        external
        returns (
            uint256,
            uint256,
            uint256,
            uint256,
            uint256,
            uint256,
            uint256,
            uint256,
            uint256
        )
    {
        (uint256 totalUnderlying, , , , , ) = getUpdatedVault(vaultStrategies);
        _redeemUser();
        
        User storage user = users[msg.sender];

        uint256 userTotalUnderlying;
        if (totalShares > 0 && user.shares > 0) {
            userTotalUnderlying = (totalUnderlying * user.shares) / totalShares;
        }

        IndexAction storage indexAction1 = userIndexAction[msg.sender][userLastInteractions[msg.sender].index1];
        IndexAction storage indexAction2 = userIndexAction[msg.sender][userLastInteractions[msg.sender].index2];

        return (
            user.shares,
            user.activeDeposit, // amount of user deposited underlying token
            user.owed, // underlying token claimable amount
            user.withdrawnDeposits, // underlying token withdrawn amount
            userTotalUnderlying,
            indexAction1.depositAmount,
            indexAction1.withdrawShares,
            indexAction2.depositAmount,
            indexAction2.withdrawShares
        );
    }

    /**
     * @notice Redeem vault strategy deposits and withdrawals after do hard work.
     *
     * Requirements:
     *
     * - the provided strategies must be valid
     *
     * @param vaultStrategies vault strategies
     */
    function redeemVaultStrategies(address[] memory vaultStrategies)
        external
        verifyStrategies(vaultStrategies)
        redeemVaultStrategiesModifier(vaultStrategies)
    {}

    /**
     * @notice Redeem vault strategy deposits and withdrawals after do hard work.
     * @dev Intended to be called as a static call for view purposes
     *
     * Requirements:
     *
     * - the provided strategies must be valid
     *
     * @param vaultStrategies vault strategies
     *
     * @return totalUnderlying total vault underlying
     * @return totalShares total vault shares
     * @return pendingDeposit1 pending vault deposit for the next index 
     * @return pendingWithdrawalShares1 pending vault withdrawal shares for the next index 
     * @return pendingDeposit2 pending vault deposit for the index after the next one 
     * @return pendingWithdrawalShares2 pending vault withdrawal shares for the after the next one 
     */
    function getUpdatedVault(address[] memory vaultStrategies)
        public
        verifyStrategies(vaultStrategies)
        redeemVaultStrategiesModifier(vaultStrategies)
        returns (
            uint256,
            uint256,
            uint256,
            uint256,
            uint256,
            uint256
        )
    {
        uint256 totalUnderlying = 0;
        for (uint256 i; i < vaultStrategies.length; i++) {
            totalUnderlying += spool.getUnderlying(vaultStrategies[i]);
        }

        IndexAction storage indexAction1 = vaultIndexAction[lastIndexInteracted.index1];
        IndexAction storage indexAction2 = vaultIndexAction[lastIndexInteracted.index2];

        return (
            totalUnderlying,
            totalShares,
            indexAction1.depositAmount,
            indexAction1.withdrawShares,
            indexAction2.depositAmount,
            indexAction2.withdrawShares
        );
    }

    /**
     * @notice Redeem user deposits and withdrawals
     *
     * @dev Can only redeem user up to last index vault has redeemed
     */
    function redeemUser()
        external
    {
        _redeemUser();
    }

    /* ========== STRATEGY REMOVED ========== */

    /**
     * @notice Notify a vault a strategy was removed from the Spool system
     * @dev
     * This can be called by anyone after a strategy has been removed from the system.
     * After the removal of the strategy that the vault contains, all actions
     * calling central Spool contract will revert. This function must be called,
     * to remove the strategy from the vault and update the strategy hash according
     * to the new strategy array.
     *
     * Requirements:
     *
     * - The Spool system must finish reallocation if it's in progress
     * - the provided strategies must be valid
     * - The strategy must belong to this vault
     * - The strategy must be removed from the system
     *
     * @param vaultStrategies Array of current vault strategies (including the removed one)
     * @param i Index of the removed strategy in the `vaultStrategies`
     */
    function notifyStrategyRemoved(
        address[] memory vaultStrategies,
        uint256 i
    )
        external
        reallocationFinished
        verifyStrategies(vaultStrategies)
        hasStrategies(vaultStrategies)
        redeemVaultStrategiesModifier(vaultStrategies)
    {
        require(
            i < vaultStrategies.length &&
            !controller.validStrategy(vaultStrategies[i]),
            "BSTR"
        );

        uint256 lastElement = vaultStrategies.length - 1;

        address[] memory newStrategies = new address[](lastElement);

        if (lastElement > 0) {
            for (uint256 j; j < lastElement; j++) {
                newStrategies[j] = vaultStrategies[j];
            }

            if (i < lastElement) {
                newStrategies[i] = vaultStrategies[lastElement];
            }

            uint256 _proportions = proportions;
            uint256 proportionsLeft = FULL_PERCENT - _proportions.get14BitUintByIndex(i);
            if (lastElement > 1 && proportionsLeft > 0) {
                if (i == lastElement) {
                    _proportions = _proportions.reset14BitUintByIndex(i);
                } else {
                    uint256 lastProportion = _proportions.get14BitUintByIndex(lastElement);
                    _proportions = _proportions.reset14BitUintByIndex(i);
                    _proportions = _proportions.set14BitUintByIndex(i, lastProportion);
                }

                uint256 newProportions;

                uint256 lastNewElement = lastElement - 1;
                uint256 newProportionsLeft = FULL_PERCENT;
                for (uint256 j; j < lastNewElement; j++) {
                    uint256 propJ = _proportions.get14BitUintByIndex(j);
                    propJ = (propJ * FULL_PERCENT) / proportionsLeft;
                    newProportions = newProportions.set14BitUintByIndex(j, propJ);
                    newProportionsLeft -= propJ;
                }

                newProportions = newProportions.set14BitUintByIndex(lastNewElement, newProportionsLeft);

                proportions = newProportions;
            } else {
                proportions = FULL_PERCENT;
            }
        } else {
            proportions = 0;
        }

        _updateStrategiesHash(newStrategies);
        emit StrategyRemoved(i, vaultStrategies[i]);
    }

    /* ========== PRIVATE FUNCTIONS ========== */

    /**
     * @notice Throws if given array of strategies is empty
     */
    function _hasStrategies(address[] memory vaultStrategies) private pure {
        require(vaultStrategies.length > 0, "NST");
    }

    /* ========== MODIFIERS ========== */

    /**
     * @notice Throws if given array of strategies is empty
     */
    modifier hasStrategies(address[] memory vaultStrategies) {
        _hasStrategies(vaultStrategies);
        _;
    }

    /**
     * @notice Revert if reallocation is not finished for this vault
     */
    modifier reallocationFinished() {
        require(
            !_isVaultReallocating() ||
            reallocationIndex <= spool.getCompletedGlobalIndex(),
            "RNF"
        );
        _;
    }
}

File 2 of 34 : ReentrancyGuard.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.0 (security/ReentrancyGuard.sol)

pragma solidity ^0.8.0;

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

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

    uint256 private _status;

    constructor() {
        _status = _NOT_ENTERED;
    }

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

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

pragma solidity ^0.8.0;

/**
 * @dev Interface of the ERC20 standard as defined in the EIP.
 */
interface IERC20 {
    /**
     * @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 `recipient`.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * Emits a {Transfer} event.
     */
    function transfer(address recipient, 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 `sender` to `recipient` 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 sender,
        address recipient,
        uint256 amount
    ) external returns (bool);

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

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

pragma solidity ^0.8.0;

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

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

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

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

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

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

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

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.0 (utils/Address.sol)

pragma solidity ^0.8.0;

/**
 * @dev Collection of functions related to the address type
 */
library Address {
    /**
     * @dev Returns true if `account` is a contract.
     *
     * [IMPORTANT]
     * ====
     * It is unsafe to assume that an address for which this function returns
     * false is an externally-owned account (EOA) and not a contract.
     *
     * Among others, `isContract` will return false for the following
     * types of addresses:
     *
     *  - an externally-owned account
     *  - a contract in construction
     *  - an address where a contract will be created
     *  - an address where a contract lived, but was destroyed
     * ====
     */
    function isContract(address account) internal view returns (bool) {
        // This method relies on extcodesize, which returns 0 for contracts in
        // construction, since the code is only stored at the end of the
        // constructor execution.

        uint256 size;
        assembly {
            size := extcodesize(account)
        }
        return size > 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 Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but performing a delegate call.
     *
     * _Available since v3.4._
     */
    function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
        return functionDelegateCall(target, data, "Address: low-level delegate call failed");
    }

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

        (bool success, bytes memory returndata) = target.delegatecall(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);
            }
        }
    }
}

// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

/**
 * @dev Wrappers over Solidity's uintXX/intXX casting operators with added overflow
 * checks.
 *
 * Downcasting from uint256/int256 in Solidity does not revert on overflow. This can
 * easily result in undesired exploitation or bugs, since developers usually
 * assume that overflows raise errors. `SafeCast` restores this intuition by
 * reverting the transaction when such an operation overflows.
 *
 * Using this library instead of the unchecked operations eliminates an entire
 * class of bugs, so it's recommended to use it always.
 *
 * Can be combined with {SafeMath} and {SignedSafeMath} to extend it to smaller types, by performing
 * all math on `uint256` and `int256` and then downcasting.
 */
library SafeCast {
    /**
     * @dev Returns the downcasted uint224 from uint256, reverting on
     * overflow (when the input is greater than largest uint224).
     *
     * Counterpart to Solidity's `uint224` operator.
     *
     * Requirements:
     *
     * - input must fit into 224 bits
     */
    function toUint224(uint256 value) internal pure returns (uint224) {
        require(value <= type(uint224).max, "SafeCast: value doesn't fit in 224 bits");
        return uint224(value);
    }

    /**
     * @dev Returns the downcasted uint192 from uint256, reverting on
     * overflow (when the input is greater than largest uint192).
     *
     * Counterpart to Solidity's `uint192` operator.
     *
     * Requirements:
     *
     * - input must fit into 192 bits
     */
    function toUint192(uint256 value) internal pure returns (uint192) {
        require(value <= type(uint192).max, "SafeCast: value doesn't fit in 128 bits");
        return uint192(value);
    }

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

// SPDX-License-Identifier: BUSL-1.1

pragma solidity 0.8.11;

import "../external/@openzeppelin/token/ERC20/IERC20.sol";

interface IController {
    /* ========== FUNCTIONS ========== */

    function strategies(uint256 i) external view returns (address);

    function validStrategy(address strategy) external view returns (bool);

    function validVault(address vault) external view returns (bool);

    function getStrategiesCount() external view returns(uint8);

    function supportedUnderlying(IERC20 underlying)
        external
        view
        returns (bool);

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

    function verifyStrategies(address[] calldata _strategies) external view;

    function transferToSpool(
        address transferFrom,
        uint256 amount
    ) external;

    function checkPaused() external view;

    /* ========== EVENTS ========== */

    event EmergencyWithdrawStrategy(address indexed strategy);
    event EmergencyRecipientUpdated(address indexed recipient);
    event EmergencyWithdrawerUpdated(address indexed withdrawer, bool set);
    event PauserUpdated(address indexed user, bool set);
    event UnpauserUpdated(address indexed user, bool set);
    event VaultCreated(address indexed vault, address underlying, address[] strategies, uint256[] proportions,
        uint16 vaultFee, address riskProvider, int8 riskTolerance);
    event StrategyAdded(address strategy);
    event StrategyRemoved(address strategy);
    event VaultInvalid(address vault);
    event DisableStrategy(address strategy);
}

File 8 of 34 : IFastWithdraw.sol
// SPDX-License-Identifier: BUSL-1.1

pragma solidity 0.8.11;

import "./ISwapData.sol";

struct FastWithdrawParams {
    bool doExecuteWithdraw;
    uint256[][] slippages;
    SwapData[][] swapData;
}

interface IFastWithdraw {
    function transferShares(
        address[] calldata vaultStrategies,
        uint128[] calldata sharesWithdrawn,
        uint256 proportionateDeposit,
        address user,
        FastWithdrawParams calldata fastWithdrawParams
    ) external;

        /* ========== EVENTS ========== */

    event StrategyWithdrawn(address indexed user, address indexed vault, address indexed strategy);
    event UserSharesSaved(address indexed user, address indexed vault);
    event FastWithdrawExecuted(address indexed user, address indexed vault, uint256 totalWithdrawn);
}

// SPDX-License-Identifier: BUSL-1.1

pragma solidity 0.8.11;

import "../external/@openzeppelin/token/ERC20/IERC20.sol";

interface IFeeHandler {
    function payFees(
        IERC20 underlying,
        uint256 profit,
        address riskProvider,
        address vaultOwner,
        uint16 vaultFee
    ) external returns (uint256 feesPaid);

    function setRiskProviderFee(address riskProvider, uint16 fee) external;

    /* ========== EVENTS ========== */

    event FeesPaid(address indexed vault, uint profit, uint ecosystemCollected, uint treasuryCollected, uint riskProviderColected, uint vaultFeeCollected);
    event RiskProviderFeeUpdated(address indexed riskProvider, uint indexed fee);
    event EcosystemFeeUpdated(uint indexed fee);
    event TreasuryFeeUpdated(uint indexed fee);
    event EcosystemCollectorUpdated(address indexed collector);
    event TreasuryCollectorUpdated(address indexed collector);
    event FeeCollected(address indexed collector, IERC20 indexed underlying, uint amount);
    event EcosystemFeeCollected(IERC20 indexed underlying, uint amount);
    event TreasuryFeeCollected(IERC20 indexed underlying, uint amount);
}

File 10 of 34 : ISpool.sol
// SPDX-License-Identifier: BUSL-1.1

pragma solidity 0.8.11;

import "./spool/ISpoolExternal.sol";
import "./spool/ISpoolReallocation.sol";
import "./spool/ISpoolDoHardWork.sol";
import "./spool/ISpoolStrategy.sol";
import "./spool/ISpoolBase.sol";

/// @notice Utility Interface for central Spool implementation
interface ISpool is ISpoolExternal, ISpoolReallocation, ISpoolDoHardWork, ISpoolStrategy, ISpoolBase {}

// SPDX-License-Identifier: BUSL-1.1

pragma solidity 0.8.11;

interface ISpoolOwner {
    function isSpoolOwner(address user) external view returns(bool);
}

File 12 of 34 : ISwapData.sol
// SPDX-License-Identifier: BUSL-1.1

pragma solidity 0.8.11;

/**
 * @notice Strict holding information how to swap the asset
 * @member slippage minumum output amount
 * @member path swap path, first byte represents an action (e.g. Uniswap V2 custom swap), rest is swap specific path
 */
struct SwapData {
    uint256 slippage; // min amount out
    bytes path; // 1st byte is action, then path 
}

// SPDX-License-Identifier: BUSL-1.1

pragma solidity 0.8.11;

interface ISpoolBase {
    /* ========== FUNCTIONS ========== */

    function getCompletedGlobalIndex() external view returns(uint24);

    function getActiveGlobalIndex() external view returns(uint24);

    function isMidReallocation() external view returns (bool);

    /* ========== EVENTS ========== */

    event ReallocationTableUpdated(
        uint24 indexed index,
        bytes32 reallocationTableHash
    );

    event ReallocationTableUpdatedWithTable(
        uint24 indexed index,
        bytes32 reallocationTableHash,
        uint256[][] reallocationTable
    );
    
    event DoHardWorkCompleted(uint24 indexed index);

    event SetAllocationProvider(address actor, bool isAllocationProvider);
    event SetIsDoHardWorker(address actor, bool isDoHardWorker);
}

File 14 of 34 : ISpoolDoHardWork.sol
// SPDX-License-Identifier: BUSL-1.1

pragma solidity 0.8.11;

interface ISpoolDoHardWork {
    /* ========== EVENTS ========== */

    event DoHardWorkStrategyCompleted(address indexed strat, uint256 indexed index);
}

// SPDX-License-Identifier: BUSL-1.1

pragma solidity 0.8.11;

import "../ISwapData.sol";

interface ISpoolExternal {
    /* ========== FUNCTIONS ========== */

    function deposit(address strategy, uint128 amount, uint256 index) external;

    function withdraw(address strategy, uint256 vaultProportion, uint256 index) external;

    function fastWithdrawStrat(address strat, address underlying, uint256 shares, uint256[] calldata slippages, SwapData[] calldata swapData) external returns(uint128);

    function redeem(address strat, uint256 index) external returns (uint128, uint128);

    function redeemUnderlying(uint128 amount) external;

    function redeemReallocation(address[] calldata vaultStrategies, uint256 depositProportions, uint256 index) external;

    function removeShares(address[] calldata vaultStrategies, uint256 vaultProportion) external returns(uint128[] memory);
}

File 16 of 34 : ISpoolReallocation.sol
// SPDX-License-Identifier: BUSL-1.1

pragma solidity 0.8.11;

interface ISpoolReallocation {
    event StartReallocation(uint24 indexed index);
}

// SPDX-License-Identifier: BUSL-1.1

pragma solidity 0.8.11;

interface ISpoolStrategy {
    /* ========== FUNCTIONS ========== */

    function getUnderlying(address strat) external returns (uint128);
    
    function getVaultTotalUnderlyingAtIndex(address strat, uint256 index) external view returns(uint128);

    function addStrategy(address strat) external;

    function disableStrategy(address strategy, bool skipDisable) external;

    function runDisableStrategy(address strategy) external;

    function emergencyWithdraw(
        address strat,
        address withdrawRecipient,
        uint256[] calldata data
    ) external;
}

// SPDX-License-Identifier: BUSL-1.1

pragma solidity 0.8.11;

import "../../external/@openzeppelin/token/ERC20/IERC20.sol";

interface IRewardDrip {
    /* ========== STRUCTS ========== */

    // The reward configuration struct, containing all the necessary data of a typical Synthetix StakingReward contract
    struct RewardConfiguration {
        uint32 rewardsDuration;
        uint32 periodFinish;
        uint192 rewardRate; // rewards per second multiplied by accuracy
        uint32 lastUpdateTime;
        uint224 rewardPerTokenStored;
        mapping(address => uint256) userRewardPerTokenPaid;
        mapping(address => uint256) rewards;
    }

    /* ========== FUNCTIONS ========== */

    function getActiveRewards(address account) external;
    function tokenBlacklist(IERC20 token) view external returns(bool);

    /* ========== EVENTS ========== */
    
    event RewardPaid(IERC20 token, address indexed user, uint256 reward);
    event RewardAdded(IERC20 indexed token, uint256 amount, uint256 duration);
    event RewardExtended(IERC20 indexed token, uint256 amount, uint256 leftover, uint256 duration, uint32 periodFinish);
    event RewardRemoved(IERC20 indexed token);
    event PeriodFinishUpdated(IERC20 indexed token, uint32 periodFinish);
}

// SPDX-License-Identifier: BUSL-1.1

pragma solidity 0.8.11;

import "./IVaultDetails.sol";

interface IVaultBase {
    /* ========== FUNCTIONS ========== */

    function initialize(VaultInitializable calldata vaultInitializable) external;

    /* ========== STRUCTS ========== */

    struct User {
        uint128 instantDeposit; // used for calculating rewards
        uint128 activeDeposit; // users deposit after deposit process and claim
        uint128 owed; // users owed underlying amount after withdraw has been processed and claimed
        uint128 withdrawnDeposits; // users withdrawn deposit, used to calculate performance fees
        uint128 shares; // users shares after deposit process and claim
    }

    /* ========== EVENTS ========== */

    event Claimed(address indexed member, uint256 claimAmount);
    event Deposit(address indexed member, uint256 indexed index, uint256 amount);
    event Withdraw(address indexed member, uint256 indexed index, uint256 shares);
    event WithdrawFast(address indexed member, uint256 shares);
    event StrategyRemoved(uint256 i, address strategy);
    event TransferVaultOwner(address owner);
    event LowerVaultFee(uint16 fee);
    event UpdateName(string name);
}

File 20 of 34 : IVaultDetails.sol
// SPDX-License-Identifier: BUSL-1.1

pragma solidity 0.8.11;

struct VaultDetails {
    address underlying;
    address[] strategies;
    uint256[] proportions;
    address creator;
    uint16 vaultFee;
    address riskProvider;
    int8 riskTolerance;
    string name;
}

struct VaultInitializable {
    string name;
    address owner;
    uint16 fee;
    address[] strategies;
    uint256[] proportions;
}

// SPDX-License-Identifier: BUSL-1.1

pragma solidity 0.8.11;

import "../../external/@openzeppelin/token/ERC20/IERC20.sol";

struct VaultImmutables {
    IERC20 underlying;
    address riskProvider;
    int8 riskTolerance;
}

interface IVaultImmutable {
    /* ========== FUNCTIONS ========== */

    function underlying() external view returns (IERC20);

    function riskProvider() external view returns (address);

    function riskTolerance() external view returns (int8);
}

File 22 of 34 : IVaultIndexActions.sol
// SPDX-License-Identifier: BUSL-1.1

pragma solidity 0.8.11;

interface IVaultIndexActions {

    /* ========== STRUCTS ========== */

    struct IndexAction {
        uint128 depositAmount;
        uint128 withdrawShares;
    }

    struct LastIndexInteracted {
        uint128 index1;
        uint128 index2;
    }

    struct Redeem {
        uint128 depositShares;
        uint128 withdrawnAmount;
    }

    /* ========== EVENTS ========== */

    event VaultRedeem(uint indexed globalIndex);
    event UserRedeem(address indexed member, uint indexed globalIndex);
}

// SPDX-License-Identifier: BUSL-1.1

pragma solidity 0.8.11;

interface IVaultRestricted {
    /* ========== FUNCTIONS ========== */
    
    function reallocate(
        address[] calldata vaultStrategies,
        uint256 newVaultProportions,
        uint256 finishedIndex,
        uint24 activeIndex
    ) external returns (uint256[] memory, uint256);

    function payFees(uint256 profit) external returns (uint256 feesPaid);

    /* ========== EVENTS ========== */

    event Reallocate(uint24 indexed index, uint256 newProportions);
}

// SPDX-License-Identifier: MIT

pragma solidity 0.8.11;

library Bitwise {
    function get8BitUintByIndex(uint256 bitwiseData, uint256 i) internal pure returns(uint256) {
        return (bitwiseData >> (8 * i)) & type(uint8).max;
    }

    // 14 bits is used for strategy proportions in a vault as FULL_PERCENT is 10_000
    function get14BitUintByIndex(uint256 bitwiseData, uint256 i) internal pure returns(uint256) {
        return (bitwiseData >> (14 * i)) & (16_383); // 16.383 is 2^14 - 1
    }

    function set14BitUintByIndex(uint256 bitwiseData, uint256 i, uint256 num14bit) internal pure returns(uint256) {
        return bitwiseData + (num14bit << (14 * i));
    }

    function reset14BitUintByIndex(uint256 bitwiseData, uint256 i) internal pure returns(uint256) {
        return bitwiseData & (~(16_383 << (14 * i)));
    }
}

// SPDX-License-Identifier: MIT

pragma solidity 0.8.11;

/**
 * @notice Library to provide utils for hashing and hash compatison of Spool related data
 */
library Hash {
    function hashReallocationTable(uint256[][] memory reallocationTable) internal pure returns(bytes32) {
        return keccak256(abi.encode(reallocationTable));
    }

    function hashStrategies(address[] memory strategies) internal pure returns(bytes32) {
        return keccak256(abi.encodePacked(strategies));
    }

    function sameStrategies(address[] memory strategies1, address[] memory strategies2) internal pure returns(bool) {
        return hashStrategies(strategies1) == hashStrategies(strategies2);
    }

    function sameStrategies(address[] memory strategies, bytes32 strategiesHash) internal pure returns(bool) {
        return hashStrategies(strategies) == strategiesHash;
    }
}

// SPDX-License-Identifier: MIT

pragma solidity 0.8.11;

import "../external/@openzeppelin/utils/SafeCast.sol";


/**
 * @notice A collection of custom math ustils used throughout the system
 */
library Math {
    function min(uint256 a, uint256 b) internal pure returns (uint256) {
        return a > b ? b : a;
    }

    function getProportion128(uint256 mul1, uint256 mul2, uint256 div) internal pure returns (uint128) {
        return SafeCast.toUint128(((mul1 * mul2) / div));
    }

    function getProportion128Unchecked(uint256 mul1, uint256 mul2, uint256 div) internal pure returns (uint128) {
        unchecked {
            return uint128((mul1 * mul2) / div);
        }
    }
}

File 27 of 34 : Constants.sol
// SPDX-License-Identifier: BUSL-1.1

pragma solidity 0.8.11;

import "../external/@openzeppelin/token/ERC20/IERC20.sol";

/// @title Common Spool contracts constants
abstract contract BaseConstants {
    /// @dev 2 digits precision
    uint256 internal constant FULL_PERCENT = 100_00;

    /// @dev Accuracy when doing shares arithmetics
    uint256 internal constant ACCURACY = 10**30;
}

/// @title Contains USDC token related values
abstract contract USDC {
    /// @notice USDC token contract address
    IERC20 internal constant USDC_ADDRESS = IERC20(0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48);
}

// SPDX-License-Identifier: BUSL-1.1

pragma solidity 0.8.11;

import "../interfaces/ISpoolOwner.sol";

/// @title Logic to help check whether the caller is the Spool owner
abstract contract SpoolOwnable {
    /// @notice Contract that checks if address is Spool owner
    ISpoolOwner internal immutable spoolOwner;

    /**
     * @notice Sets correct initial values
     * @param _spoolOwner Spool owner contract address
     */
    constructor(ISpoolOwner _spoolOwner) {
        require(
            address(_spoolOwner) != address(0),
            "SpoolOwnable::constructor: Spool owner contract address cannot be 0"
        );

        spoolOwner = _spoolOwner;
    }

    /**
     * @notice Checks if caller is Spool owner
     * @return True if caller is Spool owner, false otherwise
     */
    function isSpoolOwner() internal view returns(bool) {
        return spoolOwner.isSpoolOwner(msg.sender);
    }


    /// @notice Checks and throws if caller is not Spool owner
    function _onlyOwner() private view {
        require(isSpoolOwner(), "SpoolOwnable::onlyOwner: Caller is not the Spool owner");
    }

    /// @notice Checks and throws if caller is not Spool owner
    modifier onlyOwner() {
        _onlyOwner();
        _;
    }
}

File 29 of 34 : SpoolPausable.sol
// SPDX-License-Identifier: MIT

pragma solidity 0.8.11;

import "../interfaces/IController.sol";

/// @title Facilitates checking if the system is paused or not
abstract contract SpoolPausable {
    /* ========== STATE VARIABLES ========== */

    /// @notice The controller contract that is consulted for a strategy's and vault's validity
    IController public immutable controller;

    /**
     * @notice Sets initial values
     * @param _controller Controller contract address
     */
    constructor(IController _controller) {
        require(
            address(_controller) != address(0),
            "SpoolPausable::constructor: Controller contract address cannot be 0"
        );

        controller = _controller;
    }

    /* ========== MODIFIERS ========== */

    /// @notice Throws if system is paused
    modifier systemNotPaused() {
        controller.checkPaused();
        _;
    }
}

// SPDX-License-Identifier: BUSL-1.1

pragma solidity 0.8.11;

import "../interfaces/vault/IRewardDrip.sol";
import "./VaultBase.sol";

import "../external/@openzeppelin/utils/SafeCast.sol";
import "../external/@openzeppelin/security/ReentrancyGuard.sol";
import "../libraries/Math.sol";

/**
 * @notice Implementation of the {IRewardDrip} interface.
 *
 * @dev
 * An adaptation of the Synthetix StakingRewards contract to support multiple tokens:
 *
 * https://github.com/Synthetixio/synthetix/blob/develop/contracts/StakingRewards.sol
 *
 * Instead of storing the values of the StakingRewards contract at the contract level,
 * they are stored in a struct that is mapped to depending on the reward token instead.
 */
abstract contract RewardDrip is IRewardDrip, ReentrancyGuard, VaultBase {
    using SafeERC20 for IERC20;

    /* ========== CONSTANTS ========== */

    /// @notice Multiplier used when dealing reward calculations
    uint256 constant private REWARD_ACCURACY = 1e18;

    /* ========== STATE VARIABLES ========== */

    /// @notice All reward tokens supported by the contract
    mapping(uint256 => IERC20) public rewardTokens;

    /// @notice Vault reward token incentive configuration
    mapping(IERC20 => RewardConfiguration) public rewardConfiguration;

    /// @notice Blacklisted force-removed tokens
    mapping(IERC20 => bool) public override tokenBlacklist;

    /* ========== VIEWS ========== */

    function lastTimeRewardApplicable(IERC20 token)
        public
        view
        returns (uint32)
    {
        return uint32(Math.min(block.timestamp, rewardConfiguration[token].periodFinish));
    }

    function rewardPerToken(IERC20 token) public view returns (uint224) {
        RewardConfiguration storage config = rewardConfiguration[token];

        if (totalInstantDeposit == 0)
            return config.rewardPerTokenStored;
            
        uint256 timeDelta = lastTimeRewardApplicable(token) - config.lastUpdateTime;

        if (timeDelta == 0)
            return config.rewardPerTokenStored;

        return
            SafeCast.toUint224(
                config.rewardPerTokenStored + 
                    ((timeDelta
                        * config.rewardRate)
                        / totalInstantDeposit)
            );
    }

    function earned(IERC20 token, address account)
        public
        view
        returns (uint256)
    {
        RewardConfiguration storage config = rewardConfiguration[token];

        uint256 userShares = users[account].instantDeposit;

        if (userShares == 0)
            return config.rewards[account];
        
        uint256 userRewardPerTokenPaid = config.userRewardPerTokenPaid[account];

        return
            ((userShares * 
                (rewardPerToken(token) - userRewardPerTokenPaid))
                / REWARD_ACCURACY)
                + config.rewards[account];
    }

    function getRewardForDuration(IERC20 token)
        external
        view
        returns (uint256)
    {
        RewardConfiguration storage config = rewardConfiguration[token];
        return uint256(config.rewardRate) * config.rewardsDuration;
    }

    /* ========== MUTATIVE FUNCTIONS ========== */

    function getRewards(IERC20[] memory tokens) external nonReentrant {
        for (uint256 i; i < tokens.length; i++) {
            _getReward(tokens[i], msg.sender);
        }
    }

    function getActiveRewards(address account) external override onlyController nonReentrant {
        uint256 _rewardTokensCount = rewardTokensCount;
        for (uint256 i; i < _rewardTokensCount; i++) {
            _getReward(rewardTokens[i], account);
        }
    }

    function _getReward(IERC20 token, address account)
        internal
        updateReward(token, account)
    {
        RewardConfiguration storage config = rewardConfiguration[token];

        require(
            config.rewardsDuration != 0,
            "BTK"
        );

        uint256 reward = config.rewards[account];
        if (reward > 0) {
            config.rewards[account] = 0;
            token.safeTransfer(account, reward);
            emit RewardPaid(token, account, reward);
        }
    }

    /* ========== RESTRICTED FUNCTIONS ========== */

    /**
     * @notice Allows a new token to be added to the reward system
     *
     * @dev
     * Emits an {TokenAdded} event indicating the newly added reward token
     * and configuration
     *
     * Requirements:
     *
     * - the caller must be the reward distributor
     * - the reward duration must be non-zero
     * - the token must not have already been added
     *
     */
    function addToken(
        IERC20 token,
        uint32 rewardsDuration,
        uint256 reward
    ) external onlyVaultOwnerOrSpoolOwner exceptUnderlying(token) {
        RewardConfiguration storage config = rewardConfiguration[token];

        require(!tokenBlacklist[token], "TOBL");
        require(
            rewardsDuration != 0 &&
            config.lastUpdateTime == 0,
            "BCFG"
        );
        require(
            rewardTokensCount <= 5,
            "TMAX"
        );

        rewardTokens[rewardTokensCount] = token;
        rewardTokensCount++;

        config.rewardsDuration = rewardsDuration;

        if (reward > 0) {
            _notifyRewardAmount(token, reward);
        }
    }

    function notifyRewardAmount(IERC20 token, uint256 reward, uint32 rewardsDuration)
    external
    onlyVaultOwnerOrSpoolOwner
    {
        rewardConfiguration[token].rewardsDuration = rewardsDuration;
        _notifyRewardAmount(token, reward);
    }

    function _notifyRewardAmount(IERC20 token, uint256 reward)
        private
        updateReward(token, address(0))
    {
        RewardConfiguration storage config = rewardConfiguration[token];

        require(
            config.rewardPerTokenStored + (reward * REWARD_ACCURACY) <= type(uint192).max,
            "RTB"
        );

        token.safeTransferFrom(msg.sender, address(this), reward);
        uint32 newPeriodFinish = uint32(block.timestamp) + config.rewardsDuration;

        if (block.timestamp >= config.periodFinish) {
            config.rewardRate = SafeCast.toUint192((reward * REWARD_ACCURACY) / config.rewardsDuration);
            emit RewardAdded(token, reward, config.rewardsDuration);
        } else {
            // If extending or adding additional rewards,
            // cannot set new finish time to be less than previously configured
            require(config.periodFinish <= newPeriodFinish, "PFS");
            uint256 remaining = config.periodFinish - block.timestamp;
            uint256 leftover = remaining * config.rewardRate;
            uint192 newRewardRate = SafeCast.toUint192((reward * REWARD_ACCURACY + leftover) / config.rewardsDuration);
        
            require(
                newRewardRate >= config.rewardRate,
                "LRR"
            );

            config.rewardRate = newRewardRate;
            emit RewardExtended(token, reward, leftover, config.rewardsDuration, newPeriodFinish);
        }

        config.lastUpdateTime = uint32(block.timestamp);
        config.periodFinish = newPeriodFinish;
    }

    // End rewards emission earlier
    function updatePeriodFinish(IERC20 token, uint32 timestamp)
        external
        onlyOwner
        updateReward(token, address(0))
    {
        if (rewardConfiguration[token].lastUpdateTime > timestamp) {
            rewardConfiguration[token].periodFinish = rewardConfiguration[token].lastUpdateTime;
        } else {
            rewardConfiguration[token].periodFinish = timestamp;
        }

        emit PeriodFinishUpdated(token, rewardConfiguration[token].periodFinish);
    }

    /**
     * @notice Claim reward tokens
     * @dev
     * This is meant to be an emergency function to claim reward tokens.
     * Users that have not claimed yet will not be able to claim as
     * the rewards will be removed.
     *
     * Requirements:
     *
     * - the caller must be Spool DAO
     * - cannot claim vault underlying token
     * - cannot only execute if the reward finished
     *
     * @param token Token address to remove
     * @param amount Amount of tokens to claim
     */
    function claimFinishedRewards(IERC20 token, uint256 amount) external onlyOwner exceptUnderlying(token) onlyFinished(token) {
        token.safeTransfer(msg.sender, amount);
    }

    /**
     * @notice Force remove reward from vault rewards configuration.
     * @dev This is meant to be an emergency function if a reward token breaks.
     *
     * Requirements:
     *
     * - the caller must be Spool DAO
     *
     * @param token Token address to remove
     */
    function forceRemoveReward(IERC20 token) external onlyOwner {
        tokenBlacklist[token] = true;
        _removeReward(token);

        delete rewardConfiguration[token];
    }

    /**
     * @notice Remove reward from vault rewards configuration.
     * @dev
     * Used to sanitize vault and save on gas, after the reward has ended.
     * Users will be able to claim rewards 
     *
     * Requirements:
     *
     * - the caller must be the spool owner or Spool DAO
     * - cannot claim vault underlying token
     * - cannot only execute if the reward finished
     *
     * @param token Token address to remove
     */
    function removeReward(IERC20 token) 
        external
        onlyVaultOwnerOrSpoolOwner
        onlyFinished(token)
        updateReward(token, address(0))
    {
        _removeReward(token);
    }

    /* ========== PRIVATE FUNCTIONS ========== */

    /**
     * @notice Syncs rewards across all tokens of the system
     *
     * This function is meant to be invoked every time the instant deposit
     * of a user changes.
     */
    function _updateRewards(address account) private {
        uint256 _rewardTokensCount = rewardTokensCount;
        
        for (uint256 i; i < _rewardTokensCount; i++)
            _updateReward(rewardTokens[i], account);
    }

    function _updateReward(IERC20 token, address account) private {
        RewardConfiguration storage config = rewardConfiguration[token];
        config.rewardPerTokenStored = rewardPerToken(token);
        config.lastUpdateTime = lastTimeRewardApplicable(token);
        if (account != address(0)) {
            config.rewards[account] = earned(token, account);
            config.userRewardPerTokenPaid[account] = config
                .rewardPerTokenStored;
        }
    }

    function _removeReward(IERC20 token) private {
        uint256 _rewardTokensCount = rewardTokensCount;
        for (uint256 i; i < _rewardTokensCount; i++) {
            if (rewardTokens[i] == token) {
                rewardTokens[i] = rewardTokens[_rewardTokensCount - 1];

                delete rewardTokens[_rewardTokensCount - 1];
                rewardTokensCount--;
                emit RewardRemoved(token);

                break;
            }
        }
    }

    function _exceptUnderlying(IERC20 token) private view {
        require(
            token != _underlying(),
            "NUT"
        );
    }

    function _onlyFinished(IERC20 token) private view {
        require(
            block.timestamp > rewardConfiguration[token].periodFinish,
            "RNF"
        );
    }

    /**
    * @notice Ensures that the caller is the controller
     */
    function _onlyController() private view {
        require(
            msg.sender == address(controller),
            "OCTRL"
        );
    }

    /* ========== MODIFIERS ========== */

    modifier updateReward(IERC20 token, address account) {
        _updateReward(token, account);
        _;
    }

    modifier updateRewards() {
        _updateRewards(msg.sender);
        _;
    }

    modifier exceptUnderlying(IERC20 token) {
        _exceptUnderlying(token);
        _;
    }

    modifier onlyFinished(IERC20 token) {
        _onlyFinished(token);
        _;
    }

    /**
     * @notice Throws if called by anyone else other than the controller
     */
    modifier onlyController() {
        _onlyController();
        _;
    }
}

// SPDX-License-Identifier: BUSL-1.1

pragma solidity 0.8.11;

// libraries
import "../external/@openzeppelin/token/ERC20/utils/SafeERC20.sol";
import "../external/@openzeppelin/utils/SafeCast.sol";
import "../libraries/Bitwise.sol";
import "../libraries/Hash.sol";

// extends
import "../interfaces/vault/IVaultBase.sol";
import "./VaultImmutable.sol";
import "../shared/SpoolOwnable.sol";
import "../shared/Constants.sol";

// other imports
import "../interfaces/vault/IVaultDetails.sol";
import "../interfaces/ISpool.sol";
import "../interfaces/IController.sol";
import "../interfaces/IFastWithdraw.sol";
import "../interfaces/IFeeHandler.sol";
import "../shared/SpoolPausable.sol";

/**
 * @notice Implementation of the {IVaultBase} interface.
 *
 * @dev
 * Vault base holds vault state variables and provides some of the common vault functions.
 */
abstract contract VaultBase is IVaultBase, VaultImmutable, SpoolOwnable, SpoolPausable, BaseConstants {
    using Bitwise for uint256;
    using SafeERC20 for IERC20;

    /* ========== STATE VARIABLES ========== */

    /// @notice The central Spool contract
    ISpool internal immutable spool;

    /// @notice The fast withdraw contract
    IFastWithdraw internal immutable fastWithdraw;

    /// @notice The fee handler contract
    IFeeHandler internal immutable feeHandler;

    /// @notice Boolean signaling if the contract was initialized yet
    bool private _initialized;

    /// @notice The owner of the vault, also the vault fee recipient
    address public vaultOwner;

    /// @notice Vault owner fee
    uint16 public vaultFee;

    /// @notice The name of the vault
    string public name;

    /// @notice The total shares of a vault
    uint128 public totalShares;

    /// @notice Total instant deposit, used to calculate vault reward incentives
    uint128 public totalInstantDeposit;

    /// @notice The proportions of each strategy when depositing
    /// @dev Proportions are 14bits each, and the add up to FULL_PERCENT (10.000)
    uint256 public proportions;

    /// @notice Proportions to deposit after reallocation withdraw amount is claimed
    uint256 internal depositProportions;
    
    /// @notice Hash of the strategies list
    bytes32 public strategiesHash;

    /// @notice Number of vault incentivized tokens
    uint8 public rewardTokensCount;
    
    /// @notice Data if vault and at what index vault is reallocating
    uint24 public reallocationIndex;

    /// @notice User vault state values
    mapping(address => User) public users;

    /* ========== CONSTRUCTOR ========== */

    /**
     * @notice Sets the initial immutable values of the contract common for all vaults.
     *
     * @dev
     * All values have been sanitized by the controller contract, meaning
     * that no additional checks need to be applied here.
     *
     * @param _spool the spool implemenation
     * @param _controller the controller implementation
     * @param _fastWithdraw fast withdraw implementation
     * @param _feeHandler fee handler implementation
     */
    constructor(
        ISpool _spool,
        IController _controller,
        IFastWithdraw _fastWithdraw,
        IFeeHandler _feeHandler
    )
    SpoolPausable(_controller)
    {
        require(address(_spool) != address(0), "VaultBase::constructor: Spool address cannot be 0");
        require(address(_fastWithdraw) != address(0), "VaultBase::constructor: FastWithdraw address cannot be 0");
        require(address(_feeHandler) != address(0), "VaultBase::constructor: Fee Handler address cannot be 0");

        spool = _spool;
        fastWithdraw = _fastWithdraw;
        feeHandler = _feeHandler;
    }

    /* ========== INITIALIZE ========== */

    /**
     * @notice Initializes state of the vault at proxy creation.
     * @dev Called only once by vault factory after deploying a vault proxy.
     *      All values have been sanitized by the controller contract, meaning
     *      that no additional checks need to be applied here.
     *
     * @param vaultInitializable initial vault specific variables
     */
    function initialize(
        VaultInitializable memory vaultInitializable
    ) external override initializer {
        vaultOwner = vaultInitializable.owner;
        vaultFee = vaultInitializable.fee;
        name = vaultInitializable.name;

        proportions = _mapProportionsArrayToBits(vaultInitializable.proportions);
        _updateStrategiesHash(vaultInitializable.strategies);
    }

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

    /**
     * @notice Calculate and return proportion of passed parameters of 128 bit size
     * @dev Calculates the value using in 256 bit space, later casts back to 128 bit
     * Requirements:
     * 
     * - the result can't be bigger than maximum 128 bits value
     *
     * @param mul1 first multiplication value
     * @param mul2 second multiplication value
     * @param div result division value
     *
     * @return 128 bit proportion result
     */
    function _getProportion128(uint128 mul1, uint128 mul2, uint128 div) internal pure returns (uint128) {
        return SafeCast.toUint128((uint256(mul1) * mul2) / div);
    }

    /* ========== RESTRICTED FUNCTIONS ========== */

    /**
     * @notice Transfer vault owner to another address.
     *
     * @param _vaultOwner new vault owner address
     *
     * Requirements:
     *
     * - the caller can only be the vault owner or Spool DAO
     */
    function transferVaultOwner(address _vaultOwner) external onlyVaultOwnerOrSpoolOwner {
        vaultOwner = _vaultOwner;
        emit TransferVaultOwner(_vaultOwner);
    }

    /**
     * @notice Set lower vault fee.
     *
     * @param _vaultFee new vault fee
     *
     * Requirements:
     *
     * - the caller can only be the vault owner
     * - new vault fee must be lower than before
     */
    function lowerVaultFee(uint16 _vaultFee) external {
        require(
            msg.sender == vaultOwner &&
            _vaultFee < vaultFee,
            "FEE"
        );

        vaultFee = _vaultFee;
        emit LowerVaultFee(_vaultFee);
    }

    /**
     * @notice Update the name of the vault.
     *
     * @param _name new vault name
     *
     * Requirements:
     *
     * - the caller can only be the Spool DAO
     */
    function updateName(string memory _name) external onlyOwner {
        name = _name;
        emit UpdateName(_name);
    }

    // =========== DEPOSIT HELPERS ============ //

    /**
     * @notice Update instant deposit user and vault amounts
     *
     * @param amount deposited amount
     */
    function _addInstantDeposit(uint128 amount) internal {
        users[msg.sender].instantDeposit += amount;
        totalInstantDeposit += amount;
    }

    /**
     * @notice Get strategy deposit amount for the strategy
     * @param _proportions Vault strategy proportions (14bit each)
     * @param i index to get the proportion
     * @param amount Total deposit amount
     * @return strategyDepositAmount 
     */
    function _getStrategyDepositAmount(
        uint256 _proportions,
        uint256 i,
        uint256 amount
    ) internal pure returns (uint128) {
        return SafeCast.toUint128((_proportions.get14BitUintByIndex(i) * amount) / FULL_PERCENT);
    }

    /**
     * @notice Transfers deposited underlying asset amount from user to spool contract.
     * @dev Transfer happens from the vault or controller, defined by the user
     *
     * @param amount deposited amount
     * @param fromVault flag indicating wether the transfer is intiafed from the vault or controller
     */
    function _transferDepositToSpool(uint128 amount, bool fromVault) internal {
        if (fromVault) {
            _underlying().safeTransferFrom(msg.sender, address(spool), amount);
        } else {
            controller.transferToSpool(msg.sender, amount);
        }
    }

    /* ========== WITHDRAW HELPERS ========== */

    /**
     * @notice Calculates proportions of shares relative to the total shares
     * @dev Value has accuracy of `ACCURACY` which is 10^30
     *
     * @param sharesToWithdraw amount of shares
     *
     * @return total vault shares proportion
     */
    function _getVaultShareProportion(uint128 sharesToWithdraw) internal view returns(uint256) {
        return (ACCURACY * sharesToWithdraw) / totalShares;
    }

    // =========== PERFORMANCE FEES ============ //

    /**
     * @notice Pay fees to fee handler contract and transfer fee amount.
     * 
     * @param profit Total profit made by the users
     * @return feeSize Fee amount calculated from profit
     */
    function _payFeesAndTransfer(uint256 profit) internal returns (uint128 feeSize) {
        feeSize = SafeCast.toUint128(_payFees(profit));

        _underlying().safeTransfer(address(feeHandler), feeSize);
    }

    /**
     * @notice  Call fee handler contract to pay fees, without transfering assets
     * @dev Fee handler updates the fee storage slots and returns 
     *
     * @param profit Total profit made by the users
     * @return Fee amount calculated from profit
     */
    function _payFees(uint256 profit) internal returns (uint256) {
        return feeHandler.payFees(
            _underlying(),
            profit,
            _riskProvider(),
            vaultOwner,
            vaultFee
        );
    }

    // =========== STRATEGIIES ============ //

    /**
     * @notice Map vault strategy proportions array in one uint256 word.
     *
     * @dev Proportions sum up to `FULL_PERCENT` (10_000).
     *      There is maximum of 18 elements, and each takes maximum of 14bits.
     *
     * @param _proportions Vault strategy proportions array
     * @return Mapped propportion 256 bit word format
     */
    function _mapProportionsArrayToBits(uint256[] memory _proportions) internal pure returns (uint256) {
        uint256 proportions14bit;
        for (uint256 i = 0; i < _proportions.length; i++) {
            proportions14bit = proportions14bit.set14BitUintByIndex(i, _proportions[i]);
        }

        return proportions14bit;
    }

    /**
     * @dev Store vault strategy addresses array hash in `strategiesHash` storage
     * @param vaultStrategies Array of strategy addresses
     */
    function _updateStrategiesHash(address[] memory vaultStrategies) internal {
        strategiesHash = Hash.hashStrategies(vaultStrategies);
    }

    /**
     * @dev verify vault strategy addresses array against storage `strategiesHash`
     * @param vaultStrategies Array of strategies to verify
     */
    function _verifyStrategies(address[] memory vaultStrategies) internal view {
        require(Hash.sameStrategies(vaultStrategies, strategiesHash), "VSH");
    }

    /* ========== PRIVATE FUNCTIONS ========== */

    /**
     * @notice Verify the caller is The vault owner or Spool DAO
     *
     * @dev
     * Only callable from onlyVaultOwnerOrSpoolOwner modifier.
     *
     * Requirements:
     *
     * - msg.sender is the vault owner or Spool DAO
     */
    function _onlyVaultOwnerOrSpoolOwner() private view {
        require(
            msg.sender == vaultOwner || isSpoolOwner(),
            "OOD"
        );
    }

    /**
     * @notice Verify the caller is the spool contact
     *
     * @dev
     * Only callable from onlySpool modifier.
     *
     * Requirements:
     *
     * - msg.sender is central spool contract
     */
    function _onlySpool() private view {
        require(address(spool) == msg.sender, "OSP");
    }

    /**
     * @notice Verify caller is the spool contact
     *
     * @dev
     * Only callable from onlyFastWithdraw modifier.
     *
     * Requirements:
     *
     * - caller is fast withdraw contract
     */
    function _onlyFastWithdraw() private view {
        require(address(fastWithdraw) == msg.sender, "OFW");
    }

    /**
     * @notice Dissallow action if Spool reallocation already started
     */
    function _noMidReallocation() private view {
        require(!spool.isMidReallocation(), "NMR");
    }

    /* ========== MODIFIERS ========== */

    /**
     * @notice Ensures caller is vault owner or spool owner.
     */
    modifier onlyVaultOwnerOrSpoolOwner() {
        _onlyVaultOwnerOrSpoolOwner();
        _;
    }

    /**
     * @notice Ensures caller is central spool contract
     */
    modifier onlySpool() {
        _onlySpool();
        _;
    }

    /**
     * @notice Ensures caller is fast withdraw contract
     */
    modifier onlyFastWithdraw() {
        _onlyFastWithdraw();
        _;
    }

    /**
     * @notice Verifies given array of strategy addresses
     */
    modifier verifyStrategies(address[] memory vaultStrategies) {
        _verifyStrategies(vaultStrategies);
        _;
    }

    /**
     * @notice Ensures the system is not mid reallocation
     */
    modifier noMidReallocation() {
        _noMidReallocation();
        _;
    }

    /**
     * @notice Ensures the vault has not been initialized before
     */
    modifier initializer() {
        require(!_initialized, "AINT");
        _;
        _initialized = true;
    }
}

// SPDX-License-Identifier: BUSL-1.1

pragma solidity 0.8.11;

import "../interfaces/vault/IVaultImmutable.sol";

/**
 * @notice This contracts calls vault proxy that stores following
 *      properties as immutables. 
 */
abstract contract VaultImmutable {
    /* ========== FUNCTIONS ========== */

    /**
     * @dev Returns the underlying vault token from proxy address
     * @return Underlying token contract
     */
    function _underlying() internal view returns (IERC20) {
        return IVaultImmutable(address(this)).underlying();
    }

    /**
     * @dev Returns vaults risk provider from proxy address
     * @return Risk provider contract
     */
    function _riskProvider() internal view returns (address) {
        return IVaultImmutable(address(this)).riskProvider();
    }
}

// SPDX-License-Identifier: BUSL-1.1

pragma solidity 0.8.11;

import "../interfaces/vault/IVaultIndexActions.sol";
import "./RewardDrip.sol";

/**
 * @notice VaultIndexActions extends VaultBase and holds the logic to process index related data and actions.
 *
 * @dev
 * Index functions are executed when state changes are performed, to synchronize to vault with central Spool contract.
 * 
 * Index actions include:
 * - Redeem vault: claiming vault shares and withdrawn amount when DHW is complete
 * - Redeem user: claiming user deposit shares and/or withdrawn amount after vault claim has been processed
 */
abstract contract VaultIndexActions is IVaultIndexActions, RewardDrip {
    using SafeERC20 for IERC20;
    using Bitwise for uint256;

    /* ========== CONSTANTS ========== */

    /// @notice Value to multiply new deposit recieved to get the share amount
    uint128 private constant SHARES_MULTIPLIER = 10**6;
    
    /// @notice number of locked shares when initial shares are added
    /// @dev This is done to prevent rounding errors and share manipulation
    uint128 private constant INITIAL_SHARES_LOCKED = 10**11;

    /// @notice minimum shares size to avoid loss of share due to computation precision
    /// @dev If total shares go unders this value, new deposit is multiplied by the `SHARES_MULTIPLIER` again
    uint256 private constant MIN_SHARES_FOR_ACCURACY = INITIAL_SHARES_LOCKED * 10;

    /* ========== STATE VARIABLES ========== */

    /// @notice Holds up to 2 global indexes vault last interacted at and havent been redeemed yet
    /// @dev Second index can only be the next index of the first one
    /// Second index is used if the do-hard-work is executed in 2 transactions and actions are executed in between
    LastIndexInteracted public lastIndexInteracted;

    /// @notice Maps all vault actions to the corresponding global index
    mapping(uint256 => IndexAction) public vaultIndexAction;
    
    /// @notice Maps user actions to the corresponding global index
    mapping(address => mapping(uint256 => IndexAction)) public userIndexAction;

    /// @notice Holds up to 2 global indexes users last interacted with, and havent been redeemed yet
    mapping(address => LastIndexInteracted) public userLastInteractions;

    /// @notice Global index to deposit and withdraw vault redeem
    mapping(uint256 => Redeem) public redeems;

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

    /**
     * @notice Checks and sets the "is reallocating" flag for given index
     * @param index Index to check
     * @return isReallocating True if vault is reallocating at this `index`
     */
    function _isVaultReallocatingAtIndex(uint256 index) internal view returns (bool isReallocating) {
        if (index == reallocationIndex) {
            isReallocating = true;
        }
    }

    /**
     * @notice Check if the vault is set to reallocate
     * @dev True if in the current index or the next one
     * @return isReallocating True if vault is set to reallocate
     */
    function _isVaultReallocating() internal view returns (bool isReallocating) {
        if (reallocationIndex > 0) {
            isReallocating = true;
        }
    }

    // =========== VAULT REDEEM ============ //

    /**
     * @notice Redeem vault strategies after do hard work (DHW) has been completed
     * 
     * @dev
     * This is only possible if all vault strategy DHWs have been executed, otherwise it's reverted.
     * If the system is paused, function will revert - impacts vault functions deposit, withdraw, fastWithdraw,
     * claim, reallocate.
     * @param vaultStrategies strategies of this vault (verified internally)
     */
    function _redeemVaultStrategies(address[] memory vaultStrategies) internal systemNotPaused {
        LastIndexInteracted memory _lastIndexInteracted = lastIndexInteracted;
        if (_lastIndexInteracted.index1 > 0) {
            uint256 globalIndex1 = _lastIndexInteracted.index1;
            uint256 completedGlobalIndex = spool.getCompletedGlobalIndex();
            if (globalIndex1 <= completedGlobalIndex) {
                // redeem interacted index 1
                _redeemStrategiesIndex(globalIndex1, vaultStrategies);
                _lastIndexInteracted.index1 = 0;

                if (_lastIndexInteracted.index2 > 0) {
                    uint256 globalIndex2 = _lastIndexInteracted.index2;
                    if (globalIndex2 <= completedGlobalIndex) {
                        // redeem interacted index 2
                        _redeemStrategiesIndex(globalIndex2, vaultStrategies);
                    } else {
                        _lastIndexInteracted.index1 = _lastIndexInteracted.index2;
                    }
                    
                    _lastIndexInteracted.index2 = 0;
                }

                lastIndexInteracted = _lastIndexInteracted;
            }
        }
    }

    /**
     * @notice Redeem strategies for at index
     * @dev Causes additional gas for first interaction after DHW index has been completed
     * @param globalIndex Global index
     * @param vaultStrategies Array of vault strategy addresses
     */
    function _redeemStrategiesIndex(uint256 globalIndex, address[] memory vaultStrategies) private {
        uint128 _totalShares = totalShares;
        uint128 totalReceived = 0;
        uint128 totalWithdrawn = 0;
        uint128 totalUnderlyingAtIndex = 0;
        
        // if vault was reallocating at index claim reallocation deposit
        bool isReallocating = _isVaultReallocatingAtIndex(globalIndex);
        if (isReallocating) {
            spool.redeemReallocation(vaultStrategies, depositProportions, globalIndex);
            // Reset reallocation index to 0
            reallocationIndex = 0;
        }

        // go over strategies and redeem deposited shares and withdrawn amount
        for (uint256 i = 0; i < vaultStrategies.length; i++) {
            address strat = vaultStrategies[i];
            (uint128 receivedTokens, uint128 withdrawnTokens) = spool.redeem(strat, globalIndex);
            totalReceived += receivedTokens;
            totalWithdrawn += withdrawnTokens;
            
            totalUnderlyingAtIndex += spool.getVaultTotalUnderlyingAtIndex(strat, globalIndex);
        }

        // redeem underlying withdrawn token for all strategies at once
        if (totalWithdrawn > 0) {
            spool.redeemUnderlying(totalWithdrawn);
        }

        // substract withdrawn shares
        _totalShares -= vaultIndexAction[globalIndex].withdrawShares;

        // calculate new deposit shares
        uint128 newShares = 0;
        if (_totalShares <= MIN_SHARES_FOR_ACCURACY || totalUnderlyingAtIndex == 0) {
            // Enforce minimum shares size to avoid loss of share due to computation precision
            newShares = totalReceived * SHARES_MULTIPLIER;

            if (_totalShares < INITIAL_SHARES_LOCKED) {
                if (newShares + _totalShares >= INITIAL_SHARES_LOCKED) {
                    unchecked {
                        uint128 newLockedShares = INITIAL_SHARES_LOCKED - _totalShares;
                        _totalShares += newLockedShares;
                        newShares -= newLockedShares;
                    }
                } else {
                    unchecked {
                        _totalShares += newShares;
                    }
                    newShares = 0;
                }
            }
        } else {
            if (totalReceived < totalUnderlyingAtIndex) {
                unchecked {
                    newShares = _getProportion128(totalReceived, _totalShares, totalUnderlyingAtIndex - totalReceived);
                }
            } else {
                newShares = _totalShares;
            }
        }

        // add new deposit shares
        totalShares = _totalShares + newShares;

        redeems[globalIndex] = Redeem(newShares, totalWithdrawn);

        emit VaultRedeem(globalIndex);
    }

    // =========== USER REDEEM ============ //

    /**
     * @notice Redeem user deposit shares and withdrawn amount
     *
     * @dev
     * Check if vault has already claimed shares for itself
     */
    function _redeemUser() internal {
        LastIndexInteracted memory _lastIndexInteracted = lastIndexInteracted;
        LastIndexInteracted memory userIndexInteracted = userLastInteractions[msg.sender];

        // check if strategy for index has already been redeemed
        if (userIndexInteracted.index1 > 0 && 
            (_lastIndexInteracted.index1 == 0 || userIndexInteracted.index1 < _lastIndexInteracted.index1)) {
            // redeem interacted index 1
            _redeemUserAction(userIndexInteracted.index1, true);
            userIndexInteracted.index1 = 0;

            if (userIndexInteracted.index2 > 0) {
                if (_lastIndexInteracted.index2 == 0 || userIndexInteracted.index2 < _lastIndexInteracted.index1) {
                    // redeem interacted index 2
                    _redeemUserAction(userIndexInteracted.index2, false);
                } else {
                    userIndexInteracted.index1 = userIndexInteracted.index2;
                }
                
                userIndexInteracted.index2 = 0;
            }

            userLastInteractions[msg.sender] = userIndexInteracted;
        }
    }

    /**
     * @notice Redeem user action for the `index`
     * @param index index aw which user performed the action
     * @param isFirstIndex Is this the first user index
     */
    function _redeemUserAction(uint256 index, bool isFirstIndex) private {
        User storage user = users[msg.sender];
        IndexAction storage userIndex = userIndexAction[msg.sender][index];

        // redeem user withdrawn amount at index
        uint128 userWithdrawalShares = userIndex.withdrawShares;
        if (userWithdrawalShares > 0) {
            // calculate user withdrawn amount

            uint128 userWithdrawnAmount = _getProportion128(redeems[index].withdrawnAmount, userWithdrawalShares, vaultIndexAction[index].withdrawShares);

            user.owed += userWithdrawnAmount;

            // calculate proportionate deposit to pay for performance fees on claim
            uint128 proportionateDeposit;
            uint128 sharesAtWithdrawal = user.shares + userWithdrawalShares;
            if (isFirstIndex) {
                // if user has 2 withdraws pending sum shares from the pending one as well
                sharesAtWithdrawal += userIndexAction[msg.sender][index + 1].withdrawShares;
            }

            // check if withdrawal of all user shares was performes (all shares at the index of the action)
            if (sharesAtWithdrawal > userWithdrawalShares) {
                uint128 userTotalDeposit = user.activeDeposit;
                
                proportionateDeposit = _getProportion128(userTotalDeposit, userWithdrawalShares, sharesAtWithdrawal);
                user.activeDeposit = userTotalDeposit - proportionateDeposit;
            } else {
                proportionateDeposit = user.activeDeposit;
                user.activeDeposit = 0;
            }

            user.withdrawnDeposits += proportionateDeposit;

            // set user withdraw shares for index to 0
            userIndex.withdrawShares = 0;
        }

        // redeem user deposit shares at index
        uint128 userDepositAmount = userIndex.depositAmount;
        if (userDepositAmount > 0) {
            // calculate new user deposit shares
            uint128 newUserShares = _getProportion128(userDepositAmount, redeems[index].depositShares, vaultIndexAction[index].depositAmount);

            user.shares += newUserShares;
            user.activeDeposit += userDepositAmount;

            // set user deposit amount for index to 0
            userIndex.depositAmount = 0;
        }
        
        emit UserRedeem(msg.sender, index);
    }

    // =========== INDEX FUNCTIONS ============ //

    /**
     * @dev Saves vault last interacted global index
     * @param globalIndex Global index
     */
    function _updateInteractedIndex(uint24 globalIndex) internal {
        _updateLastIndexInteracted(lastIndexInteracted, globalIndex);
    }

    /**
     * @dev Saves last user interacted global index
     * @param globalIndex Global index
     */
    function _updateUserInteractedIndex(uint24 globalIndex) internal {
        _updateLastIndexInteracted(userLastInteractions[msg.sender], globalIndex);
    }

    /**
     * @dev Update last index with which the system interacted
     * @param lit Last interacted idex of a user or a vault
     * @param globalIndex Global index
     */
    function _updateLastIndexInteracted(LastIndexInteracted storage lit, uint24 globalIndex) private {
        if (lit.index1 > 0) {
            if (lit.index1 < globalIndex) {
                lit.index2 = globalIndex;
            }
        } else {
            lit.index1 = globalIndex;
        }

    }

    /**
     * @dev Gets current active global index from spool
     */
    function _getActiveGlobalIndex() internal view returns(uint24) {
        return spool.getActiveGlobalIndex();
    }

    /* ========== PRIVATE FUNCTIONS ========== */

    /**
     * @dev Ensures the vault is not currently reallocating
     */
    function _noReallocation() private view {
        require(!_isVaultReallocating(), "NRED");
    }

    /* ========== MODIFIERS ========== */

    /**
    * @dev Redeem given array of vault strategies
     */
    modifier redeemVaultStrategiesModifier(address[] memory vaultStrategies) {
        _redeemVaultStrategies(vaultStrategies);
        _;
    }

    /**
    * @dev Redeem user
     */
    modifier redeemUserModifier() {
        _redeemUser();
        _;
    }

    /**
     * @dev Ensures the vault is not currently reallocating
     */
    modifier noReallocation() {
        _noReallocation();
        _;
    }  
}

// SPDX-License-Identifier: BUSL-1.1

pragma solidity 0.8.11;

import "../interfaces/vault/IVaultRestricted.sol";
import "./VaultIndexActions.sol";

/**
 * @notice Implementation of the {IVaultRestricted} interface.
 *
 * @dev
 * VaultRestricted extends VaultIndexActions and exposes functions restricted for Spool specific contracts.
 * 
 * Index functions are executed when state changes are performed, to synchronize to vault with central Spool contract
 * 
 * Functions:
 * - payFees, called by fast withdraw, when user decides to fast withdraw its shares
 * - reallocate, called by spool, sets new strategy allocation values and calculates what
 *   strategies to withdraw from and deposit to, to achieve the desired allocation
 */
abstract contract VaultRestricted is IVaultRestricted, VaultIndexActions {
    using Bitwise for uint256;

    // =========== FAST WITHDRAW FEES ============ //

    /**
     * @notice  Notifies fee handler of user realized profits to calculate and store the fee.
     * @dev
     * Called by fast withdraw contract.
     * Fee handler updates the fee storage slots and returns calculated fee value
     * Fast withdraw transfers the calculated fee to the fee handler after.
     *
     * Requirements:
     *
     * - Caller must be the fast withdraw contract
     *
     * @param profit Total profit made by the user
     * @return Fee amount calculated from the profit
     */
    function payFees(uint256 profit) external override onlyFastWithdraw returns (uint256) {
        return _payFees(profit);
    }

    /* ========== SPOOL REALLOCATE ========== */

    /**
     * @notice Update vault strategy proportions and reallocate funds according to the new proportions.
     *
     * @dev 
     * Requirements:
     * 
     * - the caller must be the Spool contract
     * - reallocation must not be in progress
     * - new vault proportions must add up to `FULL_PERCENT`
     *
     * @param vaultStrategies Vault strategy addresses
     * @param newVaultProportions New vault proportions
     * @param finishedIndex Completed global index
     * @param activeIndex current active global index, that we're setting reallocate for
     *
     * @return withdrawProportionsArray array of shares to be withdrawn from each vault strategy, and be later deposited back to other vault strategies
     * @return newDepositProportions proportions to be deposited to strategies from all withdrawn funds (written in a uint word, 14bits each) values add up to `FULL_PERCENT`
     *
     */
    function reallocate(
        address[] memory vaultStrategies,
        uint256 newVaultProportions,
        uint256 finishedIndex,
        uint24 activeIndex
    ) 
        external 
        override
        onlySpool
        verifyStrategies(vaultStrategies)
        redeemVaultStrategiesModifier(vaultStrategies)
        noReallocation
        returns(uint256[] memory withdrawProportionsArray, uint256 newDepositProportions)
    {
        (withdrawProportionsArray, newDepositProportions) = _adjustAllocation(vaultStrategies, newVaultProportions, finishedIndex);

        proportions = newVaultProportions;

        reallocationIndex = activeIndex;
        _updateInteractedIndex(activeIndex);
        emit Reallocate(reallocationIndex, newVaultProportions);
    }

    /**
     * @notice Set new vault strategy allocation and calculate how the funds should be spread
     * 
     * @dev
     * Requirements:
     *
     * - new proportions must add up to 100% (`FULL_PERCENT`)
     * - vault must withdraw from at least one strategy
     * - vault must deposit to at least one strategy
     * - vault total underlying must be more than zero
     *
     * @param vaultStrategies Vault strategy addresses
     * @param newVaultProportions New vault proportions
     * @param finishedIndex Completed global index
     *
     * @return withdrawProportionsArray array of shares to be withdrawn from each vault strategy, and be later deposited back to other vault strategies
     * @return newDepositProportions proportions to be deposited to strategies from all withdrawn funds (written in a uint word, 14bits each) values add up to `FULL_PERCENT`
     */
    function _adjustAllocation(
        address[] memory vaultStrategies,
        uint256 newVaultProportions,
        uint256 finishedIndex
    )
        private returns(uint256[] memory, uint256)
    {
        uint256[] memory depositProportionsArray = new uint256[](vaultStrategies.length);
        uint256[] memory withdrawProportionsArray = new uint256[](vaultStrategies.length);

        (uint256[] memory stratUnderlyings, uint256 vaultTotalUnderlying) = _getStratsAndVaultUnderlying(vaultStrategies, finishedIndex);

        require(vaultTotalUnderlying > 0, "NUL");

        uint256 totalProportion;
        uint256 totalDepositProportion;
        uint256 lastDepositIndex;

        {
            // flags to check if reallocation will withdraw and reposit
            bool didWithdraw = false;
            bool willDeposit = false;
            for (uint256 i; i < vaultStrategies.length; i++) {
                uint256 newStratProportion = Bitwise.get14BitUintByIndex(newVaultProportions, i);
                totalProportion += newStratProportion;

                uint256 stratProportion;
                if (stratUnderlyings[i] > 0) {
                    stratProportion = (stratUnderlyings[i] * FULL_PERCENT) / vaultTotalUnderlying;
                }

                // if current proportion is more than new - withdraw
                if (stratProportion > newStratProportion) {
                    uint256 withdrawalProportion = stratProportion - newStratProportion;
                    if (withdrawalProportion < 10) // NOTE: skip if diff is less than 0.1%
                        continue;

                    uint256 withdrawalShareProportion = (withdrawalProportion * ACCURACY) / stratProportion;
                    withdrawProportionsArray[i] = withdrawalShareProportion;

                    didWithdraw = true;
                } else if (stratProportion < newStratProportion) {
                    // if less - prepare for deposit
                    uint256 depositProportion = newStratProportion - stratProportion;
                    if (depositProportion < 10) // NOTE: skip if diff is less than 0.1%
                        continue;

                    depositProportionsArray[i] = depositProportion;
                    totalDepositProportion += depositProportion;
                    lastDepositIndex = i;

                    willDeposit = true;
                }
            }

            // check if sum of new propotions equals to full percent
            require(
                totalProportion == FULL_PERCENT,
                "BPP"
            );

            // Check if withdraw happened and if deposit will, otherwise revert
            require(didWithdraw && willDeposit, "NRD");
        }

        // normalize deposit proportions to FULL_PERCENT
        uint256 newDepositProportions;
        uint256 totalDepositProp;
        for (uint256 i; i <= lastDepositIndex; i++) {
            if (depositProportionsArray[i] > 0) {
                uint256 proportion = (depositProportionsArray[i] * FULL_PERCENT) / totalDepositProportion;

                newDepositProportions = newDepositProportions.set14BitUintByIndex(i, proportion);
                
                totalDepositProp += proportion;
            }
        }
        
        newDepositProportions = newDepositProportions.set14BitUintByIndex(lastDepositIndex, FULL_PERCENT - totalDepositProp);

        // store reallocation deposit proportions
        depositProportions = newDepositProportions;

        return (withdrawProportionsArray, newDepositProportions);
    }

    /**
     * @notice Get strategies and vault underlying
     * @param vaultStrategies Array of vault strategy addresses
     * @param index Get the underlying amounts at index
     */
    function _getStratsAndVaultUnderlying(address[] memory vaultStrategies, uint256 index)
        private
        view
        returns (uint256[] memory, uint256)
    {
        uint256[] memory stratUnderlyings = new uint256[](vaultStrategies.length);

        uint256 vaultTotalUnderlying;
        for (uint256 i; i < vaultStrategies.length; i++) {
            uint256 stratUnderlying = spool.getVaultTotalUnderlyingAtIndex(vaultStrategies[i], index);

            stratUnderlyings[i] = stratUnderlying;
            vaultTotalUnderlying += stratUnderlying;
        }

        return (stratUnderlyings, vaultTotalUnderlying);
    }
}

Settings
{
  "evmVersion": "london",
  "libraries": {},
  "metadata": {
    "bytecodeHash": "ipfs",
    "useLiteralContent": true
  },
  "optimizer": {
    "enabled": true,
    "runs": 148
  },
  "remappings": [],
  "outputSelection": {
    "*": {
      "*": [
        "evm.bytecode",
        "evm.deployedBytecode",
        "devdoc",
        "userdoc",
        "metadata",
        "abi"
      ]
    }
  }
}

Contract Security Audit

Contract ABI

API
[{"inputs":[{"internalType":"contract ISpool","name":"_spool","type":"address"},{"internalType":"contract IController","name":"_controller","type":"address"},{"internalType":"contract IFastWithdraw","name":"_fastWithdraw","type":"address"},{"internalType":"contract IFeeHandler","name":"_feeHandler","type":"address"},{"internalType":"contract ISpoolOwner","name":"_spoolOwner","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"member","type":"address"},{"indexed":false,"internalType":"uint256","name":"claimAmount","type":"uint256"}],"name":"Claimed","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"member","type":"address"},{"indexed":true,"internalType":"uint256","name":"index","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"Deposit","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint16","name":"fee","type":"uint16"}],"name":"LowerVaultFee","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"contract IERC20","name":"token","type":"address"},{"indexed":false,"internalType":"uint32","name":"periodFinish","type":"uint32"}],"name":"PeriodFinishUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint24","name":"index","type":"uint24"},{"indexed":false,"internalType":"uint256","name":"newProportions","type":"uint256"}],"name":"Reallocate","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"contract IERC20","name":"token","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"duration","type":"uint256"}],"name":"RewardAdded","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"contract IERC20","name":"token","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"leftover","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"duration","type":"uint256"},{"indexed":false,"internalType":"uint32","name":"periodFinish","type":"uint32"}],"name":"RewardExtended","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"contract IERC20","name":"token","type":"address"},{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":false,"internalType":"uint256","name":"reward","type":"uint256"}],"name":"RewardPaid","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"contract IERC20","name":"token","type":"address"}],"name":"RewardRemoved","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"i","type":"uint256"},{"indexed":false,"internalType":"address","name":"strategy","type":"address"}],"name":"StrategyRemoved","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"owner","type":"address"}],"name":"TransferVaultOwner","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"string","name":"name","type":"string"}],"name":"UpdateName","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"member","type":"address"},{"indexed":true,"internalType":"uint256","name":"globalIndex","type":"uint256"}],"name":"UserRedeem","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"globalIndex","type":"uint256"}],"name":"VaultRedeem","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"member","type":"address"},{"indexed":true,"internalType":"uint256","name":"index","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"shares","type":"uint256"}],"name":"Withdraw","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"member","type":"address"},{"indexed":false,"internalType":"uint256","name":"shares","type":"uint256"}],"name":"WithdrawFast","type":"event"},{"inputs":[{"internalType":"contract IERC20","name":"token","type":"address"},{"internalType":"uint32","name":"rewardsDuration","type":"uint32"},{"internalType":"uint256","name":"reward","type":"uint256"}],"name":"addToken","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bool","name":"doRedeemVault","type":"bool"},{"internalType":"address[]","name":"vaultStrategies","type":"address[]"},{"internalType":"bool","name":"doRedeemUser","type":"bool"}],"name":"claim","outputs":[{"internalType":"uint128","name":"claimAmount","type":"uint128"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract IERC20","name":"token","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"claimFinishedRewards","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"controller","outputs":[{"internalType":"contract IController","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address[]","name":"vaultStrategies","type":"address[]"},{"internalType":"uint128","name":"amount","type":"uint128"},{"internalType":"bool","name":"transferFromVault","type":"bool"}],"name":"deposit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract IERC20","name":"token","type":"address"},{"internalType":"address","name":"account","type":"address"}],"name":"earned","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract IERC20","name":"token","type":"address"}],"name":"forceRemoveReward","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"getActiveRewards","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract IERC20","name":"token","type":"address"}],"name":"getRewardForDuration","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract IERC20[]","name":"tokens","type":"address[]"}],"name":"getRewards","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address[]","name":"vaultStrategies","type":"address[]"}],"name":"getUpdatedUser","outputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address[]","name":"vaultStrategies","type":"address[]"}],"name":"getUpdatedVault","outputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"string","name":"name","type":"string"},{"internalType":"address","name":"owner","type":"address"},{"internalType":"uint16","name":"fee","type":"uint16"},{"internalType":"address[]","name":"strategies","type":"address[]"},{"internalType":"uint256[]","name":"proportions","type":"uint256[]"}],"internalType":"struct VaultInitializable","name":"vaultInitializable","type":"tuple"}],"name":"initialize","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"lastIndexInteracted","outputs":[{"internalType":"uint128","name":"index1","type":"uint128"},{"internalType":"uint128","name":"index2","type":"uint128"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract IERC20","name":"token","type":"address"}],"name":"lastTimeRewardApplicable","outputs":[{"internalType":"uint32","name":"","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint16","name":"_vaultFee","type":"uint16"}],"name":"lowerVaultFee","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract IERC20","name":"token","type":"address"},{"internalType":"uint256","name":"reward","type":"uint256"},{"internalType":"uint32","name":"rewardsDuration","type":"uint32"}],"name":"notifyRewardAmount","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address[]","name":"vaultStrategies","type":"address[]"},{"internalType":"uint256","name":"i","type":"uint256"}],"name":"notifyStrategyRemoved","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"profit","type":"uint256"}],"name":"payFees","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"proportions","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address[]","name":"vaultStrategies","type":"address[]"},{"internalType":"uint256","name":"newVaultProportions","type":"uint256"},{"internalType":"uint256","name":"finishedIndex","type":"uint256"},{"internalType":"uint24","name":"activeIndex","type":"uint24"}],"name":"reallocate","outputs":[{"internalType":"uint256[]","name":"withdrawProportionsArray","type":"uint256[]"},{"internalType":"uint256","name":"newDepositProportions","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"reallocationIndex","outputs":[{"internalType":"uint24","name":"","type":"uint24"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"redeemUser","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address[]","name":"vaultStrategies","type":"address[]"}],"name":"redeemVaultAndUser","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address[]","name":"vaultStrategies","type":"address[]"}],"name":"redeemVaultStrategies","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"redeems","outputs":[{"internalType":"uint128","name":"depositShares","type":"uint128"},{"internalType":"uint128","name":"withdrawnAmount","type":"uint128"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract IERC20","name":"token","type":"address"}],"name":"removeReward","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract IERC20","name":"","type":"address"}],"name":"rewardConfiguration","outputs":[{"internalType":"uint32","name":"rewardsDuration","type":"uint32"},{"internalType":"uint32","name":"periodFinish","type":"uint32"},{"internalType":"uint192","name":"rewardRate","type":"uint192"},{"internalType":"uint32","name":"lastUpdateTime","type":"uint32"},{"internalType":"uint224","name":"rewardPerTokenStored","type":"uint224"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract IERC20","name":"token","type":"address"}],"name":"rewardPerToken","outputs":[{"internalType":"uint224","name":"","type":"uint224"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"rewardTokens","outputs":[{"internalType":"contract IERC20","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"rewardTokensCount","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"strategiesHash","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract IERC20","name":"","type":"address"}],"name":"tokenBlacklist","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalInstantDeposit","outputs":[{"internalType":"uint128","name":"","type":"uint128"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalShares","outputs":[{"internalType":"uint128","name":"","type":"uint128"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_vaultOwner","type":"address"}],"name":"transferVaultOwner","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string","name":"_name","type":"string"}],"name":"updateName","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract IERC20","name":"token","type":"address"},{"internalType":"uint32","name":"timestamp","type":"uint32"}],"name":"updatePeriodFinish","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"userIndexAction","outputs":[{"internalType":"uint128","name":"depositAmount","type":"uint128"},{"internalType":"uint128","name":"withdrawShares","type":"uint128"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"userLastInteractions","outputs":[{"internalType":"uint128","name":"index1","type":"uint128"},{"internalType":"uint128","name":"index2","type":"uint128"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"users","outputs":[{"internalType":"uint128","name":"instantDeposit","type":"uint128"},{"internalType":"uint128","name":"activeDeposit","type":"uint128"},{"internalType":"uint128","name":"owed","type":"uint128"},{"internalType":"uint128","name":"withdrawnDeposits","type":"uint128"},{"internalType":"uint128","name":"shares","type":"uint128"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"vaultFee","outputs":[{"internalType":"uint16","name":"","type":"uint16"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"vaultIndexAction","outputs":[{"internalType":"uint128","name":"depositAmount","type":"uint128"},{"internalType":"uint128","name":"withdrawShares","type":"uint128"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"vaultOwner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address[]","name":"vaultStrategies","type":"address[]"},{"internalType":"uint128","name":"sharesToWithdraw","type":"uint128"},{"internalType":"bool","name":"withdrawAll","type":"bool"}],"name":"withdraw","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address[]","name":"vaultStrategies","type":"address[]"},{"internalType":"uint128","name":"sharesToWithdraw","type":"uint128"},{"internalType":"bool","name":"withdrawAll","type":"bool"},{"components":[{"internalType":"bool","name":"doExecuteWithdraw","type":"bool"},{"internalType":"uint256[][]","name":"slippages","type":"uint256[][]"},{"components":[{"internalType":"uint256","name":"slippage","type":"uint256"},{"internalType":"bytes","name":"path","type":"bytes"}],"internalType":"struct SwapData[][]","name":"swapData","type":"tuple[][]"}],"internalType":"struct FastWithdrawParams","name":"fastWithdrawParams","type":"tuple"}],"name":"withdrawFast","outputs":[],"stateMutability":"nonpayable","type":"function"}]

6101206040523480156200001257600080fd5b506040516200643738038062006437833981016040819052620000359162000312565b600080558484848482856001600160a01b038116620000cd5760405162461bcd60e51b815260206004820152604360248201527f53706f6f6c4f776e61626c653a3a636f6e7374727563746f723a2053706f6f6c60448201527f206f776e657220636f6e747261637420616464726573732063616e6e6f74206260648201526206520360ec1b608482015260a4015b60405180910390fd5b6001600160a01b0390811660805281166200015d5760405162461bcd60e51b815260206004820152604360248201527f53706f6f6c5061757361626c653a3a636f6e7374727563746f723a20436f6e7460448201527f726f6c6c657220636f6e747261637420616464726573732063616e6e6f74206260648201526206520360ec1b608482015260a401620000c4565b6001600160a01b0390811660a0528416620001d55760405162461bcd60e51b815260206004820152603160248201527f5661756c74426173653a3a636f6e7374727563746f723a2053706f6f6c206164604482015270064726573732063616e6e6f74206265203607c1b6064820152608401620000c4565b6001600160a01b038216620002535760405162461bcd60e51b815260206004820152603860248201527f5661756c74426173653a3a636f6e7374727563746f723a20466173745769746860448201527f6472617720616464726573732063616e6e6f74206265203000000000000000006064820152608401620000c4565b6001600160a01b038116620002d15760405162461bcd60e51b815260206004820152603760248201527f5661756c74426173653a3a636f6e7374727563746f723a204665652048616e6460448201527f6c657220616464726573732063616e6e6f7420626520300000000000000000006064820152608401620000c4565b6001600160a01b0393841660c05290831660e052909116610100525062000392945050505050565b6001600160a01b03811681146200030f57600080fd5b50565b600080600080600060a086880312156200032b57600080fd5b85516200033881620002f9565b60208701519095506200034b81620002f9565b60408701519094506200035e81620002f9565b60608701519093506200037181620002f9565b60808701519092506200038481620002f9565b809150509295509295909350565b60805160a05160c05160e05161010051615fcd6200046a600039600081816127010152612992015260008181611f9101526129320152600081816115410152818161177101528181611ed70152818161247c01528181612f520152818161365301528181613720015281816137ed015281816138b301528181613a1201528181614051015281816141650152818161420b015281816142ce015281816143960152614b2801526000818161085e01528181611864015281816123c601528181612a760152613919015260006149b90152615fcd6000f3fe608060405234801561001057600080fd5b50600436106102955760003560e01c806375a5a10a11610167578063a4d5e67c116100ce578063d8149b9b11610087578063d8149b9b146107e7578063f1229777146107fa578063f5bdf6c714610825578063f77c479114610859578063fb6138ab14610880578063fd3c11a81461092357600080fd5b8063a4d5e67c14610703578063a87430ba14610716578063bcd110141461079e578063c52f6c62146107b1578063cfce5dd9146107cb578063d7b9d423146107d457600080fd5b80638206818511610120578063820681851461064557806384da92a7146106585780638501881e1461066b5780638ab936b81461069f5780639fa45102146106b2578063a481ea29146106e557600080fd5b806375a5a10a14610558578063780d9d2d1461059757806379ecae54146105b65780637ae79d75146105f65780637bb7bed11461060957806380aa8c0d1461063257600080fd5b80633b0b4f5b1161020b578063692a4a91116101c4578063692a4a91146104905780636af3badd146104b85780636b76f333146104cb5780636e783b981461051f5780636f9ef7481461053257806373c2ad9c1461054557600080fd5b80633b0b4f5b146103c257806341a275e4146103ca578063510ccb43146103dd57806354663290146103f0578063564356d814610447578063638634ee1461046857600080fd5b8063281b98491161025d578063281b9849146103585780632e0b00451461036d5780633084d7351461038057806335303b11146103935780633a98ef39146103a65780633aa3cd4a146103b957600080fd5b806301ac145b1461029a57806306fdde03146102c7578063084fd9b4146102dc5780631f52692b14610307578063211dc32d14610337575b600080fd5b6001546102af90600160a81b900461ffff1681565b60405161ffff90911681526020015b60405180910390f35b6102cf610936565b6040516102be9190614f19565b6102ef6102ea3660046150a3565b6109c4565b6040516001600160801b0390911681526020016102be565b60015461031f9061010090046001600160a01b031681565b6040516001600160a01b0390911681526020016102be565b61034a610345366004615106565b610b50565b6040519081526020016102be565b61036b61036636600461513f565b610c27565b005b61036b61037b366004615187565b610c48565b61034a61038e3660046151bc565b610d5f565b61036b6103a13660046151d5565b610d7a565b6003546102ef906001600160801b031681565b61034a60045481565b61036b610e29565b61036b6103d83660046151f2565b610e33565b61036b6103eb366004615230565b610e70565b6104036103fe36600461513f565b610f11565b60408051998a5260208a0198909852968801959095526060870193909352608086019190915260a085015260c084015260e0830152610100820152610120016102be565b61045a6104553660046152cf565b6111e2565b6040516102be929190615361565b61047b6104763660046151d5565b61128d565b60405163ffffffff90911681526020016102be565b6007546104a490610100900462ffffff1681565b60405162ffffff90911681526020016102be565b61036b6104c63660046151d5565b6112be565b6104ff6104d93660046151bc565b6010602052600090815260409020546001600160801b0380821691600160801b90041682565b604080516001600160801b039384168152929091166020830152016102be565b61036b61052d3660046151d5565b611312565b61036b61054036600461513f565b611377565b61036b610553366004615383565b61138b565b6104ff6105663660046153c1565b600e6020908152600092835260408084209091529082529020546001600160801b0380821691600160801b90041682565b6007546105a49060ff1681565b60405160ff90911681526020016102be565b6105c96105c436600461513f565b611515565b604080519687526020870195909552938501929092526060840152608083015260a082015260c0016102be565b61036b6106043660046153ff565b611679565b61031f6106173660046151bc565b6009602052600090815260409020546001600160a01b031681565b61036b6106403660046153c1565b61172a565b61036b61065336600461541a565b611760565b61036b6106663660046154d5565b611beb565b6104ff6106793660046151d5565b600f602052600090815260409020546001600160801b0380821691600160801b90041682565b61036b6106ad36600461551e565b611c36565b6106d56106c03660046151d5565b600b6020526000908152604090205460ff1681565b60405190151581526020016102be565b600c546104ff906001600160801b0380821691600160801b90041682565b61036b6107113660046151d5565b611dd9565b6107646107243660046151d5565b6008602052600090815260409020805460018201546002909201546001600160801b0380831693600160801b938490048216938183169391048216911685565b604080516001600160801b03968716815294861660208601529285169284019290925283166060830152909116608082015260a0016102be565b61034a6107ac3660046151d5565b611e01565b6003546102ef90600160801b90046001600160801b031681565b61034a60065481565b61036b6107e2366004615737565b611e43565b61036b6107f536600461588f565b612049565b61080d6108083660046151d5565b61210c565b6040516001600160e01b0390911681526020016102be565b6104ff6108333660046151bc565b600d602052600090815260409020546001600160801b0380821691600160801b90041682565b61031f7f000000000000000000000000000000000000000000000000000000000000000081565b6108da61088e3660046151d5565b600a602052600090815260409020805460019091015463ffffffff80831692600160201b808204831693600160401b9092046001600160c01b031692821691046001600160e01b031685565b6040805163ffffffff968716815294861660208601526001600160c01b03909316928401929092529290921660608201526001600160e01b03909116608082015260a0016102be565b61036b61093136600461551e565b612208565b6002805461094390615965565b80601f016020809104026020016040519081016040528092919081815260200182805461096f90615965565b80156109bc5780601f10610991576101008083540402835291602001916109bc565b820191906000526020600020905b81548152906001019060200180831161099f57829003601f168201915b505050505081565b33600090815260086020526040812084156109eb576109e284612383565b6109eb846123c4565b82156109f9576109f961258e565b60018101546001600160801b0316915081610a415760405162461bcd60e51b815260206004820152600360248201526204341360ec1b60448201526064015b60405180910390fd5b6001810180546001600160801b03191690819055600160801b90046001600160801b03908116908316811015610ab9576001820180546001600160801b031690556000610a8e82856159b0565b90506000610aa4826001600160801b03166126e7565b9050610ab081866159b0565b94505050610ae3565b610ac383826159b0565b6001830180546001600160801b03928316600160801b0292169190911790555b610b0933846001600160801b0316610af961272e565b6001600160a01b03169190612797565b6040516001600160801b038416815233907fd8138f8a3f377c5259ca548e70e4c2de94f129f5a11036a15b69513cba2b426a9060200160405180910390a250509392505050565b6001600160a01b038083166000908152600a60209081526040808320938516835260089091528120549091906001600160801b031680610bae57506001600160a01b0383166000908152600390910160205260409020549050610c21565b6001600160a01b03841660009081526002830160209081526040808320546003860190925290912054670de0b6b3a764000082610bea8961210c565b6001600160e01b0316610bfd91906159d8565b610c0790856159ef565b610c119190615a0e565b610c1b9190615a30565b93505050505b92915050565b80610c3181612383565b81610c3b816123c4565b610c4361258e565b505050565b610c506127ed565b816000610c5d8282612860565b6001600160a01b0384166000908152600a602052604090206001015463ffffffff80851691161115610cc9576001600160a01b0384166000908152600a602052604090206001810154815467ffffffff00000000191663ffffffff909116600160201b02179055610cff565b6001600160a01b0384166000908152600a60205260409020805467ffffffff000000001916600160201b63ffffffff8616021790555b6001600160a01b0384166000818152600a6020908152604091829020549151600160201b90920463ffffffff1682527f21b2dd8950fc3a17e42d75bdfba3bf13f5a451f2d4b1dab7ab7f8d44f8a06926910160405180910390a250505050565b6000610d69612930565b610d728261298e565b90505b919050565b610d82612a6b565b60016000541415610dd55760405162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c006044820152606401610a38565b6001600090815560075460ff16905b81811015610e2057600081815260096020526040902054610e0e906001600160a01b031684612acb565b80610e1881615a48565b915050610de4565b50506000805550565b610e3161258e565b565b610e3b612bbf565b6001600160a01b0383166000908152600a60205260409020805463ffffffff191663ffffffff8316179055610c438383612c12565b60016000541415610ec35760405162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c006044820152606401610a38565b600160009081555b8151811015610f0957610ef7828281518110610ee957610ee9615a63565b602002602001015133612acb565b80610f0181615a48565b915050610ecb565b505060008055565b600080600080600080600080600080610f298b611515565b50505050509050610f3861258e565b3360009081526008602052604081206003549091906001600160801b031615801590610f70575060028201546001600160801b031615155b15610fa15760035460028301546001600160801b0391821691610f949116856159ef565b610f9e9190615a0e565b90505b6000600e6000336001600160a01b03166001600160a01b031681526020019081526020016000206000600f6000336001600160a01b03166001600160a01b0316815260200190815260200160002060000160009054906101000a90046001600160801b03166001600160801b0316815260200190815260200160002090506000600e6000336001600160a01b03166001600160a01b031681526020019081526020016000206000600f6000336001600160a01b03166001600160a01b0316815260200190815260200160002060000160109054906101000a90046001600160801b03166001600160801b0316815260200190815260200160002090508360020160009054906101000a90046001600160801b03168460000160109054906101000a90046001600160801b03168560010160009054906101000a90046001600160801b03168660010160109054906101000a90046001600160801b0316868660000160009054906101000a90046001600160801b03168760000160109054906101000a90046001600160801b03168760000160009054906101000a90046001600160801b03168860000160109054906101000a90046001600160801b0316886001600160801b03169850876001600160801b03169750866001600160801b03169650856001600160801b03169550836001600160801b03169350826001600160801b03169250816001600160801b03169150806001600160801b031690509d509d509d509d509d509d509d509d509d5050505050509193959799909294969850565b606060006111ee612f50565b856111f881612383565b86611202816123c4565b61120a612fae565b611215888888612fec565b60048990556007805463ffffff00191661010062ffffff8a1602179055909450925061124085613368565b60075460405188815261010090910462ffffff16907f5c09fbfaf801f04455c21a7b761e0ef929b1c114f8e95d5ecc5179e4117416dd9060200160405180910390a2505094509492505050565b6001600160a01b0381166000908152600a6020526040812054610d72904290600160201b900463ffffffff16613373565b6112c66127ed565b6001600160a01b0381166000908152600b60205260409020805460ff191660011790556112f281613388565b6001600160a01b03166000908152600a6020526040812081815560010155565b61131a612bbf565b60018054610100600160a81b0319166101006001600160a01b038416908102919091179091556040519081527f595b89b61981754cfd748b6a577e0310ba8a098a483e3e3bac5feab923d7b3f4906020015b60405180910390a150565b8061138181612383565b81610c43816123c4565b611393612bbf565b8261139d816134ac565b6001600160a01b0384166000908152600a60209081526040808320600b9092529091205460ff16156113fa5760405162461bcd60e51b8152600401610a38906020808252600490820152631513d09360e21b604082015260600190565b63ffffffff8416158015906114175750600181015463ffffffff16155b61144c5760405162461bcd60e51b8152600401610a38906020808252600490820152634243464760e01b604082015260600190565b600754600560ff909116111561148d5760405162461bcd60e51b8152600401610a38906020808252600490820152630a89a82b60e31b604082015260600190565b6007805460ff908116600090815260096020526040812080546001600160a01b0319166001600160a01b038a16179055825490911691906114cd83615a79565b82546101009290920a60ff81810219909316919092169190910217905550805463ffffffff191663ffffffff8516178155821561150e5761150e8584612c12565b5050505050565b6000806000806000808661152881612383565b87611532816123c4565b6000805b8a5181101561161d577f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316634e89a7118c838151811061158057611580615a63565b60200260200101516040518263ffffffff1660e01b81526004016115b391906001600160a01b0391909116815260200190565b6020604051808303816000875af11580156115d2573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906115f69190615a99565b611609906001600160801b031683615a30565b91508061161581615a48565b915050611536565b50600c546001600160801b038082166000908152600d6020526040808220600160801b9485900484168352912060035491549054949e9183169d508083169c5083900482169a5083821699509190920490911695509350505050565b60015461010090046001600160a01b0316331480156116a8575060015461ffff600160a81b9091048116908216105b6116da5760405162461bcd60e51b815260206004820152600360248201526246454560e81b6044820152606401610a38565b6001805461ffff60a81b1916600160a81b61ffff8416908102919091179091556040519081527f11878e6c9d396e93aa9470747ab6ae3baa13958d1df62e803a0b223c652a564b9060200161136c565b6117326127ed565b8161173c816134ac565b82611746816134fb565b61175a6001600160a01b0385163385612797565b50505050565b611768613555565b158061180757507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663194b42f86040518163ffffffff1660e01b8152600401602060405180830381865afa1580156117cd573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906117f19190615ab6565b60075462ffffff91821661010090910490911611155b6118395760405162461bcd60e51b815260206004820152600360248201526229272360e91b6044820152606401610a38565b8161184381612383565b8261184d81613571565b83611857816123c4565b84518410801561191957507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663192434098686815181106118a3576118a3615a63565b60200260200101516040518263ffffffff1660e01b81526004016118d691906001600160a01b0391909116815260200190565b602060405180830381865afa1580156118f3573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906119179190615ad3565b155b61194e5760405162461bcd60e51b8152600401610a38906020808252600490820152632129aa2960e11b604082015260600190565b60006001865161195e91906159d8565b90506000816001600160401b0381111561197a5761197a614f3a565b6040519080825280602002602001820160405280156119a3578160200160208202803683370190505b5090508115611b6f5760005b82811015611a0d578781815181106119c9576119c9615a63565b60200260200101518282815181106119e3576119e3615a63565b6001600160a01b039092166020928302919091019091015280611a0581615a48565b9150506119af565b5081861015611a6357868281518110611a2857611a28615a63565b6020026020010151818781518110611a4257611a42615a63565b60200260200101906001600160a01b031690816001600160a01b0316815250505b6004546000611a7282896135a8565b611a7e906127106159d8565b9050600184118015611a905750600081115b15611b615783881415611aae57611aa782896135c4565b9150611ad7565b6000611aba83866135a8565b9050611ac6838a6135c4565b9250611ad3838a836135e1565b9250505b600080611ae56001876159d8565b905061271060005b82811015611b49576000611b0187836135a8565b905085611b10612710836159ef565b611b1a9190615a0e565b9050611b278583836135e1565b9450611b3381846159d8565b9250508080611b4190615a48565b915050611aed565b50611b558383836135e1565b60045550611b68915050565b6127106004555b5050611b75565b60006004555b611b7e816135fb565b7f7f705c9890e1e97666eb559f0069c51eb9c6d8e37d6628895f3a886cc57b572086888881518110611bb257611bb2615a63565b6020026020010151604051611bda9291909182526001600160a01b0316602082015260400190565b60405180910390a150505050505050565b611bf36127ed565b8051611c06906002906020840190614e31565b507fed41a19b397d8e610d8f9e6e52ab2bef7e51c9977a12cb779c7cd86747f8d5098160405161136c9190614f19565b82611c4081612383565b83611c4a81613571565b84611c54816123c4565b611c5c61258e565b611c653361360a565b6000856001600160801b031611611ca45760405162461bcd60e51b815260206004820152600360248201526204e44560ec1b6044820152606401610a38565b6000611cae61364f565b62ffffff81166000908152600d6020526040812080549293508892909190611ce09084906001600160801b0316615af0565b82546101009290920a6001600160801b03818102199093169183160217909155336000908152600e6020908152604080832062ffffff87168452909152812080548a94509092611d3291859116615af0565b92506101000a8154816001600160801b0302191690836001600160801b03160217905550611d6687878362ffffff166136d3565b611d6f81613368565b611d788161388e565b611d8286866138a7565b611d8b86613979565b6040516001600160801b038716815262ffffff82169033907f90890809c654f11d6e72a28fa60149770a0d11ec6c92319d6ceb2bb0a4ea1a159060200160405180910390a350505050505050565b611de1612bbf565b80611deb816134fb565b816000611df88282612860565b61175a84613388565b6001600160a01b0381166000908152600a602052604081208054611e3c9063ffffffff811690600160401b90046001600160c01b03166159ef565b9392505050565b611e4b613a10565b83611e5581612383565b84611e5f816123c4565b611e67612fae565b611e6f61258e565b611e783361360a565b611e828585613ac5565b94506000611e8f86613db5565b600380549192508791600090611eaf9084906001600160801b03166159b0565b92506101000a8154816001600160801b0302191690836001600160801b0316021790555060007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663db6b687189846040518363ffffffff1660e01b8152600401611f23929190615b54565b6000604051808303816000875af1158015611f42573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052611f6a9190810190615b67565b90506000611f7788613dea565b60405163e5c8848560e01b81529091506001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000169063e5c8848590611fce908c908690869033908d90600401615ca1565b600060405180830381600087803b158015611fe857600080fd5b505af1158015611ffc573d6000803e3d6000fd5b50506040516001600160801b038b1681523392507ff81ecc4c10049fe8abde0a7afff608b6ac1d4cf5f6d58673e466a418b47dbf57915060200160405180910390a2505050505050505050565b60015460ff16156120855760405162461bcd60e51b8152600401610a38906020808252600490820152631052539560e21b604082015260600190565b60208082015160018054604085015161ffff16600160a81b0261ffff60a81b196001600160a01b039094166101000293909316610100600160b81b031990911617919091179055815180516120de926002920190614e31565b506120ec8160800151613f71565b60045560608101516120fd906135fb565b506001805460ff191681179055565b6001600160a01b0381166000908152600a60205260408120600354600160801b90046001600160801b03166121555760010154600160201b90046001600160e01b031692915050565b600181015460009063ffffffff1661216c8561128d565b6121769190615da0565b63ffffffff1690508061219e575060010154600160201b90046001600160e01b031692915050565b600354825461220091600160801b90046001600160801b0316906121d290600160401b90046001600160c01b0316846159ef565b6121dc9190615a0e565b60018401546121fb9190600160201b90046001600160e01b0316615a30565b613fc9565b949350505050565b8261221281612383565b8361221c816123c4565b612224612fae565b61222c61258e565b6122353361360a565b61223f8484613ac5565b9350600061224b61364f565b336000908152600e6020908152604080832062ffffff8516845290915290208054919250869160109061228f908490600160801b90046001600160801b0316615af0565b92506101000a8154816001600160801b0302191690836001600160801b0316021790555084600d60008362ffffff16815260200190815260200160002060000160108282829054906101000a90046001600160801b03166122f09190615af0565b92506101000a8154816001600160801b0302191690836001600160801b0316021790555061232486868362ffffff16614036565b61232d81613368565b6123368161388e565b6040516001600160801b038616815262ffffff82169033907ff279e6a1f5e320cca91135676d9cb6e44ca8a08c0b88342bcdb1144f6511b5689060200160405180910390a3505050505050565b61238f81600654614115565b6123c15760405162461bcd60e51b81526020600482015260036024820152620aca6960eb1b6044820152606401610a38565b50565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316634c8900606040518163ffffffff1660e01b815260040160006040518083038186803b15801561241d57600080fd5b505afa158015612431573d6000803e3d6000fd5b505060408051808201909152600c546001600160801b03808216808452600160801b90920416602083015290925015905061258a57600081600001516001600160801b0316905060007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663194b42f86040518163ffffffff1660e01b8152600401602060405180830381865afa1580156124d8573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906124fc9190615ab6565b62ffffff16905080821161175a576125148285614129565b6000835260208301516001600160801b0316156125695760208301516001600160801b031681811161254f5761254a8186614129565b612560565b60208401516001600160801b031684525b50600060208401525b825160208401516001600160801b03908116600160801b02911617600c5550505b5050565b604080518082018252600c546001600160801b038082168352600160801b918290048116602080850191909152336000908152600f82528590208551808701909652548083168087529390049091169084015290919015801590612612575081516001600160801b031615806126125750815181516001600160801b039182169116105b1561258a57805161262d906001600160801b03166001614590565b6000815260208101516001600160801b0316156126b25760208201516001600160801b03161580612677575081600001516001600160801b031681602001516001600160801b0316105b156126995761269481602001516001600160801b03166000614590565b6126aa565b60208101516001600160801b031681525b600060208201525b336000908152600f602090815260409091208251918301516001600160801b03908116600160801b0292169190911790555050565b60006126fa6126f58361298e565b6148a6565b9050610d757f0000000000000000000000000000000000000000000000000000000000000000826001600160801b0316610af95b6000306001600160a01b0316636f307dc36040518163ffffffff1660e01b8152600401602060405180830381865afa15801561276e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906127929190615dbd565b905090565b610c438363a9059cbb60e01b84846040516024016127b6929190615dda565b60408051601f198184030181529190526020810180516001600160e01b03166001600160e01b0319909316929092179091526148cf565b6127f56149a1565b610e315760405162461bcd60e51b815260206004820152603660248201527f53706f6f6c4f776e61626c653a3a6f6e6c794f776e65723a2043616c6c65722060448201527534b9903737ba103a34329029b837b7b61037bbb732b960511b6064820152608401610a38565b6001600160a01b0382166000908152600a602052604090206128818361210c565b8160010160046101000a8154816001600160e01b0302191690836001600160e01b031602179055506128b28361128d565b60018201805463ffffffff191663ffffffff929092169190911790556001600160a01b03821615610c43576128e78383610b50565b6001600160a01b0392909216600090815260038201602090815260408083209490945560018301546002909301905291909120600160201b9091046001600160e01b0316905550565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03163314610e315760405162461bcd60e51b81526020600482015260036024820152624f465760e81b6044820152606401610a38565b60007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316634b6229016129c761272e565b846129d0614a2c565b60015460405160e086901b6001600160e01b03191681526001600160a01b0394851660048201526024810193909352908316604483015261010081049092166064820152600160a81b90910461ffff16608482015260a4016020604051808303816000875af1158015612a47573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610d729190615df3565b336001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001614610e315760405162461bcd60e51b815260206004820152600560248201526413d0d5149360da1b6044820152606401610a38565b8181612ad78282612860565b6001600160a01b0384166000908152600a60205260409020805463ffffffff16612b295760405162461bcd60e51b815260206004820152600360248201526242544b60e81b6044820152606401610a38565b6001600160a01b03841660009081526003820160205260409020548015612bb7576001600160a01b038086166000908152600384016020526040812055612b739087168683612797565b846001600160a01b03167f540798df468d7b23d11f156fdb954cb19ad414d150722a7b6d55ba369dea792e8783604051612bae929190615dda565b60405180910390a25b505050505050565b60015461010090046001600160a01b0316331480612be05750612be06149a1565b610e315760405162461bcd60e51b815260206004820152600360248201526213d3d160ea1b6044820152606401610a38565b816000612c1f8282612860565b6001600160a01b0384166000908152600a602052604090206001600160c01b03612c51670de0b6b3a7640000866159ef565b6001830154612c709190600160201b90046001600160e01b0316615a30565b1115612ca45760405162461bcd60e51b8152602060048201526003602482015262292a2160e91b6044820152606401610a38565b612cb96001600160a01b038616333087614a6c565b8054600090612cce9063ffffffff1642615e0c565b8254909150600160201b900463ffffffff164210612d8c578154612d139063ffffffff16612d04670de0b6b3a7640000886159ef565b612d0e9190615a0e565b614aa4565b82546001600160c01b0391909116600160401b026001600160401b038216811784556040805188815263ffffffff928316929093169190911760208301526001600160a01b038816917f6a6f77044107a33658235d41bedbbaf2fe9ccdceb313143c947a5e76e1ec8474910160405180910390a2612f12565b815463ffffffff808316600160201b909204161115612dd35760405162461bcd60e51b815260206004820152600360248201526250465360e81b6044820152606401610a38565b8154600090612df0904290600160201b900463ffffffff166159d8565b8354909150600090612e1290600160401b90046001600160c01b0316836159ef565b8454909150600090612e419063ffffffff1683612e37670de0b6b3a76400008c6159ef565b612d049190615a30565b85549091506001600160c01b03600160401b90910481169082161015612e8f5760405162461bcd60e51b815260206004820152600360248201526226292960e91b6044820152606401610a38565b84546001600160c01b038216600160401b026001600160401b03821681178755604080518b81526020810186905263ffffffff9283169383169390931790830152851660608201526001600160a01b038a16907e4f7db2945633cb30dd3539fd30caced03aa57f25ff7039750216e9321b94be9060800160405180910390a25050505b60018201805463ffffffff42811663ffffffff199092169190911790915582549116600160201b0267ffffffff000000001990911617905550505050565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03163314610e315760405162461bcd60e51b815260206004820152600360248201526204f53560ec1b6044820152606401610a38565b612fb6613555565b15610e315760405162461bcd60e51b8152600401610a38906020808252600490820152631394915160e21b604082015260600190565b606060008085516001600160401b0381111561300a5761300a614f3a565b604051908082528060200260200182016040528015613033578160200160208202803683370190505b509050600086516001600160401b0381111561305157613051614f3a565b60405190808252806020026020018201604052801561307a578160200160208202803683370190505b50905060008061308a8988614acd565b91509150600081116130c45760405162461bcd60e51b815260206004820152600360248201526213955360ea1b6044820152606401610a38565b600080808080805b8e5181101561322d5760006130e18f836135a8565b90506130ed8188615a30565b96506000808a848151811061310457613104615a63565b6020026020010151111561314757886127108b858151811061312857613128615a63565b602002602001015161313a91906159ef565b6131449190615a0e565b90505b818111156131be57600061315b83836159d8565b9050600a81101561316e5750505061321b565b6000826131886c0c9f2c9cd04674edea40000000846159ef565b6131929190615a0e565b9050808d86815181106131a7576131a7615a63565b602002602001018181525050600196505050613218565b818110156132185760006131d282846159d8565b9050600a8110156131e55750505061321b565b808d85815181106131f8576131f8615a63565b602090810291909101015261320d8189615a30565b975083965060019450505b50505b8061322581615a48565b9150506130cc565b5061271085146132655760405162461bcd60e51b815260206004820152600360248201526204250560ec1b6044820152606401610a38565b81801561326f5750805b6132a15760405162461bcd60e51b815260206004820152600360248201526213949160ea1b6044820152606401610a38565b505060008060005b8381116133355760008a82815181106132c4576132c4615a63565b60200260200101511115613323576000856127108c84815181106132ea576132ea615a63565b60200260200101516132fc91906159ef565b6133069190615a0e565b90506133138483836135e1565b935061331f8184615a30565b9250505b8061332d81615a48565b9150506132a9565b5061334e83613346836127106159d8565b8491906135e1565b6005819055979e979d50969b505050505050505050505050565b6123c1600c82614c23565b60008183116133825782611e3c565b50919050565b60075460ff1660005b81811015610c43576000818152600960205260409020546001600160a01b038481169116141561349a57600960006133ca6001856159d8565b8152602080820192909252604090810160009081205484825260099384905291812080546001600160a01b0319166001600160a01b03909316929092179091556134156001856159d8565b81526020810191909152604001600090812080546001600160a01b03191690556007805460ff169161344683615e2b565b91906101000a81548160ff021916908360ff16021790555050826001600160a01b03167f755c47ac85b75fe2251607db5a480aac818b88bb535814bf1e3c4784ae4f6baa60405160405180910390a2505050565b806134a481615a48565b915050613391565b6134b461272e565b6001600160a01b0316816001600160a01b031614156123c15760405162461bcd60e51b815260206004820152600360248201526213955560ea1b6044820152606401610a38565b6001600160a01b0381166000908152600a6020526040902054600160201b900463ffffffff1642116123c15760405162461bcd60e51b815260206004820152600360248201526229272360e91b6044820152606401610a38565b600754600090610100900462ffffff161561356e575060015b90565b60008151116123c15760405162461bcd60e51b81526020600482015260036024820152621394d560ea1b6044820152606401610a38565b60006135b582600e6159ef565b83901c613fff16905092915050565b60006135d182600e6159ef565b613fff901b198316905092915050565b60006135ee83600e6159ef565b6122009083901b85615a30565b61360481614c83565b60065550565b60075460ff1660005b81811015610c435760008181526009602052604090205461363d906001600160a01b031684612860565b8061364781615a48565b915050613613565b60007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03166359b4ad5b6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156136af573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906127929190615ab6565b825182906000906136e6906001906159d8565b60045490915060005b828110156137db57600061370d8383896001600160801b0316614cb3565b90506001600160801b038116156137c8577f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03166360fb0f5989848151811061375f5761375f615a63565b602002602001015183896040518463ffffffff1660e01b815260040161378793929190615e48565b600060405180830381600087803b1580156137a157600080fd5b505af11580156137b5573d6000803e3d6000fd5b5050505080856137c591906159b0565b94505b50806137d381615a48565b9150506136ef565b506001600160801b03831615612bb7577f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03166360fb0f5987848151811061382c5761382c615a63565b602002602001015185876040518463ffffffff1660e01b815260040161385493929190615e48565b600060405180830381600087803b15801561386e57600080fd5b505af1158015613882573d6000803e3d6000fd5b50505050505050505050565b336000908152600f602052604090206123c19082614c23565b80156138f55761258a337f0000000000000000000000000000000000000000000000000000000000000000846001600160801b03166138e461272e565b6001600160a01b0316929190614a6c565b6040516320bae68360e21b81523360048201526001600160801b03831660248201527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906382eb9a0c90604401600060405180830381600087803b15801561396557600080fd5b505af1158015612bb7573d6000803e3d6000fd5b33600090815260086020526040812080548392906139a19084906001600160801b0316615af0565b92506101000a8154816001600160801b0302191690836001600160801b0316021790555080600360108282829054906101000a90046001600160801b03166139e99190615af0565b92506101000a8154816001600160801b0302191690836001600160801b0316021790555050565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316636bbbfc626040518163ffffffff1660e01b8152600401602060405180830381865afa158015613a6e573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613a929190615ad3565b15610e315760405162461bcd60e51b81526020600482015260036024820152622726a960e91b6044820152606401610a38565b33600090815260086020908152604080832060028101548154600f85528386208451808601909552546001600160801b03818116808752600160801b90920481169686019690965292949182169391169115613b9a57336000908152600e6020908152604080832084516001600160801b039081168552925290912054613b4d9116836159b0565b60208201519092506001600160801b031615613b9a57336000908152600e60209081526040808320848301516001600160801b039081168552925290912054613b979116836159b0565b91505b8580613bcb57506000836001600160801b0316118015613bcb5750866001600160801b0316836001600160801b0316145b15613c7f576002840180546001600160801b0319169055600380549397508793839190601090613c0c908490600160801b90046001600160801b03166159b0565b92506101000a8154816001600160801b0302191690836001600160801b03160217905550818460000160008282829054906101000a90046001600160801b0316613c5691906159b0565b92506101000a8154816001600160801b0302191690836001600160801b03160217905550613daa565b866001600160801b0316836001600160801b031610158015613caa57506000876001600160801b0316115b613cdc5760405162461bcd60e51b81526020600482015260036024820152620aea6960eb1b6044820152606401610a38565b6000613ce9838986614cda565b905080600360108282829054906101000a90046001600160801b0316613d0f91906159b0565b92506101000a8154816001600160801b0302191690836001600160801b03160217905550808560000160008282829054906101000a90046001600160801b0316613d5991906159b0565b92506101000a8154816001600160801b0302191690836001600160801b031602179055508784038560020160006101000a8154816001600160801b0302191690836001600160801b03160217905550505b509495945050505050565b6003546000906001600160801b0390811690613de09084166c0c9f2c9cd04674edea400000006159ef565b610d729190615a0e565b336000908152600860209081526040808320600f83528184208251808401909352546001600160801b038082168452600160801b9091048116938301939093526002810154909284918291613e4191889116615af0565b83519091506001600160801b031615613ee357336000908152600e6020908152604080832086516001600160801b039081168552925290912054613e8e91600160801b9091041682615af0565b60208401519091506001600160801b031615613ee357336000908152600e60209081526040808320868301516001600160801b039081168552925290912054613ee091600160801b9091041682615af0565b90505b856001600160801b0316816001600160801b03161115613f44578354600160801b90046001600160801b0316613f1a818884614cda565b9250613f2683826159b0565b85546001600160801b03918216600160801b02911617855550613f5f565b83546001600160801b038082168655600160801b9091041691505b506001600160801b0316949350505050565b60008060005b8351811015613fc257613fae81858381518110613f9657613f96615a63565b6020026020010151846135e19092919063ffffffff16565b915080613fba81615a48565b915050613f77565b5092915050565b60006001600160e01b038211156140325760405162461bcd60e51b815260206004820152602760248201527f53616665436173743a2076616c756520646f65736e27742066697420696e20326044820152663234206269747360c81b6064820152608401610a38565b5090565b600061404183613db5565b905060005b845181101561150e577f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663b5c5f67286838151811061409057614090615a63565b60209081029190910101516040516001600160e01b031960e084901b1681526001600160a01b0390911660048201526024810185905260448101869052606401600060405180830381600087803b1580156140ea57600080fd5b505af11580156140fe573d6000803e3d6000fd5b50505050808061410d90615a48565b915050614046565b60008161412184614c83565b149392505050565b6003546001600160801b0316600080808061414387614d07565b905080156141dc576005546040516317c78b0960e01b81526001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016916317c78b099161419c918a918c90600401615e72565b600060405180830381600087803b1580156141b657600080fd5b505af11580156141ca573d6000803e3d6000fd5b50506007805463ffffff001916905550505b60005b86518110156143685760008782815181106141fc576141fc615a63565b602002602001015190506000807f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316631e9a6950848d6040518363ffffffff1660e01b8152600401614257929190615dda565b60408051808303816000875af1158015614275573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906142999190615e97565b90925090506142a88289615af0565b97506142b48188615af0565b60405163a2b0de8b60e01b81529097506001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000169063a2b0de8b906143059086908f90600401615dda565b602060405180830381865afa158015614322573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906143469190615a99565b6143509087615af0565b9550505050808061436090615a48565b9150506141df565b506001600160801b038316156143fb576040516341c78ddd60e01b81526001600160801b03841660048201527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906341c78ddd90602401600060405180830381600087803b1580156143e257600080fd5b505af11580156143f6573d6000803e3d6000fd5b505050505b6000878152600d602052604090205461442490600160801b90046001600160801b0316866159b0565b9450600061443864174876e800600a615ec6565b6001600160801b0316866001600160801b031611158061445f57506001600160801b038316155b156144ce57614471620f424086615ec6565b905064174876e8006001600160801b03871610156144c95764174876e8006144998783615af0565b6001600160801b0316106144c15764174876e8008690038601950164174876e7ff19016144ff565b949094019360005b6144ff565b826001600160801b0316856001600160801b031610156144fc576144f58587878603614cda565b90506144ff565b50845b6145098187615af0565b600380546001600160801b039283166001600160801b03199091161790556040805180820182528383168152868316602080830191825260008d81526010909152838120925191518516600160801b02919094161790555189917f32568d6390d10efe40ae666165d7e927786da1a0bf60ad350fbd637da51dee0791a25050505050505050565b336000908152600860209081526040808320600e83528184208685529092529091208054600160801b90046001600160801b0316801561478f57600085815260106020908152604080832054600d909252822054614606916001600160801b03600160801b918290048116928692900416614cda565b60018501805491925082916000906146289084906001600160801b0316615af0565b92506101000a8154816001600160801b0302191690836001600160801b03160217905550600080838660020160009054906101000a90046001600160801b03166146729190615af0565b905086156146c357336000908152600e60205260408120906146958a6001615a30565b81526020810191909152604001600020546146c090600160801b90046001600160801b031682615af0565b90505b836001600160801b0316816001600160801b03161115614724578554600160801b90046001600160801b03166146fa818684614cda565b925061470683826159b0565b87546001600160801b03918216600160801b0291161787555061473f565b85546001600160801b038082168855600160801b9091041691505b818660010160108282829054906101000a90046001600160801b03166147659190615af0565b82546101009290920a6001600160801b038181021990931691831602179091558654168655505050505b81546001600160801b0316801561487157600086815260106020908152604080832054600d9092528220546147d29184916001600160801b039182169116614cda565b60028601805491925082916000906147f49084906001600160801b0316615af0565b92506101000a8154816001600160801b0302191690836001600160801b03160217905550818560000160108282829054906101000a90046001600160801b031661483e9190615af0565b82546001600160801b039182166101009390930a9283029190920219909116179055505082546001600160801b03191683555b604051869033907f961e41c19e060d605836d2061ee68d182e66923d6c75ab7685e39f63e9f8684d90600090a3505050505050565b60006001600160801b038211156140325760405162461bcd60e51b8152600401610a3890615ef5565b6000614924826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b0316614d269092919063ffffffff16565b805190915015610c4357808060200190518101906149429190615ad3565b610c435760405162461bcd60e51b815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e6044820152691bdd081cdd58d8d9595960b21b6064820152608401610a38565b604051634c24e8cd60e01b81523360048201526000907f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031690634c24e8cd90602401602060405180830381865afa158015614a08573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906127929190615ad3565b6000306001600160a01b031663bdff15156040518163ffffffff1660e01b8152600401602060405180830381865afa15801561276e573d6000803e3d6000fd5b6040516001600160a01b038085166024830152831660448201526064810182905261175a9085906323b872dd60e01b906084016127b6565b60006001600160c01b038211156140325760405162461bcd60e51b8152600401610a3890615ef5565b606060008084516001600160401b03811115614aeb57614aeb614f3a565b604051908082528060200260200182016040528015614b14578160200160208202803683370190505b5090506000805b8651811015614c175760007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663a2b0de8b898481518110614b6757614b67615a63565b6020026020010151896040518363ffffffff1660e01b8152600401614b8d929190615dda565b602060405180830381865afa158015614baa573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190614bce9190615a99565b6001600160801b0316905080848381518110614bec57614bec615a63565b6020908102919091010152614c018184615a30565b9250508080614c0f90615a48565b915050614b1b565b50909590945092505050565b81546001600160801b031615614c6a57815462ffffff82166001600160801b03909116101561258a5781546001600160801b031662ffffff91909116600160801b02179055565b81546001600160801b03191662ffffff82161782555050565b600081604051602001614c969190615f3c565b604051602081830303815290604052805190602001209050919050565b600061220061271083614cc687876135a8565b614cd091906159ef565b6126f59190615a0e565b6000612200826001600160801b0316846001600160801b0316866001600160801b0316614cd091906159ef565b600754600090610100900462ffffff16821415610d7557506001919050565b6060612200848460008585843b614d7f5760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152606401610a38565b600080866001600160a01b03168587604051614d9b9190615f7b565b60006040518083038185875af1925050503d8060008114614dd8576040519150601f19603f3d011682016040523d82523d6000602084013e614ddd565b606091505b5091509150614ded828286614df8565b979650505050505050565b60608315614e07575081611e3c565b825115614e175782518084602001fd5b8160405162461bcd60e51b8152600401610a389190614f19565b828054614e3d90615965565b90600052602060002090601f016020900481019282614e5f5760008555614ea5565b82601f10614e7857805160ff1916838001178555614ea5565b82800160010185558215614ea5579182015b82811115614ea5578251825591602001919060010190614e8a565b506140329291505b808211156140325760008155600101614ead565b60005b83811015614edc578181015183820152602001614ec4565b8381111561175a5750506000910152565b60008151808452614f05816020860160208601614ec1565b601f01601f19169290920160200192915050565b602081526000611e3c6020830184614eed565b80151581146123c157600080fd5b634e487b7160e01b600052604160045260246000fd5b604080519081016001600160401b0381118282101715614f7257614f72614f3a565b60405290565b604051606081016001600160401b0381118282101715614f7257614f72614f3a565b60405160a081016001600160401b0381118282101715614f7257614f72614f3a565b604051601f8201601f191681016001600160401b0381118282101715614fe457614fe4614f3a565b604052919050565b60006001600160401b0382111561500557615005614f3a565b5060051b60200190565b6001600160a01b03811681146123c157600080fd5b8035610d758161500f565b600082601f83011261504057600080fd5b8135602061505561505083614fec565b614fbc565b82815260059290921b8401810191818101908684111561507457600080fd5b8286015b8481101561509857803561508b8161500f565b8352918301918301615078565b509695505050505050565b6000806000606084860312156150b857600080fd5b83356150c381614f2c565b925060208401356001600160401b038111156150de57600080fd5b6150ea8682870161502f565b92505060408401356150fb81614f2c565b809150509250925092565b6000806040838503121561511957600080fd5b82356151248161500f565b915060208301356151348161500f565b809150509250929050565b60006020828403121561515157600080fd5b81356001600160401b0381111561516757600080fd5b6122008482850161502f565b803563ffffffff81168114610d7557600080fd5b6000806040838503121561519a57600080fd5b82356151a58161500f565b91506151b360208401615173565b90509250929050565b6000602082840312156151ce57600080fd5b5035919050565b6000602082840312156151e757600080fd5b8135611e3c8161500f565b60008060006060848603121561520757600080fd5b83356152128161500f565b92506020840135915061522760408501615173565b90509250925092565b6000602080838503121561524357600080fd5b82356001600160401b0381111561525957600080fd5b8301601f8101851361526a57600080fd5b803561527861505082614fec565b81815260059190911b8201830190838101908783111561529757600080fd5b928401925b82841015614ded5783356152af8161500f565b8252928401929084019061529c565b62ffffff811681146123c157600080fd5b600080600080608085870312156152e557600080fd5b84356001600160401b038111156152fb57600080fd5b6153078782880161502f565b94505060208501359250604085013591506060850135615326816152be565b939692955090935050565b600081518084526020808501945080840160005b83811015613daa57815187529582019590820190600101615345565b6040815260006153746040830185615331565b90508260208301529392505050565b60008060006060848603121561539857600080fd5b83356153a38161500f565b92506153b160208501615173565b9150604084013590509250925092565b600080604083850312156153d457600080fd5b82356153df8161500f565b946020939093013593505050565b803561ffff81168114610d7557600080fd5b60006020828403121561541157600080fd5b611e3c826153ed565b6000806040838503121561542d57600080fd5b82356001600160401b0381111561544357600080fd5b61544f8582860161502f565b95602094909401359450505050565b60006001600160401b0383111561547757615477614f3a565b61548a601f8401601f1916602001614fbc565b905082815283838301111561549e57600080fd5b828260208301376000602084830101529392505050565b600082601f8301126154c657600080fd5b611e3c8383356020850161545e565b6000602082840312156154e757600080fd5b81356001600160401b038111156154fd57600080fd5b612200848285016154b5565b6001600160801b03811681146123c157600080fd5b60008060006060848603121561553357600080fd5b83356001600160401b0381111561554957600080fd5b6155558682870161502f565b935050602084013561556681615509565b915060408401356150fb81614f2c565b600082601f83011261558757600080fd5b8135602061559761505083614fec565b82815260059290921b840181019181810190868411156155b657600080fd5b8286015b8481101561509857803583529183019183016155ba565b600082601f8301126155e257600080fd5b6155ef6150508335614fec565b82358082526020808301929160051b85010185101561560d57600080fd5b602084015b6020853560051b86010181101561572e576001600160401b03808235111561563957600080fd5b8135860187603f82011261564c57600080fd5b61565c6150506020830135614fec565b602082810135808352908201919060051b83016040018a81111561567f57600080fd5b604084015b8181101561571757858135111561569a57600080fd5b803585016040818e03603f190112156156b257600080fd5b6156ba614f50565b6040820135815287606083013511156156d257600080fd5b6060820135820191508d605f8301126156ea57600080fd5b6156fc8e60408401356060850161545e565b60208201528086525050602084019350602081019050615684565b505086525050602093840193919091019050615612565b50949350505050565b6000806000806080858703121561574d57600080fd5b6001600160401b03808635111561576357600080fd5b615770878735880161502f565b945060208087013561578181615509565b9450604087013561579181614f2c565b93506060870135828111156157a557600080fd5b87016060818a0312156157b757600080fd5b6157bf614f78565b6157c98235614f2c565b8135815282820135848111156157de57600080fd5b8201601f81018b136157ef57600080fd5b80356157fd61505082614fec565b81815260059190911b8201850190858101908d83111561581c57600080fd5b8684015b8381101561585257888135111561583657600080fd5b6158458f898335880101615576565b8352918701918701615820565b50808786015250505050604082013592508383111561587057600080fd5b61587c8a8484016155d1565b6040820152969995985093965050505050565b6000602082840312156158a157600080fd5b81356001600160401b03808211156158b857600080fd5b9083019060a082860312156158cc57600080fd5b6158d4614f9a565b8235828111156158e357600080fd5b6158ef878286016154b5565b8252506158fe60208401615024565b602082015261590f604084016153ed565b604082015260608301358281111561592657600080fd5b6159328782860161502f565b60608301525060808301358281111561594a57600080fd5b61595687828601615576565b60808301525095945050505050565b600181811c9082168061597957607f821691505b6020821081141561338257634e487b7160e01b600052602260045260246000fd5b634e487b7160e01b600052601160045260246000fd5b60006001600160801b03838116908316818110156159d0576159d061599a565b039392505050565b6000828210156159ea576159ea61599a565b500390565b6000816000190483118215151615615a0957615a0961599a565b500290565b600082615a2b57634e487b7160e01b600052601260045260246000fd5b500490565b60008219821115615a4357615a4361599a565b500190565b6000600019821415615a5c57615a5c61599a565b5060010190565b634e487b7160e01b600052603260045260246000fd5b600060ff821660ff811415615a9057615a9061599a565b60010192915050565b600060208284031215615aab57600080fd5b8151611e3c81615509565b600060208284031215615ac857600080fd5b8151611e3c816152be565b600060208284031215615ae557600080fd5b8151611e3c81614f2c565b60006001600160801b03828116848216808303821115615b1257615b1261599a565b01949350505050565b600081518084526020808501945080840160005b83811015613daa5781516001600160a01b031687529582019590820190600101615b2f565b6040815260006153746040830185615b1b565b60006020808385031215615b7a57600080fd5b82516001600160401b03811115615b9057600080fd5b8301601f81018513615ba157600080fd5b8051615baf61505082614fec565b81815260059190911b82018301908381019087831115615bce57600080fd5b928401925b82841015614ded578351615be681615509565b82529284019290840190615bd3565b600081518084526020808501808196506005915083821b81018387016000805b87811015615c92578484038b5282518051808652908801908886019080891b87018a01855b82811015615c7c57888203601f190184528451805183528c015160408d8401819052615c6881850183614eed565b968e0196958e019593505050600101615c3a565b509d8a019d965050509287019250600101615c15565b50919998505050505050505050565b60a081526000615cb460a0830188615b1b565b82810360208481019190915287518083528882019282019060005b81811015615cf45784516001600160801b031683529383019391830191600101615ccf565b505087604086015260018060a01b038716606086015284810360808601526060810192508551151581528186015160608383015283815180865260808401915060808160051b8501019550848301925060005b81811015615d7557607f19858803018352615d63878551615331565b96509285019291850191600101615d47565b5050505060408601519150808303604082015250615d938282615bf5565b9998505050505050505050565b600063ffffffff838116908316818110156159d0576159d061599a565b600060208284031215615dcf57600080fd5b8151611e3c8161500f565b6001600160a01b03929092168252602082015260400190565b600060208284031215615e0557600080fd5b5051919050565b600063ffffffff808316818516808303821115615b1257615b1261599a565b600060ff821680615e3e57615e3e61599a565b6000190192915050565b6001600160a01b039390931683526001600160801b03919091166020830152604082015260600190565b606081526000615e856060830186615b1b565b60208301949094525060400152919050565b60008060408385031215615eaa57600080fd5b8251615eb581615509565b602084015190925061513481615509565b60006001600160801b0382811684821681151582840482111615615eec57615eec61599a565b02949350505050565b60208082526027908201527f53616665436173743a2076616c756520646f65736e27742066697420696e20316040820152663238206269747360c81b606082015260800190565b815160009082906020808601845b83811015615f6f5781516001600160a01b031685529382019390820190600101615f4a565b50929695505050505050565b60008251615f8d818460208701614ec1565b919091019291505056fea26469706673582212207361c12125b82fd6bc8ae7a18788ffa318a06f25efb8bb8d0ec886b1b94a5f6464736f6c634300080b0033000000000000000000000000e140bb5f424a53e0687bfc10f6845a5672d7e242000000000000000000000000dd4051c3571c143b989c3227e8eb50983974835c0000000000000000000000004788c0a425abb27721e05a39c417b69cba6b065a0000000000000000000000003d3d0a7876d18770a21a5ea05fef211eba808e720000000000000000000000004f03f70a99e5c3b49d733ddd7458f80fa9b5a5b5

Deployed Bytecode

0x608060405234801561001057600080fd5b50600436106102955760003560e01c806375a5a10a11610167578063a4d5e67c116100ce578063d8149b9b11610087578063d8149b9b146107e7578063f1229777146107fa578063f5bdf6c714610825578063f77c479114610859578063fb6138ab14610880578063fd3c11a81461092357600080fd5b8063a4d5e67c14610703578063a87430ba14610716578063bcd110141461079e578063c52f6c62146107b1578063cfce5dd9146107cb578063d7b9d423146107d457600080fd5b80638206818511610120578063820681851461064557806384da92a7146106585780638501881e1461066b5780638ab936b81461069f5780639fa45102146106b2578063a481ea29146106e557600080fd5b806375a5a10a14610558578063780d9d2d1461059757806379ecae54146105b65780637ae79d75146105f65780637bb7bed11461060957806380aa8c0d1461063257600080fd5b80633b0b4f5b1161020b578063692a4a91116101c4578063692a4a91146104905780636af3badd146104b85780636b76f333146104cb5780636e783b981461051f5780636f9ef7481461053257806373c2ad9c1461054557600080fd5b80633b0b4f5b146103c257806341a275e4146103ca578063510ccb43146103dd57806354663290146103f0578063564356d814610447578063638634ee1461046857600080fd5b8063281b98491161025d578063281b9849146103585780632e0b00451461036d5780633084d7351461038057806335303b11146103935780633a98ef39146103a65780633aa3cd4a146103b957600080fd5b806301ac145b1461029a57806306fdde03146102c7578063084fd9b4146102dc5780631f52692b14610307578063211dc32d14610337575b600080fd5b6001546102af90600160a81b900461ffff1681565b60405161ffff90911681526020015b60405180910390f35b6102cf610936565b6040516102be9190614f19565b6102ef6102ea3660046150a3565b6109c4565b6040516001600160801b0390911681526020016102be565b60015461031f9061010090046001600160a01b031681565b6040516001600160a01b0390911681526020016102be565b61034a610345366004615106565b610b50565b6040519081526020016102be565b61036b61036636600461513f565b610c27565b005b61036b61037b366004615187565b610c48565b61034a61038e3660046151bc565b610d5f565b61036b6103a13660046151d5565b610d7a565b6003546102ef906001600160801b031681565b61034a60045481565b61036b610e29565b61036b6103d83660046151f2565b610e33565b61036b6103eb366004615230565b610e70565b6104036103fe36600461513f565b610f11565b60408051998a5260208a0198909852968801959095526060870193909352608086019190915260a085015260c084015260e0830152610100820152610120016102be565b61045a6104553660046152cf565b6111e2565b6040516102be929190615361565b61047b6104763660046151d5565b61128d565b60405163ffffffff90911681526020016102be565b6007546104a490610100900462ffffff1681565b60405162ffffff90911681526020016102be565b61036b6104c63660046151d5565b6112be565b6104ff6104d93660046151bc565b6010602052600090815260409020546001600160801b0380821691600160801b90041682565b604080516001600160801b039384168152929091166020830152016102be565b61036b61052d3660046151d5565b611312565b61036b61054036600461513f565b611377565b61036b610553366004615383565b61138b565b6104ff6105663660046153c1565b600e6020908152600092835260408084209091529082529020546001600160801b0380821691600160801b90041682565b6007546105a49060ff1681565b60405160ff90911681526020016102be565b6105c96105c436600461513f565b611515565b604080519687526020870195909552938501929092526060840152608083015260a082015260c0016102be565b61036b6106043660046153ff565b611679565b61031f6106173660046151bc565b6009602052600090815260409020546001600160a01b031681565b61036b6106403660046153c1565b61172a565b61036b61065336600461541a565b611760565b61036b6106663660046154d5565b611beb565b6104ff6106793660046151d5565b600f602052600090815260409020546001600160801b0380821691600160801b90041682565b61036b6106ad36600461551e565b611c36565b6106d56106c03660046151d5565b600b6020526000908152604090205460ff1681565b60405190151581526020016102be565b600c546104ff906001600160801b0380821691600160801b90041682565b61036b6107113660046151d5565b611dd9565b6107646107243660046151d5565b6008602052600090815260409020805460018201546002909201546001600160801b0380831693600160801b938490048216938183169391048216911685565b604080516001600160801b03968716815294861660208601529285169284019290925283166060830152909116608082015260a0016102be565b61034a6107ac3660046151d5565b611e01565b6003546102ef90600160801b90046001600160801b031681565b61034a60065481565b61036b6107e2366004615737565b611e43565b61036b6107f536600461588f565b612049565b61080d6108083660046151d5565b61210c565b6040516001600160e01b0390911681526020016102be565b6104ff6108333660046151bc565b600d602052600090815260409020546001600160801b0380821691600160801b90041682565b61031f7f000000000000000000000000dd4051c3571c143b989c3227e8eb50983974835c81565b6108da61088e3660046151d5565b600a602052600090815260409020805460019091015463ffffffff80831692600160201b808204831693600160401b9092046001600160c01b031692821691046001600160e01b031685565b6040805163ffffffff968716815294861660208601526001600160c01b03909316928401929092529290921660608201526001600160e01b03909116608082015260a0016102be565b61036b61093136600461551e565b612208565b6002805461094390615965565b80601f016020809104026020016040519081016040528092919081815260200182805461096f90615965565b80156109bc5780601f10610991576101008083540402835291602001916109bc565b820191906000526020600020905b81548152906001019060200180831161099f57829003601f168201915b505050505081565b33600090815260086020526040812084156109eb576109e284612383565b6109eb846123c4565b82156109f9576109f961258e565b60018101546001600160801b0316915081610a415760405162461bcd60e51b815260206004820152600360248201526204341360ec1b60448201526064015b60405180910390fd5b6001810180546001600160801b03191690819055600160801b90046001600160801b03908116908316811015610ab9576001820180546001600160801b031690556000610a8e82856159b0565b90506000610aa4826001600160801b03166126e7565b9050610ab081866159b0565b94505050610ae3565b610ac383826159b0565b6001830180546001600160801b03928316600160801b0292169190911790555b610b0933846001600160801b0316610af961272e565b6001600160a01b03169190612797565b6040516001600160801b038416815233907fd8138f8a3f377c5259ca548e70e4c2de94f129f5a11036a15b69513cba2b426a9060200160405180910390a250509392505050565b6001600160a01b038083166000908152600a60209081526040808320938516835260089091528120549091906001600160801b031680610bae57506001600160a01b0383166000908152600390910160205260409020549050610c21565b6001600160a01b03841660009081526002830160209081526040808320546003860190925290912054670de0b6b3a764000082610bea8961210c565b6001600160e01b0316610bfd91906159d8565b610c0790856159ef565b610c119190615a0e565b610c1b9190615a30565b93505050505b92915050565b80610c3181612383565b81610c3b816123c4565b610c4361258e565b505050565b610c506127ed565b816000610c5d8282612860565b6001600160a01b0384166000908152600a602052604090206001015463ffffffff80851691161115610cc9576001600160a01b0384166000908152600a602052604090206001810154815467ffffffff00000000191663ffffffff909116600160201b02179055610cff565b6001600160a01b0384166000908152600a60205260409020805467ffffffff000000001916600160201b63ffffffff8616021790555b6001600160a01b0384166000818152600a6020908152604091829020549151600160201b90920463ffffffff1682527f21b2dd8950fc3a17e42d75bdfba3bf13f5a451f2d4b1dab7ab7f8d44f8a06926910160405180910390a250505050565b6000610d69612930565b610d728261298e565b90505b919050565b610d82612a6b565b60016000541415610dd55760405162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c006044820152606401610a38565b6001600090815560075460ff16905b81811015610e2057600081815260096020526040902054610e0e906001600160a01b031684612acb565b80610e1881615a48565b915050610de4565b50506000805550565b610e3161258e565b565b610e3b612bbf565b6001600160a01b0383166000908152600a60205260409020805463ffffffff191663ffffffff8316179055610c438383612c12565b60016000541415610ec35760405162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c006044820152606401610a38565b600160009081555b8151811015610f0957610ef7828281518110610ee957610ee9615a63565b602002602001015133612acb565b80610f0181615a48565b915050610ecb565b505060008055565b600080600080600080600080600080610f298b611515565b50505050509050610f3861258e565b3360009081526008602052604081206003549091906001600160801b031615801590610f70575060028201546001600160801b031615155b15610fa15760035460028301546001600160801b0391821691610f949116856159ef565b610f9e9190615a0e565b90505b6000600e6000336001600160a01b03166001600160a01b031681526020019081526020016000206000600f6000336001600160a01b03166001600160a01b0316815260200190815260200160002060000160009054906101000a90046001600160801b03166001600160801b0316815260200190815260200160002090506000600e6000336001600160a01b03166001600160a01b031681526020019081526020016000206000600f6000336001600160a01b03166001600160a01b0316815260200190815260200160002060000160109054906101000a90046001600160801b03166001600160801b0316815260200190815260200160002090508360020160009054906101000a90046001600160801b03168460000160109054906101000a90046001600160801b03168560010160009054906101000a90046001600160801b03168660010160109054906101000a90046001600160801b0316868660000160009054906101000a90046001600160801b03168760000160109054906101000a90046001600160801b03168760000160009054906101000a90046001600160801b03168860000160109054906101000a90046001600160801b0316886001600160801b03169850876001600160801b03169750866001600160801b03169650856001600160801b03169550836001600160801b03169350826001600160801b03169250816001600160801b03169150806001600160801b031690509d509d509d509d509d509d509d509d509d5050505050509193959799909294969850565b606060006111ee612f50565b856111f881612383565b86611202816123c4565b61120a612fae565b611215888888612fec565b60048990556007805463ffffff00191661010062ffffff8a1602179055909450925061124085613368565b60075460405188815261010090910462ffffff16907f5c09fbfaf801f04455c21a7b761e0ef929b1c114f8e95d5ecc5179e4117416dd9060200160405180910390a2505094509492505050565b6001600160a01b0381166000908152600a6020526040812054610d72904290600160201b900463ffffffff16613373565b6112c66127ed565b6001600160a01b0381166000908152600b60205260409020805460ff191660011790556112f281613388565b6001600160a01b03166000908152600a6020526040812081815560010155565b61131a612bbf565b60018054610100600160a81b0319166101006001600160a01b038416908102919091179091556040519081527f595b89b61981754cfd748b6a577e0310ba8a098a483e3e3bac5feab923d7b3f4906020015b60405180910390a150565b8061138181612383565b81610c43816123c4565b611393612bbf565b8261139d816134ac565b6001600160a01b0384166000908152600a60209081526040808320600b9092529091205460ff16156113fa5760405162461bcd60e51b8152600401610a38906020808252600490820152631513d09360e21b604082015260600190565b63ffffffff8416158015906114175750600181015463ffffffff16155b61144c5760405162461bcd60e51b8152600401610a38906020808252600490820152634243464760e01b604082015260600190565b600754600560ff909116111561148d5760405162461bcd60e51b8152600401610a38906020808252600490820152630a89a82b60e31b604082015260600190565b6007805460ff908116600090815260096020526040812080546001600160a01b0319166001600160a01b038a16179055825490911691906114cd83615a79565b82546101009290920a60ff81810219909316919092169190910217905550805463ffffffff191663ffffffff8516178155821561150e5761150e8584612c12565b5050505050565b6000806000806000808661152881612383565b87611532816123c4565b6000805b8a5181101561161d577f000000000000000000000000e140bb5f424a53e0687bfc10f6845a5672d7e2426001600160a01b0316634e89a7118c838151811061158057611580615a63565b60200260200101516040518263ffffffff1660e01b81526004016115b391906001600160a01b0391909116815260200190565b6020604051808303816000875af11580156115d2573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906115f69190615a99565b611609906001600160801b031683615a30565b91508061161581615a48565b915050611536565b50600c546001600160801b038082166000908152600d6020526040808220600160801b9485900484168352912060035491549054949e9183169d508083169c5083900482169a5083821699509190920490911695509350505050565b60015461010090046001600160a01b0316331480156116a8575060015461ffff600160a81b9091048116908216105b6116da5760405162461bcd60e51b815260206004820152600360248201526246454560e81b6044820152606401610a38565b6001805461ffff60a81b1916600160a81b61ffff8416908102919091179091556040519081527f11878e6c9d396e93aa9470747ab6ae3baa13958d1df62e803a0b223c652a564b9060200161136c565b6117326127ed565b8161173c816134ac565b82611746816134fb565b61175a6001600160a01b0385163385612797565b50505050565b611768613555565b158061180757507f000000000000000000000000e140bb5f424a53e0687bfc10f6845a5672d7e2426001600160a01b031663194b42f86040518163ffffffff1660e01b8152600401602060405180830381865afa1580156117cd573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906117f19190615ab6565b60075462ffffff91821661010090910490911611155b6118395760405162461bcd60e51b815260206004820152600360248201526229272360e91b6044820152606401610a38565b8161184381612383565b8261184d81613571565b83611857816123c4565b84518410801561191957507f000000000000000000000000dd4051c3571c143b989c3227e8eb50983974835c6001600160a01b031663192434098686815181106118a3576118a3615a63565b60200260200101516040518263ffffffff1660e01b81526004016118d691906001600160a01b0391909116815260200190565b602060405180830381865afa1580156118f3573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906119179190615ad3565b155b61194e5760405162461bcd60e51b8152600401610a38906020808252600490820152632129aa2960e11b604082015260600190565b60006001865161195e91906159d8565b90506000816001600160401b0381111561197a5761197a614f3a565b6040519080825280602002602001820160405280156119a3578160200160208202803683370190505b5090508115611b6f5760005b82811015611a0d578781815181106119c9576119c9615a63565b60200260200101518282815181106119e3576119e3615a63565b6001600160a01b039092166020928302919091019091015280611a0581615a48565b9150506119af565b5081861015611a6357868281518110611a2857611a28615a63565b6020026020010151818781518110611a4257611a42615a63565b60200260200101906001600160a01b031690816001600160a01b0316815250505b6004546000611a7282896135a8565b611a7e906127106159d8565b9050600184118015611a905750600081115b15611b615783881415611aae57611aa782896135c4565b9150611ad7565b6000611aba83866135a8565b9050611ac6838a6135c4565b9250611ad3838a836135e1565b9250505b600080611ae56001876159d8565b905061271060005b82811015611b49576000611b0187836135a8565b905085611b10612710836159ef565b611b1a9190615a0e565b9050611b278583836135e1565b9450611b3381846159d8565b9250508080611b4190615a48565b915050611aed565b50611b558383836135e1565b60045550611b68915050565b6127106004555b5050611b75565b60006004555b611b7e816135fb565b7f7f705c9890e1e97666eb559f0069c51eb9c6d8e37d6628895f3a886cc57b572086888881518110611bb257611bb2615a63565b6020026020010151604051611bda9291909182526001600160a01b0316602082015260400190565b60405180910390a150505050505050565b611bf36127ed565b8051611c06906002906020840190614e31565b507fed41a19b397d8e610d8f9e6e52ab2bef7e51c9977a12cb779c7cd86747f8d5098160405161136c9190614f19565b82611c4081612383565b83611c4a81613571565b84611c54816123c4565b611c5c61258e565b611c653361360a565b6000856001600160801b031611611ca45760405162461bcd60e51b815260206004820152600360248201526204e44560ec1b6044820152606401610a38565b6000611cae61364f565b62ffffff81166000908152600d6020526040812080549293508892909190611ce09084906001600160801b0316615af0565b82546101009290920a6001600160801b03818102199093169183160217909155336000908152600e6020908152604080832062ffffff87168452909152812080548a94509092611d3291859116615af0565b92506101000a8154816001600160801b0302191690836001600160801b03160217905550611d6687878362ffffff166136d3565b611d6f81613368565b611d788161388e565b611d8286866138a7565b611d8b86613979565b6040516001600160801b038716815262ffffff82169033907f90890809c654f11d6e72a28fa60149770a0d11ec6c92319d6ceb2bb0a4ea1a159060200160405180910390a350505050505050565b611de1612bbf565b80611deb816134fb565b816000611df88282612860565b61175a84613388565b6001600160a01b0381166000908152600a602052604081208054611e3c9063ffffffff811690600160401b90046001600160c01b03166159ef565b9392505050565b611e4b613a10565b83611e5581612383565b84611e5f816123c4565b611e67612fae565b611e6f61258e565b611e783361360a565b611e828585613ac5565b94506000611e8f86613db5565b600380549192508791600090611eaf9084906001600160801b03166159b0565b92506101000a8154816001600160801b0302191690836001600160801b0316021790555060007f000000000000000000000000e140bb5f424a53e0687bfc10f6845a5672d7e2426001600160a01b031663db6b687189846040518363ffffffff1660e01b8152600401611f23929190615b54565b6000604051808303816000875af1158015611f42573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052611f6a9190810190615b67565b90506000611f7788613dea565b60405163e5c8848560e01b81529091506001600160a01b037f0000000000000000000000004788c0a425abb27721e05a39c417b69cba6b065a169063e5c8848590611fce908c908690869033908d90600401615ca1565b600060405180830381600087803b158015611fe857600080fd5b505af1158015611ffc573d6000803e3d6000fd5b50506040516001600160801b038b1681523392507ff81ecc4c10049fe8abde0a7afff608b6ac1d4cf5f6d58673e466a418b47dbf57915060200160405180910390a2505050505050505050565b60015460ff16156120855760405162461bcd60e51b8152600401610a38906020808252600490820152631052539560e21b604082015260600190565b60208082015160018054604085015161ffff16600160a81b0261ffff60a81b196001600160a01b039094166101000293909316610100600160b81b031990911617919091179055815180516120de926002920190614e31565b506120ec8160800151613f71565b60045560608101516120fd906135fb565b506001805460ff191681179055565b6001600160a01b0381166000908152600a60205260408120600354600160801b90046001600160801b03166121555760010154600160201b90046001600160e01b031692915050565b600181015460009063ffffffff1661216c8561128d565b6121769190615da0565b63ffffffff1690508061219e575060010154600160201b90046001600160e01b031692915050565b600354825461220091600160801b90046001600160801b0316906121d290600160401b90046001600160c01b0316846159ef565b6121dc9190615a0e565b60018401546121fb9190600160201b90046001600160e01b0316615a30565b613fc9565b949350505050565b8261221281612383565b8361221c816123c4565b612224612fae565b61222c61258e565b6122353361360a565b61223f8484613ac5565b9350600061224b61364f565b336000908152600e6020908152604080832062ffffff8516845290915290208054919250869160109061228f908490600160801b90046001600160801b0316615af0565b92506101000a8154816001600160801b0302191690836001600160801b0316021790555084600d60008362ffffff16815260200190815260200160002060000160108282829054906101000a90046001600160801b03166122f09190615af0565b92506101000a8154816001600160801b0302191690836001600160801b0316021790555061232486868362ffffff16614036565b61232d81613368565b6123368161388e565b6040516001600160801b038616815262ffffff82169033907ff279e6a1f5e320cca91135676d9cb6e44ca8a08c0b88342bcdb1144f6511b5689060200160405180910390a3505050505050565b61238f81600654614115565b6123c15760405162461bcd60e51b81526020600482015260036024820152620aca6960eb1b6044820152606401610a38565b50565b7f000000000000000000000000dd4051c3571c143b989c3227e8eb50983974835c6001600160a01b0316634c8900606040518163ffffffff1660e01b815260040160006040518083038186803b15801561241d57600080fd5b505afa158015612431573d6000803e3d6000fd5b505060408051808201909152600c546001600160801b03808216808452600160801b90920416602083015290925015905061258a57600081600001516001600160801b0316905060007f000000000000000000000000e140bb5f424a53e0687bfc10f6845a5672d7e2426001600160a01b031663194b42f86040518163ffffffff1660e01b8152600401602060405180830381865afa1580156124d8573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906124fc9190615ab6565b62ffffff16905080821161175a576125148285614129565b6000835260208301516001600160801b0316156125695760208301516001600160801b031681811161254f5761254a8186614129565b612560565b60208401516001600160801b031684525b50600060208401525b825160208401516001600160801b03908116600160801b02911617600c5550505b5050565b604080518082018252600c546001600160801b038082168352600160801b918290048116602080850191909152336000908152600f82528590208551808701909652548083168087529390049091169084015290919015801590612612575081516001600160801b031615806126125750815181516001600160801b039182169116105b1561258a57805161262d906001600160801b03166001614590565b6000815260208101516001600160801b0316156126b25760208201516001600160801b03161580612677575081600001516001600160801b031681602001516001600160801b0316105b156126995761269481602001516001600160801b03166000614590565b6126aa565b60208101516001600160801b031681525b600060208201525b336000908152600f602090815260409091208251918301516001600160801b03908116600160801b0292169190911790555050565b60006126fa6126f58361298e565b6148a6565b9050610d757f0000000000000000000000003d3d0a7876d18770a21a5ea05fef211eba808e72826001600160801b0316610af95b6000306001600160a01b0316636f307dc36040518163ffffffff1660e01b8152600401602060405180830381865afa15801561276e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906127929190615dbd565b905090565b610c438363a9059cbb60e01b84846040516024016127b6929190615dda565b60408051601f198184030181529190526020810180516001600160e01b03166001600160e01b0319909316929092179091526148cf565b6127f56149a1565b610e315760405162461bcd60e51b815260206004820152603660248201527f53706f6f6c4f776e61626c653a3a6f6e6c794f776e65723a2043616c6c65722060448201527534b9903737ba103a34329029b837b7b61037bbb732b960511b6064820152608401610a38565b6001600160a01b0382166000908152600a602052604090206128818361210c565b8160010160046101000a8154816001600160e01b0302191690836001600160e01b031602179055506128b28361128d565b60018201805463ffffffff191663ffffffff929092169190911790556001600160a01b03821615610c43576128e78383610b50565b6001600160a01b0392909216600090815260038201602090815260408083209490945560018301546002909301905291909120600160201b9091046001600160e01b0316905550565b7f0000000000000000000000004788c0a425abb27721e05a39c417b69cba6b065a6001600160a01b03163314610e315760405162461bcd60e51b81526020600482015260036024820152624f465760e81b6044820152606401610a38565b60007f0000000000000000000000003d3d0a7876d18770a21a5ea05fef211eba808e726001600160a01b0316634b6229016129c761272e565b846129d0614a2c565b60015460405160e086901b6001600160e01b03191681526001600160a01b0394851660048201526024810193909352908316604483015261010081049092166064820152600160a81b90910461ffff16608482015260a4016020604051808303816000875af1158015612a47573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610d729190615df3565b336001600160a01b037f000000000000000000000000dd4051c3571c143b989c3227e8eb50983974835c1614610e315760405162461bcd60e51b815260206004820152600560248201526413d0d5149360da1b6044820152606401610a38565b8181612ad78282612860565b6001600160a01b0384166000908152600a60205260409020805463ffffffff16612b295760405162461bcd60e51b815260206004820152600360248201526242544b60e81b6044820152606401610a38565b6001600160a01b03841660009081526003820160205260409020548015612bb7576001600160a01b038086166000908152600384016020526040812055612b739087168683612797565b846001600160a01b03167f540798df468d7b23d11f156fdb954cb19ad414d150722a7b6d55ba369dea792e8783604051612bae929190615dda565b60405180910390a25b505050505050565b60015461010090046001600160a01b0316331480612be05750612be06149a1565b610e315760405162461bcd60e51b815260206004820152600360248201526213d3d160ea1b6044820152606401610a38565b816000612c1f8282612860565b6001600160a01b0384166000908152600a602052604090206001600160c01b03612c51670de0b6b3a7640000866159ef565b6001830154612c709190600160201b90046001600160e01b0316615a30565b1115612ca45760405162461bcd60e51b8152602060048201526003602482015262292a2160e91b6044820152606401610a38565b612cb96001600160a01b038616333087614a6c565b8054600090612cce9063ffffffff1642615e0c565b8254909150600160201b900463ffffffff164210612d8c578154612d139063ffffffff16612d04670de0b6b3a7640000886159ef565b612d0e9190615a0e565b614aa4565b82546001600160c01b0391909116600160401b026001600160401b038216811784556040805188815263ffffffff928316929093169190911760208301526001600160a01b038816917f6a6f77044107a33658235d41bedbbaf2fe9ccdceb313143c947a5e76e1ec8474910160405180910390a2612f12565b815463ffffffff808316600160201b909204161115612dd35760405162461bcd60e51b815260206004820152600360248201526250465360e81b6044820152606401610a38565b8154600090612df0904290600160201b900463ffffffff166159d8565b8354909150600090612e1290600160401b90046001600160c01b0316836159ef565b8454909150600090612e419063ffffffff1683612e37670de0b6b3a76400008c6159ef565b612d049190615a30565b85549091506001600160c01b03600160401b90910481169082161015612e8f5760405162461bcd60e51b815260206004820152600360248201526226292960e91b6044820152606401610a38565b84546001600160c01b038216600160401b026001600160401b03821681178755604080518b81526020810186905263ffffffff9283169383169390931790830152851660608201526001600160a01b038a16907e4f7db2945633cb30dd3539fd30caced03aa57f25ff7039750216e9321b94be9060800160405180910390a25050505b60018201805463ffffffff42811663ffffffff199092169190911790915582549116600160201b0267ffffffff000000001990911617905550505050565b7f000000000000000000000000e140bb5f424a53e0687bfc10f6845a5672d7e2426001600160a01b03163314610e315760405162461bcd60e51b815260206004820152600360248201526204f53560ec1b6044820152606401610a38565b612fb6613555565b15610e315760405162461bcd60e51b8152600401610a38906020808252600490820152631394915160e21b604082015260600190565b606060008085516001600160401b0381111561300a5761300a614f3a565b604051908082528060200260200182016040528015613033578160200160208202803683370190505b509050600086516001600160401b0381111561305157613051614f3a565b60405190808252806020026020018201604052801561307a578160200160208202803683370190505b50905060008061308a8988614acd565b91509150600081116130c45760405162461bcd60e51b815260206004820152600360248201526213955360ea1b6044820152606401610a38565b600080808080805b8e5181101561322d5760006130e18f836135a8565b90506130ed8188615a30565b96506000808a848151811061310457613104615a63565b6020026020010151111561314757886127108b858151811061312857613128615a63565b602002602001015161313a91906159ef565b6131449190615a0e565b90505b818111156131be57600061315b83836159d8565b9050600a81101561316e5750505061321b565b6000826131886c0c9f2c9cd04674edea40000000846159ef565b6131929190615a0e565b9050808d86815181106131a7576131a7615a63565b602002602001018181525050600196505050613218565b818110156132185760006131d282846159d8565b9050600a8110156131e55750505061321b565b808d85815181106131f8576131f8615a63565b602090810291909101015261320d8189615a30565b975083965060019450505b50505b8061322581615a48565b9150506130cc565b5061271085146132655760405162461bcd60e51b815260206004820152600360248201526204250560ec1b6044820152606401610a38565b81801561326f5750805b6132a15760405162461bcd60e51b815260206004820152600360248201526213949160ea1b6044820152606401610a38565b505060008060005b8381116133355760008a82815181106132c4576132c4615a63565b60200260200101511115613323576000856127108c84815181106132ea576132ea615a63565b60200260200101516132fc91906159ef565b6133069190615a0e565b90506133138483836135e1565b935061331f8184615a30565b9250505b8061332d81615a48565b9150506132a9565b5061334e83613346836127106159d8565b8491906135e1565b6005819055979e979d50969b505050505050505050505050565b6123c1600c82614c23565b60008183116133825782611e3c565b50919050565b60075460ff1660005b81811015610c43576000818152600960205260409020546001600160a01b038481169116141561349a57600960006133ca6001856159d8565b8152602080820192909252604090810160009081205484825260099384905291812080546001600160a01b0319166001600160a01b03909316929092179091556134156001856159d8565b81526020810191909152604001600090812080546001600160a01b03191690556007805460ff169161344683615e2b565b91906101000a81548160ff021916908360ff16021790555050826001600160a01b03167f755c47ac85b75fe2251607db5a480aac818b88bb535814bf1e3c4784ae4f6baa60405160405180910390a2505050565b806134a481615a48565b915050613391565b6134b461272e565b6001600160a01b0316816001600160a01b031614156123c15760405162461bcd60e51b815260206004820152600360248201526213955560ea1b6044820152606401610a38565b6001600160a01b0381166000908152600a6020526040902054600160201b900463ffffffff1642116123c15760405162461bcd60e51b815260206004820152600360248201526229272360e91b6044820152606401610a38565b600754600090610100900462ffffff161561356e575060015b90565b60008151116123c15760405162461bcd60e51b81526020600482015260036024820152621394d560ea1b6044820152606401610a38565b60006135b582600e6159ef565b83901c613fff16905092915050565b60006135d182600e6159ef565b613fff901b198316905092915050565b60006135ee83600e6159ef565b6122009083901b85615a30565b61360481614c83565b60065550565b60075460ff1660005b81811015610c435760008181526009602052604090205461363d906001600160a01b031684612860565b8061364781615a48565b915050613613565b60007f000000000000000000000000e140bb5f424a53e0687bfc10f6845a5672d7e2426001600160a01b03166359b4ad5b6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156136af573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906127929190615ab6565b825182906000906136e6906001906159d8565b60045490915060005b828110156137db57600061370d8383896001600160801b0316614cb3565b90506001600160801b038116156137c8577f000000000000000000000000e140bb5f424a53e0687bfc10f6845a5672d7e2426001600160a01b03166360fb0f5989848151811061375f5761375f615a63565b602002602001015183896040518463ffffffff1660e01b815260040161378793929190615e48565b600060405180830381600087803b1580156137a157600080fd5b505af11580156137b5573d6000803e3d6000fd5b5050505080856137c591906159b0565b94505b50806137d381615a48565b9150506136ef565b506001600160801b03831615612bb7577f000000000000000000000000e140bb5f424a53e0687bfc10f6845a5672d7e2426001600160a01b03166360fb0f5987848151811061382c5761382c615a63565b602002602001015185876040518463ffffffff1660e01b815260040161385493929190615e48565b600060405180830381600087803b15801561386e57600080fd5b505af1158015613882573d6000803e3d6000fd5b50505050505050505050565b336000908152600f602052604090206123c19082614c23565b80156138f55761258a337f000000000000000000000000e140bb5f424a53e0687bfc10f6845a5672d7e242846001600160801b03166138e461272e565b6001600160a01b0316929190614a6c565b6040516320bae68360e21b81523360048201526001600160801b03831660248201527f000000000000000000000000dd4051c3571c143b989c3227e8eb50983974835c6001600160a01b0316906382eb9a0c90604401600060405180830381600087803b15801561396557600080fd5b505af1158015612bb7573d6000803e3d6000fd5b33600090815260086020526040812080548392906139a19084906001600160801b0316615af0565b92506101000a8154816001600160801b0302191690836001600160801b0316021790555080600360108282829054906101000a90046001600160801b03166139e99190615af0565b92506101000a8154816001600160801b0302191690836001600160801b0316021790555050565b7f000000000000000000000000e140bb5f424a53e0687bfc10f6845a5672d7e2426001600160a01b0316636bbbfc626040518163ffffffff1660e01b8152600401602060405180830381865afa158015613a6e573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613a929190615ad3565b15610e315760405162461bcd60e51b81526020600482015260036024820152622726a960e91b6044820152606401610a38565b33600090815260086020908152604080832060028101548154600f85528386208451808601909552546001600160801b03818116808752600160801b90920481169686019690965292949182169391169115613b9a57336000908152600e6020908152604080832084516001600160801b039081168552925290912054613b4d9116836159b0565b60208201519092506001600160801b031615613b9a57336000908152600e60209081526040808320848301516001600160801b039081168552925290912054613b979116836159b0565b91505b8580613bcb57506000836001600160801b0316118015613bcb5750866001600160801b0316836001600160801b0316145b15613c7f576002840180546001600160801b0319169055600380549397508793839190601090613c0c908490600160801b90046001600160801b03166159b0565b92506101000a8154816001600160801b0302191690836001600160801b03160217905550818460000160008282829054906101000a90046001600160801b0316613c5691906159b0565b92506101000a8154816001600160801b0302191690836001600160801b03160217905550613daa565b866001600160801b0316836001600160801b031610158015613caa57506000876001600160801b0316115b613cdc5760405162461bcd60e51b81526020600482015260036024820152620aea6960eb1b6044820152606401610a38565b6000613ce9838986614cda565b905080600360108282829054906101000a90046001600160801b0316613d0f91906159b0565b92506101000a8154816001600160801b0302191690836001600160801b03160217905550808560000160008282829054906101000a90046001600160801b0316613d5991906159b0565b92506101000a8154816001600160801b0302191690836001600160801b031602179055508784038560020160006101000a8154816001600160801b0302191690836001600160801b03160217905550505b509495945050505050565b6003546000906001600160801b0390811690613de09084166c0c9f2c9cd04674edea400000006159ef565b610d729190615a0e565b336000908152600860209081526040808320600f83528184208251808401909352546001600160801b038082168452600160801b9091048116938301939093526002810154909284918291613e4191889116615af0565b83519091506001600160801b031615613ee357336000908152600e6020908152604080832086516001600160801b039081168552925290912054613e8e91600160801b9091041682615af0565b60208401519091506001600160801b031615613ee357336000908152600e60209081526040808320868301516001600160801b039081168552925290912054613ee091600160801b9091041682615af0565b90505b856001600160801b0316816001600160801b03161115613f44578354600160801b90046001600160801b0316613f1a818884614cda565b9250613f2683826159b0565b85546001600160801b03918216600160801b02911617855550613f5f565b83546001600160801b038082168655600160801b9091041691505b506001600160801b0316949350505050565b60008060005b8351811015613fc257613fae81858381518110613f9657613f96615a63565b6020026020010151846135e19092919063ffffffff16565b915080613fba81615a48565b915050613f77565b5092915050565b60006001600160e01b038211156140325760405162461bcd60e51b815260206004820152602760248201527f53616665436173743a2076616c756520646f65736e27742066697420696e20326044820152663234206269747360c81b6064820152608401610a38565b5090565b600061404183613db5565b905060005b845181101561150e577f000000000000000000000000e140bb5f424a53e0687bfc10f6845a5672d7e2426001600160a01b031663b5c5f67286838151811061409057614090615a63565b60209081029190910101516040516001600160e01b031960e084901b1681526001600160a01b0390911660048201526024810185905260448101869052606401600060405180830381600087803b1580156140ea57600080fd5b505af11580156140fe573d6000803e3d6000fd5b50505050808061410d90615a48565b915050614046565b60008161412184614c83565b149392505050565b6003546001600160801b0316600080808061414387614d07565b905080156141dc576005546040516317c78b0960e01b81526001600160a01b037f000000000000000000000000e140bb5f424a53e0687bfc10f6845a5672d7e24216916317c78b099161419c918a918c90600401615e72565b600060405180830381600087803b1580156141b657600080fd5b505af11580156141ca573d6000803e3d6000fd5b50506007805463ffffff001916905550505b60005b86518110156143685760008782815181106141fc576141fc615a63565b602002602001015190506000807f000000000000000000000000e140bb5f424a53e0687bfc10f6845a5672d7e2426001600160a01b0316631e9a6950848d6040518363ffffffff1660e01b8152600401614257929190615dda565b60408051808303816000875af1158015614275573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906142999190615e97565b90925090506142a88289615af0565b97506142b48188615af0565b60405163a2b0de8b60e01b81529097506001600160a01b037f000000000000000000000000e140bb5f424a53e0687bfc10f6845a5672d7e242169063a2b0de8b906143059086908f90600401615dda565b602060405180830381865afa158015614322573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906143469190615a99565b6143509087615af0565b9550505050808061436090615a48565b9150506141df565b506001600160801b038316156143fb576040516341c78ddd60e01b81526001600160801b03841660048201527f000000000000000000000000e140bb5f424a53e0687bfc10f6845a5672d7e2426001600160a01b0316906341c78ddd90602401600060405180830381600087803b1580156143e257600080fd5b505af11580156143f6573d6000803e3d6000fd5b505050505b6000878152600d602052604090205461442490600160801b90046001600160801b0316866159b0565b9450600061443864174876e800600a615ec6565b6001600160801b0316866001600160801b031611158061445f57506001600160801b038316155b156144ce57614471620f424086615ec6565b905064174876e8006001600160801b03871610156144c95764174876e8006144998783615af0565b6001600160801b0316106144c15764174876e8008690038601950164174876e7ff19016144ff565b949094019360005b6144ff565b826001600160801b0316856001600160801b031610156144fc576144f58587878603614cda565b90506144ff565b50845b6145098187615af0565b600380546001600160801b039283166001600160801b03199091161790556040805180820182528383168152868316602080830191825260008d81526010909152838120925191518516600160801b02919094161790555189917f32568d6390d10efe40ae666165d7e927786da1a0bf60ad350fbd637da51dee0791a25050505050505050565b336000908152600860209081526040808320600e83528184208685529092529091208054600160801b90046001600160801b0316801561478f57600085815260106020908152604080832054600d909252822054614606916001600160801b03600160801b918290048116928692900416614cda565b60018501805491925082916000906146289084906001600160801b0316615af0565b92506101000a8154816001600160801b0302191690836001600160801b03160217905550600080838660020160009054906101000a90046001600160801b03166146729190615af0565b905086156146c357336000908152600e60205260408120906146958a6001615a30565b81526020810191909152604001600020546146c090600160801b90046001600160801b031682615af0565b90505b836001600160801b0316816001600160801b03161115614724578554600160801b90046001600160801b03166146fa818684614cda565b925061470683826159b0565b87546001600160801b03918216600160801b0291161787555061473f565b85546001600160801b038082168855600160801b9091041691505b818660010160108282829054906101000a90046001600160801b03166147659190615af0565b82546101009290920a6001600160801b038181021990931691831602179091558654168655505050505b81546001600160801b0316801561487157600086815260106020908152604080832054600d9092528220546147d29184916001600160801b039182169116614cda565b60028601805491925082916000906147f49084906001600160801b0316615af0565b92506101000a8154816001600160801b0302191690836001600160801b03160217905550818560000160108282829054906101000a90046001600160801b031661483e9190615af0565b82546001600160801b039182166101009390930a9283029190920219909116179055505082546001600160801b03191683555b604051869033907f961e41c19e060d605836d2061ee68d182e66923d6c75ab7685e39f63e9f8684d90600090a3505050505050565b60006001600160801b038211156140325760405162461bcd60e51b8152600401610a3890615ef5565b6000614924826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b0316614d269092919063ffffffff16565b805190915015610c4357808060200190518101906149429190615ad3565b610c435760405162461bcd60e51b815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e6044820152691bdd081cdd58d8d9595960b21b6064820152608401610a38565b604051634c24e8cd60e01b81523360048201526000907f0000000000000000000000004f03f70a99e5c3b49d733ddd7458f80fa9b5a5b56001600160a01b031690634c24e8cd90602401602060405180830381865afa158015614a08573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906127929190615ad3565b6000306001600160a01b031663bdff15156040518163ffffffff1660e01b8152600401602060405180830381865afa15801561276e573d6000803e3d6000fd5b6040516001600160a01b038085166024830152831660448201526064810182905261175a9085906323b872dd60e01b906084016127b6565b60006001600160c01b038211156140325760405162461bcd60e51b8152600401610a3890615ef5565b606060008084516001600160401b03811115614aeb57614aeb614f3a565b604051908082528060200260200182016040528015614b14578160200160208202803683370190505b5090506000805b8651811015614c175760007f000000000000000000000000e140bb5f424a53e0687bfc10f6845a5672d7e2426001600160a01b031663a2b0de8b898481518110614b6757614b67615a63565b6020026020010151896040518363ffffffff1660e01b8152600401614b8d929190615dda565b602060405180830381865afa158015614baa573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190614bce9190615a99565b6001600160801b0316905080848381518110614bec57614bec615a63565b6020908102919091010152614c018184615a30565b9250508080614c0f90615a48565b915050614b1b565b50909590945092505050565b81546001600160801b031615614c6a57815462ffffff82166001600160801b03909116101561258a5781546001600160801b031662ffffff91909116600160801b02179055565b81546001600160801b03191662ffffff82161782555050565b600081604051602001614c969190615f3c565b604051602081830303815290604052805190602001209050919050565b600061220061271083614cc687876135a8565b614cd091906159ef565b6126f59190615a0e565b6000612200826001600160801b0316846001600160801b0316866001600160801b0316614cd091906159ef565b600754600090610100900462ffffff16821415610d7557506001919050565b6060612200848460008585843b614d7f5760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152606401610a38565b600080866001600160a01b03168587604051614d9b9190615f7b565b60006040518083038185875af1925050503d8060008114614dd8576040519150601f19603f3d011682016040523d82523d6000602084013e614ddd565b606091505b5091509150614ded828286614df8565b979650505050505050565b60608315614e07575081611e3c565b825115614e175782518084602001fd5b8160405162461bcd60e51b8152600401610a389190614f19565b828054614e3d90615965565b90600052602060002090601f016020900481019282614e5f5760008555614ea5565b82601f10614e7857805160ff1916838001178555614ea5565b82800160010185558215614ea5579182015b82811115614ea5578251825591602001919060010190614e8a565b506140329291505b808211156140325760008155600101614ead565b60005b83811015614edc578181015183820152602001614ec4565b8381111561175a5750506000910152565b60008151808452614f05816020860160208601614ec1565b601f01601f19169290920160200192915050565b602081526000611e3c6020830184614eed565b80151581146123c157600080fd5b634e487b7160e01b600052604160045260246000fd5b604080519081016001600160401b0381118282101715614f7257614f72614f3a565b60405290565b604051606081016001600160401b0381118282101715614f7257614f72614f3a565b60405160a081016001600160401b0381118282101715614f7257614f72614f3a565b604051601f8201601f191681016001600160401b0381118282101715614fe457614fe4614f3a565b604052919050565b60006001600160401b0382111561500557615005614f3a565b5060051b60200190565b6001600160a01b03811681146123c157600080fd5b8035610d758161500f565b600082601f83011261504057600080fd5b8135602061505561505083614fec565b614fbc565b82815260059290921b8401810191818101908684111561507457600080fd5b8286015b8481101561509857803561508b8161500f565b8352918301918301615078565b509695505050505050565b6000806000606084860312156150b857600080fd5b83356150c381614f2c565b925060208401356001600160401b038111156150de57600080fd5b6150ea8682870161502f565b92505060408401356150fb81614f2c565b809150509250925092565b6000806040838503121561511957600080fd5b82356151248161500f565b915060208301356151348161500f565b809150509250929050565b60006020828403121561515157600080fd5b81356001600160401b0381111561516757600080fd5b6122008482850161502f565b803563ffffffff81168114610d7557600080fd5b6000806040838503121561519a57600080fd5b82356151a58161500f565b91506151b360208401615173565b90509250929050565b6000602082840312156151ce57600080fd5b5035919050565b6000602082840312156151e757600080fd5b8135611e3c8161500f565b60008060006060848603121561520757600080fd5b83356152128161500f565b92506020840135915061522760408501615173565b90509250925092565b6000602080838503121561524357600080fd5b82356001600160401b0381111561525957600080fd5b8301601f8101851361526a57600080fd5b803561527861505082614fec565b81815260059190911b8201830190838101908783111561529757600080fd5b928401925b82841015614ded5783356152af8161500f565b8252928401929084019061529c565b62ffffff811681146123c157600080fd5b600080600080608085870312156152e557600080fd5b84356001600160401b038111156152fb57600080fd5b6153078782880161502f565b94505060208501359250604085013591506060850135615326816152be565b939692955090935050565b600081518084526020808501945080840160005b83811015613daa57815187529582019590820190600101615345565b6040815260006153746040830185615331565b90508260208301529392505050565b60008060006060848603121561539857600080fd5b83356153a38161500f565b92506153b160208501615173565b9150604084013590509250925092565b600080604083850312156153d457600080fd5b82356153df8161500f565b946020939093013593505050565b803561ffff81168114610d7557600080fd5b60006020828403121561541157600080fd5b611e3c826153ed565b6000806040838503121561542d57600080fd5b82356001600160401b0381111561544357600080fd5b61544f8582860161502f565b95602094909401359450505050565b60006001600160401b0383111561547757615477614f3a565b61548a601f8401601f1916602001614fbc565b905082815283838301111561549e57600080fd5b828260208301376000602084830101529392505050565b600082601f8301126154c657600080fd5b611e3c8383356020850161545e565b6000602082840312156154e757600080fd5b81356001600160401b038111156154fd57600080fd5b612200848285016154b5565b6001600160801b03811681146123c157600080fd5b60008060006060848603121561553357600080fd5b83356001600160401b0381111561554957600080fd5b6155558682870161502f565b935050602084013561556681615509565b915060408401356150fb81614f2c565b600082601f83011261558757600080fd5b8135602061559761505083614fec565b82815260059290921b840181019181810190868411156155b657600080fd5b8286015b8481101561509857803583529183019183016155ba565b600082601f8301126155e257600080fd5b6155ef6150508335614fec565b82358082526020808301929160051b85010185101561560d57600080fd5b602084015b6020853560051b86010181101561572e576001600160401b03808235111561563957600080fd5b8135860187603f82011261564c57600080fd5b61565c6150506020830135614fec565b602082810135808352908201919060051b83016040018a81111561567f57600080fd5b604084015b8181101561571757858135111561569a57600080fd5b803585016040818e03603f190112156156b257600080fd5b6156ba614f50565b6040820135815287606083013511156156d257600080fd5b6060820135820191508d605f8301126156ea57600080fd5b6156fc8e60408401356060850161545e565b60208201528086525050602084019350602081019050615684565b505086525050602093840193919091019050615612565b50949350505050565b6000806000806080858703121561574d57600080fd5b6001600160401b03808635111561576357600080fd5b615770878735880161502f565b945060208087013561578181615509565b9450604087013561579181614f2c565b93506060870135828111156157a557600080fd5b87016060818a0312156157b757600080fd5b6157bf614f78565b6157c98235614f2c565b8135815282820135848111156157de57600080fd5b8201601f81018b136157ef57600080fd5b80356157fd61505082614fec565b81815260059190911b8201850190858101908d83111561581c57600080fd5b8684015b8381101561585257888135111561583657600080fd5b6158458f898335880101615576565b8352918701918701615820565b50808786015250505050604082013592508383111561587057600080fd5b61587c8a8484016155d1565b6040820152969995985093965050505050565b6000602082840312156158a157600080fd5b81356001600160401b03808211156158b857600080fd5b9083019060a082860312156158cc57600080fd5b6158d4614f9a565b8235828111156158e357600080fd5b6158ef878286016154b5565b8252506158fe60208401615024565b602082015261590f604084016153ed565b604082015260608301358281111561592657600080fd5b6159328782860161502f565b60608301525060808301358281111561594a57600080fd5b61595687828601615576565b60808301525095945050505050565b600181811c9082168061597957607f821691505b6020821081141561338257634e487b7160e01b600052602260045260246000fd5b634e487b7160e01b600052601160045260246000fd5b60006001600160801b03838116908316818110156159d0576159d061599a565b039392505050565b6000828210156159ea576159ea61599a565b500390565b6000816000190483118215151615615a0957615a0961599a565b500290565b600082615a2b57634e487b7160e01b600052601260045260246000fd5b500490565b60008219821115615a4357615a4361599a565b500190565b6000600019821415615a5c57615a5c61599a565b5060010190565b634e487b7160e01b600052603260045260246000fd5b600060ff821660ff811415615a9057615a9061599a565b60010192915050565b600060208284031215615aab57600080fd5b8151611e3c81615509565b600060208284031215615ac857600080fd5b8151611e3c816152be565b600060208284031215615ae557600080fd5b8151611e3c81614f2c565b60006001600160801b03828116848216808303821115615b1257615b1261599a565b01949350505050565b600081518084526020808501945080840160005b83811015613daa5781516001600160a01b031687529582019590820190600101615b2f565b6040815260006153746040830185615b1b565b60006020808385031215615b7a57600080fd5b82516001600160401b03811115615b9057600080fd5b8301601f81018513615ba157600080fd5b8051615baf61505082614fec565b81815260059190911b82018301908381019087831115615bce57600080fd5b928401925b82841015614ded578351615be681615509565b82529284019290840190615bd3565b600081518084526020808501808196506005915083821b81018387016000805b87811015615c92578484038b5282518051808652908801908886019080891b87018a01855b82811015615c7c57888203601f190184528451805183528c015160408d8401819052615c6881850183614eed565b968e0196958e019593505050600101615c3a565b509d8a019d965050509287019250600101615c15565b50919998505050505050505050565b60a081526000615cb460a0830188615b1b565b82810360208481019190915287518083528882019282019060005b81811015615cf45784516001600160801b031683529383019391830191600101615ccf565b505087604086015260018060a01b038716606086015284810360808601526060810192508551151581528186015160608383015283815180865260808401915060808160051b8501019550848301925060005b81811015615d7557607f19858803018352615d63878551615331565b96509285019291850191600101615d47565b5050505060408601519150808303604082015250615d938282615bf5565b9998505050505050505050565b600063ffffffff838116908316818110156159d0576159d061599a565b600060208284031215615dcf57600080fd5b8151611e3c8161500f565b6001600160a01b03929092168252602082015260400190565b600060208284031215615e0557600080fd5b5051919050565b600063ffffffff808316818516808303821115615b1257615b1261599a565b600060ff821680615e3e57615e3e61599a565b6000190192915050565b6001600160a01b039390931683526001600160801b03919091166020830152604082015260600190565b606081526000615e856060830186615b1b565b60208301949094525060400152919050565b60008060408385031215615eaa57600080fd5b8251615eb581615509565b602084015190925061513481615509565b60006001600160801b0382811684821681151582840482111615615eec57615eec61599a565b02949350505050565b60208082526027908201527f53616665436173743a2076616c756520646f65736e27742066697420696e20316040820152663238206269747360c81b606082015260800190565b815160009082906020808601845b83811015615f6f5781516001600160a01b031685529382019390820190600101615f4a565b50929695505050505050565b60008251615f8d818460208701614ec1565b919091019291505056fea26469706673582212207361c12125b82fd6bc8ae7a18788ffa318a06f25efb8bb8d0ec886b1b94a5f6464736f6c634300080b0033

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

000000000000000000000000e140bb5f424a53e0687bfc10f6845a5672d7e242000000000000000000000000dd4051c3571c143b989c3227e8eb50983974835c0000000000000000000000004788c0a425abb27721e05a39c417b69cba6b065a0000000000000000000000003d3d0a7876d18770a21a5ea05fef211eba808e720000000000000000000000004f03f70a99e5c3b49d733ddd7458f80fa9b5a5b5

-----Decoded View---------------
Arg [0] : _spool (address): 0xe140bB5F424A53e0687bfC10F6845a5672D7e242
Arg [1] : _controller (address): 0xdD4051c3571C143b989C3227E8eB50983974835C
Arg [2] : _fastWithdraw (address): 0x4788c0A425Abb27721e05A39C417b69cbA6B065a
Arg [3] : _feeHandler (address): 0x3d3d0A7876D18770a21a5Ea05FeF211EBa808E72
Arg [4] : _spoolOwner (address): 0x4f03f70A99E5c3b49d733dDd7458f80Fa9B5A5B5

-----Encoded View---------------
5 Constructor Arguments found :
Arg [0] : 000000000000000000000000e140bb5f424a53e0687bfc10f6845a5672d7e242
Arg [1] : 000000000000000000000000dd4051c3571c143b989c3227e8eb50983974835c
Arg [2] : 0000000000000000000000004788c0a425abb27721e05a39c417b69cba6b065a
Arg [3] : 0000000000000000000000003d3d0a7876d18770a21a5ea05fef211eba808e72
Arg [4] : 0000000000000000000000004f03f70a99e5c3b49d733ddd7458f80fa9b5a5b5


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

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