S Price: $0.608746 (+2.78%)

Contract

0x19d815667267d46254574E62647c2f500449A387

Overview

S Balance

Sonic LogoSonic LogoSonic Logo0 S

S Value

$0.00

Multichain Info

No addresses found
Transaction Hash
Method
Block
From
To

There are no matching entries

Please try again later

Parent Transaction Hash Block From To
View All Internal Transactions
Loading...
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
OracleRewardVault

Compiler Version
v0.8.10+commit.fc410830

Optimization Enabled:
Yes with 800 runs

Other Settings:
london EvmVersion
File 1 of 36 : OracleRewardVault.sol
// SPDX-License-Identifier: MIT

pragma solidity 0.8.10;

import {Uint256x256Math} from "joe-v2/libraries/math/Uint256x256Math.sol";

import {OracleVault} from "./OracleVault.sol";
import {IStrategy} from "./interfaces/IStrategy.sol";
import {IOracleRewardVault} from "./interfaces/IOracleRewardVault.sol";
import {IVaultFactory} from "./interfaces/IVaultFactory.sol";
import {IAggregatorV3} from "./interfaces/IAggregatorV3.sol";
import {IERC20} from "./interfaces/IHooksRewarder.sol";

import {TokenHelper} from "./libraries/TokenHelper.sol";
import {Precision} from "./libraries/Precision.sol";
/**
 * @title Liquidity Book Oracle Reward Vault contract
 * @author BlueLabs
 * @notice This contract is used to interact with the Liquidity Book Pair contract.
 *
 * This is a slightly modified version of the original source code, in order to receive rewards for LB Hook Farms.
 *
 * WARNING: You should not add this erc20 token to a MasterChef contract when this vault is acting on LBPairs with LB Hook farm rewards.
 * In this case LB Hook rewards will stay in MasterChef and are not claimable by the user.
 * We also don't recommend to use the vault shares for trading e.g. on a DEX.
 *
 * WARNING 2: Rewards will be claimed on transfer, so if you transfer to a contract, make sure the contract can handle the rewards.
 *
 * How it works:
 *  - The strategy detects if lb pair has a farm hook with lb rewards
 *  - acctTokenPerShare is increased when strategety sends reward tokens to the vault (this happens on rebalance)
 *  - user pending rewards: user balance * accTokenPerShare - rewardDebt
 *  - claim rewards: user balance * accTokenPerShare - rewardDebt
 *
 * The two tokens of the pair has to have an oracle.
 * The oracle is used to get the price of the token X in token Y.
 * The price is used to value the balance of the strategy and mint shares accordingly.
 * The immutable data should be encoded as follow:
 * - 0x00: 20 bytes: The address of the LB pair.
 * - 0x14: 20 bytes: The address of the token X.
 * - 0x28: 20 bytes: The address of the token Y.
 * - 0x3C: 1 bytes: The decimals of the token X.
 * - 0x3D: 1 bytes: The decimals of the token Y.
 * - 0x3E: 20 bytes: The address of the oracle of the token X.
 * - 0x52: 20 bytes: The address of the oracle of the token Y.
 */
contract OracleRewardVault is OracleVault, IOracleRewardVault {
    using Uint256x256Math for uint256;
    using Precision for uint256;

    uint256 private constant PRECISION = 1e12;

    /// @notice Grace period time after sequencer is up
    uint256 private constant GRACE_PERIOD_TIME = 3600;

    /// @notice Accumulated reward per share
    uint256 private _accRewardsPerShare;

    /// @notice Accumulated reward per share for extra rewards
    uint256 private _extraRewardsPerShare;

    /// @notice Last reward balance
    uint256 private _lastRewardBalance;

    /// @notice Last extra reward balance
    uint256 private _lastExtraRewardBalance;

    /// @notice Tracks total supply of shares from users (without vauls e.g. on queueWithawal)
    uint256 private _shareTotalSupply;

    /// @notice User data
    mapping(address => User) public _users;




    /**
     * @dev Constructor of the contract.
     * @param factory Address of the factory.
     */
    constructor(IVaultFactory factory) OracleVault(factory) {}


    /**
     * @dev Returns the last reward balance and the last extra reward balance.
     * @return lastRewardBalance The last reward balance.
     * @return lastExtraRewardBalance The last extra reward balance.
     */
    function getLastRewardBalances() external view returns (
        uint256 lastRewardBalance, 
        uint256 lastExtraRewardBalance
    ) {
        return (_lastRewardBalance, _lastExtraRewardBalance);
    }

    function getUserInfo(address user) external view override returns (User memory) {
        return _users[user];
    }

    /**
     * @dev Returns pending rewards for user
     * @param user user
     * @return rewards rewards
     * @return extraRewards extra rewards
     */
    function getPendingRewards(address user) external view override returns (uint256 rewards, uint256 extraRewards) {
        User storage userData = _users[user];

        // calculate rewards (if there is no lb hook rewarders this will be always 0)
        uint256 accRewardsPerShare = _accRewardsPerShare;
        uint256 rewardBalance = TokenHelper.safeBalanceOf(getStrategy().getRewardToken(), address(this));
        uint256 lastRewardBalance = _lastRewardBalance;

        if (lastRewardBalance != rewardBalance && _shareTotalSupply > 0) {
            uint256 rewardDiff = rewardBalance - lastRewardBalance;
            accRewardsPerShare = accRewardsPerShare + rewardDiff.shiftPrecision() / _shareTotalSupply;
        }
        rewards =
            userData.amount > 0 ? (userData.amount * accRewardsPerShare).unshiftPrecision() - userData.rewardDebt : 0;

        // calculate extra rewards (if there is no lb hook rewarders this will be always 0)
        uint256 accExtraRewardsPerShare = _extraRewardsPerShare;
        uint256 extraRewardBalance = TokenHelper.safeBalanceOf(getStrategy().getExtraRewardToken(), address(this));
        uint256 lastExtraRewardBalance = _lastExtraRewardBalance;

        if (lastExtraRewardBalance != extraRewardBalance && _shareTotalSupply > 0) {
            uint256 extraRewardDiff = extraRewardBalance - lastExtraRewardBalance;
            accExtraRewardsPerShare = accExtraRewardsPerShare + extraRewardDiff.shiftPrecision() / _shareTotalSupply;
        }
        extraRewards = userData.amount > 0
            ? (userData.amount * accExtraRewardsPerShare).unshiftPrecision() - userData.extraRewardDebt
            : 0;
    }


    function _updatePool() internal override {
        if (address(getStrategy()) == address(0)) {
            return;
        }

        if (getStrategy().hasRewards()) {
            uint256 accRewardsPerShare = _accRewardsPerShare;
            uint256 rewardBalance = TokenHelper.safeBalanceOf(getStrategy().getRewardToken(), address(this));
            uint256 lastRewardBalance = _lastRewardBalance;

            // recompute accRewardsPerShare if not up to date
            if (lastRewardBalance != rewardBalance && _shareTotalSupply > 0) {
                uint256 accruedReward = rewardBalance - lastRewardBalance;
                uint256 calcAccRewardsPerShare =
                    accRewardsPerShare + ((accruedReward.shiftPrecision()) / _shareTotalSupply);

                _accRewardsPerShare = calcAccRewardsPerShare;
                _lastRewardBalance = rewardBalance;

                emit PoolUpdated(block.timestamp, calcAccRewardsPerShare);
            }
        }

        // checki if extra rewards
        // make sure the extra reward token is not the same as the reward token
        if (getStrategy().hasExtraRewards() && getStrategy().getExtraRewardToken() != getStrategy().getRewardToken()) {
            uint256 accExtraRewardsPerShare = _extraRewardsPerShare;
            uint256 extraRewardBalance = TokenHelper.safeBalanceOf(getStrategy().getExtraRewardToken(), address(this));
            uint256 lastExtraRewardBalance = _lastExtraRewardBalance;

            // recompute accRewardsPerShare if not up to date
            if (lastExtraRewardBalance == extraRewardBalance || _shareTotalSupply == 0) {
                return;
            }

            uint256 accruedExtraReward = extraRewardBalance - lastExtraRewardBalance;
            uint256 calcAccExtraRewardsPerShare =
                accExtraRewardsPerShare + ((accruedExtraReward.shiftPrecision()) / _shareTotalSupply);

            _extraRewardsPerShare = calcAccExtraRewardsPerShare;
            _lastExtraRewardBalance = extraRewardBalance;

            emit PoolUpdated(block.timestamp, calcAccExtraRewardsPerShare);
        }
    }

    /**
     * @dev will be called on base vault deposit and withdrawal
     * Update pool must be is called before.
     * @param user user
     * @param amount amount
     */
    function _modifyUser(address user, int256 amount) internal virtual override {
        User storage userData = _users[user];

        uint256 uAmount = uint256(amount < 0 ? -amount : amount); // cast to uint256

        // we claim rewards on deposit, withdrawal and harvest
        if (amount > 0) {
            // deposit
            _harvest(user);

            userData.amount = userData.amount + uAmount;

            _shareTotalSupply = _shareTotalSupply + uAmount;

            _updateUserDebt(userData);
        } else if (amount < 0) {
            // withdrawal

            _harvest(user);

            userData.amount = userData.amount - uAmount;

            _shareTotalSupply = _shareTotalSupply - uAmount;

            _updateUserDebt(userData);
        } else {
            // harvest
            _harvest(user);
            _updateUserDebt(userData);
        }
    }

    function _updateUserDebt(User storage userData) internal {
        userData.rewardDebt = (userData.amount * _accRewardsPerShare).unshiftPrecision(); // / PRECISION;
        userData.extraRewardDebt = (userData.amount * _extraRewardsPerShare).unshiftPrecision(); // / PRECISION;
    }


    function _harvest(address user) internal {
        if (address(getStrategy()) == address(0)) {
            return;
        }

        User storage userData = _users[user];
        uint256 pending = ((userData.amount * _accRewardsPerShare).unshiftPrecision()) - userData.rewardDebt;
        uint256 extraPending = ((userData.amount * _extraRewardsPerShare).unshiftPrecision()) - userData.extraRewardDebt;

        _safeRewardTransfer(getStrategy().getRewardToken(), user, pending, false);
        _safeRewardTransfer(getStrategy().getExtraRewardToken(), user, extraPending, true);
    }

    function _safeRewardTransfer(IERC20 token, address to, uint256 amount, bool isExtra) internal {
        if (amount == 0) return;

        uint256 balance = TokenHelper.safeBalanceOf(token, address(this));

        uint256 rewardPayout = amount;

        if (amount > balance) {
            rewardPayout = balance;
        }

        if (isExtra) {
            _lastExtraRewardBalance = _lastExtraRewardBalance - rewardPayout;
        } else {
            _lastRewardBalance = _lastRewardBalance - rewardPayout;
        }

        // transfer token
        TokenHelper.safeTransfer(token, to, rewardPayout);
    }

    /**
     * @dev Claim rewards of the sender.
     */
    function claim() external override nonReentrant {
        _updatePool();
        _modifyUser(msg.sender, 0);
    }

    /**
     * @dev claim rewards of sender before transfering it to recipient
     */
    function _transfer(address sender, address recipient, uint256 amount) internal override {
        if (!_isIgnored(recipient) && !_isIgnored(sender)) {
            _updatePool();
            _modifyUser(sender, -int256(amount));
            _modifyUser(recipient, int256(amount));
        }
        super._transfer(sender, recipient, amount);
    }

    /**
     * Check if address is ingored for rewards (e.g strategy, or other addresses)
     * @param _address address
     */
    function _isIgnored(address _address) internal view returns (bool) {
        if (_address == address(getStrategy())) {
            return true;
        }
        return getFactory().isTransferIgnored(_address);
    }

    function _beforeEmergencyMode() internal virtual override {
        // nothing
    }
}

File 2 of 36 : Uint256x256Math.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.10;

/**
 * @title Liquidity Book Uint256x256 Math Library
 * @author Trader Joe
 * @notice Helper contract used for full precision calculations
 */
library Uint256x256Math {
    error Uint256x256Math__MulShiftOverflow();
    error Uint256x256Math__MulDivOverflow();

    /**
     * @notice Calculates floor(x*y/denominator) with full precision
     * The result will be rounded down
     * @dev Credit to Remco Bloemen under MIT license https://xn--2-umb.com/21/muldiv
     * Requirements:
     * - The denominator cannot be zero
     * - The result must fit within uint256
     * Caveats:
     * - This function does not work with fixed-point numbers
     * @param x The multiplicand as an uint256
     * @param y The multiplier as an uint256
     * @param denominator The divisor as an uint256
     * @return result The result as an uint256
     */
    function mulDivRoundDown(uint256 x, uint256 y, uint256 denominator) internal pure returns (uint256 result) {
        (uint256 prod0, uint256 prod1) = _getMulProds(x, y);

        return _getEndOfDivRoundDown(x, y, denominator, prod0, prod1);
    }

    /**
     * @notice Calculates ceil(x*y/denominator) with full precision
     * The result will be rounded up
     * @dev Credit to Remco Bloemen under MIT license https://xn--2-umb.com/21/muldiv
     * Requirements:
     * - The denominator cannot be zero
     * - The result must fit within uint256
     * Caveats:
     * - This function does not work with fixed-point numbers
     * @param x The multiplicand as an uint256
     * @param y The multiplier as an uint256
     * @param denominator The divisor as an uint256
     * @return result The result as an uint256
     */
    function mulDivRoundUp(uint256 x, uint256 y, uint256 denominator) internal pure returns (uint256 result) {
        result = mulDivRoundDown(x, y, denominator);
        if (mulmod(x, y, denominator) != 0) result += 1;
    }

    /**
     * @notice Calculates floor(x * y / 2**offset) with full precision
     * The result will be rounded down
     * @dev Credit to Remco Bloemen under MIT license https://xn--2-umb.com/21/muldiv
     * Requirements:
     * - The offset needs to be strictly lower than 256
     * - The result must fit within uint256
     * Caveats:
     * - This function does not work with fixed-point numbers
     * @param x The multiplicand as an uint256
     * @param y The multiplier as an uint256
     * @param offset The offset as an uint256, can't be greater than 256
     * @return result The result as an uint256
     */
    function mulShiftRoundDown(uint256 x, uint256 y, uint8 offset) internal pure returns (uint256 result) {
        (uint256 prod0, uint256 prod1) = _getMulProds(x, y);

        if (prod0 != 0) result = prod0 >> offset;
        if (prod1 != 0) {
            // Make sure the result is less than 2^256.
            if (prod1 >= 1 << offset) revert Uint256x256Math__MulShiftOverflow();

            unchecked {
                result += prod1 << (256 - offset);
            }
        }
    }

    /**
     * @notice Calculates floor(x * y / 2**offset) with full precision
     * The result will be rounded down
     * @dev Credit to Remco Bloemen under MIT license https://xn--2-umb.com/21/muldiv
     * Requirements:
     * - The offset needs to be strictly lower than 256
     * - The result must fit within uint256
     * Caveats:
     * - This function does not work with fixed-point numbers
     * @param x The multiplicand as an uint256
     * @param y The multiplier as an uint256
     * @param offset The offset as an uint256, can't be greater than 256
     * @return result The result as an uint256
     */
    function mulShiftRoundUp(uint256 x, uint256 y, uint8 offset) internal pure returns (uint256 result) {
        result = mulShiftRoundDown(x, y, offset);
        if (mulmod(x, y, 1 << offset) != 0) result += 1;
    }

    /**
     * @notice Calculates floor(x << offset / y) with full precision
     * The result will be rounded down
     * @dev Credit to Remco Bloemen under MIT license https://xn--2-umb.com/21/muldiv
     * Requirements:
     * - The offset needs to be strictly lower than 256
     * - The result must fit within uint256
     * Caveats:
     * - This function does not work with fixed-point numbers
     * @param x The multiplicand as an uint256
     * @param offset The number of bit to shift x as an uint256
     * @param denominator The divisor as an uint256
     * @return result The result as an uint256
     */
    function shiftDivRoundDown(uint256 x, uint8 offset, uint256 denominator) internal pure returns (uint256 result) {
        uint256 prod0;
        uint256 prod1;

        prod0 = x << offset; // Least significant 256 bits of the product
        unchecked {
            prod1 = x >> (256 - offset); // Most significant 256 bits of the product
        }

        return _getEndOfDivRoundDown(x, 1 << offset, denominator, prod0, prod1);
    }

    /**
     * @notice Calculates ceil(x << offset / y) with full precision
     * The result will be rounded up
     * @dev Credit to Remco Bloemen under MIT license https://xn--2-umb.com/21/muldiv
     * Requirements:
     * - The offset needs to be strictly lower than 256
     * - The result must fit within uint256
     * Caveats:
     * - This function does not work with fixed-point numbers
     * @param x The multiplicand as an uint256
     * @param offset The number of bit to shift x as an uint256
     * @param denominator The divisor as an uint256
     * @return result The result as an uint256
     */
    function shiftDivRoundUp(uint256 x, uint8 offset, uint256 denominator) internal pure returns (uint256 result) {
        result = shiftDivRoundDown(x, offset, denominator);
        if (mulmod(x, 1 << offset, denominator) != 0) result += 1;
    }

    /**
     * @notice Helper function to return the result of `x * y` as 2 uint256
     * @param x The multiplicand as an uint256
     * @param y The multiplier as an uint256
     * @return prod0 The least significant 256 bits of the product
     * @return prod1 The most significant 256 bits of the product
     */
    function _getMulProds(uint256 x, uint256 y) private pure returns (uint256 prod0, uint256 prod1) {
        // 512-bit multiply [prod1 prod0] = x * y. Compute the product mod 2^256 and mod 2^256 - 1, then use
        // use the Chinese Remainder Theorem to reconstruct the 512 bit result. The result is stored in two 256
        // variables such that product = prod1 * 2^256 + prod0.
        assembly {
            let mm := mulmod(x, y, not(0))
            prod0 := mul(x, y)
            prod1 := sub(sub(mm, prod0), lt(mm, prod0))
        }
    }

    /**
     * @notice Helper function to return the result of `x * y / denominator` with full precision
     * @param x The multiplicand as an uint256
     * @param y The multiplier as an uint256
     * @param denominator The divisor as an uint256
     * @param prod0 The least significant 256 bits of the product
     * @param prod1 The most significant 256 bits of the product
     * @return result The result as an uint256
     */
    function _getEndOfDivRoundDown(uint256 x, uint256 y, uint256 denominator, uint256 prod0, uint256 prod1)
        private
        pure
        returns (uint256 result)
    {
        // Handle non-overflow cases, 256 by 256 division
        if (prod1 == 0) {
            unchecked {
                result = prod0 / denominator;
            }
        } else {
            // Make sure the result is less than 2^256. Also prevents denominator == 0
            if (prod1 >= denominator) revert Uint256x256Math__MulDivOverflow();

            // Make division exact by subtracting the remainder from [prod1 prod0].
            uint256 remainder;
            assembly {
                // Compute remainder using mulmod.
                remainder := mulmod(x, y, denominator)

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

            // Factor powers of two out of denominator and compute largest power of two divisor of denominator. Always >= 1
            // See https://cs.stackexchange.com/q/138556/92363
            unchecked {
                // Does not overflow because the denominator cannot be zero at this stage in the function
                uint256 lpotdod = denominator & (~denominator + 1);
                assembly {
                    // Divide denominator by lpotdod.
                    denominator := div(denominator, lpotdod)

                    // Divide [prod1 prod0] by lpotdod.
                    prod0 := div(prod0, lpotdod)

                    // Flip lpotdod such that it is 2^256 / lpotdod. If lpotdod is zero, then it becomes one
                    lpotdod := add(div(sub(0, lpotdod), lpotdod), 1)
                }

                // Shift in bits from prod1 into prod0
                prod0 |= prod1 * lpotdod;

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

                // Use the Newton-Raphson iteration to improve the precision. Thanks to Hensel's lifting lemma, this also works
                // in modular arithmetic, doubling the correct bits in each step
                inverse *= 2 - denominator * inverse; // inverse mod 2^8
                inverse *= 2 - denominator * inverse; // inverse mod 2^16
                inverse *= 2 - denominator * inverse; // inverse mod 2^32
                inverse *= 2 - denominator * inverse; // inverse mod 2^64
                inverse *= 2 - denominator * inverse; // inverse mod 2^128
                inverse *= 2 - denominator * inverse; // inverse mod 2^256

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

File 3 of 36 : OracleVault.sol
// SPDX-License-Identifier: MIT

pragma solidity 0.8.10;

import {Uint256x256Math} from "joe-v2/libraries/math/Uint256x256Math.sol";

import {BaseVault} from "./BaseVault.sol";
import {IStrategy} from "./interfaces/IStrategy.sol";
import {IOracleVault} from "./interfaces/IOracleVault.sol";
import {IVaultFactory} from "./interfaces/IVaultFactory.sol";
import {IAggregatorV3} from "./interfaces/IAggregatorV3.sol";

/**
 * @title Liquidity Book Oracle Vault contract
 * @author Trader Joe
 * @notice This contract is used to interact with the Liquidity Book Pair contract.
 * The two tokens of the pair has to have an oracle.
 * The oracle is used to get the price of the token X in token Y.
 * The price is used to value the balance of the strategy and mint shares accordingly.
 * The immutable data should be encoded as follow:
 * - 0x00: 20 bytes: The address of the LB pair.
 * - 0x14: 20 bytes: The address of the token X.
 * - 0x28: 20 bytes: The address of the token Y.
 * - 0x3C: 1 bytes: The decimals of the token X.
 * - 0x3D: 1 bytes: The decimals of the token Y.
 * - 0x3E: 20 bytes: The address of the oracle of the token X.
 * - 0x52: 20 bytes: The address of the oracle of the token Y.
 */
contract OracleVault is BaseVault, IOracleVault {
    using Uint256x256Math for uint256;

    uint8 private constant _PRICE_OFFSET = 128;

    /// @notice Grace period time after sequencer is up
    uint256 private constant GRACE_PERIOD_TIME = 3600;

    /// @notice Sequencer Uptime Feed
    IAggregatorV3 private _sequencerUptimeFeed;

    /// @notice Oracle min price
    uint256 private _oracleMinPrice;

    /// @notice Oracle max price
    uint256 private _oracleMaxPrice;

    /// @notice Oracle heartbeat for datafeedX, e.g. 24 hours
    uint24 private _dataFeedHeartbeatX;

    /// @notice Oracle heartbeat for datafeedY, e.g. 24 hours
    uint24 private _dataFeedHeartbeatY;

    /// @notice Whether TWAP price checking is enabled
    bool private _twapPriceCheckEnabled;

    /// @notice TWAP interval for price checking
    uint40 private _twapInterval;

    /// @notice Maximum allowed deviation between spot and TWAP price e.g. 5% (in percentage)
    uint256 private _deviationThreshold;

    /**
     * @dev Constructor of the contract.
     * @param factory Address of the factory.
     */
    constructor(IVaultFactory factory) BaseVault(factory) {}

    /**
     * @dev Returns the address of the oracle of the token X.
     * @return oracleX The address of the oracle of the token X.
     */
    function getOracleX()
        external
        pure
        override
        returns (IAggregatorV3 oracleX)
    {
        return _dataFeedX();
    }

    /**
     * @dev Returns the address of the oracle of the token Y.
     * @return oracleY The address of the oracle of the token Y.
     */
    function getOracleY()
        external
        pure
        override
        returns (IAggregatorV3 oracleY)
    {
        return _dataFeedY();
    }


    /**
     * @dev Returns the price of token X in token Y, in 128.128 binary fixed point format.
     * @return price The price of token X in token Y in 128.128 binary fixed point format.
     */
    function getPrice() external view override returns (uint256 price) {
        return _getPrice();
    }


    /**
     * @dev Returns the oracle parameters.
     * @return minPrice The minimum price of token X in token Y.
     * @return maxPrice The maximum price of token X in token Y.
     * @return heartbeatX The heartbeat for data feed X.
     * @return heartbeatY The heartbeat for data feed Y.
     * @return deviationThreshold The deviation threshold.
     * @return twapPriceCheckEnabled Whether TWAP price checking is enabled.
     * @return twapInterval The TWAP interval.
     */
    function getOracleParameters() external view returns (
                uint256 minPrice, 
                uint256 maxPrice, 
                uint256 heartbeatX, 
                uint256 heartbeatY,
                uint256 deviationThreshold,
                bool twapPriceCheckEnabled,
                uint40 twapInterval
                ) {
        return (_oracleMinPrice, _oracleMaxPrice, _dataFeedHeartbeatX, _dataFeedHeartbeatY, 
        _deviationThreshold, _twapPriceCheckEnabled, _twapInterval);
    }

    /**
     * @dev Sets the sequencer uptime feed. Can only be called by the factory.
     * @param sequencerUptimeFeed The sequencer uptime feed.
     */
    function setSequenzerUptimeFeed(
        IAggregatorV3 sequencerUptimeFeed
    ) external override onlyFactory {
        _sequencerUptimeFeed = sequencerUptimeFeed;
    }

    /**
     * @dev Sets the min and max price of the oracle
     * @param minPrice  min price
     * @param maxPrice  max price
     */
    function setMinMaxPrice(
        uint256 minPrice,
        uint256 maxPrice
    ) external override onlyFactory {
        if (minPrice > maxPrice) revert OracleVault__InvalidPrice();
        _oracleMinPrice = minPrice;
        _oracleMaxPrice = maxPrice;
    }


    /**
     * @dev Sets the data feed heartbeat for data feed X and Y.
     * @param heartbeatX The heartbeat for data feed X.
     * @param heartbeatY The heartbeat for data feed Y.
     */
    function setDatafeedHeartbeat(
        uint24 heartbeatX,
        uint24 heartbeatY
    ) external override onlyFactory {
        _dataFeedHeartbeatX = heartbeatX;
        _dataFeedHeartbeatY = heartbeatY;
    }

    /**
     * @dev Enables or disables TWAP price checking. Can only be called by the factory.
     * @param enabled Whether to enable TWAP price checking
     */
    function enableTWAPPriceCheck(bool enabled) external override onlyFactory {
        _twapPriceCheckEnabled = enabled;
    }

    /**
     * @dev Updates the TWAP interval for price checking. Can only be called by the factory.
     * @param interval The TWAP interval in seconds
     */
    function setTwapInterval(uint40 interval) external override onlyFactory {
        if (interval == 0) revert OracleVault__InvalidInterval();
        _twapInterval = interval;
    }

    /**
     * @dev Sets the maximum allowed price deviation threshold. Can only be called by the factory.
     * @param threshold The maximum allowed deviation in percentage
     */
    function setDeviationThreshold(uint256 threshold) external override onlyFactory {
        _deviationThreshold = threshold;
    }

    /**
     * @dev Returns the data feed of the token X.
     * @return dataFeedX The data feed of the token X.
     */
    function _dataFeedX() internal pure returns (IAggregatorV3 dataFeedX) {
        return IAggregatorV3(_getArgAddress(62));
    }

    /**
     * @dev Returns the data feed of the token Y.
     * @return dataFeedY The data feed of the token Y.
     */
    function _dataFeedY() internal pure returns (IAggregatorV3 dataFeedY) {
        return IAggregatorV3(_getArgAddress(82));
    }

    /**
     * @dev Returns the price of a token using its oracle.
     * @param dataFeed The data feed of the token.
     * @return uintPrice The oracle latest answer.
     */
    function _getOraclePrice(
        IAggregatorV3 dataFeed
    ) internal view returns (uint256 uintPrice) {
        _checkSequenzerUp();

        (, int256 price, , uint256 updatedAt, ) = dataFeed.latestRoundData();

        uint24 heartbeat = dataFeed == _dataFeedX()
            ? _dataFeedHeartbeatX
            : _dataFeedHeartbeatY;

        if (updatedAt == 0 || updatedAt + heartbeat < block.timestamp) {
            revert OracleVault__StalePrice();
        }

        if (
            uint256(price) < _oracleMinPrice || uint256(price) > _oracleMaxPrice
        ) {
            revert OracleVault__InvalidPrice();
        }

        uintPrice = uint256(price);
    }

    function _checkSequenzerUp() internal view {
        if (address(_sequencerUptimeFeed) == address(0)) {
            return;
        }

        // prettier-ignore
        (
            /*uint80 roundID*/,
            int256 answer,
            uint256 startedAt,
            /*uint256 updatedAt*/,
            /*uint80 answeredInRound*/
        ) = _sequencerUptimeFeed.latestRoundData();

        bool isSequencerUp = answer == 0;
        if (!isSequencerUp) {
            revert OracleVault__SequencerDown();
        }

        uint256 timeSinceUp = block.timestamp - startedAt;
        if (timeSinceUp <= GRACE_PERIOD_TIME) {
            revert OracleVault__GracePeriodNotOver();
        }
    }

    /**
     * @dev Returns the price of token X in token Y.
     * WARNING: Both oracles needs to return the same decimals and use the same quote currency.
     * @return price The price of token X in token Y.
     */
    function _getPrice() internal view returns (uint256 price) {
        uint256 scaledPriceX = _getOraclePrice(_dataFeedX()) *
            10 ** _decimalsY();
        uint256 scaledPriceY = _getOraclePrice(_dataFeedY()) *
            10 ** _decimalsX();

        // Essentially does `price = (priceX / 1eDecimalsX) / (priceY / 1eDecimalsY)`
        // with 128.128 binary fixed point arithmetic.
        price = scaledPriceX.shiftDivRoundDown(_PRICE_OFFSET, scaledPriceY);

        if (price == 0) revert OracleVault__InvalidPrice();
    }

    /**
     * @dev Returns the shares that will be minted when depositing `expectedAmountX` of token X and
     * `expectedAmountY` of token Y. The effective amounts will never be greater than the input amounts.
     * @param strategy The strategy to deposit to.
     * @param amountX The amount of token X to deposit.
     * @param amountY The amount of token Y to deposit.
     * @return shares The amount of shares that will be minted.
     * @return effectiveX The effective amount of token X that will be deposited.
     * @return effectiveY The effective amount of token Y that will be deposited.
     */
    function _previewShares(
        IStrategy strategy,
        uint256 amountX,
        uint256 amountY
    )
        internal
        view
        override
        returns (uint256 shares, uint256 effectiveX, uint256 effectiveY)
    {
        if (amountX == 0 && amountY == 0) return (0, 0, 0);

        // the price is in quoteToken
        uint256 price = _getPrice();

        // check if the price is within the allowed deviation
        _checkPrice(price);

        uint256 totalShares = totalSupply();

        uint256 valueInY = _getValueInY(price, amountX, amountY);

        if (totalShares == 0) {
            return (valueInY * _SHARES_PRECISION, amountX, amountY);
        }

        (uint256 totalX, uint256 totalY) = _getBalances(strategy);
        uint256 totalValueInY = _getValueInY(price, totalX, totalY);

        shares = valueInY.mulDivRoundDown(totalShares, totalValueInY);

        return (shares, amountX, amountY);
    }

    function _checkPrice(uint256 spotPriceInY) internal view {
        if (!_twapPriceCheckEnabled) return;

        uint40 twapStart = uint40(block.timestamp - _twapInterval);
        uint40 twapEnd = uint40(block.timestamp);

        if (twapEnd <= twapStart) revert OracleVault__InvalidTimestamps();

        // Fetch cumulative bin IDs at the specified timestamps
        (uint64 cumulativeId1, , ) = _pair().getOracleSampleAt(twapStart);
        (uint64 cumulativeId2, , ) = _pair().getOracleSampleAt(twapEnd);

        // Calculate the time difference
        uint40 timeElapsed = twapEnd - twapStart;

        // Compute the TWAP bin ID
        uint256 twapBinId = (cumulativeId2 - cumulativeId1) / timeElapsed;

        // Returns priceInY in 128.128 fixed-point format
        uint256 twapPriceInY = _pair().getPriceFromId(uint24(twapBinId));

        // both prices are in tokenY, check deviation
        if (
            spotPriceInY > (twapPriceInY * (100 + _deviationThreshold)) / 100 ||
            spotPriceInY < (twapPriceInY * (100 - _deviationThreshold)) / 100
        ) {
            revert OracleVault__PriceDeviation();
        }
    }

    /**
     * @dev Returns the value of amounts in token Y.
     * @param price The price of token X in token Y.
     * @param amountX The amount of token X.
     * @param amountY The amount of token Y.
     * @return valueInY The value of amounts in token Y.
     */
    function _getValueInY(
        uint256 price,
        uint256 amountX,
        uint256 amountY
    ) internal pure returns (uint256 valueInY) {
        uint256 amountXInY = price.mulShiftRoundDown(amountX, _PRICE_OFFSET);
        return amountXInY + amountY;
    }

    function _updatePool() internal virtual override {
        // nothing
    }

    function _modifyUser(
        address user,
        int256 amount
    ) internal virtual override {
        // nothing
    }

    function _beforeEmergencyMode() internal virtual override {
        // nothing
    }

}

File 4 of 36 : IStrategy.sol
// SPDX-License-Identifier: MIT

pragma solidity 0.8.10;

import {IERC20Upgradeable} from "openzeppelin-upgradeable/token/ERC20/IERC20Upgradeable.sol";
import {ILBPair} from "joe-v2/interfaces/ILBPair.sol";

import {IOneInchRouter} from "./IOneInchRouter.sol";
import {IVaultFactory} from "./IVaultFactory.sol";
import {IERC20} from "./IHooksRewarder.sol";

/**
 * @title Strategy Interface
 * @author Trader Joe
 * @notice Interface used to interact with Liquidity Book Vaults' Strategies
 */
interface IStrategy {
    error Strategy__OnlyFactory();
    error Strategy__OnlyVault();
    error Strategy__OnlyOperators();
    error Strategy__ZeroAmounts();
    error Strategy__InvalidAmount();
    error Strategy__InvalidToken();
    error Strategy__InvalidReceiver();
    error Strategy__InvalidRange();
    error Strategy__InvalidFee();
    error Strategy__ActiveIdSlippage();
    error Strategy__RangeAlreadySet();
    error Strategy__RangeTooWide();
    error Strategy__InvalidLength();

    event OperatorSet(address operator);

    event AumFeeCollected(
        address indexed sender, uint256 totalBalanceX, uint256 totalBalanceY, uint256 feeX, uint256 feeY
    );

    event AumAnnualFeeSet(uint256 fee);

    event PendingAumAnnualFeeSet(uint256 fee);

    event PendingAumAnnualFeeReset();

    event RangeSet(uint24 low, uint24 upper);

    function getFactory() external view returns (IVaultFactory);

    function getVault() external pure returns (address);

    function getPair() external pure returns (ILBPair);

    function getTokenX() external pure returns (IERC20Upgradeable);

    function getTokenY() external pure returns (IERC20Upgradeable);

    function getRange() external view returns (uint24 low, uint24 upper);

    function getAumAnnualFee() external view returns (uint256 aumAnnualFee);

    function getLastRebalance() external view returns (uint256 lastRebalance);

    function getPendingAumAnnualFee() external view returns (bool isSet, uint256 pendingAumAnnualFee);

    function getRewardToken() external view returns (IERC20);

    function getExtraRewardToken() external view returns (IERC20);

    function hasRewards() external view returns (bool);

    function hasExtraRewards() external view returns (bool);

    function getOperator() external view returns (address);

    function getBalances() external view returns (uint256 amountX, uint256 amountY);

    function getIdleBalances() external view returns (uint256 amountX, uint256 amountY);

    function getMaxRange() external view returns (uint256);

    function initialize() external;

    function withdrawAll() external;

    function rebalance(
        uint24 newLower,
        uint24 newUpper,
        uint24 desiredActiveId,
        uint24 slippageActiveId,
        uint256 amountX,
        uint256 amountY,
        bytes calldata distributions
    ) external;

    function swap(address executor, IOneInchRouter.SwapDescription memory desc, bytes memory data) external;

    function setOperator(address operator) external;

    function setPendingAumAnnualFee(uint16 pendingAumAnnualFee) external;

    function resetPendingAumAnnualFee() external;
}

File 5 of 36 : IOracleRewardVault.sol
// SPDX-License-Identifier: MIT

pragma solidity 0.8.10;

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

/**
 * @title Oracle Reard Vault Interface
 * @author BlueLabs
 * @notice Interface used to interact with Liquidity Book Oracle Vaults
 */
interface IOracleRewardVault is IOracleVault {

    struct User {
        uint256 amount;
        uint256 rewardDebt;
        uint256 extraRewardDebt;
    }

    event PoolUpdated(uint256 indexed timestamp, uint256 indexed accRewardShare);

    function getLastRewardBalances() external view returns (uint256 lastRewardBalance, uint256 lastExtraRewardBalance);

    function getUserInfo(address user) external view returns (User memory);

    function getPendingRewards(address _user)
        external
        view
        returns (uint256 pendingRewards, uint256 pendingExtraRewards);


    function claim() external;


}

File 6 of 36 : IVaultFactory.sol
// SPDX-License-Identifier: MIT

pragma solidity 0.8.10;

import {IERC20Upgradeable} from "openzeppelin-upgradeable/token/ERC20/IERC20Upgradeable.sol";
import {ILBPair} from "joe-v2/interfaces/ILBPair.sol";

import {IAggregatorV3} from "./IAggregatorV3.sol";
import {IStrategy} from "./IStrategy.sol";
import {IBaseVault} from "./IBaseVault.sol";

/**
 * @title Vault Factory Interface
 * @author Trader Joe
 * @notice Interface used to interact with the Factory for Liquidity Book Vaults
 */
interface IVaultFactory {
    error VaultFactory__VaultImplementationNotSet(VaultType vType);
    error VaultFactory__StrategyImplementationNotSet(StrategyType sType);
    error VaultFactory__InvalidType();
    error VaultFactory__InvalidOraclePrice();
    error VaultFactory__InvalidStrategy();
    error VaultFactory__InvalidFeeRecipient();
    error VaultFactory__InvalidOwner();
    error VaultFactory__InvalidLength();
    error VaultFactory__InvalidDecimals();
    error VaultFactory__InvalidCreationFee();
    error VaultFactory__InvalidAumFee();
    error VaultFactory__TwapInvalidOracleSize();
    error VaultFactory__VaultNotWhitelisted();

    enum VaultType {
        None,
        Simple,
        Oracle
    }

    enum StrategyType {
        None,
        Default
    }

    struct RebalanceSetting {
        uint24 newLower;
        uint24 newUpper;
        uint24 desiredActiveId;
        uint24 slippageActiveId;
        uint256 amountX;
        uint256 amountY;
        bytes distributions;
    }

    struct MakerVault {
        address vault;
        address operator;
    }

    event VaultCreated(
        VaultType indexed vType,
        address indexed vault,
        ILBPair indexed lbPair,
        uint256 vaultIndex,
        address tokenX,
        address tokenY
    );

    event StrategyCreated(
        StrategyType indexed sType,
        address indexed strategy,
        address indexed vault,
        ILBPair lbPair,
        uint256 strategyIndex
    );

    event TransferIgnoreListSet(address[] addresses);

    event VaultImplementationSet(VaultType indexed vType, address indexed vaultImplementation);

    event StrategyImplementationSet(StrategyType indexed sType, address indexed strategyImplementation);

    event DefaultOperatorSet(address indexed sender, address indexed defaultOperator);

    event FeeRecipientSet(address indexed sender, address indexed feeRecipient);

    event RebalanceSettingSet(address indexed vault, address indexed user);

    event DeviationThresholdUpdated(address indexed vault, uint256 threshold);

    event PairWhitelistSet(address[] pairs, bool isWhitelisted);

    function getWNative() external view returns (address);

    function getVaultAt(VaultType vType, uint256 index) external view returns (address);

    function getVaultType(address vault) external view returns (VaultType);

    function getStrategyAt(StrategyType sType, uint256 index) external view returns (address);

    function getStrategyType(address strategy) external view returns (StrategyType);

    function getNumberOfVaults(VaultType vType) external view returns (uint256);

    function getNumberOfStrategies(StrategyType sType) external view returns (uint256);

    function getDefaultOperator() external view returns (address);

    function getFeeRecipient() external view returns (address);

    function getFeeRecipientByVault(address vault) external view returns (address);

    function getVaultImplementation(VaultType vType) external view returns (address);

    function getStrategyImplementation(StrategyType sType) external view returns (address);

    function isTransferIgnored(address _address) external view returns (bool);

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

    function isPairWhitelisted(address pair) external view returns (bool);

    function batchRedeemQueuedWithdrawals(
        address[] calldata vaults,
        uint256[] calldata rounds,
        bool[] calldata withdrawNative
    ) external;

    function setVaultImplementation(VaultType vType, address vaultImplementation) external;

    function setStrategyImplementation(StrategyType sType, address strategyImplementation) external;

    function setDefaultOperator(address defaultOperator) external;

    function setOperator(IStrategy strategy, address operator) external;

    function setPendingAumAnnualFee(IBaseVault vault, uint16 pendingAumAnnualFee) external;

    function resetPendingAumAnnualFee(IBaseVault vault) external;

    function setFeeRecipient(address feeRecipient) external;

    function setDefaultSequencerUptimeFeed(IAggregatorV3 sequencerUptimeFeed) external;

    function setSequenzerUptimeFeed(address oracleVault, IAggregatorV3 sequencerUptimeFeed) external;

    function setTwapInterval(address oracleVault, uint40 interval) external;

    function enableTWAPPriceCheck(address oracleVault, bool enabled) external;

    function setMinMaxPrice(address oracleVault, uint256 minPrice, uint256 maxPrice) external;

    function setDatafeedHeartbeat(address oracleVault, uint24 dataFeedHeartbeatX, uint24 dataFeedHeartbeatY) external;

    function setPairWhitelist(address[] calldata pairs, bool isWhitelisted) external;

    function createOracleVaultAndDefaultStrategy(ILBPair lbPair, IAggregatorV3 dataFeedX, IAggregatorV3 dataFeedY)
        external
        returns (address vault, address strategy);

    function createSimpleVaultAndDefaultStrategy(ILBPair lbPair) external returns (address vault, address strategy);

    function createOracleVault(ILBPair lbPair, IAggregatorV3 dataFeedX, IAggregatorV3 dataFeedY)
        external
        returns (address vault);

    function createSimpleVault(ILBPair lbPair) external returns (address vault);

    function createDefaultStrategy(IBaseVault vault) external returns (address strategy);

    function linkVaultToStrategy(IBaseVault vault, address strategy) external;

    function setEmergencyMode(IBaseVault vault) external;

    function cancelShutdown(address oracleVault) external;

    function recoverERC20(IBaseVault vault, IERC20Upgradeable token, address recipient, uint256 amount) external;
    
    function setDeviationThreshold(address oracleVault, uint256 threshold) external;

    function setTransferIgnoreList(address[] calldata addresses) external;

}

File 7 of 36 : IAggregatorV3.sol
// SPDX-License-Identifier: MIT

pragma solidity 0.8.10;

/**
 * @title Aggregator V3 Interface
 * @author Trader Joe
 * @notice Interface used to interact with Chainlink datafeeds.
 */
interface IAggregatorV3 {
    function decimals() external view returns (uint8);

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

    function version() external view returns (uint256);

    function getRoundData(uint80 _roundId)
        external
        view
        returns (uint80 roundId, int256 answer, uint256 startedAt, uint256 updatedAt, uint80 answeredInRound);

    function latestRoundData()
        external
        view
        returns (uint80 roundId, int256 answer, uint256 startedAt, uint256 updatedAt, uint80 answeredInRound);
}

File 8 of 36 : IHooksRewarder.sol
// SPDX-License-Identifier: MIT

pragma solidity 0.8.10;

import {IERC20} from "joe-v2/interfaces/ILBPair.sol";

interface IHooksRewarder {
    function getRewardToken() external view returns (IERC20);

    function getLBHooksManager() external view returns (address);

    function isStopped() external view returns (bool);

    function getRewardedRange() external view returns (uint256 binStart, uint256 binEnd);

    function getPendingRewards(address user, uint256[] calldata ids) external view returns (uint256 pendingRewards);

    function claim(address user, uint256[] calldata ids) external;

    function getExtraHooksParameters() external view returns (bytes32 extraHooksParameters);
}

File 9 of 36 : TokenHelper.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.10;

import {SafeERC20, IERC20} from "openzeppelin/token/ERC20/utils/SafeERC20.sol";

/**
 * @title Token Helper
 * @dev Helper library to handle ERC20 and native tokens
 */
library TokenHelper {
    using SafeERC20 for IERC20;

    error TokenHelper__NativeTransferFailed();

    /**
     * @dev Helper function to return the balance of an account for the given token
     * address(0) is used for native tokens
     * @param token The address of the token
     * @param account The address of the account
     * @return The balance of this contract for the given token
     */
    function safeBalanceOf(IERC20 token, address account) internal view returns (uint256) {
        return address(token) == address(0) ? address(account).balance : token.balanceOf(account);
    }

    /**
     * @dev Helper function to transfer the given amount of tokens to the given address
     * address(0) is used for native tokens
     * @param token The address of the token
     * @param to The address of the recipient
     * @param amount The amount of tokens
     */
    function safeTransfer(IERC20 token, address to, uint256 amount) internal {
        if (amount > 0) {
            if (address(token) == address(0)) {
                (bool s,) = to.call{value: amount}("");
                if (!s) revert TokenHelper__NativeTransferFailed();
            } else {
                token.safeTransfer(to, amount);
            }
        }
    }
}

File 10 of 36 : Precision.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.10;

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

library Precision {
    /**
     * @dev Shifts value to the left by the precision bits.
     * @param value value to shift
     */
    function shiftPrecision(uint256 value) internal pure returns (uint256) {
        return value << Constants.ACC_PRECISION_BITS;
    }

    /**
     * @dev Unshifts value to the right by the precision bits.
     * @param value value to unshift
     */
    function unshiftPrecision(uint256 value) internal pure returns (uint256) {
        return value >> Constants.ACC_PRECISION_BITS;
    }
}

File 11 of 36 : BaseVault.sol
// SPDX-License-Identifier: MIT

pragma solidity 0.8.10;

import {Clone} from "joe-v2/libraries/Clone.sol";
import {ERC20Upgradeable} from "openzeppelin-upgradeable/token/ERC20/ERC20Upgradeable.sol";
import {IERC20Upgradeable} from "openzeppelin-upgradeable/token/ERC20/IERC20Upgradeable.sol";
import {ILBPair} from "joe-v2/interfaces/ILBPair.sol";
import {Uint256x256Math} from "joe-v2/libraries/math/Uint256x256Math.sol";
import {ReentrancyGuardUpgradeable} from "openzeppelin-upgradeable/security/ReentrancyGuardUpgradeable.sol";
import {SafeERC20Upgradeable} from "openzeppelin-upgradeable/token/ERC20/utils/SafeERC20Upgradeable.sol";
import {SafeCast} from "joe-v2/libraries/math/SafeCast.sol";

import {IBaseVault} from "./interfaces/IBaseVault.sol";
import {IStrategy} from "./interfaces/IStrategy.sol";
import {IVaultFactory} from "./interfaces/IVaultFactory.sol";
import {IWNative} from "./interfaces/IWNative.sol";

/**
 * @title Liquidity Book Base Vault contract
 * @author Trader Joe
 * @notice This contract is used to interact with the Liquidity Book Pair contract. It should be inherited by a Vault
 * contract that defines the `_previewShares` function to calculate the amount of shares to mint.
 * The immutable data should be encoded as follows:
 * - 0x00: 20 bytes: The address of the LB pair.
 * - 0x14: 20 bytes: The address of the token X.
 * - 0x28: 20 bytes: The address of the token Y.
 * - 0x3C: 1 bytes: The decimals of the token X.
 * - 0x3D: 1 bytes: The decimals of the token Y.
 */
abstract contract BaseVault is Clone, ERC20Upgradeable, ReentrancyGuardUpgradeable, IBaseVault {
    using SafeERC20Upgradeable for IERC20Upgradeable;
    using Uint256x256Math for uint256;
    using SafeCast for uint256;

    uint8 internal constant _SHARES_DECIMALS = 6;
    uint256 internal constant _SHARES_PRECISION = 10 ** _SHARES_DECIMALS;

    IVaultFactory internal immutable _factory;
    address private immutable _wnative;

    IStrategy private _strategy;
    bool private _depositsPaused;

    QueuedWithdrawal[] private _queuedWithdrawalsByRound;

    uint256 private _totalAmountX;
    uint256 private _totalAmountY;

    /// @notice
    bool private _flaggedForShutdown;


    /**
     * @dev Modifier to check if the caller is the factory.
     */
    modifier onlyFactory() {
        if (msg.sender != address(_factory)) revert BaseVault__OnlyFactory();
        _;
    }

    /**
     * @notice Modifier to check if the caller is the operator or the default operator.
     */
    modifier onlyOperators() {
        if (address(getStrategy()) == address(0)) {
            if (msg.sender != _factory.getDefaultOperator()) {
                revert BaseVault__OnlyOperators();
            }
        } else if (msg.sender != getStrategy().getOperator() && msg.sender != _factory.getDefaultOperator()) {
            revert BaseVault__OnlyOperators();
        }
        _;
    }

    /**
     * @dev Modifier to check if deposits are allowed for the sender.
     */
    modifier depositsAllowed() {
        if (_depositsPaused) revert BaseVault__DepositsPaused();
        _;
    }

    /**
     * @dev Modifier to check if one of the two vault tokens is the wrapped native token.
     */
    modifier onlyVaultWithNativeToken() {
        if (address(_tokenX()) != _wnative && address(_tokenY()) != _wnative) revert BaseVault__NoNativeToken();
        _;
    }

    /**
     * @dev Modifier to check if the recipient is not the address(0)
     */
    modifier onlyValidRecipient(address recipient) {
        if (recipient == address(0)) revert BaseVault__InvalidRecipient();
        _;
    }

    /**
     * @dev Modifier to check that the amount of shares is greater than zero.
     */
    modifier NonZeroShares(uint256 shares) {
        if (shares == 0) revert BaseVault__ZeroShares();
        _;
    }

    /**
     * @dev Constructor of the contract.
     * @param factory Address of the factory.
     */
    constructor(IVaultFactory factory) {
        _factory = factory;
        _wnative = factory.getWNative();
    }

    /**
     * @dev Receive function. Mainly added to silence the compiler warning.
     * Highly unlikely to be used as the base vault needs at least 62 bytes of immutable data added to the payload
     * (3 addresses and 2 bytes for length), so this function should never be called.
     */
    receive() external payable {
        if (msg.sender != _wnative) revert BaseVault__OnlyWNative();
    }

    /**
     * @notice Allows the contract to receive native tokens from the WNative contract.
     * @dev We can't use the `receive` function because the immutable clone library adds calldata to the payload
     * that are taken as a function signature and parameters.
     */
    fallback() external payable {
        if (msg.sender != _wnative) revert BaseVault__OnlyWNative();
    }

    /**
     * @dev Initializes the contract.
     * @param name The name of the token.
     * @param symbol The symbol of the token.
     */
    function initialize(string memory name, string memory symbol) public virtual override initializer {
        __ERC20_init(name, symbol);
        __ReentrancyGuard_init();

        // Initialize the first round of queued withdrawals.
        _queuedWithdrawalsByRound.push();
    }

    /**
     * @notice Returns the decimals of the vault token.
     * @return The decimals of the vault token.
     */
    function decimals() public view virtual override returns (uint8) {
        return _decimalsY() + _SHARES_DECIMALS;
    }

    /**
     * @dev Returns the address of the factory.
     * @return The address of the factory.
     */
    function getFactory() public view virtual override returns (IVaultFactory) {
        return _factory;
    }

    /**
     * @dev Returns the address of the pair.
     * @return The address of the pair.
     */
    function getPair() public pure virtual override returns (ILBPair) {
        return _pair();
    }

    /**
     * @dev Returns the address of the token X.
     * @return The address of the token X.
     */
    function getTokenX() public pure virtual override returns (IERC20Upgradeable) {
        return _tokenX();
    }

    /**
     * @dev Returns the address of the token Y.
     * @return The address of the token Y.
     */
    function getTokenY() public pure virtual override returns (IERC20Upgradeable) {
        return _tokenY();
    }

    /**
     * @dev Returns the address of the current strategy.
     * @return The address of the strategy
     */
    function getStrategy() public view virtual override returns (IStrategy) {
        return _strategy;
    }

    /**
     * @dev Returns the AUM annual fee of the strategy.
     * @return
     */
    function getAumAnnualFee() public view virtual override returns (uint256) {
        IStrategy strategy = _strategy;

        return address(strategy) == address(0) ? 0 : strategy.getAumAnnualFee();
    }

    /**
     * @dev Returns the range of the strategy.
     * @return low The lower bound of the range.
     * @return upper The upper bound of the range.
     */
    function getRange() public view virtual override returns (uint24 low, uint24 upper) {
        IStrategy strategy = _strategy;

        return address(strategy) == address(0) ? (0, 0) : strategy.getRange();
    }

    /**
     * @dev Returns operators of the strategy.
     * @return defaultOperator The default operator.
     * @return operator The operator.
     */
    function getOperators() public view virtual override returns (address defaultOperator, address operator) {
        IStrategy strategy = _strategy;

        defaultOperator = _factory.getDefaultOperator();
        operator = address(strategy) == address(0) ? address(0) : strategy.getOperator();
    }

    /**
     * @dev Returns the total balances of the pair.
     * @return amountX The total balance of token X.
     * @return amountY The total balance of token Y.
     */
    function getBalances() public view virtual override returns (uint256 amountX, uint256 amountY) {
        (amountX, amountY) = _getBalances(_strategy);
    }

    /**
     * @dev Preview the amount of shares to be minted.
     * @param amountX The amount of token X to be deposited.
     * @param amountY The amount of token Y to be deposited.
     * @return shares The amount of shares to be minted.
     * @return effectiveX The effective amount of token X to be deposited.
     * @return effectiveY The effective amount of token Y to be deposited.
     */
    function previewShares(uint256 amountX, uint256 amountY)
        public
        view
        virtual
        override
        returns (uint256 shares, uint256 effectiveX, uint256 effectiveY)
    {
        return _previewShares(_strategy, amountX, amountY);
    }

    /**
     * @dev Preview the amount of tokens to be redeemed on withdrawal.
     * @param shares The amount of shares to be redeemed.
     * @return amountX The amount of token X to be redeemed.
     * @return amountY The amount of token Y to be redeemed.
     */
    function previewAmounts(uint256 shares) public view virtual override returns (uint256 amountX, uint256 amountY) {
        return _previewAmounts(_strategy, shares, totalSupply());
    }

    /**
     * @notice Returns if the deposits are paused.
     * @return paused True if the deposits are paused.
     */
    function isDepositsPaused() public view virtual override returns (bool paused) {
        return _depositsPaused;
    }

    /**
     * @notice Returns the current round of queued withdrawals.
     * @return round The current round of queued withdrawals.
     */
    function getCurrentRound() public view virtual override returns (uint256 round) {
        return _queuedWithdrawalsByRound.length - 1;
    }

    /**
     * @notice Returns the queued withdrawal of the round for an user.
     * @param round The round.
     * @param user The user.
     * @return shares The amount of shares that are queued for withdrawal.
     */
    function getQueuedWithdrawal(uint256 round, address user) public view virtual override returns (uint256 shares) {
        return _queuedWithdrawalsByRound[round].userWithdrawals[user];
    }

    /**
     * @notice Returns the total shares that were queued for the round.
     * @param round The round.
     * @return totalQueuedShares The total shares that were queued for the round.
     */
    function getTotalQueuedWithdrawal(uint256 round) public view virtual override returns (uint256 totalQueuedShares) {
        return _queuedWithdrawalsByRound[round].totalQueuedShares;
    }

    /**
     * @notice Returns the total shares that were queued for the current round.
     * @return totalQueuedShares The total shares that were queued for the current round.
     */
    function getCurrentTotalQueuedWithdrawal() public view virtual override returns (uint256 totalQueuedShares) {
        return _queuedWithdrawalsByRound[_queuedWithdrawalsByRound.length - 1].totalQueuedShares;
    }

    /**
     * @notice Returns the amounts that can be redeemed for an user on the round.
     * @param round The round.
     * @param user The user.
     * @return amountX The amount of token X that can be redeemed.
     * @return amountY The amount of token Y that can be redeemed.
     */
    function getRedeemableAmounts(uint256 round, address user)
        public
        view
        virtual
        override
        returns (uint256 amountX, uint256 amountY)
    {
        // Get the queued withdrawal of the round.
        QueuedWithdrawal storage queuedWithdrawal = _queuedWithdrawalsByRound[round];

        // Get the total amount of tokens that were queued for the round.
        uint256 totalAmountX = queuedWithdrawal.totalAmountX;
        uint256 totalAmountY = queuedWithdrawal.totalAmountY;

        // Get the shares that were queued for the user and the total of shares.
        uint256 shares = queuedWithdrawal.userWithdrawals[user];
        uint256 totalShares = queuedWithdrawal.totalQueuedShares;

        // Calculate the amounts to be redeemed.
        if (totalShares > 0) {
            amountX = totalAmountX.mulDivRoundDown(shares, totalShares);
            amountY = totalAmountY.mulDivRoundDown(shares, totalShares);
        }
    }


    /**
     * @dev Returns if the vault is flagged for shutdown
     */
    function isFlaggedForShutdown() external view override returns (bool) {
        return _flaggedForShutdown;
    }


    /**
     * @notice This will flag the vault as shutdown candidate and possible emergency mode in the near future
     * The flag indicate users to withdraw from the vault and claim any rewards before vault is in emergency mode.
     *
     * @dev Flag the vault for shutdown, withdraws all funds from strategy
     */
    function submitShutdown() external override onlyOperators {
        getStrategy().withdrawAll();

        _flaggedForShutdown = true;

        emit ShutdownSubmitted();
    }

    /**
     * @notice Cancel the shutdown of the vault
     * Only callable by the factory
     */
    function cancelShutdown() external override onlyFactory {
        _flaggedForShutdown = false;

        emit ShutdownCancelled();
    }

    /**
     * @dev Deposits tokens to the strategy.
     * @param amountX The amount of token X to be deposited.
     * @param amountY The amount of token Y to be deposited.
     * @param minShares The minimum amount of shares to be minted.
     * @return shares The amount of shares to be minted.
     * @return effectiveX The effective amount of token X to be deposited.
     * @return effectiveY The effective amount of token Y to be deposited.
     */
    function deposit(uint256 amountX, uint256 amountY, uint256 minShares)
        public
        virtual
        override
        nonReentrant
        returns (uint256 shares, uint256 effectiveX, uint256 effectiveY)
    {
        _updatePool();

        // Calculate the shares and effective amounts, also returns the strategy to save gas.
        IStrategy strategy;
        (strategy, shares, effectiveX, effectiveY) = _deposit(amountX, amountY);

        if (shares < minShares) revert BaseVault__InsufficientShares();

        _modifyUser(msg.sender, int256(shares));

        // Transfer the tokens to the strategy
        if (effectiveX > 0) _tokenX().safeTransferFrom(msg.sender, address(strategy), effectiveX);
        if (effectiveY > 0) _tokenY().safeTransferFrom(msg.sender, address(strategy), effectiveY);
    }

    /**
     * @dev Deposits native tokens and send the tokens to the strategy.
     * @param amountX The amount of token X to be deposited.
     * @param amountY The amount of token Y to be deposited.
     * @param minShares The minimum amount of shares to be minted.
     * @return shares The amount of shares to be minted.
     * @return effectiveX The effective amount of token X to be deposited.
     * @return effectiveY The effective amount of token Y to be deposited.
     */
    function depositNative(uint256 amountX, uint256 amountY, uint256 minShares)
        public
        payable
        virtual
        override
        nonReentrant
        onlyVaultWithNativeToken
        returns (uint256 shares, uint256 effectiveX, uint256 effectiveY)
    {
        (IERC20Upgradeable tokenX, IERC20Upgradeable tokenY) = (_tokenX(), _tokenY());

        address wnative = _wnative;
        bool isNativeX = address(tokenX) == wnative;

        // Check that the native token amount matches the amount of native tokens sent.
        if (isNativeX && amountX != msg.value || !isNativeX && amountY != msg.value) {
            revert BaseVault__InvalidNativeAmount();
        }

        _updatePool();

        // Calculate the shares and effective amounts
        IStrategy strategy;
        (strategy, shares, effectiveX, effectiveY) = _deposit(amountX, amountY);

        if (shares < minShares) revert BaseVault__InsufficientShares();

        _modifyUser(msg.sender, int256(shares));

        // Calculate the effective native amount and transfer the other token to the strategy.
        uint256 effectiveNative;
        if (isNativeX) {
            // Transfer the token Y to the strategy and cache the native amount.
            effectiveNative = effectiveX;
            if (effectiveY > 0) tokenY.safeTransferFrom(msg.sender, address(strategy), effectiveY);
        } else {
            // Transfer the token X to the strategy and cache the native amount.
            if (effectiveX > 0) tokenX.safeTransferFrom(msg.sender, address(strategy), effectiveX);
            effectiveNative = effectiveY;
        }

        // Deposit and send wnative to the strategy.
        if (effectiveNative > 0) {
            IWNative(wnative).deposit{value: effectiveNative}();
            IERC20Upgradeable(wnative).safeTransfer(address(strategy), effectiveNative);
        }

        // Refund dust native tokens, if any.
        if (msg.value > effectiveNative) {
            unchecked {
                _transferNative(msg.sender, msg.value - effectiveNative);
            }
        }
    }

    /**
     * @notice Queues withdrawal for `recipient`. The withdrawal will be effective after the next
     * rebalance. The user can withdraw the tokens after the rebalance, this allows users to withdraw
     * from LB positions without having to pay the gas price.
     * @param shares The shares to be queued for withdrawal.
     * @param recipient The address that will receive the withdrawn tokens after the rebalance.
     * @return round The round of the withdrawal.
     */
    function queueWithdrawal(uint256 shares, address recipient)
        public
        virtual
        override
        nonReentrant
        onlyValidRecipient(recipient)
        NonZeroShares(shares)
        returns (uint256 round)
    {
        // Check that the strategy is set.
        address strategy = address(_strategy);
        if (strategy == address(0)) revert BaseVault__InvalidStrategy();

        _updatePool();

        // Transfer the shares to the strategy, will revert if the user does not have enough shares.
        _transfer(msg.sender, strategy, shares);

        _modifyUser(msg.sender, -int256(shares));

        // Get the current round and the queued withdrawals for the round.
        round = _queuedWithdrawalsByRound.length - 1;
        QueuedWithdrawal storage queuedWithdrawals = _queuedWithdrawalsByRound[round];

        // Updates the total queued shares and the shares for the user.
        queuedWithdrawals.totalQueuedShares += shares;
        unchecked {
            // Can't overflow as the user can't have more shares than the total.
            queuedWithdrawals.userWithdrawals[recipient] += shares;
        }

        emit WithdrawalQueued(msg.sender, recipient, round, shares);
    }

    /**
     * @notice Cancels a queued withdrawal of `shares`. Cancelling a withdrawal is
     * only possible before the next rebalance. The user can cancel the withdrawal if they want to
     * stay in the vault. They will receive the vault shares back.
     * @param shares The shares to be cancelled for withdrawal.
     * @return round The round of the withdrawal that was cancelled.
     */
    function cancelQueuedWithdrawal(uint256 shares)
        public
        virtual
        override
        nonReentrant
        NonZeroShares(shares)
        returns (uint256 round)
    {
        // Check that the strategy is set.
        address strategy = address(_strategy);
        if (strategy == address(0)) revert BaseVault__InvalidStrategy();

        _updatePool();

        // Get the current round and the queued withdrawals for the round.
        round = _queuedWithdrawalsByRound.length - 1;
        QueuedWithdrawal storage queuedWithdrawals = _queuedWithdrawalsByRound[round];

        // Check that the user has enough shares queued for withdrawal.
        uint256 maxShares = queuedWithdrawals.userWithdrawals[msg.sender];
        if (shares > maxShares) revert BaseVault__MaxSharesExceeded();

        // Updates the total queued shares and the shares for the user.
        unchecked {
            // Can't underflow as the user can't have more shares than the total, and its shares
            // were already checked.
            queuedWithdrawals.userWithdrawals[msg.sender] = maxShares - shares;
            queuedWithdrawals.totalQueuedShares -= shares;
        }

        _modifyUser(msg.sender, int256(shares));

        // Transfer the shares back to the user.
        _transfer(strategy, msg.sender, shares);

        emit WithdrawalCancelled(msg.sender, msg.sender, round, shares);
    }

    /**
     * @notice Redeems a queued withdrawal for `recipient`. The user can redeem the tokens after the
     * rebalance. This can be easily check by comparing the current round with the round of the
     * withdrawal, if they're equal, the withdrawal is still pending.
     * @param recipient The address that will receive the withdrawn tokens after the rebalance.
     * @return amountX The amount of token X to be withdrawn.
     * @return amountY The amount of token Y to be withdrawn.
     */
    function redeemQueuedWithdrawal(uint256 round, address recipient)
        public
        virtual
        override
        nonReentrant
        onlyValidRecipient(recipient)
        returns (uint256 amountX, uint256 amountY)
    {
        // Get the amounts to be redeemed.
        (amountX, amountY) = _redeemWithdrawal(round, recipient);

        // Transfer the tokens to the recipient.
        if (amountX > 0) _tokenX().safeTransfer(recipient, amountX);
        if (amountY > 0) _tokenY().safeTransfer(recipient, amountY);
    }

    /**
     * @notice Redeems a queued withdrawal for `recipient`. The user can redeem the tokens after the
     * rebalance. This can be easily check by comparing the current round with the round of the
     * withdrawal, if they're equal, the withdrawal is still pending.
     * The wrapped native token will be unwrapped and sent to the recipient.
     * @param recipient The address that will receive the withdrawn tokens after the rebalance.
     * @return amountX The amount of token X to be withdrawn.
     * @return amountY The amount of token Y to be withdrawn.
     */
    function redeemQueuedWithdrawalNative(uint256 round, address recipient)
        public
        virtual
        override
        nonReentrant
        onlyVaultWithNativeToken
        onlyValidRecipient(recipient)
        returns (uint256 amountX, uint256 amountY)
    {
        // Get the amounts to be redeemed.
        (amountX, amountY) = _redeemWithdrawal(round, recipient);

        // Transfer the tokens to the recipient.
        if (amountX > 0) _transferTokenOrNative(_tokenX(), recipient, amountX);
        if (amountY > 0) _transferTokenOrNative(_tokenY(), recipient, amountY);
    }

    /**
     * @notice Emergency withdraws from the vault and sends the tokens to the sender according to its share.
     * If the user had queued withdrawals, they will be claimable using the `redeemQueuedWithdrawal` and
     * `redeemQueuedWithdrawalNative` functions as usual. This function is only for users that didn't queue
     * any withdrawals and still have shares in the vault.
     * @dev This will only work if the vault is in emergency mode or flagged for shutdown
     */
    function emergencyWithdraw() public virtual override nonReentrant {
        // Check that the vault is in emergency mode.
        if (address(_strategy) != address(0) && !_flaggedForShutdown) revert BaseVault__NotInEmergencyMode();

        _updatePool();

        // Get the amount of shares the user has. If the user has no shares, it will revert.
        uint256 shares = balanceOf(msg.sender);
        if (shares == 0) revert BaseVault__ZeroShares();

        // Get the balances of the vault and the total shares.
        // The balances of the vault will not contain the executed withdrawals.
        (uint256 balanceX, uint256 balanceY) = _getBalances(IStrategy(address(0)));
        uint256 totalShares = totalSupply();

        // Calculate the amounts to be withdrawn.
        uint256 amountX = balanceX.mulDivRoundDown(shares, totalShares);
        uint256 amountY = balanceY.mulDivRoundDown(shares, totalShares);

        _modifyUser(msg.sender, -int256(shares));

        // Burn the shares of the user.
        _burn(msg.sender, shares);

        // Transfer the tokens to the user.
        if (amountX > 0) _tokenX().safeTransfer(msg.sender, amountX);
        if (amountY > 0) _tokenY().safeTransfer(msg.sender, amountY);

        emit EmergencyWithdrawal(msg.sender, shares, amountX, amountY);
    }

    /**
     * @notice Executes the queued withdrawals for the current round. The strategy should call this
     * function after having sent the queued withdrawals to the vault.
     * This function will burn the shares of the users that queued withdrawals and will update the
     * total amount of tokens in the vault and increase the round.
     * @dev Only the strategy can call this function.
     */
    function executeQueuedWithdrawals() public virtual override nonReentrant {
        // Check that the caller is the strategy, it also checks that the strategy was set.
        address strategy = address(_strategy);
        if (strategy != msg.sender) revert BaseVault__OnlyStrategy();

        // Get the current round and the queued withdrawals for that round.
        uint256 round = _queuedWithdrawalsByRound.length - 1;
        QueuedWithdrawal storage queuedWithdrawals = _queuedWithdrawalsByRound[round];

        // Check that the round has queued withdrawals, if none, the function will stop.
        uint256 totalQueuedShares = queuedWithdrawals.totalQueuedShares;
        if (totalQueuedShares == 0) return;

        // Burn the shares of the users that queued withdrawals and update the queued withdrawals.
        _burn(strategy, totalQueuedShares);
        _queuedWithdrawalsByRound.push();

        // Cache the total amounts of tokens in the vault.
        uint256 totalAmountX = _totalAmountX;
        uint256 totalAmountY = _totalAmountY;

        // Get the amount of tokens received by the vault after executing the withdrawals.
        uint256 receivedX = _tokenX().balanceOf(address(this)) - totalAmountX;
        uint256 receivedY = _tokenY().balanceOf(address(this)) - totalAmountY;

        // Update the total amounts of tokens in the vault.
        _totalAmountX = totalAmountX + receivedX;
        _totalAmountY = totalAmountY + receivedY;

        // Update the total amounts of tokens in the queued withdrawals.
        queuedWithdrawals.totalAmountX = uint128(receivedX);
        queuedWithdrawals.totalAmountY = uint128(receivedY);

        emit WithdrawalExecuted(round, totalQueuedShares, receivedX, receivedY);
    }

    /**
     * @dev Sets the address of the strategy.
     * Will send all tokens to the new strategy.
     * @param newStrategy The address of the new strategy.
     */
    function setStrategy(IStrategy newStrategy) public virtual override onlyFactory nonReentrant {
        IStrategy currentStrategy = _strategy;

        // Verify that the strategy is not the same as the current strategy
        if (currentStrategy == newStrategy) revert BaseVault__SameStrategy();

        // Verify that the strategy is valid, i.e. it is for this vault and for the correct pair and tokens.
        if (
            newStrategy.getVault() != address(this) || newStrategy.getPair() != _pair()
                || newStrategy.getTokenX() != _tokenX() || newStrategy.getTokenY() != _tokenY()
        ) revert BaseVault__InvalidStrategy();

        // Check if there is a strategy currently set, if so, withdraw all tokens from it.
        if (address(currentStrategy) != address(0)) {
            IStrategy(currentStrategy).withdrawAll();
        }

        // Get the balances of the vault, this will not contain the executed withdrawals.
        (uint256 balanceX, uint256 balanceY) = _getBalances(IStrategy(address(0)));

        // Transfer all balances to the new strategy
        if (balanceX > 0) _tokenX().safeTransfer(address(newStrategy), balanceX);
        if (balanceY > 0) _tokenY().safeTransfer(address(newStrategy), balanceY);

        // Set the new strategy
        _setStrategy(newStrategy);
    }

    /**
     * @dev Pauses deposits.
     */
    function pauseDeposits() public virtual override onlyOperators nonReentrant {
        _depositsPaused = true;

        emit DepositsPaused();
    }

    /**
     * @dev Resumes deposits.
     */
    function resumeDeposits() public virtual override onlyOperators nonReentrant {
        _depositsPaused = false;

        emit DepositsResumed();
    }

    /**
     * @notice Sets the vault in emergency mode.
     * @dev This will pause deposits and withdraw all tokens from the strategy.
     */
    function setEmergencyMode() public virtual override onlyFactory nonReentrant {
        // Withdraw all tokens from the strategy.
        _strategy.withdrawAll();

        // signal vault emergency mode before unsetting strategy
        _beforeEmergencyMode();

        // Sets the strategy to the zero address, this will prevent any deposits.
        _setStrategy(IStrategy(address(0)));

        emit EmergencyMode();
    }

    /**
     * @dev Recovers ERC20 tokens sent to the vault.
     * @param token The address of the token to be recovered.
     * @param recipient The address of the recipient.
     * @param amount The amount of tokens to be recovered.
     */
    function recoverERC20(IERC20Upgradeable token, address recipient, uint256 amount)
        public
        virtual
        override
        nonReentrant
        onlyFactory
    {
        address strategy = address(_strategy);

        // Checks that the amount of token X to be recovered is not from any withdrawal. This will simply revert
        // if the vault is in emergency mode.
        if (token == _tokenX() && (strategy == address(0) || token.balanceOf(address(this)) < _totalAmountX + amount)) {
            revert BaseVault__InvalidToken();
        }

        // Checks that the amount of token Y to be recovered is not from any withdrawal. This will simply revert
        // if the vault is in emergency mode.
        if (token == _tokenY() && (strategy == address(0) || token.balanceOf(address(this)) < _totalAmountY + amount)) {
            revert BaseVault__InvalidToken();
        }

        if (token == this) {
            uint256 excessStrategy = balanceOf(strategy) - getCurrentTotalQueuedWithdrawal();

            // If the token is the vault's token, the remaining amount must be greater than the minimum shares.
            if (balanceOf(address(this)) + excessStrategy < amount + _SHARES_PRECISION) {
                revert BaseVault__BurnMinShares();
            }

            // Allow to recover vault tokens that were mistakenly sent to the strategy.
            if (excessStrategy > 0) {
                _transfer(strategy, address(this), excessStrategy);
            }
        }

        token.safeTransfer(recipient, amount);

        // Safety check for tokens with double entry points.
        if (
            strategy == address(0)
                && (
                    _tokenX().balanceOf(address(this)) < _totalAmountX || _tokenY().balanceOf(address(this)) < _totalAmountY
                )
        ) {
            revert BaseVault__InvalidToken();
        }

        emit Recovered(address(token), recipient, amount);
    }

    /**
     * @dev Returns the address of the pair.
     * @return The address of the pair.
     */
    function _pair() internal pure virtual returns (ILBPair) {
        return ILBPair(_getArgAddress(0));
    }

    /**
     * @dev Returns the address of the token X.
     * @return The address of the token X.
     */
    function _tokenX() internal pure virtual returns (IERC20Upgradeable) {
        return IERC20Upgradeable(_getArgAddress(20));
    }

    /**
     * @dev Returns the address of the token Y.
     * @return The address of the token Y.
     */
    function _tokenY() internal pure virtual returns (IERC20Upgradeable) {
        return IERC20Upgradeable(_getArgAddress(40));
    }

    /**
     * @dev Returns the decimals of the token X.
     * @return decimalsX The decimals of the token X.
     */
    function _decimalsX() internal pure virtual returns (uint8 decimalsX) {
        return _getArgUint8(60);
    }

    /**
     * @dev Returns the decimals of the token Y.
     * @return decimalsY The decimals of the token Y.
     */
    function _decimalsY() internal pure virtual returns (uint8 decimalsY) {
        return _getArgUint8(61);
    }

    /**
     * @dev Returns shares and amounts of token X and token Y to be deposited.
     * @param strategy The address of the strategy.
     * @param amountX The amount of token X to be deposited.
     * @param amountY The amount of token Y to be deposited.
     * @return shares The amount of shares to be minted.
     * @return effectiveX The amount of token X to be deposited.
     * @return effectiveY The amount of token Y to be deposited.
     */
    function _previewShares(IStrategy strategy, uint256 amountX, uint256 amountY)
        internal
        view
        virtual
        returns (uint256 shares, uint256, uint256);

    /**
     * @dev Returns amounts of token X and token Y to be withdrawn.
     * @param strategy The address of the strategy.
     * @param shares The amount of shares to be withdrawn.
     * @param totalShares The total amount of shares.
     * @return amountX The amount of token X to be withdrawn.
     * @return amountY The amount of token Y to be withdrawn.
     */
    function _previewAmounts(IStrategy strategy, uint256 shares, uint256 totalShares)
        internal
        view
        virtual
        returns (uint256 amountX, uint256 amountY)
    {
        if (shares == 0) return (0, 0);

        if (shares > totalShares) revert BaseVault__InvalidShares();

        // Get the total amount of tokens held in the strategy
        (uint256 totalX, uint256 totalY) = _getBalances(strategy);

        // Calculate the amount of tokens to be withdrawn, pro rata to the amount of shares
        amountX = totalX.mulDivRoundDown(shares, totalShares);
        amountY = totalY.mulDivRoundDown(shares, totalShares);
    }

    /**
     * @dev Returns the total amount of tokens held in the strategy. This includes the balance of the contract and the
     * amount of tokens deposited in LB.
     * Will return the balance of the vault if no strategy is set.
     * @param strategy The address of the strategy.
     * @return amountX The amount of token X held in the strategy.
     * @return amountY The amount of token Y held in the strategy.
     */
    function _getBalances(IStrategy strategy) internal view virtual returns (uint256 amountX, uint256 amountY) {
        return address(strategy) == address(0)
            ? (_tokenX().balanceOf(address(this)) - _totalAmountX, _tokenY().balanceOf(address(this)) - _totalAmountY)
            : strategy.getBalances();
    }

    /**
     * @dev Sets the address of the strategy.
     * @param strategy The address of the strategy.
     */
    function _setStrategy(IStrategy strategy) internal virtual {
        _strategy = strategy;

        emit StrategySet(strategy);
    }

    /**
     * @dev Calculate the effective amounts to take from the user and mint the shares.
     * Will not transfer the tokens from the user.
     * @param amountX The amount of token X to be deposited.
     * @param amountY The amount of token Y to be deposited.
     * @return strategy The address of the strategy.
     * @return shares The amount of shares to be minted.
     * @return effectiveX The amount of token X to be deposited.
     * @return effectiveY The amount of token Y to be deposited.
     */
    function _deposit(uint256 amountX, uint256 amountY)
        internal
        virtual
        depositsAllowed
        returns (IStrategy strategy, uint256 shares, uint256 effectiveX, uint256 effectiveY)
    {
        // Check that at least one token is being deposited
        if (amountX == 0 && amountY == 0) revert BaseVault__ZeroAmount();

        // Verify that the strategy is set
        strategy = _strategy;
        if (address(strategy) == address(0)) revert BaseVault__InvalidStrategy();

        // Calculate the effective amounts to take from the user and the amount of shares to mint
        (shares, effectiveX, effectiveY) = _previewShares(strategy, amountX, amountY);

        if (shares == 0) revert BaseVault__ZeroShares();

        if (totalSupply() == 0) {
            // Avoid exploit when very little shares, min of total shares will always be _SHARES_PRECISION (1e6)
            shares -= _SHARES_PRECISION;
            _mint(address(this), _SHARES_PRECISION);
        }

        // Mint the shares
        _mint(msg.sender, shares);

        emit Deposited(msg.sender, effectiveX, effectiveY, shares);
    }

    /**
     * @dev Redeems the queued withdrawal for a given round and a given user.
     * Does not transfer the tokens to the user.
     * @param user The address of the user.
     * @return amountX The amount of token X to be withdrawn.
     * @return amountY The amount of token Y to be withdrawn.
     */
    function _redeemWithdrawal(uint256 round, address user) internal returns (uint256 amountX, uint256 amountY) {
        // Prevent redeeming withdrawals for the current round that have not been executed yet
        uint256 currentRound = _queuedWithdrawalsByRound.length - 1;
        if (round >= currentRound) revert BaseVault__InvalidRound();

        QueuedWithdrawal storage queuedWithdrawals = _queuedWithdrawalsByRound[round];

        // Get the amount of shares to redeem, will revert if the user has no queued withdrawal
        uint256 shares = queuedWithdrawals.userWithdrawals[user];
        if (shares == 0) revert BaseVault__NoQueuedWithdrawal();

        // Only the user can redeem their withdrawal. The factory is also allowed as it batches withdrawals for users
        if (user != msg.sender && msg.sender != address(_factory)) revert BaseVault__Unauthorized();

        // Calculate the amount of tokens to be withdrawn, pro rata to the amount of shares
        uint256 totalQueuedShares = queuedWithdrawals.totalQueuedShares;
        queuedWithdrawals.userWithdrawals[user] = 0;

        amountX = uint256(queuedWithdrawals.totalAmountX).mulDivRoundDown(shares, totalQueuedShares);
        amountY = uint256(queuedWithdrawals.totalAmountY).mulDivRoundDown(shares, totalQueuedShares);

        if (amountX == 0 && amountY == 0) revert BaseVault__ZeroAmount();

        // Update the total amount of shares queued for withdrawal
        if (amountX != 0) _totalAmountX -= amountX;
        if (amountY != 0) _totalAmountY -= amountY;

        emit WithdrawalRedeemed(msg.sender, user, round, shares, amountX, amountY);
    }

    /**
     * @dev Helper function to transfer tokens to the recipient. If the token is the wrapped native token, it will be
     * unwrapped first and then transferred as native tokens.
     * @param token The address of the token to be transferred.
     * @param recipient The address to receive the tokens.
     * @param amount The amount of tokens to be transferred.
     */
    function _transferTokenOrNative(IERC20Upgradeable token, address recipient, uint256 amount) internal {
        address wnative = _wnative;
        if (address(token) == wnative) {
            IWNative(wnative).withdraw(amount);
            _transferNative(recipient, amount);
        } else {
            token.safeTransfer(recipient, amount);
        }
    }

    /**
     * @dev Helper function to transfer native tokens to the recipient.
     * @param recipient The address to receive the tokens.
     * @param amount The amount of tokens to be transferred.
     */
    function _transferNative(address recipient, uint256 amount) internal virtual {
        (bool success,) = recipient.call{value: amount}("");
        if (!success) revert BaseVault__NativeTransferFailed();
    }

    /**
     * @dev will be called on deposit, harvest, and withdraw
     */
    function _updatePool() internal virtual;

    /**
     * @dev will be called on deposit, harvest, and withdraw
     * @param user The address of the user.
     * @param amount The amount of shares to be minted or burned.
     */
    function _modifyUser(address user, int256 amount) internal virtual;

    /**
     * @dev will be called on emergency mode before the strategy is set to address(0)
     */
    function _beforeEmergencyMode() internal virtual;
}

File 12 of 36 : IOracleVault.sol
// SPDX-License-Identifier: MIT

pragma solidity 0.8.10;

import {IERC20Upgradeable} from "openzeppelin-upgradeable/token/ERC20/IERC20Upgradeable.sol";
import {ILBPair} from "joe-v2/interfaces/ILBPair.sol";

import {IStrategy} from "./IStrategy.sol";
import {IBaseVault} from "./IBaseVault.sol";
import {IAggregatorV3} from "./IAggregatorV3.sol";

/**
 * @title Oracle Vault Interface
 * @author Trader Joe
 * @notice Interface used to interact with Liquidity Book Oracle Vaults
 */
interface IOracleVault is IBaseVault {
    
    error OracleVault__InvalidPrice();
    error OracleVault__StalePrice();

    error OracleVault__SequencerDown();
    error OracleVault__GracePeriodNotOver();
    error OracleVault__PriceDeviation();
    error OracleVault__InvalidInterval();
    error OracleVault__InvalidTimestamps();

    function getOracleX() external pure returns (IAggregatorV3 oracleX);

    function getOracleY() external pure returns (IAggregatorV3 oracleY);

    function getPrice() external view returns (uint256 price);

    function getOracleParameters() external view returns (
        uint256 minPrice, 
        uint256 maxPrice, 
        uint256 heartbeatX, 
        uint256 heartbeatY,
        uint256 deviationThreshold,
        bool twapPriceCheckEnabled,
        uint40 twapInterval
    );

    function setSequenzerUptimeFeed(IAggregatorV3 sequencerUptimeFeed) external;

    function setMinMaxPrice(uint256 minPrice, uint256 maxPrice) external;

    function setDatafeedHeartbeat(uint24 heartbeatX, uint24 heartbeatY) external;

    function enableTWAPPriceCheck(bool enabled) external;

    function setTwapInterval(uint40 interval) external;

    function setDeviationThreshold(uint256 threshold) external;
}

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

pragma solidity ^0.8.0;

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

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

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

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

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

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

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

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

File 14 of 36 : ILBPair.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.10;

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

import {ILBFactory} from "./ILBFactory.sol";
import {ILBFlashLoanCallback} from "./ILBFlashLoanCallback.sol";
import {ILBToken} from "./ILBToken.sol";

interface ILBPair is ILBToken {
    error LBPair__ZeroBorrowAmount();
    error LBPair__AddressZero();
    error LBPair__AlreadyInitialized();
    error LBPair__EmptyMarketConfigs();
    error LBPair__FlashLoanCallbackFailed();
    error LBPair__FlashLoanInsufficientAmount();
    error LBPair__InsufficientAmountIn();
    error LBPair__InsufficientAmountOut();
    error LBPair__InvalidInput();
    error LBPair__InvalidStaticFeeParameters();
    error LBPair__OnlyFactory();
    error LBPair__OnlyProtocolFeeRecipient();
    error LBPair__OutOfLiquidity();
    error LBPair__TokenNotSupported();
    error LBPair__ZeroAmount(uint24 id);
    error LBPair__ZeroAmountsOut(uint24 id);
    error LBPair__ZeroShares(uint24 id);
    error LBPair__MaxTotalFeeExceeded();

    struct MintArrays {
        uint256[] ids;
        bytes32[] amounts;
        uint256[] liquidityMinted;
    }

    event DepositedToBins(address indexed sender, address indexed to, uint256[] ids, bytes32[] amounts);

    event WithdrawnFromBins(address indexed sender, address indexed to, uint256[] ids, bytes32[] amounts);

    event CompositionFees(address indexed sender, uint24 id, bytes32 totalFees, bytes32 protocolFees);

    event CollectedProtocolFees(address indexed feeRecipient, bytes32 protocolFees);

    event Swap(
        address indexed sender,
        address indexed to,
        uint24 id,
        bytes32 amountsIn,
        bytes32 amountsOut,
        uint24 volatilityAccumulator,
        bytes32 totalFees,
        bytes32 protocolFees
    );

    event StaticFeeParametersSet(
        address indexed sender,
        uint16 baseFactor,
        uint16 filterPeriod,
        uint16 decayPeriod,
        uint16 reductionFactor,
        uint24 variableFeeControl,
        uint16 protocolShare,
        uint24 maxVolatilityAccumulator
    );

    event FlashLoan(
        address indexed sender,
        ILBFlashLoanCallback indexed receiver,
        uint24 activeId,
        bytes32 amounts,
        bytes32 totalFees,
        bytes32 protocolFees
    );

    event OracleLengthIncreased(address indexed sender, uint16 oracleLength);

    event ForcedDecay(address indexed sender, uint24 idReference, uint24 volatilityReference);

    function initialize(
        uint16 baseFactor,
        uint16 filterPeriod,
        uint16 decayPeriod,
        uint16 reductionFactor,
        uint24 variableFeeControl,
        uint16 protocolShare,
        uint24 maxVolatilityAccumulator,
        uint24 activeId
    ) external;

    function getFactory() external view returns (ILBFactory factory);

    function getTokenX() external view returns (IERC20 tokenX);

    function getTokenY() external view returns (IERC20 tokenY);

    function getBinStep() external view returns (uint16 binStep);

    function getReserves() external view returns (uint128 reserveX, uint128 reserveY);

    function getActiveId() external view returns (uint24 activeId);

    function getBin(uint24 id) external view returns (uint128 binReserveX, uint128 binReserveY);

    function getNextNonEmptyBin(bool swapForY, uint24 id) external view returns (uint24 nextId);

    function getProtocolFees() external view returns (uint128 protocolFeeX, uint128 protocolFeeY);

    function getStaticFeeParameters()
        external
        view
        returns (
            uint16 baseFactor,
            uint16 filterPeriod,
            uint16 decayPeriod,
            uint16 reductionFactor,
            uint24 variableFeeControl,
            uint16 protocolShare,
            uint24 maxVolatilityAccumulator
        );

    function getVariableFeeParameters()
        external
        view
        returns (uint24 volatilityAccumulator, uint24 volatilityReference, uint24 idReference, uint40 timeOfLastUpdate);

    function getOracleParameters()
        external
        view
        returns (uint8 sampleLifetime, uint16 size, uint16 activeSize, uint40 lastUpdated, uint40 firstTimestamp);

    function getOracleSampleAt(uint40 lookupTimestamp)
        external
        view
        returns (uint64 cumulativeId, uint64 cumulativeVolatility, uint64 cumulativeBinCrossed);

    function getPriceFromId(uint24 id) external view returns (uint256 price);

    function getIdFromPrice(uint256 price) external view returns (uint24 id);

    function getSwapIn(uint128 amountOut, bool swapForY)
        external
        view
        returns (uint128 amountIn, uint128 amountOutLeft, uint128 fee);

    function getSwapOut(uint128 amountIn, bool swapForY)
        external
        view
        returns (uint128 amountInLeft, uint128 amountOut, uint128 fee);

    function swap(bool swapForY, address to) external returns (bytes32 amountsOut);

    function flashLoan(ILBFlashLoanCallback receiver, bytes32 amounts, bytes calldata data) external;

    function mint(address to, bytes32[] calldata liquidityConfigs, address refundTo)
        external
        returns (bytes32 amountsReceived, bytes32 amountsLeft, uint256[] memory liquidityMinted);

    function burn(address from, address to, uint256[] calldata ids, uint256[] calldata amountsToBurn)
        external
        returns (bytes32[] memory amounts);

    function collectProtocolFees() external returns (bytes32 collectedProtocolFees);

    function increaseOracleLength(uint16 newLength) external;

    function setStaticFeeParameters(
        uint16 baseFactor,
        uint16 filterPeriod,
        uint16 decayPeriod,
        uint16 reductionFactor,
        uint24 variableFeeControl,
        uint16 protocolShare,
        uint24 maxVolatilityAccumulator
    ) external;

    function forceDecay() external;
}

File 15 of 36 : IOneInchRouter.sol
// SPDX-License-Identifier: MIT

pragma solidity 0.8.10;

import {IERC20Upgradeable} from "openzeppelin-upgradeable/token/ERC20/IERC20Upgradeable.sol";

interface IOneInchRouter {
    struct SwapDescription {
        IERC20Upgradeable srcToken;
        IERC20Upgradeable dstToken;
        address payable srcReceiver;
        address payable dstReceiver;
        uint256 amount;
        uint256 minReturnAmount;
        uint256 flags;
    }

    function swap(address executor, SwapDescription calldata desc, bytes calldata permit, bytes calldata data)
        external
        payable
        returns (uint256 returnAmount, uint256 spentAmount);
}

File 16 of 36 : IBaseVault.sol
// SPDX-License-Identifier: MIT

pragma solidity 0.8.10;

import {IERC20Upgradeable} from "openzeppelin-upgradeable/token/ERC20/IERC20Upgradeable.sol";
import {ILBPair} from "joe-v2/interfaces/ILBPair.sol";

import {IStrategy} from "./IStrategy.sol";
import {IVaultFactory} from "./IVaultFactory.sol";

/**
 * @title Base Vault Interface
 * @author Trader Joe
 * @notice Interface used to interact with Liquidity Book Vaults
 */
interface IBaseVault is IERC20Upgradeable {
    error BaseVault__AlreadyWhitelisted(address user);
    error BaseVault__BurnMinShares();
    error BaseVault__DepositsPaused();
    error BaseVault__InvalidNativeAmount();
    error BaseVault__InvalidRecipient();
    error BaseVault__InvalidShares();
    error BaseVault__InvalidStrategy();
    error BaseVault__InvalidToken();
    error BaseVault__NoNativeToken();
    error BaseVault__NoQueuedWithdrawal();
    error BaseVault__MaxSharesExceeded();
    error BaseVault__NativeTransferFailed();
    error BaseVault__NotInEmergencyMode();
    error BaseVault__NotWhitelisted(address user);
    error BaseVault__OnlyFactory();
    error BaseVault__OnlyWNative();
    error BaseVault__OnlyStrategy();
    error BaseVault__SameStrategy();
    error BaseVault__SameWhitelistState();
    error BaseVault__ZeroAmount();
    error BaseVault__ZeroShares();
    error BaseVault__InvalidRound();
    error BaseVault__Unauthorized();
    error BaseVault__InsufficientShares();
    error BaseVault__OnlyOperators();

    struct QueuedWithdrawal {
        mapping(address => uint256) userWithdrawals;
        uint256 totalQueuedShares;
        uint128 totalAmountX;
        uint128 totalAmountY;
    }

    event Deposited(address indexed user, uint256 amountX, uint256 amountY, uint256 shares);

    event WithdrawalQueued(address indexed sender, address indexed user, uint256 indexed round, uint256 shares);

    event WithdrawalCancelled(address indexed sender, address indexed recipient, uint256 indexed round, uint256 shares);

    event WithdrawalRedeemed(
        address indexed sender,
        address indexed recipient,
        uint256 indexed round,
        uint256 shares,
        uint256 amountX,
        uint256 amountY
    );

    event WithdrawalExecuted(uint256 indexed round, uint256 totalQueuedQhares, uint256 amountX, uint256 amountY);

    event StrategySet(IStrategy strategy);

    event Recovered(address token, address recipient, uint256 amount);

    event DepositsPaused();

    event DepositsResumed();

    event EmergencyMode();

    event EmergencyWithdrawal(address indexed sender, uint256 shares, uint256 amountX, uint256 amountY);

    event ShutdownSubmitted();

    event ShutdownCancelled();

    function getFactory() external view returns (IVaultFactory);

    function getPair() external view returns (ILBPair);

    function getTokenX() external view returns (IERC20Upgradeable);

    function getTokenY() external view returns (IERC20Upgradeable);

    function getStrategy() external view returns (IStrategy);

    function getAumAnnualFee() external view returns (uint256);

    function getRange() external view returns (uint24 low, uint24 upper);

    function getOperators() external view returns (address defaultOperator, address operator);

    function getBalances() external view returns (uint256 amountX, uint256 amountY);

    function previewShares(uint256 amountX, uint256 amountY)
        external
        view
        returns (uint256 shares, uint256 effectiveX, uint256 effectiveY);

    function previewAmounts(uint256 shares) external view returns (uint256 amountX, uint256 amountY);

    function isDepositsPaused() external view returns (bool);

    function getCurrentRound() external view returns (uint256 round);

    function getQueuedWithdrawal(uint256 round, address user) external view returns (uint256 shares);

    function getTotalQueuedWithdrawal(uint256 round) external view returns (uint256 totalQueuedShares);

    function getCurrentTotalQueuedWithdrawal() external view returns (uint256 totalQueuedShares);

    function getRedeemableAmounts(uint256 round, address user)
        external
        view
        returns (uint256 amountX, uint256 amountY);

    function deposit(uint256 amountX, uint256 amountY, uint256 minShares)
        external
        returns (uint256 shares, uint256 effectiveX, uint256 effectiveY);

    function depositNative(uint256 amountX, uint256 amountY, uint256 minShares)
        external
        payable
        returns (uint256 shares, uint256 effectiveX, uint256 effectiveY);

    function queueWithdrawal(uint256 shares, address recipient) external returns (uint256 round);

    function cancelQueuedWithdrawal(uint256 shares) external returns (uint256 round);

    function redeemQueuedWithdrawal(uint256 round, address recipient)
        external
        returns (uint256 amountX, uint256 amountY);

    function redeemQueuedWithdrawalNative(uint256 round, address recipient)
        external
        returns (uint256 amountX, uint256 amountY);

    function emergencyWithdraw() external;

    function executeQueuedWithdrawals() external;

    function initialize(string memory name, string memory symbol) external;

    function setStrategy(IStrategy newStrategy) external;

    function pauseDeposits() external;

    function resumeDeposits() external;

    function setEmergencyMode() external;

    function recoverERC20(IERC20Upgradeable token, address recipient, uint256 amount) external;

    function isFlaggedForShutdown() external view returns (bool);

    function submitShutdown() external;

    function cancelShutdown() external;
}

File 17 of 36 : SafeERC20.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.0) (token/ERC20/utils/SafeERC20.sol)

pragma solidity ^0.8.0;

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

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

    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));
        }
    }

    function safePermit(
        IERC20Permit token,
        address owner,
        address spender,
        uint256 value,
        uint256 deadline,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) internal {
        uint256 nonceBefore = token.nonces(owner);
        token.permit(owner, spender, value, deadline, v, r, s);
        uint256 nonceAfter = token.nonces(owner);
        require(nonceAfter == nonceBefore + 1, "SafeERC20: permit did not succeed");
    }

    /**
     * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
     * on the return value: the return value is optional (but if data is returned, it must not be false).
     * @param token The token targeted by the call.
     * @param data The call data (encoded using abi.encode or one of its variants).
     */
    function _callOptionalReturn(IERC20 token, bytes memory data) private {
        // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
        // we're implementing it ourselves. We use {Address-functionCall} to perform this call, which verifies that
        // the target address contains contract code and also asserts for success in the low-level call.

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

File 18 of 36 : Constants.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.10;

/**
 * @title Constants Library
 * @dev A library that defines various constants used throughout the codebase.
 */
library Constants {
    uint256 internal constant ACC_PRECISION_BITS = 64;
    uint256 internal constant PRECISION = 1e18;

    // current max range for iota
    uint256 internal constant IOTA_MAX_RANGE = 151;

    // current max range for default (all other chains)
    uint256 internal constant DEFAULT_MAX_RANGE = 51; 
}

File 19 of 36 : Clone.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.10;

/**
 * @title Clone
 * @notice Class with helper read functions for clone with immutable args.
 * @author Trader Joe
 * @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/Clone.sol)
 * @author Adapted from clones with immutable args by zefram.eth, Saw-mon & Natalie
 * (https://github.com/Saw-mon-and-Natalie/clones-with-immutable-args)
 */
abstract contract Clone {
    /**
     * @dev Reads an immutable arg with type bytes
     * @param argOffset The offset of the arg in the immutable args
     * @param length The length of the arg
     * @return arg The immutable bytes arg
     */
    function _getArgBytes(uint256 argOffset, uint256 length) internal pure returns (bytes memory arg) {
        uint256 offset = _getImmutableArgsOffset();
        /// @solidity memory-safe-assembly
        assembly {
            // Grab the free memory pointer.
            arg := mload(0x40)
            // Store the array length.
            mstore(arg, length)
            // Copy the array.
            calldatacopy(add(arg, 0x20), add(offset, argOffset), length)
            // Allocate the memory, rounded up to the next 32 byte boundary.
            mstore(0x40, and(add(add(arg, 0x3f), length), not(0x1f)))
        }
    }

    /**
     * @dev Reads an immutable arg with type address
     * @param argOffset The offset of the arg in the immutable args
     * @return arg The immutable address arg
     */
    function _getArgAddress(uint256 argOffset) internal pure returns (address arg) {
        uint256 offset = _getImmutableArgsOffset();
        /// @solidity memory-safe-assembly
        assembly {
            arg := shr(0x60, calldataload(add(offset, argOffset)))
        }
    }

    /**
     * @dev Reads an immutable arg with type uint256
     * @param argOffset The offset of the arg in the immutable args
     * @return arg The immutable uint256 arg
     */
    function _getArgUint256(uint256 argOffset) internal pure returns (uint256 arg) {
        uint256 offset = _getImmutableArgsOffset();
        /// @solidity memory-safe-assembly
        assembly {
            arg := calldataload(add(offset, argOffset))
        }
    }

    /**
     * @dev Reads a uint256 array stored in the immutable args.
     * @param argOffset The offset of the arg in the immutable args
     * @param length The length of the arg
     * @return arg The immutable uint256 array arg
     */
    function _getArgUint256Array(uint256 argOffset, uint256 length) internal pure returns (uint256[] memory arg) {
        uint256 offset = _getImmutableArgsOffset();
        /// @solidity memory-safe-assembly
        assembly {
            // Grab the free memory pointer.
            arg := mload(0x40)
            // Store the array length.
            mstore(arg, length)
            // Copy the array.
            calldatacopy(add(arg, 0x20), add(offset, argOffset), shl(5, length))
            // Allocate the memory.
            mstore(0x40, add(add(arg, 0x20), shl(5, length)))
        }
    }

    /**
     * @dev Reads an immutable arg with type uint64
     * @param argOffset The offset of the arg in the immutable args
     * @return arg The immutable uint64 arg
     */
    function _getArgUint64(uint256 argOffset) internal pure returns (uint64 arg) {
        uint256 offset = _getImmutableArgsOffset();
        /// @solidity memory-safe-assembly
        assembly {
            arg := shr(0xc0, calldataload(add(offset, argOffset)))
        }
    }

    /**
     * @dev Reads an immutable arg with type uint16
     * @param argOffset The offset of the arg in the immutable args
     * @return arg The immutable uint16 arg
     */
    function _getArgUint16(uint256 argOffset) internal pure returns (uint16 arg) {
        uint256 offset = _getImmutableArgsOffset();
        /// @solidity memory-safe-assembly
        assembly {
            arg := shr(0xf0, calldataload(add(offset, argOffset)))
        }
    }

    /**
     * @dev Reads an immutable arg with type uint8
     * @param argOffset The offset of the arg in the immutable args
     * @return arg The immutable uint8 arg
     */
    function _getArgUint8(uint256 argOffset) internal pure returns (uint8 arg) {
        uint256 offset = _getImmutableArgsOffset();
        /// @solidity memory-safe-assembly
        assembly {
            arg := shr(0xf8, calldataload(add(offset, argOffset)))
        }
    }

    /**
     * @dev Reads the offset of the packed immutable args in calldata.
     * @return offset The offset of the packed immutable args in calldata.
     */
    function _getImmutableArgsOffset() internal pure returns (uint256 offset) {
        /// @solidity memory-safe-assembly
        assembly {
            offset := sub(calldatasize(), shr(0xf0, calldataload(sub(calldatasize(), 2))))
        }
    }
}

File 20 of 36 : ERC20Upgradeable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.0) (token/ERC20/ERC20.sol)

pragma solidity ^0.8.0;

import "./IERC20Upgradeable.sol";
import "./extensions/IERC20MetadataUpgradeable.sol";
import "../../utils/ContextUpgradeable.sol";
import "../../proxy/utils/Initializable.sol";

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

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

    uint256 private _totalSupply;

    string private _name;
    string private _symbol;

    /**
     * @dev Sets the values for {name} and {symbol}.
     *
     * The default value of {decimals} is 18. To select a different value for
     * {decimals} you should overload it.
     *
     * All two of these values are immutable: they can only be set once during
     * construction.
     */
    function __ERC20_init(string memory name_, string memory symbol_) internal onlyInitializing {
        __ERC20_init_unchained(name_, symbol_);
    }

    function __ERC20_init_unchained(string memory name_, string memory symbol_) internal onlyInitializing {
        _name = name_;
        _symbol = symbol_;
    }

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

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

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

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

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

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

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

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

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

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

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

        return true;
    }

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

        _beforeTokenTransfer(from, to, amount);

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

        emit Transfer(from, to, amount);

        _afterTokenTransfer(from, to, amount);
    }

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

File 21 of 36 : ReentrancyGuardUpgradeable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.0) (security/ReentrancyGuard.sol)

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

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

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

    uint256 private _status;

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

    function __ReentrancyGuard_init_unchained() internal onlyInitializing {
        _status = _NOT_ENTERED;
    }

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

    function _nonReentrantBefore() private {
        // On the first call to nonReentrant, _status will be _NOT_ENTERED
        require(_status != _ENTERED, "ReentrancyGuard: reentrant call");

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

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

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

File 22 of 36 : SafeERC20Upgradeable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.0) (token/ERC20/utils/SafeERC20.sol)

pragma solidity ^0.8.0;

import "../IERC20Upgradeable.sol";
import "../extensions/draft-IERC20PermitUpgradeable.sol";
import "../../../utils/AddressUpgradeable.sol";

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

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

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

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

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

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

    function safePermit(
        IERC20PermitUpgradeable token,
        address owner,
        address spender,
        uint256 value,
        uint256 deadline,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) internal {
        uint256 nonceBefore = token.nonces(owner);
        token.permit(owner, spender, value, deadline, v, r, s);
        uint256 nonceAfter = token.nonces(owner);
        require(nonceAfter == nonceBefore + 1, "SafeERC20: permit did not succeed");
    }

    /**
     * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
     * on the return value: the return value is optional (but if data is returned, it must not be false).
     * @param token The token targeted by the call.
     * @param data The call data (encoded using abi.encode or one of its variants).
     */
    function _callOptionalReturn(IERC20Upgradeable token, bytes memory data) private {
        // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
        // we're implementing it ourselves. We use {Address-functionCall} to perform this call, which verifies that
        // the target address contains contract code and also asserts for success in the low-level call.

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

File 23 of 36 : SafeCast.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.10;

/**
 * @title Liquidity Book Safe Cast Library
 * @author Trader Joe
 * @notice This library contains functions to safely cast uint256 to different uint types.
 */
library SafeCast {
    error SafeCast__Exceeds248Bits();
    error SafeCast__Exceeds240Bits();
    error SafeCast__Exceeds232Bits();
    error SafeCast__Exceeds224Bits();
    error SafeCast__Exceeds216Bits();
    error SafeCast__Exceeds208Bits();
    error SafeCast__Exceeds200Bits();
    error SafeCast__Exceeds192Bits();
    error SafeCast__Exceeds184Bits();
    error SafeCast__Exceeds176Bits();
    error SafeCast__Exceeds168Bits();
    error SafeCast__Exceeds160Bits();
    error SafeCast__Exceeds152Bits();
    error SafeCast__Exceeds144Bits();
    error SafeCast__Exceeds136Bits();
    error SafeCast__Exceeds128Bits();
    error SafeCast__Exceeds120Bits();
    error SafeCast__Exceeds112Bits();
    error SafeCast__Exceeds104Bits();
    error SafeCast__Exceeds96Bits();
    error SafeCast__Exceeds88Bits();
    error SafeCast__Exceeds80Bits();
    error SafeCast__Exceeds72Bits();
    error SafeCast__Exceeds64Bits();
    error SafeCast__Exceeds56Bits();
    error SafeCast__Exceeds48Bits();
    error SafeCast__Exceeds40Bits();
    error SafeCast__Exceeds32Bits();
    error SafeCast__Exceeds24Bits();
    error SafeCast__Exceeds16Bits();
    error SafeCast__Exceeds8Bits();

    /**
     * @dev Returns x on uint248 and check that it does not overflow
     * @param x The value as an uint256
     * @return y The value as an uint248
     */
    function safe248(uint256 x) internal pure returns (uint248 y) {
        if ((y = uint248(x)) != x) revert SafeCast__Exceeds248Bits();
    }

    /**
     * @dev Returns x on uint240 and check that it does not overflow
     * @param x The value as an uint256
     * @return y The value as an uint240
     */
    function safe240(uint256 x) internal pure returns (uint240 y) {
        if ((y = uint240(x)) != x) revert SafeCast__Exceeds240Bits();
    }

    /**
     * @dev Returns x on uint232 and check that it does not overflow
     * @param x The value as an uint256
     * @return y The value as an uint232
     */
    function safe232(uint256 x) internal pure returns (uint232 y) {
        if ((y = uint232(x)) != x) revert SafeCast__Exceeds232Bits();
    }

    /**
     * @dev Returns x on uint224 and check that it does not overflow
     * @param x The value as an uint256
     * @return y The value as an uint224
     */
    function safe224(uint256 x) internal pure returns (uint224 y) {
        if ((y = uint224(x)) != x) revert SafeCast__Exceeds224Bits();
    }

    /**
     * @dev Returns x on uint216 and check that it does not overflow
     * @param x The value as an uint256
     * @return y The value as an uint216
     */
    function safe216(uint256 x) internal pure returns (uint216 y) {
        if ((y = uint216(x)) != x) revert SafeCast__Exceeds216Bits();
    }

    /**
     * @dev Returns x on uint208 and check that it does not overflow
     * @param x The value as an uint256
     * @return y The value as an uint208
     */
    function safe208(uint256 x) internal pure returns (uint208 y) {
        if ((y = uint208(x)) != x) revert SafeCast__Exceeds208Bits();
    }

    /**
     * @dev Returns x on uint200 and check that it does not overflow
     * @param x The value as an uint256
     * @return y The value as an uint200
     */
    function safe200(uint256 x) internal pure returns (uint200 y) {
        if ((y = uint200(x)) != x) revert SafeCast__Exceeds200Bits();
    }

    /**
     * @dev Returns x on uint192 and check that it does not overflow
     * @param x The value as an uint256
     * @return y The value as an uint192
     */
    function safe192(uint256 x) internal pure returns (uint192 y) {
        if ((y = uint192(x)) != x) revert SafeCast__Exceeds192Bits();
    }

    /**
     * @dev Returns x on uint184 and check that it does not overflow
     * @param x The value as an uint256
     * @return y The value as an uint184
     */
    function safe184(uint256 x) internal pure returns (uint184 y) {
        if ((y = uint184(x)) != x) revert SafeCast__Exceeds184Bits();
    }

    /**
     * @dev Returns x on uint176 and check that it does not overflow
     * @param x The value as an uint256
     * @return y The value as an uint176
     */
    function safe176(uint256 x) internal pure returns (uint176 y) {
        if ((y = uint176(x)) != x) revert SafeCast__Exceeds176Bits();
    }

    /**
     * @dev Returns x on uint168 and check that it does not overflow
     * @param x The value as an uint256
     * @return y The value as an uint168
     */
    function safe168(uint256 x) internal pure returns (uint168 y) {
        if ((y = uint168(x)) != x) revert SafeCast__Exceeds168Bits();
    }

    /**
     * @dev Returns x on uint160 and check that it does not overflow
     * @param x The value as an uint256
     * @return y The value as an uint160
     */
    function safe160(uint256 x) internal pure returns (uint160 y) {
        if ((y = uint160(x)) != x) revert SafeCast__Exceeds160Bits();
    }

    /**
     * @dev Returns x on uint152 and check that it does not overflow
     * @param x The value as an uint256
     * @return y The value as an uint152
     */
    function safe152(uint256 x) internal pure returns (uint152 y) {
        if ((y = uint152(x)) != x) revert SafeCast__Exceeds152Bits();
    }

    /**
     * @dev Returns x on uint144 and check that it does not overflow
     * @param x The value as an uint256
     * @return y The value as an uint144
     */
    function safe144(uint256 x) internal pure returns (uint144 y) {
        if ((y = uint144(x)) != x) revert SafeCast__Exceeds144Bits();
    }

    /**
     * @dev Returns x on uint136 and check that it does not overflow
     * @param x The value as an uint256
     * @return y The value as an uint136
     */
    function safe136(uint256 x) internal pure returns (uint136 y) {
        if ((y = uint136(x)) != x) revert SafeCast__Exceeds136Bits();
    }

    /**
     * @dev Returns x on uint128 and check that it does not overflow
     * @param x The value as an uint256
     * @return y The value as an uint128
     */
    function safe128(uint256 x) internal pure returns (uint128 y) {
        if ((y = uint128(x)) != x) revert SafeCast__Exceeds128Bits();
    }

    /**
     * @dev Returns x on uint120 and check that it does not overflow
     * @param x The value as an uint256
     * @return y The value as an uint120
     */
    function safe120(uint256 x) internal pure returns (uint120 y) {
        if ((y = uint120(x)) != x) revert SafeCast__Exceeds120Bits();
    }

    /**
     * @dev Returns x on uint112 and check that it does not overflow
     * @param x The value as an uint256
     * @return y The value as an uint112
     */
    function safe112(uint256 x) internal pure returns (uint112 y) {
        if ((y = uint112(x)) != x) revert SafeCast__Exceeds112Bits();
    }

    /**
     * @dev Returns x on uint104 and check that it does not overflow
     * @param x The value as an uint256
     * @return y The value as an uint104
     */
    function safe104(uint256 x) internal pure returns (uint104 y) {
        if ((y = uint104(x)) != x) revert SafeCast__Exceeds104Bits();
    }

    /**
     * @dev Returns x on uint96 and check that it does not overflow
     * @param x The value as an uint256
     * @return y The value as an uint96
     */
    function safe96(uint256 x) internal pure returns (uint96 y) {
        if ((y = uint96(x)) != x) revert SafeCast__Exceeds96Bits();
    }

    /**
     * @dev Returns x on uint88 and check that it does not overflow
     * @param x The value as an uint256
     * @return y The value as an uint88
     */
    function safe88(uint256 x) internal pure returns (uint88 y) {
        if ((y = uint88(x)) != x) revert SafeCast__Exceeds88Bits();
    }

    /**
     * @dev Returns x on uint80 and check that it does not overflow
     * @param x The value as an uint256
     * @return y The value as an uint80
     */
    function safe80(uint256 x) internal pure returns (uint80 y) {
        if ((y = uint80(x)) != x) revert SafeCast__Exceeds80Bits();
    }

    /**
     * @dev Returns x on uint72 and check that it does not overflow
     * @param x The value as an uint256
     * @return y The value as an uint72
     */
    function safe72(uint256 x) internal pure returns (uint72 y) {
        if ((y = uint72(x)) != x) revert SafeCast__Exceeds72Bits();
    }

    /**
     * @dev Returns x on uint64 and check that it does not overflow
     * @param x The value as an uint256
     * @return y The value as an uint64
     */
    function safe64(uint256 x) internal pure returns (uint64 y) {
        if ((y = uint64(x)) != x) revert SafeCast__Exceeds64Bits();
    }

    /**
     * @dev Returns x on uint56 and check that it does not overflow
     * @param x The value as an uint256
     * @return y The value as an uint56
     */
    function safe56(uint256 x) internal pure returns (uint56 y) {
        if ((y = uint56(x)) != x) revert SafeCast__Exceeds56Bits();
    }

    /**
     * @dev Returns x on uint48 and check that it does not overflow
     * @param x The value as an uint256
     * @return y The value as an uint48
     */
    function safe48(uint256 x) internal pure returns (uint48 y) {
        if ((y = uint48(x)) != x) revert SafeCast__Exceeds48Bits();
    }

    /**
     * @dev Returns x on uint40 and check that it does not overflow
     * @param x The value as an uint256
     * @return y The value as an uint40
     */
    function safe40(uint256 x) internal pure returns (uint40 y) {
        if ((y = uint40(x)) != x) revert SafeCast__Exceeds40Bits();
    }

    /**
     * @dev Returns x on uint32 and check that it does not overflow
     * @param x The value as an uint256
     * @return y The value as an uint32
     */
    function safe32(uint256 x) internal pure returns (uint32 y) {
        if ((y = uint32(x)) != x) revert SafeCast__Exceeds32Bits();
    }

    /**
     * @dev Returns x on uint24 and check that it does not overflow
     * @param x The value as an uint256
     * @return y The value as an uint24
     */
    function safe24(uint256 x) internal pure returns (uint24 y) {
        if ((y = uint24(x)) != x) revert SafeCast__Exceeds24Bits();
    }

    /**
     * @dev Returns x on uint16 and check that it does not overflow
     * @param x The value as an uint256
     * @return y The value as an uint16
     */
    function safe16(uint256 x) internal pure returns (uint16 y) {
        if ((y = uint16(x)) != x) revert SafeCast__Exceeds16Bits();
    }

    /**
     * @dev Returns x on uint8 and check that it does not overflow
     * @param x The value as an uint256
     * @return y The value as an uint8
     */
    function safe8(uint256 x) internal pure returns (uint8 y) {
        if ((y = uint8(x)) != x) revert SafeCast__Exceeds8Bits();
    }
}

File 24 of 36 : IWNative.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

import "openzeppelin-upgradeable/token/ERC20/IERC20Upgradeable.sol";

/**
 * @title Wrapped Native Interface
 * @author Trader Joe
 * @notice Interface used to interact with wNative tokens
 */
interface IWNative is IERC20Upgradeable {
    function deposit() external payable;

    function withdraw(uint256 wad) external;
}

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

pragma solidity ^0.8.0;

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

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

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

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

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

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

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

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

File 26 of 36 : ILBFactory.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.10;

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

import {ILBPair} from "./ILBPair.sol";
import {IPendingOwnable} from "./IPendingOwnable.sol";

/**
 * @title Liquidity Book Factory Interface
 * @author Trader Joe
 * @notice Required interface of LBFactory contract
 */
interface ILBFactory is IPendingOwnable {
    error LBFactory__IdenticalAddresses(IERC20 token);
    error LBFactory__QuoteAssetNotWhitelisted(IERC20 quoteAsset);
    error LBFactory__QuoteAssetAlreadyWhitelisted(IERC20 quoteAsset);
    error LBFactory__AddressZero();
    error LBFactory__LBPairAlreadyExists(IERC20 tokenX, IERC20 tokenY, uint256 _binStep);
    error LBFactory__LBPairDoesNotExist(IERC20 tokenX, IERC20 tokenY, uint256 binStep);
    error LBFactory__LBPairNotCreated(IERC20 tokenX, IERC20 tokenY, uint256 binStep);
    error LBFactory__FlashLoanFeeAboveMax(uint256 fees, uint256 maxFees);
    error LBFactory__BinStepTooLow(uint256 binStep);
    error LBFactory__PresetIsLockedForUsers(address user, uint256 binStep);
    error LBFactory__LBPairIgnoredIsAlreadyInTheSameState();
    error LBFactory__BinStepHasNoPreset(uint256 binStep);
    error LBFactory__PresetOpenStateIsAlreadyInTheSameState();
    error LBFactory__SameFeeRecipient(address feeRecipient);
    error LBFactory__SameFlashLoanFee(uint256 flashLoanFee);
    error LBFactory__LBPairSafetyCheckFailed(address LBPairImplementation);
    error LBFactory__SameImplementation(address LBPairImplementation);
    error LBFactory__ImplementationNotSet();

    /**
     * @dev Structure to store the LBPair information, such as:
     * binStep: The bin step of the LBPair
     * LBPair: The address of the LBPair
     * createdByOwner: Whether the pair was created by the owner of the factory
     * ignoredForRouting: Whether the pair is ignored for routing or not. An ignored pair will not be explored during routes finding
     */
    struct LBPairInformation {
        uint16 binStep;
        ILBPair LBPair;
        bool createdByOwner;
        bool ignoredForRouting;
    }

    event LBPairCreated(
        IERC20 indexed tokenX, IERC20 indexed tokenY, uint256 indexed binStep, ILBPair LBPair, uint256 pid
    );

    event FeeRecipientSet(address oldRecipient, address newRecipient);

    event FlashLoanFeeSet(uint256 oldFlashLoanFee, uint256 newFlashLoanFee);

    event LBPairImplementationSet(address oldLBPairImplementation, address LBPairImplementation);

    event LBPairIgnoredStateChanged(ILBPair indexed LBPair, bool ignored);

    event PresetSet(
        uint256 indexed binStep,
        uint256 baseFactor,
        uint256 filterPeriod,
        uint256 decayPeriod,
        uint256 reductionFactor,
        uint256 variableFeeControl,
        uint256 protocolShare,
        uint256 maxVolatilityAccumulator
    );

    event PresetOpenStateChanged(uint256 indexed binStep, bool indexed isOpen);

    event PresetRemoved(uint256 indexed binStep);

    event QuoteAssetAdded(IERC20 indexed quoteAsset);

    event QuoteAssetRemoved(IERC20 indexed quoteAsset);

    function getMinBinStep() external pure returns (uint256);

    function getFeeRecipient() external view returns (address);

    function getMaxFlashLoanFee() external pure returns (uint256);

    function getFlashLoanFee() external view returns (uint256);

    function getLBPairImplementation() external view returns (address);

    function getNumberOfLBPairs() external view returns (uint256);

    function getLBPairAtIndex(uint256 id) external returns (ILBPair);

    function getNumberOfQuoteAssets() external view returns (uint256);

    function getQuoteAssetAtIndex(uint256 index) external view returns (IERC20);

    function isQuoteAsset(IERC20 token) external view returns (bool);

    function getLBPairInformation(IERC20 tokenX, IERC20 tokenY, uint256 binStep)
        external
        view
        returns (LBPairInformation memory);

    function getPreset(uint256 binStep)
        external
        view
        returns (
            uint256 baseFactor,
            uint256 filterPeriod,
            uint256 decayPeriod,
            uint256 reductionFactor,
            uint256 variableFeeControl,
            uint256 protocolShare,
            uint256 maxAccumulator,
            bool isOpen
        );

    function getAllBinSteps() external view returns (uint256[] memory presetsBinStep);

    function getOpenBinSteps() external view returns (uint256[] memory openBinStep);

    function getAllLBPairs(IERC20 tokenX, IERC20 tokenY)
        external
        view
        returns (LBPairInformation[] memory LBPairsBinStep);

    function setLBPairImplementation(address lbPairImplementation) external;

    function createLBPair(IERC20 tokenX, IERC20 tokenY, uint24 activeId, uint16 binStep)
        external
        returns (ILBPair pair);

    function setLBPairIgnored(IERC20 tokenX, IERC20 tokenY, uint16 binStep, bool ignored) external;

    function setPreset(
        uint16 binStep,
        uint16 baseFactor,
        uint16 filterPeriod,
        uint16 decayPeriod,
        uint16 reductionFactor,
        uint24 variableFeeControl,
        uint16 protocolShare,
        uint24 maxVolatilityAccumulator,
        bool isOpen
    ) external;

    function setPresetOpenState(uint16 binStep, bool isOpen) external;

    function removePreset(uint16 binStep) external;

    function setFeesParametersOnPair(
        IERC20 tokenX,
        IERC20 tokenY,
        uint16 binStep,
        uint16 baseFactor,
        uint16 filterPeriod,
        uint16 decayPeriod,
        uint16 reductionFactor,
        uint24 variableFeeControl,
        uint16 protocolShare,
        uint24 maxVolatilityAccumulator
    ) external;

    function setFeeRecipient(address feeRecipient) external;

    function setFlashLoanFee(uint256 flashLoanFee) external;

    function addQuoteAsset(IERC20 quoteAsset) external;

    function removeQuoteAsset(IERC20 quoteAsset) external;

    function forceDecay(ILBPair lbPair) external;
}

File 27 of 36 : ILBFlashLoanCallback.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.10;

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

/// @title Liquidity Book Flashloan Callback Interface
/// @author Trader Joe
/// @notice Required interface to interact with LB flash loans
interface ILBFlashLoanCallback {
    function LBFlashLoanCallback(
        address sender,
        IERC20 tokenX,
        IERC20 tokenY,
        bytes32 amounts,
        bytes32 totalFees,
        bytes calldata data
    ) external returns (bytes32);
}

File 28 of 36 : ILBToken.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.10;

/**
 * @title Liquidity Book Token Interface
 * @author Trader Joe
 * @notice Interface to interact with the LBToken.
 */
interface ILBToken {
    error LBToken__AddressThisOrZero();
    error LBToken__InvalidLength();
    error LBToken__SelfApproval(address owner);
    error LBToken__SpenderNotApproved(address from, address spender);
    error LBToken__TransferExceedsBalance(address from, uint256 id, uint256 amount);
    error LBToken__BurnExceedsBalance(address from, uint256 id, uint256 amount);

    event TransferBatch(
        address indexed sender, address indexed from, address indexed to, uint256[] ids, uint256[] amounts
    );

    event ApprovalForAll(address indexed account, address indexed sender, bool approved);

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

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

    function totalSupply(uint256 id) external view returns (uint256);

    function balanceOf(address account, uint256 id) external view returns (uint256);

    function balanceOfBatch(address[] calldata accounts, uint256[] calldata ids)
        external
        view
        returns (uint256[] memory);

    function isApprovedForAll(address owner, address spender) external view returns (bool);

    function approveForAll(address spender, bool approved) external;

    function batchTransferFrom(address from, address to, uint256[] calldata ids, uint256[] calldata amounts) external;
}

File 29 of 36 : draft-IERC20Permit.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (token/ERC20/extensions/draft-IERC20Permit.sol)

pragma solidity ^0.8.0;

/**
 * @dev Interface of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in
 * https://eips.ethereum.org/EIPS/eip-2612[EIP-2612].
 *
 * Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by
 * presenting a message signed by the account. By not relying on {IERC20-approve}, the token holder account doesn't
 * need to send a transaction, and thus is not required to hold Ether at all.
 */
interface IERC20Permit {
    /**
     * @dev Sets `value` as the allowance of `spender` over ``owner``'s tokens,
     * given ``owner``'s signed approval.
     *
     * IMPORTANT: The same issues {IERC20-approve} has related to transaction
     * ordering also apply here.
     *
     * Emits an {Approval} event.
     *
     * Requirements:
     *
     * - `spender` cannot be the zero address.
     * - `deadline` must be a timestamp in the future.
     * - `v`, `r` and `s` must be a valid `secp256k1` signature from `owner`
     * over the EIP712-formatted function arguments.
     * - the signature must use ``owner``'s current nonce (see {nonces}).
     *
     * For more information on the signature format, see the
     * https://eips.ethereum.org/EIPS/eip-2612#specification[relevant EIP
     * section].
     */
    function permit(
        address owner,
        address spender,
        uint256 value,
        uint256 deadline,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) external;

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

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

File 30 of 36 : Address.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.0) (utils/Address.sol)

pragma solidity ^0.8.1;

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

        return account.code.length > 0;
    }

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

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

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

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

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

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

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

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

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

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

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

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

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

File 31 of 36 : IERC20MetadataUpgradeable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (token/ERC20/extensions/IERC20Metadata.sol)

pragma solidity ^0.8.0;

import "../IERC20Upgradeable.sol";

/**
 * @dev Interface for the optional metadata functions from the ERC20 standard.
 *
 * _Available since v4.1._
 */
interface IERC20MetadataUpgradeable is IERC20Upgradeable {
    /**
     * @dev Returns the name of the token.
     */
    function name() external view returns (string memory);

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

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

File 32 of 36 : ContextUpgradeable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/Context.sol)

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

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

    function __Context_init_unchained() internal onlyInitializing {
    }
    function _msgSender() internal view virtual returns (address) {
        return msg.sender;
    }

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

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

File 33 of 36 : Initializable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.1) (proxy/utils/Initializable.sol)

pragma solidity ^0.8.2;

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

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

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

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

    /**
     * @dev A modifier that defines a protected initializer function that can be invoked at most once. In its scope,
     * `onlyInitializing` functions can be used to initialize parent contracts.
     *
     * Similar to `reinitializer(1)`, except that functions marked with `initializer` can be nested in the context of a
     * constructor.
     *
     * Emits an {Initialized} event.
     */
    modifier initializer() {
        bool isTopLevelCall = !_initializing;
        require(
            (isTopLevelCall && _initialized < 1) || (!AddressUpgradeable.isContract(address(this)) && _initialized == 1),
            "Initializable: contract is already initialized"
        );
        _initialized = 1;
        if (isTopLevelCall) {
            _initializing = true;
        }
        _;
        if (isTopLevelCall) {
            _initializing = false;
            emit Initialized(1);
        }
    }

    /**
     * @dev A modifier that defines a protected reinitializer function that can be invoked at most once, and only if the
     * contract hasn't been initialized to a greater version before. In its scope, `onlyInitializing` functions can be
     * used to initialize parent contracts.
     *
     * A reinitializer may be used after the original initialization step. This is essential to configure modules that
     * are added through upgrades and that require initialization.
     *
     * When `version` is 1, this modifier is similar to `initializer`, except that functions marked with `reinitializer`
     * cannot be nested. If one is invoked in the context of another, execution will revert.
     *
     * Note that versions can jump in increments greater than 1; this implies that if multiple reinitializers coexist in
     * a contract, executing them in the right order is up to the developer or operator.
     *
     * WARNING: setting the version to 255 will prevent any future reinitialization.
     *
     * Emits an {Initialized} event.
     */
    modifier reinitializer(uint8 version) {
        require(!_initializing && _initialized < version, "Initializable: contract is already initialized");
        _initialized = version;
        _initializing = true;
        _;
        _initializing = false;
        emit Initialized(version);
    }

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

    /**
     * @dev Locks the contract, preventing any future reinitialization. This cannot be part of an initializer call.
     * Calling this in the constructor of a contract will prevent that contract from being initialized or reinitialized
     * to any version. It is recommended to use this to lock implementation contracts that are designed to be called
     * through proxies.
     *
     * Emits an {Initialized} event the first time it is successfully executed.
     */
    function _disableInitializers() internal virtual {
        require(!_initializing, "Initializable: contract is initializing");
        if (_initialized < type(uint8).max) {
            _initialized = type(uint8).max;
            emit Initialized(type(uint8).max);
        }
    }

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

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

File 34 of 36 : draft-IERC20PermitUpgradeable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (token/ERC20/extensions/draft-IERC20Permit.sol)

pragma solidity ^0.8.0;

/**
 * @dev Interface of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in
 * https://eips.ethereum.org/EIPS/eip-2612[EIP-2612].
 *
 * Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by
 * presenting a message signed by the account. By not relying on {IERC20-approve}, the token holder account doesn't
 * need to send a transaction, and thus is not required to hold Ether at all.
 */
interface IERC20PermitUpgradeable {
    /**
     * @dev Sets `value` as the allowance of `spender` over ``owner``'s tokens,
     * given ``owner``'s signed approval.
     *
     * IMPORTANT: The same issues {IERC20-approve} has related to transaction
     * ordering also apply here.
     *
     * Emits an {Approval} event.
     *
     * Requirements:
     *
     * - `spender` cannot be the zero address.
     * - `deadline` must be a timestamp in the future.
     * - `v`, `r` and `s` must be a valid `secp256k1` signature from `owner`
     * over the EIP712-formatted function arguments.
     * - the signature must use ``owner``'s current nonce (see {nonces}).
     *
     * For more information on the signature format, see the
     * https://eips.ethereum.org/EIPS/eip-2612#specification[relevant EIP
     * section].
     */
    function permit(
        address owner,
        address spender,
        uint256 value,
        uint256 deadline,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) external;

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

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

File 35 of 36 : AddressUpgradeable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.0) (utils/Address.sol)

pragma solidity ^0.8.1;

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

        return account.code.length > 0;
    }

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

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

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

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

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

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

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

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

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

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

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

File 36 of 36 : IPendingOwnable.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.10;

/**
 * @title Liquidity Book Pending Ownable Interface
 * @author Trader Joe
 * @notice Required interface of Pending Ownable contract used for LBFactory
 */
interface IPendingOwnable {
    error PendingOwnable__AddressZero();
    error PendingOwnable__NoPendingOwner();
    error PendingOwnable__NotOwner();
    error PendingOwnable__NotPendingOwner();
    error PendingOwnable__PendingOwnerAlreadySet();

    event PendingOwnerSet(address indexed pendingOwner);

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

    function owner() external view returns (address);

    function pendingOwner() external view returns (address);

    function setPendingOwner(address pendingOwner) external;

    function revokePendingOwner() external;

    function becomeOwner() external;

    function renounceOwnership() external;
}

Settings
{
  "remappings": [
    "clones-with-immutable-args/=lib/clones-with-immutable-args/src/",
    "ds-test/=lib/forge-std/lib/ds-test/src/",
    "forge-std/=lib/forge-std/src/",
    "joe-v2-periphery/=lib/joe-v2-periphery/src/",
    "joe-v2/=lib/joe-v2/src/",
    "openzeppelin-contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/",
    "openzeppelin-contracts/=lib/openzeppelin-contracts/",
    "openzeppelin-upgradeable/=lib/openzeppelin-contracts-upgradeable/contracts/",
    "openzeppelin/=lib/openzeppelin-contracts/contracts/",
    "erc4626-tests/=lib/joe-v2-periphery/lib/joe-v2/lib/openzeppelin-contracts/lib/erc4626-tests/"
  ],
  "optimizer": {
    "enabled": true,
    "runs": 800
  },
  "metadata": {
    "useLiteralContent": false,
    "bytecodeHash": "ipfs"
  },
  "outputSelection": {
    "*": {
      "*": [
        "evm.bytecode",
        "evm.deployedBytecode",
        "devdoc",
        "userdoc",
        "metadata",
        "abi"
      ]
    }
  },
  "evmVersion": "london",
  "viaIR": false,
  "libraries": {}
}

Contract Security Audit

Contract ABI

API
[{"inputs":[{"internalType":"contract IVaultFactory","name":"factory","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[{"internalType":"address","name":"user","type":"address"}],"name":"BaseVault__AlreadyWhitelisted","type":"error"},{"inputs":[],"name":"BaseVault__BurnMinShares","type":"error"},{"inputs":[],"name":"BaseVault__DepositsPaused","type":"error"},{"inputs":[],"name":"BaseVault__InsufficientShares","type":"error"},{"inputs":[],"name":"BaseVault__InvalidNativeAmount","type":"error"},{"inputs":[],"name":"BaseVault__InvalidRecipient","type":"error"},{"inputs":[],"name":"BaseVault__InvalidRound","type":"error"},{"inputs":[],"name":"BaseVault__InvalidShares","type":"error"},{"inputs":[],"name":"BaseVault__InvalidStrategy","type":"error"},{"inputs":[],"name":"BaseVault__InvalidToken","type":"error"},{"inputs":[],"name":"BaseVault__MaxSharesExceeded","type":"error"},{"inputs":[],"name":"BaseVault__NativeTransferFailed","type":"error"},{"inputs":[],"name":"BaseVault__NoNativeToken","type":"error"},{"inputs":[],"name":"BaseVault__NoQueuedWithdrawal","type":"error"},{"inputs":[],"name":"BaseVault__NotInEmergencyMode","type":"error"},{"inputs":[{"internalType":"address","name":"user","type":"address"}],"name":"BaseVault__NotWhitelisted","type":"error"},{"inputs":[],"name":"BaseVault__OnlyFactory","type":"error"},{"inputs":[],"name":"BaseVault__OnlyOperators","type":"error"},{"inputs":[],"name":"BaseVault__OnlyStrategy","type":"error"},{"inputs":[],"name":"BaseVault__OnlyWNative","type":"error"},{"inputs":[],"name":"BaseVault__SameStrategy","type":"error"},{"inputs":[],"name":"BaseVault__SameWhitelistState","type":"error"},{"inputs":[],"name":"BaseVault__Unauthorized","type":"error"},{"inputs":[],"name":"BaseVault__ZeroAmount","type":"error"},{"inputs":[],"name":"BaseVault__ZeroShares","type":"error"},{"inputs":[],"name":"OracleVault__GracePeriodNotOver","type":"error"},{"inputs":[],"name":"OracleVault__InvalidInterval","type":"error"},{"inputs":[],"name":"OracleVault__InvalidPrice","type":"error"},{"inputs":[],"name":"OracleVault__InvalidTimestamps","type":"error"},{"inputs":[],"name":"OracleVault__PriceDeviation","type":"error"},{"inputs":[],"name":"OracleVault__SequencerDown","type":"error"},{"inputs":[],"name":"OracleVault__StalePrice","type":"error"},{"inputs":[],"name":"TokenHelper__NativeTransferFailed","type":"error"},{"inputs":[],"name":"Uint256x256Math__MulDivOverflow","type":"error"},{"inputs":[],"name":"Uint256x256Math__MulShiftOverflow","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"spender","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":false,"internalType":"uint256","name":"amountX","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amountY","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"shares","type":"uint256"}],"name":"Deposited","type":"event"},{"anonymous":false,"inputs":[],"name":"DepositsPaused","type":"event"},{"anonymous":false,"inputs":[],"name":"DepositsResumed","type":"event"},{"anonymous":false,"inputs":[],"name":"EmergencyMode","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"sender","type":"address"},{"indexed":false,"internalType":"uint256","name":"shares","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amountX","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amountY","type":"uint256"}],"name":"EmergencyWithdrawal","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint8","name":"version","type":"uint8"}],"name":"Initialized","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"timestamp","type":"uint256"},{"indexed":true,"internalType":"uint256","name":"accRewardShare","type":"uint256"}],"name":"PoolUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"token","type":"address"},{"indexed":false,"internalType":"address","name":"recipient","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"Recovered","type":"event"},{"anonymous":false,"inputs":[],"name":"ShutdownCancelled","type":"event"},{"anonymous":false,"inputs":[],"name":"ShutdownSubmitted","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"contract IStrategy","name":"strategy","type":"address"}],"name":"StrategySet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"Transfer","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"sender","type":"address"},{"indexed":true,"internalType":"address","name":"recipient","type":"address"},{"indexed":true,"internalType":"uint256","name":"round","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"shares","type":"uint256"}],"name":"WithdrawalCancelled","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"round","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"totalQueuedQhares","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amountX","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amountY","type":"uint256"}],"name":"WithdrawalExecuted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"sender","type":"address"},{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":true,"internalType":"uint256","name":"round","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"shares","type":"uint256"}],"name":"WithdrawalQueued","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"sender","type":"address"},{"indexed":true,"internalType":"address","name":"recipient","type":"address"},{"indexed":true,"internalType":"uint256","name":"round","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"shares","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amountX","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amountY","type":"uint256"}],"name":"WithdrawalRedeemed","type":"event"},{"stateMutability":"payable","type":"fallback"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"_users","outputs":[{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"rewardDebt","type":"uint256"},{"internalType":"uint256","name":"extraRewardDebt","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"spender","type":"address"}],"name":"allowance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"approve","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"shares","type":"uint256"}],"name":"cancelQueuedWithdrawal","outputs":[{"internalType":"uint256","name":"round","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"cancelShutdown","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"claim","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"decimals","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"subtractedValue","type":"uint256"}],"name":"decreaseAllowance","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amountX","type":"uint256"},{"internalType":"uint256","name":"amountY","type":"uint256"},{"internalType":"uint256","name":"minShares","type":"uint256"}],"name":"deposit","outputs":[{"internalType":"uint256","name":"shares","type":"uint256"},{"internalType":"uint256","name":"effectiveX","type":"uint256"},{"internalType":"uint256","name":"effectiveY","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amountX","type":"uint256"},{"internalType":"uint256","name":"amountY","type":"uint256"},{"internalType":"uint256","name":"minShares","type":"uint256"}],"name":"depositNative","outputs":[{"internalType":"uint256","name":"shares","type":"uint256"},{"internalType":"uint256","name":"effectiveX","type":"uint256"},{"internalType":"uint256","name":"effectiveY","type":"uint256"}],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"emergencyWithdraw","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bool","name":"enabled","type":"bool"}],"name":"enableTWAPPriceCheck","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"executeQueuedWithdrawals","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"getAumAnnualFee","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getBalances","outputs":[{"internalType":"uint256","name":"amountX","type":"uint256"},{"internalType":"uint256","name":"amountY","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getCurrentRound","outputs":[{"internalType":"uint256","name":"round","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getCurrentTotalQueuedWithdrawal","outputs":[{"internalType":"uint256","name":"totalQueuedShares","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getFactory","outputs":[{"internalType":"contract IVaultFactory","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getLastRewardBalances","outputs":[{"internalType":"uint256","name":"lastRewardBalance","type":"uint256"},{"internalType":"uint256","name":"lastExtraRewardBalance","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getOperators","outputs":[{"internalType":"address","name":"defaultOperator","type":"address"},{"internalType":"address","name":"operator","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getOracleParameters","outputs":[{"internalType":"uint256","name":"minPrice","type":"uint256"},{"internalType":"uint256","name":"maxPrice","type":"uint256"},{"internalType":"uint256","name":"heartbeatX","type":"uint256"},{"internalType":"uint256","name":"heartbeatY","type":"uint256"},{"internalType":"uint256","name":"deviationThreshold","type":"uint256"},{"internalType":"bool","name":"twapPriceCheckEnabled","type":"bool"},{"internalType":"uint40","name":"twapInterval","type":"uint40"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getOracleX","outputs":[{"internalType":"contract IAggregatorV3","name":"oracleX","type":"address"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"getOracleY","outputs":[{"internalType":"contract IAggregatorV3","name":"oracleY","type":"address"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"getPair","outputs":[{"internalType":"contract ILBPair","name":"","type":"address"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"}],"name":"getPendingRewards","outputs":[{"internalType":"uint256","name":"rewards","type":"uint256"},{"internalType":"uint256","name":"extraRewards","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getPrice","outputs":[{"internalType":"uint256","name":"price","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"round","type":"uint256"},{"internalType":"address","name":"user","type":"address"}],"name":"getQueuedWithdrawal","outputs":[{"internalType":"uint256","name":"shares","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getRange","outputs":[{"internalType":"uint24","name":"low","type":"uint24"},{"internalType":"uint24","name":"upper","type":"uint24"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"round","type":"uint256"},{"internalType":"address","name":"user","type":"address"}],"name":"getRedeemableAmounts","outputs":[{"internalType":"uint256","name":"amountX","type":"uint256"},{"internalType":"uint256","name":"amountY","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getStrategy","outputs":[{"internalType":"contract IStrategy","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getTokenX","outputs":[{"internalType":"contract IERC20Upgradeable","name":"","type":"address"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"getTokenY","outputs":[{"internalType":"contract IERC20Upgradeable","name":"","type":"address"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"round","type":"uint256"}],"name":"getTotalQueuedWithdrawal","outputs":[{"internalType":"uint256","name":"totalQueuedShares","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"}],"name":"getUserInfo","outputs":[{"components":[{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"rewardDebt","type":"uint256"},{"internalType":"uint256","name":"extraRewardDebt","type":"uint256"}],"internalType":"struct IOracleRewardVault.User","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"addedValue","type":"uint256"}],"name":"increaseAllowance","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string","name":"name","type":"string"},{"internalType":"string","name":"symbol","type":"string"}],"name":"initialize","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"isDepositsPaused","outputs":[{"internalType":"bool","name":"paused","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"isFlaggedForShutdown","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"pauseDeposits","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"shares","type":"uint256"}],"name":"previewAmounts","outputs":[{"internalType":"uint256","name":"amountX","type":"uint256"},{"internalType":"uint256","name":"amountY","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"amountX","type":"uint256"},{"internalType":"uint256","name":"amountY","type":"uint256"}],"name":"previewShares","outputs":[{"internalType":"uint256","name":"shares","type":"uint256"},{"internalType":"uint256","name":"effectiveX","type":"uint256"},{"internalType":"uint256","name":"effectiveY","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"shares","type":"uint256"},{"internalType":"address","name":"recipient","type":"address"}],"name":"queueWithdrawal","outputs":[{"internalType":"uint256","name":"round","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract IERC20Upgradeable","name":"token","type":"address"},{"internalType":"address","name":"recipient","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"recoverERC20","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"round","type":"uint256"},{"internalType":"address","name":"recipient","type":"address"}],"name":"redeemQueuedWithdrawal","outputs":[{"internalType":"uint256","name":"amountX","type":"uint256"},{"internalType":"uint256","name":"amountY","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"round","type":"uint256"},{"internalType":"address","name":"recipient","type":"address"}],"name":"redeemQueuedWithdrawalNative","outputs":[{"internalType":"uint256","name":"amountX","type":"uint256"},{"internalType":"uint256","name":"amountY","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"resumeDeposits","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint24","name":"heartbeatX","type":"uint24"},{"internalType":"uint24","name":"heartbeatY","type":"uint24"}],"name":"setDatafeedHeartbeat","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"threshold","type":"uint256"}],"name":"setDeviationThreshold","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"setEmergencyMode","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"minPrice","type":"uint256"},{"internalType":"uint256","name":"maxPrice","type":"uint256"}],"name":"setMinMaxPrice","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract IAggregatorV3","name":"sequencerUptimeFeed","type":"address"}],"name":"setSequenzerUptimeFeed","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract IStrategy","name":"newStrategy","type":"address"}],"name":"setStrategy","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint40","name":"interval","type":"uint40"}],"name":"setTwapInterval","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"submitShutdown","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"transfer","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"transferFrom","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"stateMutability":"payable","type":"receive"}]

60c06040523480156200001157600080fd5b50604051620061a8380380620061a88339810160408190526200003491620000e4565b8080806001600160a01b03166080816001600160a01b031681525050806001600160a01b031663719a08a36040518163ffffffff1660e01b8152600401602060405180830381865afa1580156200008f573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190620000b59190620000e4565b6001600160a01b031660a052506200010b915050565b6001600160a01b0381168114620000e157600080fd5b50565b600060208284031215620000f757600080fd5b81516200010481620000cb565b9392505050565b60805160a051615fc1620001e7600039600081816103a9015281816103f4015281816118500152818161189601528181612c9c01528181612ce201528181612d5501526142c401526000818161098001528181610d8801528181610ec90152818161109c015281816111b401528181611b8501528181611cb1015281816122160152818161230c015281816123aa015281816124d401528181612615015281816128e601528181612c1201528181612f90015281816130d1015281816132260152818161349e015281816141520152614b2b0152615fc16000f3fe6080604052600436106103995760003560e01c806355182894116101dc578063c5a160d111610102578063dbb6bfba116100a0578063f3bc7ef01161006f578063f3bc7ef014610c0d578063f5553c1a14610c22578063f6ed201714610c42578063fcdb2ebc14610c62576103e9565b8063dbb6bfba14610b7f578063dd62ed3e14610b94578063e1730da714610bda578063f33467d414610bed576103e9565b8063ce8120f8116100dc578063ce8120f814610b05578063d58457b214610b25578063da10610c14610b45578063db2e21bc14610b6a576103e9565b8063c5a160d114610ab0578063cb02636814610ad0578063cdaa70c414610af0576103e9565b806395d89b411161017a578063a32bf59711610149578063a32bf59714610a39578063a457c2d714610a4e578063a9059cbb14610a6e578063c1f1b1b514610a8e576103e9565b806395d89b41146109c957806398d5fdca146109de5780639b85961f146109f35780639ca0ac3814610a24576103e9565b806370a08231116101b657806370a082311461091b5780637611bf0a1461095157806388cc58e4146109715780638a061f0b146109a4576103e9565b806355182894146108395780636386c1c7146108b95780636791a389146108fb576103e9565b80631c776443116102c1578063395093511161025f5780634f9c8f9d1161022e5780634f9c8f9d146107b4578063534947ae146107d957806353a15edc146107f957806354d29c3814610819576103e9565b806339509351146107235780633cacd7d6146107435780634cd88b761461077f5780634e71d92d1461079f576103e9565b806327a099d81161029b57806327a099d81461068f578063313ce567146106c4578063330e93bf146106eb57806333a100ca14610703576103e9565b80631c7764431461063b57806323b872dd1461065057806327042b8414610670576103e9565b8063085d0b83116103395780631589ea29116103085780631589ea29146105c457806318160ddd146105dc57806319fa7a15146105fb5780631b0aed2c1461061b576103e9565b8063085d0b831461054a578063095ea7b31461055f5780631171bda91461058f578063152c7c4e146105af576103e9565b806303c162121161037557806303c16212146104b157806305e8746d146104d157806306fdde031461050a57806307da06031461052c576103e9565b8062113e0814610432578062aeef8a14610461578063021919801461049c576103e9565b366103e957336001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016146103e75760405163754ed24960e01b815260040160405180910390fd5b005b336001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016146103e75760405163754ed24960e01b815260040160405180910390fd5b34801561043e57600080fd5b50610447610c82565b604080519283526020830191909152015b60405180910390f35b34801561046d57600080fd5b5061048161047c366004615827565b610ca5565b60408051938452602084019290925290820152606001610458565b3480156104a857600080fd5b506103e7610d63565b3480156104bd57600080fd5b506104476104cc366004615853565b610fcd565b3480156104dd57600080fd5b503660011981013560f01c90036014013560601c5b6040516001600160a01b039091168152602001610458565b34801561051657600080fd5b5061051f610fff565b6040516104589190615898565b34801561053857600080fd5b506097546001600160a01b03166104f2565b34801561055657600080fd5b506103e7611091565b34801561056b57600080fd5b5061057f61057a3660046158e0565b611187565b6040519015158152602001610458565b34801561059b57600080fd5b506103e76105aa36600461590c565b6111a1565b3480156105bb57600080fd5b506103e76115ea565b3480156105d057600080fd5b50609b5460ff1661057f565b3480156105e857600080fd5b506035545b604051908152602001610458565b34801561060757600080fd5b5061044761061636600461594d565b61183b565b34801561062757600080fd5b506105ed610636366004615853565b611979565b34801561064757600080fd5b506105ed611abd565b34801561065c57600080fd5b5061057f61066b36600461590c565b611b42565b34801561067c57600080fd5b50609754600160a01b900460ff1661057f565b34801561069b57600080fd5b506106a4611b66565b604080516001600160a01b03938416815292909116602083015201610458565b3480156106d057600080fd5b506106d9611c87565b60405160ff9091168152602001610458565b3480156106f757600080fd5b5060a25460a354610447565b34801561070f57600080fd5b506103e761071e36600461597d565b611ca6565b34801561072f57600080fd5b5061057f61073e3660046158e0565b61204a565b34801561074f57600080fd5b5061048161075e36600461597d565b60a56020526000908152604090208054600182015460029092015490919083565b34801561078b57600080fd5b506103e761079a366004615a3d565b612089565b3480156107ab57600080fd5b506103e76121c5565b3480156107c057600080fd5b503660011981013560f01c90036052013560601c6104f2565b3480156107e557600080fd5b506104816107f4366004615aa1565b6121e0565b34801561080557600080fd5b506103e7610814366004615853565b61220b565b34801561082557600080fd5b506105ed61083436600461594d565b612259565b34801561084557600080fd5b50609c54609d54609e54609f5460408051948552602085019390935262ffffff8083169385019390935263010000008204909216606084015260808301919091526601000000000000810460ff16151560a0830152670100000000000000900464ffffffffff1660c082015260e001610458565b3480156108c557600080fd5b506108d96108d436600461597d565b61229b565b6040805182518152602080840151908201529181015190820152606001610458565b34801561090757600080fd5b506103e7610916366004615ac3565b612301565b34801561092757600080fd5b506105ed61093636600461597d565b6001600160a01b031660009081526033602052604090205490565b34801561095d57600080fd5b506103e761096c366004615af8565b61239f565b34801561097d57600080fd5b507f00000000000000000000000000000000000000000000000000000000000000006104f2565b3480156109b057600080fd5b503660011981013560f01c9003603e013560601c6104f2565b3480156109d557600080fd5b5061051f61240c565b3480156109ea57600080fd5b506105ed61241b565b3480156109ff57600080fd5b50610a08612425565b6040805162ffffff938416815292909116602083015201610458565b348015610a3057600080fd5b506103e76124af565b348015610a4557600080fd5b506105ed612711565b348015610a5a57600080fd5b5061057f610a693660046158e0565b612723565b348015610a7a57600080fd5b5061057f610a893660046158e0565b6127cd565b348015610a9a57600080fd5b503660011981013560f01c90033560601c6104f2565b348015610abc57600080fd5b50610447610acb36600461594d565b6127db565b348015610adc57600080fd5b506105ed610aeb366004615853565b612872565b348015610afc57600080fd5b506105ed6128a0565b348015610b1157600080fd5b506103e7610b20366004615aa1565b6128db565b348015610b3157600080fd5b506105ed610b4036600461594d565b612950565b348015610b5157600080fd5b503660011981013560f01c90036028013560601c6104f2565b348015610b7657600080fd5b506103e7612aa2565b348015610b8b57600080fd5b506103e7612c07565b348015610ba057600080fd5b506105ed610baf366004615b15565b6001600160a01b03918216600090815260346020908152604080832093909416825291909152205490565b610481610be8366004615827565b612c85565b348015610bf957600080fd5b50610447610c0836600461594d565b612ee3565b348015610c1957600080fd5b506103e7612f6b565b348015610c2e57600080fd5b506103e7610c3d366004615b54565b61321b565b348015610c4e57600080fd5b50610447610c5d36600461597d565b613290565b348015610c6e57600080fd5b506103e7610c7d36600461597d565b613493565b6097546000908190610c9c906001600160a01b031661351b565b90939092509050565b6000806000610cb26136a6565b610cba613700565b6000610cc68787613a58565b91965094509250905084841015610cf057604051630ad1667b60e41b815260040160405180910390fd5b610cfa3385613b9c565b8215610d2b57610d2b3382853660011981013560f01c90036014013560601c5b6001600160a01b0316929190613c54565b8115610d4f57610d4f3382843660011981013560f01c90036028013560601c610d1a565b50610d5a6001606555565b93509350939050565b6000610d776097546001600160a01b031690565b6001600160a01b03161415610e3e577f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663b80ec4c76040518163ffffffff1660e01b8152600401602060405180830381865afa158015610de4573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610e089190615b82565b6001600160a01b0316336001600160a01b031614610e395760405163574d01c560e11b815260040160405180910390fd5b610f7d565b6097546001600160a01b03166001600160a01b031663e7f43c686040518163ffffffff1660e01b8152600401602060405180830381865afa158015610e87573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610eab9190615b82565b6001600160a01b0316336001600160a01b031614158015610f5f57507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663b80ec4c76040518163ffffffff1660e01b8152600401602060405180830381865afa158015610f25573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610f499190615b82565b6001600160a01b0316336001600160a01b031614155b15610f7d5760405163574d01c560e11b815260040160405180910390fd5b610f856136a6565b6097805460ff60a01b1916600160a01b1790556040517fdeeb69430b7153361c25d630947115165636e6a723fa8daea4b0de34b324745990600090a1610fcb6001606555565b565b6097546000908190610ff1906001600160a01b031684610fec60355490565b613cf3565b91509150915091565b905090565b60606036805461100e90615b9f565b80601f016020809104026020016040519081016040528092919081815260200182805461103a90615b9f565b80156110875780601f1061105c57610100808354040283529160200191611087565b820191906000526020600020905b81548152906001019060200180831161106a57829003601f168201915b5050505050905090565b336001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016146110da576040516369b562ff60e11b815260040160405180910390fd5b6110e26136a6565b609760009054906101000a90046001600160a01b03166001600160a01b031663853828b66040518163ffffffff1660e01b8152600401600060405180830381600087803b15801561113257600080fd5b505af1158015611146573d6000803e3d6000fd5b505050506111546000613d5d565b6040517f185079b91d6fc95ee30b386eaca3291e9bc2783621ef973a33d1450c41b6acf790600090a1610fcb6001606555565b600033611195818585613dc9565b60019150505b92915050565b6111a96136a6565b336001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016146111f2576040516369b562ff60e11b815260040160405180910390fd5b6097546001600160a01b03163660011981013560f01c90036014013560601c6001600160a01b0316846001600160a01b03161480156112b557506001600160a01b03811615806112b557508160995461124b9190615bf0565b6040516370a0823160e01b81523060048201526001600160a01b038616906370a0823190602401602060405180830381865afa15801561128f573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906112b39190615c08565b105b156112d35760405163b79333bb60e01b815260040160405180910390fd5b3660011981013560f01c90036028013560601c6001600160a01b03851614801561138157506001600160a01b0381161580611381575081609a546113179190615bf0565b6040516370a0823160e01b81523060048201526001600160a01b038616906370a0823190602401602060405180830381865afa15801561135b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061137f9190615c08565b105b1561139f5760405163b79333bb60e01b815260040160405180910390fd5b6001600160a01b0384163014156114425760006113ba6128a0565b6001600160a01b0383166000908152603360205260409020546113dd9190615c21565b90506113eb6006600a615d1c565b6113f59084615bf0565b30600090815260336020526040902054611410908390615bf0565b101561142f576040516328165e7760e01b815260040160405180910390fd5b801561144057611440823083613eed565b505b6114566001600160a01b0385168484613f38565b6001600160a01b03811615801561157157506099543660011981013560f01c90036014013560601c6040516370a0823160e01b81523060048201526001600160a01b0391909116906370a0823190602401602060405180830381865afa1580156114c4573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906114e89190615c08565b10806115715750609a543660011981013560f01c90036028013560601c6040516370a0823160e01b81523060048201526001600160a01b0391909116906370a0823190602401602060405180830381865afa15801561154b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061156f9190615c08565b105b1561158f5760405163b79333bb60e01b815260040160405180910390fd5b604080516001600160a01b038087168252851660208201529081018390527ffff3b3844276f57024e0b42afec1a37f75db36511e43819a4f2a63ab7862b6489060600160405180910390a1506115e56001606555565b505050565b6115f26136a6565b6097546001600160a01b031633811461161e57604051630639d2a760e11b815260040160405180910390fd5b60985460009061163090600190615c21565b905060006098828154811061164757611647615d2b565b9060005260206000209060030201905060008160010154905080600014156116725750505050611831565b61167c8482613f68565b6098805460010181556000908152609954609a549091823660011981013560f01c90036014013560601c6040516370a0823160e01b81523060048201526001600160a01b0391909116906370a0823190602401602060405180830381865afa1580156116ec573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906117109190615c08565b61171a9190615c21565b90506000823660011981013560f01c90036028013560601c6040516370a0823160e01b81523060048201526001600160a01b0391909116906370a0823190602401602060405180830381865afa158015611778573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061179c9190615c08565b6117a69190615c21565b90506117b28285615bf0565b6099556117bf8184615bf0565b609a556fffffffffffffffffffffffffffffffff818116600160801b02908316176002870155604080518681526020810184905290810182905287907f27cf0c9d33856fac5d46a73a8569c7ebf6470ffc7631d277fb593e62bafe3baf9060600160405180910390a250505050505050505b610fcb6001606555565b6000806118466136a6565b6001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000163660011981013560f01c90036014013560601c148015906118cc57506001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000163660011981013560f01c90036028013560601c14155b156118ea576040516330fcd9e760e01b815260040160405180910390fd5b826001600160a01b038116611912576040516302b70c7960e21b815260040160405180910390fd5b61191c858561409c565b90935091508215611944576119443660011981013560f01c90036014013560601c85856142c2565b8115611967576119673660011981013560f01c90036028013560601c85846142c2565b506119726001606555565b9250929050565b60006119836136a6565b81806119a25760405163ebca017960e01b815260040160405180910390fd5b6097546001600160a01b0316806119cc5760405163629c918560e01b815260040160405180910390fd5b6119d4613700565b6098546119e390600190615c21565b92506000609884815481106119fa576119fa615d2b565b6000918252602080832033845260039092029091019081905260409091205490915080861115611a3d57604051632979b43f60e11b815260040160405180910390fd5b33600081815260208490526040902087830390556001830180548890039055611a669087613b9c565b611a71833388613eed565b6040518681528590339081907fc0b6b4b152b639767dd4ffd28f2a0f05b4ffd672264a6c4d34447185a03cc5179060200160405180910390a450505050611ab86001606555565b919050565b6097546000906001600160a01b03168015611b3957806001600160a01b0316631c7764436040518163ffffffff1660e01b8152600401602060405180830381865afa158015611b10573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611b349190615c08565b611b3c565b60005b91505090565b600033611b5085828561436f565b611b5b858585613eed565b506001949350505050565b6000806000609760009054906101000a90046001600160a01b031690507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663b80ec4c76040518163ffffffff1660e01b8152600401602060405180830381865afa158015611be1573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611c059190615b82565b92506001600160a01b03811615611c7d57806001600160a01b031663e7f43c686040518163ffffffff1660e01b8152600401602060405180830381865afa158015611c54573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611c789190615b82565b611c80565b60005b9150509091565b6000610ffa60063660011981013560f01c9003603d013560f81c615d41565b336001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001614611cef576040516369b562ff60e11b815260040160405180910390fd5b611cf76136a6565b6097546001600160a01b03908116908216811415611d285760405163e4a1ddf760e01b815260040160405180910390fd5b306001600160a01b0316826001600160a01b0316638d928af86040518163ffffffff1660e01b8152600401602060405180830381865afa158015611d70573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611d949190615b82565b6001600160a01b0316141580611e2c57503660011981013560f01c90033560601c6001600160a01b0316826001600160a01b031663c1f1b1b56040518163ffffffff1660e01b8152600401602060405180830381865afa158015611dfc573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611e209190615b82565b6001600160a01b031614155b80611ebc57503660011981013560f01c90036014013560601c6001600160a01b0316826001600160a01b03166305e8746d6040518163ffffffff1660e01b8152600401602060405180830381865afa158015611e8c573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611eb09190615b82565b6001600160a01b031614155b80611f4c57503660011981013560f01c90036028013560601c6001600160a01b0316826001600160a01b031663da10610c6040518163ffffffff1660e01b8152600401602060405180830381865afa158015611f1c573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611f409190615b82565b6001600160a01b031614155b15611f6a5760405163629c918560e01b815260040160405180910390fd5b6001600160a01b03811615611fcd57806001600160a01b031663853828b66040518163ffffffff1660e01b8152600401600060405180830381600087803b158015611fb457600080fd5b505af1158015611fc8573d6000803e3d6000fd5b505050505b600080611fda600061351b565b9092509050811561200e5761200e84833660011981013560f01c90036014013560601c5b6001600160a01b03169190613f38565b80156120315761203184823660011981013560f01c90036028013560601c611ffe565b61203a84613d5d565b5050506120476001606555565b50565b3360008181526034602090815260408083206001600160a01b03871684529091528120549091906111959082908690612084908790615bf0565b613dc9565b600054610100900460ff16158080156120a95750600054600160ff909116105b806120c35750303b1580156120c3575060005460ff166001145b61213a5760405162461bcd60e51b815260206004820152602e60248201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160448201527f647920696e697469616c697a656400000000000000000000000000000000000060648201526084015b60405180910390fd5b6000805460ff19166001179055801561215d576000805461ff0019166101001790555b61216783836143fb565b61216f614474565b60988054600101815560005280156115e5576000805461ff0019169055604051600181527f7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb38474024989060200160405180910390a1505050565b6121cd6136a6565b6121d5613700565b611831336000613b9c565b609754600090819081906121fe906001600160a01b031686866144e7565b9250925092509250925092565b336001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001614612254576040516369b562ff60e11b815260040160405180910390fd5b609f55565b60006098838154811061226e5761226e615d2b565b600091825260208083206001600160a01b0386168452600390920290910190526040902054905092915050565b6122bf60405180606001604052806000815260200160008152602001600081525090565b506001600160a01b0316600090815260a56020908152604091829020825160608101845281548152600182015492810192909252600201549181019190915290565b336001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000161461234a576040516369b562ff60e11b815260040160405180910390fd5b64ffffffffff811661236f576040516301924eab60e51b815260040160405180910390fd5b609e805464ffffffffff909216670100000000000000026bffffffffff0000000000000019909216919091179055565b336001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016146123e8576040516369b562ff60e11b815260040160405180910390fd5b609e805491151566010000000000000266ff00000000000019909216919091179055565b60606037805461100e90615b9f565b6000610ffa6145a2565b60975460009081906001600160a01b031680156124a257806001600160a01b0316639b85961f6040518163ffffffff1660e01b81526004016040805180830381865afa158015612479573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061249d9190615d66565b6124a6565b6000805b92509250509091565b60006124c36097546001600160a01b031690565b6001600160a01b0316141561258a577f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663b80ec4c76040518163ffffffff1660e01b8152600401602060405180830381865afa158015612530573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906125549190615b82565b6001600160a01b0316336001600160a01b0316146125855760405163574d01c560e11b815260040160405180910390fd5b6126c9565b6097546001600160a01b03166001600160a01b031663e7f43c686040518163ffffffff1660e01b8152600401602060405180830381865afa1580156125d3573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906125f79190615b82565b6001600160a01b0316336001600160a01b0316141580156126ab57507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663b80ec4c76040518163ffffffff1660e01b8152600401602060405180830381865afa158015612671573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906126959190615b82565b6001600160a01b0316336001600160a01b031614155b156126c95760405163574d01c560e11b815260040160405180910390fd5b6126d16136a6565b6097805460ff60a01b191690556040517f1ba9bbaac2497ed7a7c42445bdab75d210756e8147f5dc1796858f05d17d04b190600090a1610fcb6001606555565b609854600090610ffa90600190615c21565b3360008181526034602090815260408083206001600160a01b0387168452909152812054909190838110156127c05760405162461bcd60e51b815260206004820152602560248201527f45524332303a2064656372656173656420616c6c6f77616e63652062656c6f7760448201527f207a65726f0000000000000000000000000000000000000000000000000000006064820152608401612131565b611b5b8286868403613dc9565b600033611195818585613eed565b6000806000609885815481106127f3576127f3615d2b565b600091825260208083206002600390930201918201546001600160a01b03881684529082905260409092205460018201549193506fffffffffffffffffffffffffffffffff80841693600160801b90041691801561286657612856848383614662565b9650612863838383614662565b95505b50505050509250929050565b60006098828154811061288757612887615d2b565b9060005260206000209060030201600101549050919050565b60988054600091906128b490600190615c21565b815481106128c4576128c4615d2b565b906000526020600020906003020160010154905090565b336001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001614612924576040516369b562ff60e11b815260040160405180910390fd5b80821115612945576040516309f0f20160e41b815260040160405180910390fd5b609c91909155609d55565b600061295a6136a6565b816001600160a01b038116612982576040516302b70c7960e21b815260040160405180910390fd5b83806129a15760405163ebca017960e01b815260040160405180910390fd5b6097546001600160a01b0316806129cb5760405163629c918560e01b815260040160405180910390fd5b6129d3613700565b6129de338288613eed565b6129f0336129eb88615d95565b613b9c565b6098546129ff90600190615c21565b9350600060988581548110612a1657612a16615d2b565b9060005260206000209060030201905086816001016000828254612a3a9190615bf0565b90915550506001600160a01b0386166000818152602083815260409182902080548b019055905189815287929133917f73da1edbee7a0ca003c721a21da1720c9f0128489276764d733e2fd53fda7f04910160405180910390a45050505061119b6001606555565b612aaa6136a6565b6097546001600160a01b031615801590612ac75750609b5460ff16155b15612ae5576040516394fd57bf60e01b815260040160405180910390fd5b612aed613700565b3360009081526033602052604090205480612b1b5760405163ebca017960e01b815260040160405180910390fd5b600080612b28600061351b565b915091506000612b3760355490565b90506000612b46848684614662565b90506000612b55848785614662565b9050612b64336129eb88615d95565b612b6e3387613f68565b8115612b9157612b9133833660011981013560f01c90036014013560601c611ffe565b8015612bb457612bb433823660011981013560f01c90036028013560601c611ffe565b604080518781526020810184905290810182905233907f5e51ee1d526b1e287667d5b5aa0f1595afa8e91197fb257f8351274f8fca50be9060600160405180910390a2505050505050610fcb6001606555565b336001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001614612c50576040516369b562ff60e11b815260040160405180910390fd5b609b805460ff191690556040517f54fba861a001b606a3b53d3309e55be337ecd71afff2156d375f212fe0ddac2b90600090a1565b6000806000612c926136a6565b6001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000163660011981013560f01c90036014013560601c14801590612d1857506001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000163660011981013560f01c90036028013560601c14155b15612d36576040516330fcd9e760e01b815260040160405180910390fd5b3660011981013560f01c90036014810135606090811c9160280135901c7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0381168314808015612d8d5750348a14155b80612da1575080158015612da15750348914155b15612dbf57604051634314b40960e01b815260040160405180910390fd5b612dc7613700565b6000612dd38b8b613a58565b919a5098509650905088881015612dfd57604051630ad1667b60e41b815260040160405180910390fd5b612e073389613b9c565b60008215612e315750868615612e2c57612e2c6001600160a01b03861633848a613c54565b612e4f565b8715612e4c57612e4c6001600160a01b03871633848b613c54565b50855b8015612ebf57836001600160a01b031663d0e30db0826040518263ffffffff1660e01b81526004016000604051808303818588803b158015612e9057600080fd5b505af1158015612ea4573d6000803e3d6000fd5b50612ebf935050506001600160a01b03861690508383613f38565b80341115612ed357612ed33382340361468c565b505050505050610d5a6001606555565b600080612eee6136a6565b826001600160a01b038116612f16576040516302b70c7960e21b815260040160405180910390fd5b612f20858561409c565b90935091508215612f4857612f4884843660011981013560f01c90036014013560601c611ffe565b81156119675761196784833660011981013560f01c90036028013560601c611ffe565b6000612f7f6097546001600160a01b031690565b6001600160a01b03161415613046577f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663b80ec4c76040518163ffffffff1660e01b8152600401602060405180830381865afa158015612fec573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906130109190615b82565b6001600160a01b0316336001600160a01b0316146130415760405163574d01c560e11b815260040160405180910390fd5b613185565b6097546001600160a01b03166001600160a01b031663e7f43c686040518163ffffffff1660e01b8152600401602060405180830381865afa15801561308f573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906130b39190615b82565b6001600160a01b0316336001600160a01b03161415801561316757507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663b80ec4c76040518163ffffffff1660e01b8152600401602060405180830381865afa15801561312d573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906131519190615b82565b6001600160a01b0316336001600160a01b031614155b156131855760405163574d01c560e11b815260040160405180910390fd5b6097546001600160a01b03166001600160a01b031663853828b66040518163ffffffff1660e01b8152600401600060405180830381600087803b1580156131cb57600080fd5b505af11580156131df573d6000803e3d6000fd5b5050609b805460ff1916600117905550506040517fabff7593ea272f2457671346fb8cd01c35939d15d915947810aeb81730ce9a9090600090a1565b336001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001614613264576040516369b562ff60e11b815260040160405180910390fd5b609e805462ffffff92831663010000000265ffffffffffff199091169290931691909117919091179055565b6001600160a01b038116600090815260a56020526040812060a0548291908261332b6132c46097546001600160a01b031690565b6001600160a01b03166369940d796040518163ffffffff1660e01b8152600401602060405180830381865afa158015613301573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906133259190615b82565b30614700565b60a2549091508082148015906133435750600060a454115b1561337b5760006133548284615c21565b905060a4546133638260401b90565b61336d9190615dc8565b6133779085615bf0565b9350505b83546133885760006133ae565b600184015484546133a49061339e908690615ddc565b60401c90565b6133ae9190615c21565b60a15490965060006134086133cb6097546001600160a01b031690565b6001600160a01b0316635b41b35f6040518163ffffffff1660e01b8152600401602060405180830381865afa158015613301573d6000803e3d6000fd5b60a3549091508082148015906134205750600060a454115b156134585760006134318284615c21565b905060a4546134408260401b90565b61344a9190615dc8565b6134549085615bf0565b9350505b8654613465576000613485565b6002870154875461347b9061339e908690615ddc565b6134859190615c21565b975050505050505050915091565b336001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016146134dc576040516369b562ff60e11b815260040160405180910390fd5b609b80546001600160a01b03909216610100027fffffffffffffffffffffff0000000000000000000000000000000000000000ff909216919091179055565b6000806001600160a01b0383161561359257826001600160a01b031662113e086040518163ffffffff1660e01b81526004016040805180830381865afa158015613569573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061358d9190615dfb565b610ff1565b6099543660011981013560f01c90036014013560601c6040516370a0823160e01b81523060048201526001600160a01b0391909116906370a0823190602401602060405180830381865afa1580156135ee573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906136129190615c08565b61361c9190615c21565b609a543660011981013560f01c90036028013560601c6040516370a0823160e01b81523060048201526001600160a01b0391909116906370a0823190602401602060405180830381865afa158015613678573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061369c9190615c08565b610ff19190615c21565b600260655414156136f95760405162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c006044820152606401612131565b6002606555565b60006137146097546001600160a01b031690565b6001600160a01b0316141561372557565b6097546001600160a01b03166001600160a01b031663ecf04e156040518163ffffffff1660e01b8152600401602060405180830381865afa15801561376e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906137929190615e1f565b156138405760a05460006137b16132c46097546001600160a01b031690565b60a2549091508082148015906137c95750600060a454115b1561383c5760006137da8284615c21565b9050600060a4546137eb8360401b90565b6137f59190615dc8565b6137ff9086615bf0565b60a081905560a2859055604051909150819042907f7fa9647ec1cc14e3822b46d05a2b9d4e019bde8875c0088c46b6503d71bf172290600090a350505b5050505b6097546001600160a01b03166001600160a01b0316631216bb956040518163ffffffff1660e01b8152600401602060405180830381865afa158015613889573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906138ad9190615e1f565b80156139a957506097546001600160a01b03166001600160a01b03166369940d796040518163ffffffff1660e01b8152600401602060405180830381865afa1580156138fd573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906139219190615b82565b6001600160a01b031661393c6097546001600160a01b031690565b6001600160a01b0316635b41b35f6040518163ffffffff1660e01b8152600401602060405180830381865afa158015613979573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061399d9190615b82565b6001600160a01b031614155b15610fcb5760a15460006139c86133cb6097546001600160a01b031690565b60a354909150808214806139dc575060a454155b156139e657505050565b60006139f28284615c21565b9050600060a454613a038360401b90565b613a0d9190615dc8565b613a179086615bf0565b60a181905560a3859055604051909150819042907f7fa9647ec1cc14e3822b46d05a2b9d4e019bde8875c0088c46b6503d71bf172290600090a35050505050565b600080600080609760149054906101000a900460ff1615613a8c5760405163572e1c0f60e11b815260040160405180910390fd5b85158015613a98575084155b15613ab657604051632837d7d960e11b815260040160405180910390fd5b6097546001600160a01b0316935083613ae25760405163629c918560e01b815260040160405180910390fd5b613aed8487876144e7565b9194509250905082613b125760405163ebca017960e01b815260040160405180910390fd5b603554613b4657613b256006600a615d1c565b613b2f9084615c21565b9250613b4630613b416006600a615d1c565b614793565b613b503384614793565b604080518381526020810183905290810184905233907f91ede45f04a37a7c170f5c1207df3b6bc748dc1e04ad5e917a241d0f52feada39060600160405180910390a292959194509250565b6001600160a01b038216600090815260a56020526040812090808312613bc25782613bcb565b613bcb83615d95565b90506000831315613c0d57613bdf84614854565b8154613bec908290615bf0565b825560a454613bfc908290615bf0565b60a455613c08826149de565b613c4e565b6000831215613c3c57613c1f84614854565b8154613c2c908290615c21565b825560a454613bfc908290615c21565b613c4584614854565b613c4e826149de565b50505050565b6040516001600160a01b0380851660248301528316604482015260648101829052613c4e9085906323b872dd60e01b906084015b60408051601f198184030181529190526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff0000000000000000000000000000000000000000000000000000000090931692909217909152614a0f565b6001606555565b60008083613d0657506000905080613d55565b82841115613d275760405163557452c160e01b815260040160405180910390fd5b600080613d338761351b565b9092509050613d43828787614662565b9350613d50818787614662565b925050505b935093915050565b609780547fffffffffffffffffffffffff0000000000000000000000000000000000000000166001600160a01b0383169081179091556040519081527fe70d79dad95c835bdd87e9cf4665651c9e5abb3b756e4fd2bf45f29c95c3aa409060200160405180910390a150565b6001600160a01b038316613e2b5760405162461bcd60e51b8152602060048201526024808201527f45524332303a20617070726f76652066726f6d20746865207a65726f206164646044820152637265737360e01b6064820152608401612131565b6001600160a01b038216613e8c5760405162461bcd60e51b815260206004820152602260248201527f45524332303a20617070726f766520746f20746865207a65726f206164647265604482015261737360f01b6064820152608401612131565b6001600160a01b0383811660008181526034602090815260408083209487168084529482529182902085905590518481527f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925910160405180910390a3505050565b613ef682614af4565b158015613f095750613f0783614af4565b155b15613f2d57613f16613700565b613f23836129eb83615d95565b613f2d8282613b9c565b6115e5838383614bb6565b6040516001600160a01b0383166024820152604481018290526115e590849063a9059cbb60e01b90606401613c88565b6001600160a01b038216613fc85760405162461bcd60e51b815260206004820152602160248201527f45524332303a206275726e2066726f6d20746865207a65726f206164647265736044820152607360f81b6064820152608401612131565b6001600160a01b0382166000908152603360205260409020548181101561403c5760405162461bcd60e51b815260206004820152602260248201527f45524332303a206275726e20616d6f756e7420657863656564732062616c616e604482015261636560f01b6064820152608401612131565b6001600160a01b03831660008181526033602090815260408083208686039055603580548790039055518581529192917fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef910160405180910390a3505050565b600080600060016098805490506140b39190615c21565b90508085106140d557604051632569b20f60e11b815260040160405180910390fd5b6000609886815481106140ea576140ea615d2b565b600091825260208083206001600160a01b03891684526003909202909101908190526040909120549091508061413357604051631d4e61bb60e11b815260040160405180910390fd5b6001600160a01b03861633148015906141755750336001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001614155b1561419357604051637b9086ef60e01b815260040160405180910390fd5b60018201546001600160a01b03871660009081526020849052604081205560028301546141d3906fffffffffffffffffffffffffffffffff168383614662565b60028401549096506141ff90600160801b90046fffffffffffffffffffffffffffffffff168383614662565b94508515801561420d575084155b1561422b57604051632837d7d960e11b815260040160405180910390fd5b85156142495785609960008282546142439190615c21565b90915550505b84156142675784609a60008282546142619190615c21565b90915550505b604080518381526020810188905290810186905288906001600160a01b0389169033907ff7bca8bc4482b10f8c441e0662577734ab29da4c9ff67cfe1592a5d1a8db062a9060600160405180910390a4505050509250929050565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03848116908216141561435b57604051632e1a7d4d60e01b8152600481018390526001600160a01b03821690632e1a7d4d90602401600060405180830381600087803b15801561433957600080fd5b505af115801561434d573d6000803e3d6000fd5b50505050613c08838361468c565b613c4e6001600160a01b0385168484613f38565b6001600160a01b038381166000908152603460209081526040808320938616835292905220546000198114613c4e57818110156143ee5760405162461bcd60e51b815260206004820152601d60248201527f45524332303a20696e73756666696369656e7420616c6c6f77616e63650000006044820152606401612131565b613c4e8484848403613dc9565b600054610100900460ff166144665760405162461bcd60e51b815260206004820152602b60248201527f496e697469616c697a61626c653a20636f6e7472616374206973206e6f74206960448201526a6e697469616c697a696e6760a81b6064820152608401612131565b6144708282614d90565b5050565b600054610100900460ff166144df5760405162461bcd60e51b815260206004820152602b60248201527f496e697469616c697a61626c653a20636f6e7472616374206973206e6f74206960448201526a6e697469616c697a696e6760a81b6064820152608401612131565b610fcb614e22565b60008080841580156144f7575083155b1561450a57506000915081905080610d5a565b60006145146145a2565b905061451f81614e8d565b600061452a60355490565b90506000614539838989615142565b9050816145665761454c6006600a615d1c565b6145569082615ddc565b8888955095509550505050610d5a565b6000806145728b61351b565b915091506000614583868484615142565b9050614590848683614662565b9c9a9b50989998505050505050505050565b6000803660011981013560f01c9003603d013560f81c6145c390600a615d1c565b6145de3660011981013560f01c9003603e013560601c615166565b6145e89190615ddc565b905060003660011981013560f01c9003603c013560f81c61460a90600a615d1c565b6146253660011981013560f01c90036052013560601c615166565b61462f9190615ddc565b905061463d8260808361529f565b92508261465d576040516309f0f20160e41b815260040160405180910390fd5b505090565b600080600061467186866152c9565b9150915061468286868685856152e8565b9695505050505050565b6000826001600160a01b03168260405160006040518083038185875af1925050503d80600081146146d9576040519150601f19603f3d011682016040523d82523d6000602084013e6146de565b606091505b50509050806115e557604051633935f08760e01b815260040160405180910390fd5b60006001600160a01b03831615614780576040516370a0823160e01b81526001600160a01b0383811660048301528416906370a0823190602401602060405180830381865afa158015614757573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061477b9190615c08565b61478c565b816001600160a01b0316315b9392505050565b6001600160a01b0382166147e95760405162461bcd60e51b815260206004820152601f60248201527f45524332303a206d696e7420746f20746865207a65726f2061646472657373006044820152606401612131565b80603560008282546147fb9190615bf0565b90915550506001600160a01b0382166000818152603360209081526040808320805486019055518481527fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef910160405180910390a35050565b60006148686097546001600160a01b031690565b6001600160a01b0316141561487a5750565b6001600160a01b038116600090815260a560205260408120600181015460a05482549293926148ac9161339e91615ddc565b6148b69190615c21565b9050600082600201546148d460a154856000015461339e9190615ddc565b6148de9190615c21565b905061495f6148f56097546001600160a01b031690565b6001600160a01b03166369940d796040518163ffffffff1660e01b8152600401602060405180830381865afa158015614932573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906149569190615b82565b85846000615391565b613c4e6149746097546001600160a01b031690565b6001600160a01b0316635b41b35f6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156149b1573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906149d59190615b82565b85836001615391565b60a05481546149f09161339e91615ddc565b600182015560a1548154614a079161339e91615ddc565b600290910155565b6000614a64826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b03166153f69092919063ffffffff16565b8051909150156115e55780806020019051810190614a829190615e1f565b6115e55760405162461bcd60e51b815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e60448201527f6f742073756363656564000000000000000000000000000000000000000000006064820152608401612131565b6000614b086097546001600160a01b031690565b6001600160a01b0316826001600160a01b03161415614b2957506001919050565b7f0000000000000000000000000000000000000000000000000000000000000000604051630512582560e41b81526001600160a01b0384811660048301529190911690635125825090602401602060405180830381865afa158015614b92573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061119b9190615e1f565b6001600160a01b038316614c325760405162461bcd60e51b815260206004820152602560248201527f45524332303a207472616e736665722066726f6d20746865207a65726f20616460448201527f64726573730000000000000000000000000000000000000000000000000000006064820152608401612131565b6001600160a01b038216614c945760405162461bcd60e51b815260206004820152602360248201527f45524332303a207472616e7366657220746f20746865207a65726f206164647260448201526265737360e81b6064820152608401612131565b6001600160a01b03831660009081526033602052604090205481811015614d235760405162461bcd60e51b815260206004820152602660248201527f45524332303a207472616e7366657220616d6f756e742065786365656473206260448201527f616c616e636500000000000000000000000000000000000000000000000000006064820152608401612131565b6001600160a01b0380851660008181526033602052604080822086860390559286168082529083902080548601905591517fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef90614d839086815260200190565b60405180910390a3613c4e565b600054610100900460ff16614dfb5760405162461bcd60e51b815260206004820152602b60248201527f496e697469616c697a61626c653a20636f6e7472616374206973206e6f74206960448201526a6e697469616c697a696e6760a81b6064820152608401612131565b8151614e0e90603690602085019061578e565b5080516115e590603790602084019061578e565b600054610100900460ff16613cec5760405162461bcd60e51b815260206004820152602b60248201527f496e697469616c697a61626c653a20636f6e7472616374206973206e6f74206960448201526a6e697469616c697a696e6760a81b6064820152608401612131565b609e546601000000000000900460ff16614ea45750565b609e54600090614ec690670100000000000000900464ffffffffff1642615c21565b90504264ffffffffff80831690821611614ef3576040516301cd40d760e61b815260040160405180910390fd5b60003660011981013560f01c90033560601c6040516344a050b560e11b815264ffffffffff851660048201526001600160a01b039190911690638940a16a90602401606060405180830381865afa158015614f52573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190614f769190615e54565b505090506000614f93600119369081013560f01c90033560601c90565b6040516344a050b560e11b815264ffffffffff851660048201526001600160a01b039190911690638940a16a90602401606060405180830381865afa158015614fe0573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906150049190615e54565b50509050600084846150169190615e97565b9050600064ffffffffff821661502c8585615ebd565b6150369190615ede565b67ffffffffffffffff16905060003660011981013560f01c90033560601c604051634c7cffbd60e01b815262ffffff841660048201526001600160a01b039190911690634c7cffbd90602401602060405180830381865afa15801561509f573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906150c39190615c08565b90506064609f5460646150d69190615bf0565b6150e09083615ddc565b6150ea9190615dc8565b88118061511a57506064609f5460646151039190615c21565b61510d9083615ddc565b6151179190615dc8565b88105b1561513857604051630440f9d760e21b815260040160405180910390fd5b5050505050505050565b6000806151518585608061540d565b905061515d8382615bf0565b95945050505050565b600061517061547b565b600080836001600160a01b031663feaf968c6040518163ffffffff1660e01b815260040160a060405180830381865afa1580156151b1573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906151d59190615f1f565b5093505092505060006151f8603e600119369081013560f01c9003013560601c90565b6001600160a01b0316856001600160a01b03161461522457609e546301000000900462ffffff1661522d565b609e5462ffffff165b905081158061524957504261524762ffffff831684615bf0565b105b1561526757604051630ee735cf60e11b815260040160405180910390fd5b609c548310806152785750609d5483115b15615296576040516309f0f20160e41b815260040160405180910390fd5b50909392505050565b600060ff831684811b9061ffff6101008290031686901c906146829087906001901b8685856152e8565b6000806000198385098385029250828110838203039150509250929050565b600081615306578383816152fe576152fe615db2565b04905061515d565b838210615326576040516313eae71560e01b815260040160405180910390fd5b600084868809600186198101871660008190038190049091018683119095039490940294038390049390931760029290940460038102831880820284030280820284030280820284030280820284030280820284030290810290920390910292909202949350505050565b8161539b57613c4e565b60006153a78530614700565b905082818111156153b55750805b82156153d1578060a3546153c99190615c21565b60a3556153e3565b8060a2546153df9190615c21565b60a2555b6153ee868683615565565b505050505050565b60606154058484600085615601565b949350505050565b600080600061541c86866152c9565b9150915081600014615432578360ff1682901c92505b801561547257600160ff85161b811061545e57604051638e471a8960e01b815260040160405180910390fd5b8360ff166101000361ffff1681901b830192505b50509392505050565b609b5461010090046001600160a01b031661549257565b600080609b60019054906101000a90046001600160a01b03166001600160a01b031663feaf968c6040518163ffffffff1660e01b815260040160a060405180830381865afa1580156154e8573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061550c9190615f1f565b50919450925050821590508061553557604051636e1d410960e01b815260040160405180910390fd5b60006155418342615c21565b9050610e108111613c4e5760405163fe1a0def60e01b815260040160405180910390fd5b80156115e5576001600160a01b0383166155ed576000826001600160a01b03168260405160006040518083038185875af1925050503d80600081146155c6576040519150601f19603f3d011682016040523d82523d6000602084013e6155cb565b606091505b5050905080613c4e57604051636750787b60e11b815260040160405180910390fd5b6115e56001600160a01b0384168383613f38565b6060824710156156795760405162461bcd60e51b815260206004820152602660248201527f416464726573733a20696e73756666696369656e742062616c616e636520666f60448201527f722063616c6c00000000000000000000000000000000000000000000000000006064820152608401612131565b600080866001600160a01b031685876040516156959190615f6f565b60006040518083038185875af1925050503d80600081146156d2576040519150601f19603f3d011682016040523d82523d6000602084013e6156d7565b606091505b50915091506156e8878383876156f3565b979650505050505050565b6060831561575f578251615758576001600160a01b0385163b6157585760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152606401612131565b5081615405565b61540583838151156157745781518083602001fd5b8060405162461bcd60e51b81526004016121319190615898565b82805461579a90615b9f565b90600052602060002090601f0160209004810192826157bc5760008555615802565b82601f106157d557805160ff1916838001178555615802565b82800160010185558215615802579182015b828111156158025782518255916020019190600101906157e7565b5061580e929150615812565b5090565b5b8082111561580e5760008155600101615813565b60008060006060848603121561583c57600080fd5b505081359360208301359350604090920135919050565b60006020828403121561586557600080fd5b5035919050565b60005b8381101561588757818101518382015260200161586f565b83811115613c4e5750506000910152565b60208152600082518060208401526158b781604085016020870161586c565b601f01601f19169190910160400192915050565b6001600160a01b038116811461204757600080fd5b600080604083850312156158f357600080fd5b82356158fe816158cb565b946020939093013593505050565b60008060006060848603121561592157600080fd5b833561592c816158cb565b9250602084013561593c816158cb565b929592945050506040919091013590565b6000806040838503121561596057600080fd5b823591506020830135615972816158cb565b809150509250929050565b60006020828403121561598f57600080fd5b813561478c816158cb565b634e487b7160e01b600052604160045260246000fd5b600082601f8301126159c157600080fd5b813567ffffffffffffffff808211156159dc576159dc61599a565b604051601f8301601f19908116603f01168101908282118183101715615a0457615a0461599a565b81604052838152866020858801011115615a1d57600080fd5b836020870160208301376000602085830101528094505050505092915050565b60008060408385031215615a5057600080fd5b823567ffffffffffffffff80821115615a6857600080fd5b615a74868387016159b0565b93506020850135915080821115615a8a57600080fd5b50615a97858286016159b0565b9150509250929050565b60008060408385031215615ab457600080fd5b50508035926020909101359150565b600060208284031215615ad557600080fd5b813564ffffffffff8116811461478c57600080fd5b801515811461204757600080fd5b600060208284031215615b0a57600080fd5b813561478c81615aea565b60008060408385031215615b2857600080fd5b8235615b33816158cb565b91506020830135615972816158cb565b62ffffff8116811461204757600080fd5b60008060408385031215615b6757600080fd5b8235615b7281615b43565b9150602083013561597281615b43565b600060208284031215615b9457600080fd5b815161478c816158cb565b600181811c90821680615bb357607f821691505b60208210811415615bd457634e487b7160e01b600052602260045260246000fd5b50919050565b634e487b7160e01b600052601160045260246000fd5b60008219821115615c0357615c03615bda565b500190565b600060208284031215615c1a57600080fd5b5051919050565b600082821015615c3357615c33615bda565b500390565b600181815b80851115615c73578160001904821115615c5957615c59615bda565b80851615615c6657918102915b93841c9390800290615c3d565b509250929050565b600082615c8a5750600161119b565b81615c975750600061119b565b8160018114615cad5760028114615cb757615cd3565b600191505061119b565b60ff841115615cc857615cc8615bda565b50506001821b61119b565b5060208310610133831016604e8410600b8410161715615cf6575081810a61119b565b615d008383615c38565b8060001904821115615d1457615d14615bda565b029392505050565b600061478c60ff841683615c7b565b634e487b7160e01b600052603260045260246000fd5b600060ff821660ff84168060ff03821115615d5e57615d5e615bda565b019392505050565b60008060408385031215615d7957600080fd5b8251615d8481615b43565b602084015190925061597281615b43565b6000600160ff1b821415615dab57615dab615bda565b5060000390565b634e487b7160e01b600052601260045260246000fd5b600082615dd757615dd7615db2565b500490565b6000816000190483118215151615615df657615df6615bda565b500290565b60008060408385031215615e0e57600080fd5b505080516020909101519092909150565b600060208284031215615e3157600080fd5b815161478c81615aea565b805167ffffffffffffffff81168114611ab857600080fd5b600080600060608486031215615e6957600080fd5b615e7284615e3c565b9250615e8060208501615e3c565b9150615e8e60408501615e3c565b90509250925092565b600064ffffffffff83811690831681811015615eb557615eb5615bda565b039392505050565b600067ffffffffffffffff83811690831681811015615eb557615eb5615bda565b600067ffffffffffffffff80841680615ef957615ef9615db2565b92169190910492915050565b805169ffffffffffffffffffff81168114611ab857600080fd5b600080600080600060a08688031215615f3757600080fd5b615f4086615f05565b9450602086015193506040860151925060608601519150615f6360808701615f05565b90509295509295909350565b60008251615f8181846020870161586c565b919091019291505056fea26469706673582212209898d1f8c076df8e394a428deab122eb8e24d72880347a339085abbcd9defe3264736f6c634300080a0033000000000000000000000000197d40b36677248e82939f96930bf4e7fe8ad1c2

Deployed Bytecode

0x6080604052600436106103995760003560e01c806355182894116101dc578063c5a160d111610102578063dbb6bfba116100a0578063f3bc7ef01161006f578063f3bc7ef014610c0d578063f5553c1a14610c22578063f6ed201714610c42578063fcdb2ebc14610c62576103e9565b8063dbb6bfba14610b7f578063dd62ed3e14610b94578063e1730da714610bda578063f33467d414610bed576103e9565b8063ce8120f8116100dc578063ce8120f814610b05578063d58457b214610b25578063da10610c14610b45578063db2e21bc14610b6a576103e9565b8063c5a160d114610ab0578063cb02636814610ad0578063cdaa70c414610af0576103e9565b806395d89b411161017a578063a32bf59711610149578063a32bf59714610a39578063a457c2d714610a4e578063a9059cbb14610a6e578063c1f1b1b514610a8e576103e9565b806395d89b41146109c957806398d5fdca146109de5780639b85961f146109f35780639ca0ac3814610a24576103e9565b806370a08231116101b657806370a082311461091b5780637611bf0a1461095157806388cc58e4146109715780638a061f0b146109a4576103e9565b806355182894146108395780636386c1c7146108b95780636791a389146108fb576103e9565b80631c776443116102c1578063395093511161025f5780634f9c8f9d1161022e5780634f9c8f9d146107b4578063534947ae146107d957806353a15edc146107f957806354d29c3814610819576103e9565b806339509351146107235780633cacd7d6146107435780634cd88b761461077f5780634e71d92d1461079f576103e9565b806327a099d81161029b57806327a099d81461068f578063313ce567146106c4578063330e93bf146106eb57806333a100ca14610703576103e9565b80631c7764431461063b57806323b872dd1461065057806327042b8414610670576103e9565b8063085d0b83116103395780631589ea29116103085780631589ea29146105c457806318160ddd146105dc57806319fa7a15146105fb5780631b0aed2c1461061b576103e9565b8063085d0b831461054a578063095ea7b31461055f5780631171bda91461058f578063152c7c4e146105af576103e9565b806303c162121161037557806303c16212146104b157806305e8746d146104d157806306fdde031461050a57806307da06031461052c576103e9565b8062113e0814610432578062aeef8a14610461578063021919801461049c576103e9565b366103e957336001600160a01b037f000000000000000000000000039e2fb66102314ce7b64ce5ce3e5183bc94ad3816146103e75760405163754ed24960e01b815260040160405180910390fd5b005b336001600160a01b037f000000000000000000000000039e2fb66102314ce7b64ce5ce3e5183bc94ad3816146103e75760405163754ed24960e01b815260040160405180910390fd5b34801561043e57600080fd5b50610447610c82565b604080519283526020830191909152015b60405180910390f35b34801561046d57600080fd5b5061048161047c366004615827565b610ca5565b60408051938452602084019290925290820152606001610458565b3480156104a857600080fd5b506103e7610d63565b3480156104bd57600080fd5b506104476104cc366004615853565b610fcd565b3480156104dd57600080fd5b503660011981013560f01c90036014013560601c5b6040516001600160a01b039091168152602001610458565b34801561051657600080fd5b5061051f610fff565b6040516104589190615898565b34801561053857600080fd5b506097546001600160a01b03166104f2565b34801561055657600080fd5b506103e7611091565b34801561056b57600080fd5b5061057f61057a3660046158e0565b611187565b6040519015158152602001610458565b34801561059b57600080fd5b506103e76105aa36600461590c565b6111a1565b3480156105bb57600080fd5b506103e76115ea565b3480156105d057600080fd5b50609b5460ff1661057f565b3480156105e857600080fd5b506035545b604051908152602001610458565b34801561060757600080fd5b5061044761061636600461594d565b61183b565b34801561062757600080fd5b506105ed610636366004615853565b611979565b34801561064757600080fd5b506105ed611abd565b34801561065c57600080fd5b5061057f61066b36600461590c565b611b42565b34801561067c57600080fd5b50609754600160a01b900460ff1661057f565b34801561069b57600080fd5b506106a4611b66565b604080516001600160a01b03938416815292909116602083015201610458565b3480156106d057600080fd5b506106d9611c87565b60405160ff9091168152602001610458565b3480156106f757600080fd5b5060a25460a354610447565b34801561070f57600080fd5b506103e761071e36600461597d565b611ca6565b34801561072f57600080fd5b5061057f61073e3660046158e0565b61204a565b34801561074f57600080fd5b5061048161075e36600461597d565b60a56020526000908152604090208054600182015460029092015490919083565b34801561078b57600080fd5b506103e761079a366004615a3d565b612089565b3480156107ab57600080fd5b506103e76121c5565b3480156107c057600080fd5b503660011981013560f01c90036052013560601c6104f2565b3480156107e557600080fd5b506104816107f4366004615aa1565b6121e0565b34801561080557600080fd5b506103e7610814366004615853565b61220b565b34801561082557600080fd5b506105ed61083436600461594d565b612259565b34801561084557600080fd5b50609c54609d54609e54609f5460408051948552602085019390935262ffffff8083169385019390935263010000008204909216606084015260808301919091526601000000000000810460ff16151560a0830152670100000000000000900464ffffffffff1660c082015260e001610458565b3480156108c557600080fd5b506108d96108d436600461597d565b61229b565b6040805182518152602080840151908201529181015190820152606001610458565b34801561090757600080fd5b506103e7610916366004615ac3565b612301565b34801561092757600080fd5b506105ed61093636600461597d565b6001600160a01b031660009081526033602052604090205490565b34801561095d57600080fd5b506103e761096c366004615af8565b61239f565b34801561097d57600080fd5b507f000000000000000000000000197d40b36677248e82939f96930bf4e7fe8ad1c26104f2565b3480156109b057600080fd5b503660011981013560f01c9003603e013560601c6104f2565b3480156109d557600080fd5b5061051f61240c565b3480156109ea57600080fd5b506105ed61241b565b3480156109ff57600080fd5b50610a08612425565b6040805162ffffff938416815292909116602083015201610458565b348015610a3057600080fd5b506103e76124af565b348015610a4557600080fd5b506105ed612711565b348015610a5a57600080fd5b5061057f610a693660046158e0565b612723565b348015610a7a57600080fd5b5061057f610a893660046158e0565b6127cd565b348015610a9a57600080fd5b503660011981013560f01c90033560601c6104f2565b348015610abc57600080fd5b50610447610acb36600461594d565b6127db565b348015610adc57600080fd5b506105ed610aeb366004615853565b612872565b348015610afc57600080fd5b506105ed6128a0565b348015610b1157600080fd5b506103e7610b20366004615aa1565b6128db565b348015610b3157600080fd5b506105ed610b4036600461594d565b612950565b348015610b5157600080fd5b503660011981013560f01c90036028013560601c6104f2565b348015610b7657600080fd5b506103e7612aa2565b348015610b8b57600080fd5b506103e7612c07565b348015610ba057600080fd5b506105ed610baf366004615b15565b6001600160a01b03918216600090815260346020908152604080832093909416825291909152205490565b610481610be8366004615827565b612c85565b348015610bf957600080fd5b50610447610c0836600461594d565b612ee3565b348015610c1957600080fd5b506103e7612f6b565b348015610c2e57600080fd5b506103e7610c3d366004615b54565b61321b565b348015610c4e57600080fd5b50610447610c5d36600461597d565b613290565b348015610c6e57600080fd5b506103e7610c7d36600461597d565b613493565b6097546000908190610c9c906001600160a01b031661351b565b90939092509050565b6000806000610cb26136a6565b610cba613700565b6000610cc68787613a58565b91965094509250905084841015610cf057604051630ad1667b60e41b815260040160405180910390fd5b610cfa3385613b9c565b8215610d2b57610d2b3382853660011981013560f01c90036014013560601c5b6001600160a01b0316929190613c54565b8115610d4f57610d4f3382843660011981013560f01c90036028013560601c610d1a565b50610d5a6001606555565b93509350939050565b6000610d776097546001600160a01b031690565b6001600160a01b03161415610e3e577f000000000000000000000000197d40b36677248e82939f96930bf4e7fe8ad1c26001600160a01b031663b80ec4c76040518163ffffffff1660e01b8152600401602060405180830381865afa158015610de4573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610e089190615b82565b6001600160a01b0316336001600160a01b031614610e395760405163574d01c560e11b815260040160405180910390fd5b610f7d565b6097546001600160a01b03166001600160a01b031663e7f43c686040518163ffffffff1660e01b8152600401602060405180830381865afa158015610e87573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610eab9190615b82565b6001600160a01b0316336001600160a01b031614158015610f5f57507f000000000000000000000000197d40b36677248e82939f96930bf4e7fe8ad1c26001600160a01b031663b80ec4c76040518163ffffffff1660e01b8152600401602060405180830381865afa158015610f25573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610f499190615b82565b6001600160a01b0316336001600160a01b031614155b15610f7d5760405163574d01c560e11b815260040160405180910390fd5b610f856136a6565b6097805460ff60a01b1916600160a01b1790556040517fdeeb69430b7153361c25d630947115165636e6a723fa8daea4b0de34b324745990600090a1610fcb6001606555565b565b6097546000908190610ff1906001600160a01b031684610fec60355490565b613cf3565b91509150915091565b905090565b60606036805461100e90615b9f565b80601f016020809104026020016040519081016040528092919081815260200182805461103a90615b9f565b80156110875780601f1061105c57610100808354040283529160200191611087565b820191906000526020600020905b81548152906001019060200180831161106a57829003601f168201915b5050505050905090565b336001600160a01b037f000000000000000000000000197d40b36677248e82939f96930bf4e7fe8ad1c216146110da576040516369b562ff60e11b815260040160405180910390fd5b6110e26136a6565b609760009054906101000a90046001600160a01b03166001600160a01b031663853828b66040518163ffffffff1660e01b8152600401600060405180830381600087803b15801561113257600080fd5b505af1158015611146573d6000803e3d6000fd5b505050506111546000613d5d565b6040517f185079b91d6fc95ee30b386eaca3291e9bc2783621ef973a33d1450c41b6acf790600090a1610fcb6001606555565b600033611195818585613dc9565b60019150505b92915050565b6111a96136a6565b336001600160a01b037f000000000000000000000000197d40b36677248e82939f96930bf4e7fe8ad1c216146111f2576040516369b562ff60e11b815260040160405180910390fd5b6097546001600160a01b03163660011981013560f01c90036014013560601c6001600160a01b0316846001600160a01b03161480156112b557506001600160a01b03811615806112b557508160995461124b9190615bf0565b6040516370a0823160e01b81523060048201526001600160a01b038616906370a0823190602401602060405180830381865afa15801561128f573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906112b39190615c08565b105b156112d35760405163b79333bb60e01b815260040160405180910390fd5b3660011981013560f01c90036028013560601c6001600160a01b03851614801561138157506001600160a01b0381161580611381575081609a546113179190615bf0565b6040516370a0823160e01b81523060048201526001600160a01b038616906370a0823190602401602060405180830381865afa15801561135b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061137f9190615c08565b105b1561139f5760405163b79333bb60e01b815260040160405180910390fd5b6001600160a01b0384163014156114425760006113ba6128a0565b6001600160a01b0383166000908152603360205260409020546113dd9190615c21565b90506113eb6006600a615d1c565b6113f59084615bf0565b30600090815260336020526040902054611410908390615bf0565b101561142f576040516328165e7760e01b815260040160405180910390fd5b801561144057611440823083613eed565b505b6114566001600160a01b0385168484613f38565b6001600160a01b03811615801561157157506099543660011981013560f01c90036014013560601c6040516370a0823160e01b81523060048201526001600160a01b0391909116906370a0823190602401602060405180830381865afa1580156114c4573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906114e89190615c08565b10806115715750609a543660011981013560f01c90036028013560601c6040516370a0823160e01b81523060048201526001600160a01b0391909116906370a0823190602401602060405180830381865afa15801561154b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061156f9190615c08565b105b1561158f5760405163b79333bb60e01b815260040160405180910390fd5b604080516001600160a01b038087168252851660208201529081018390527ffff3b3844276f57024e0b42afec1a37f75db36511e43819a4f2a63ab7862b6489060600160405180910390a1506115e56001606555565b505050565b6115f26136a6565b6097546001600160a01b031633811461161e57604051630639d2a760e11b815260040160405180910390fd5b60985460009061163090600190615c21565b905060006098828154811061164757611647615d2b565b9060005260206000209060030201905060008160010154905080600014156116725750505050611831565b61167c8482613f68565b6098805460010181556000908152609954609a549091823660011981013560f01c90036014013560601c6040516370a0823160e01b81523060048201526001600160a01b0391909116906370a0823190602401602060405180830381865afa1580156116ec573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906117109190615c08565b61171a9190615c21565b90506000823660011981013560f01c90036028013560601c6040516370a0823160e01b81523060048201526001600160a01b0391909116906370a0823190602401602060405180830381865afa158015611778573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061179c9190615c08565b6117a69190615c21565b90506117b28285615bf0565b6099556117bf8184615bf0565b609a556fffffffffffffffffffffffffffffffff818116600160801b02908316176002870155604080518681526020810184905290810182905287907f27cf0c9d33856fac5d46a73a8569c7ebf6470ffc7631d277fb593e62bafe3baf9060600160405180910390a250505050505050505b610fcb6001606555565b6000806118466136a6565b6001600160a01b037f000000000000000000000000039e2fb66102314ce7b64ce5ce3e5183bc94ad38163660011981013560f01c90036014013560601c148015906118cc57506001600160a01b037f000000000000000000000000039e2fb66102314ce7b64ce5ce3e5183bc94ad38163660011981013560f01c90036028013560601c14155b156118ea576040516330fcd9e760e01b815260040160405180910390fd5b826001600160a01b038116611912576040516302b70c7960e21b815260040160405180910390fd5b61191c858561409c565b90935091508215611944576119443660011981013560f01c90036014013560601c85856142c2565b8115611967576119673660011981013560f01c90036028013560601c85846142c2565b506119726001606555565b9250929050565b60006119836136a6565b81806119a25760405163ebca017960e01b815260040160405180910390fd5b6097546001600160a01b0316806119cc5760405163629c918560e01b815260040160405180910390fd5b6119d4613700565b6098546119e390600190615c21565b92506000609884815481106119fa576119fa615d2b565b6000918252602080832033845260039092029091019081905260409091205490915080861115611a3d57604051632979b43f60e11b815260040160405180910390fd5b33600081815260208490526040902087830390556001830180548890039055611a669087613b9c565b611a71833388613eed565b6040518681528590339081907fc0b6b4b152b639767dd4ffd28f2a0f05b4ffd672264a6c4d34447185a03cc5179060200160405180910390a450505050611ab86001606555565b919050565b6097546000906001600160a01b03168015611b3957806001600160a01b0316631c7764436040518163ffffffff1660e01b8152600401602060405180830381865afa158015611b10573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611b349190615c08565b611b3c565b60005b91505090565b600033611b5085828561436f565b611b5b858585613eed565b506001949350505050565b6000806000609760009054906101000a90046001600160a01b031690507f000000000000000000000000197d40b36677248e82939f96930bf4e7fe8ad1c26001600160a01b031663b80ec4c76040518163ffffffff1660e01b8152600401602060405180830381865afa158015611be1573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611c059190615b82565b92506001600160a01b03811615611c7d57806001600160a01b031663e7f43c686040518163ffffffff1660e01b8152600401602060405180830381865afa158015611c54573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611c789190615b82565b611c80565b60005b9150509091565b6000610ffa60063660011981013560f01c9003603d013560f81c615d41565b336001600160a01b037f000000000000000000000000197d40b36677248e82939f96930bf4e7fe8ad1c21614611cef576040516369b562ff60e11b815260040160405180910390fd5b611cf76136a6565b6097546001600160a01b03908116908216811415611d285760405163e4a1ddf760e01b815260040160405180910390fd5b306001600160a01b0316826001600160a01b0316638d928af86040518163ffffffff1660e01b8152600401602060405180830381865afa158015611d70573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611d949190615b82565b6001600160a01b0316141580611e2c57503660011981013560f01c90033560601c6001600160a01b0316826001600160a01b031663c1f1b1b56040518163ffffffff1660e01b8152600401602060405180830381865afa158015611dfc573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611e209190615b82565b6001600160a01b031614155b80611ebc57503660011981013560f01c90036014013560601c6001600160a01b0316826001600160a01b03166305e8746d6040518163ffffffff1660e01b8152600401602060405180830381865afa158015611e8c573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611eb09190615b82565b6001600160a01b031614155b80611f4c57503660011981013560f01c90036028013560601c6001600160a01b0316826001600160a01b031663da10610c6040518163ffffffff1660e01b8152600401602060405180830381865afa158015611f1c573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611f409190615b82565b6001600160a01b031614155b15611f6a5760405163629c918560e01b815260040160405180910390fd5b6001600160a01b03811615611fcd57806001600160a01b031663853828b66040518163ffffffff1660e01b8152600401600060405180830381600087803b158015611fb457600080fd5b505af1158015611fc8573d6000803e3d6000fd5b505050505b600080611fda600061351b565b9092509050811561200e5761200e84833660011981013560f01c90036014013560601c5b6001600160a01b03169190613f38565b80156120315761203184823660011981013560f01c90036028013560601c611ffe565b61203a84613d5d565b5050506120476001606555565b50565b3360008181526034602090815260408083206001600160a01b03871684529091528120549091906111959082908690612084908790615bf0565b613dc9565b600054610100900460ff16158080156120a95750600054600160ff909116105b806120c35750303b1580156120c3575060005460ff166001145b61213a5760405162461bcd60e51b815260206004820152602e60248201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160448201527f647920696e697469616c697a656400000000000000000000000000000000000060648201526084015b60405180910390fd5b6000805460ff19166001179055801561215d576000805461ff0019166101001790555b61216783836143fb565b61216f614474565b60988054600101815560005280156115e5576000805461ff0019169055604051600181527f7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb38474024989060200160405180910390a1505050565b6121cd6136a6565b6121d5613700565b611831336000613b9c565b609754600090819081906121fe906001600160a01b031686866144e7565b9250925092509250925092565b336001600160a01b037f000000000000000000000000197d40b36677248e82939f96930bf4e7fe8ad1c21614612254576040516369b562ff60e11b815260040160405180910390fd5b609f55565b60006098838154811061226e5761226e615d2b565b600091825260208083206001600160a01b0386168452600390920290910190526040902054905092915050565b6122bf60405180606001604052806000815260200160008152602001600081525090565b506001600160a01b0316600090815260a56020908152604091829020825160608101845281548152600182015492810192909252600201549181019190915290565b336001600160a01b037f000000000000000000000000197d40b36677248e82939f96930bf4e7fe8ad1c2161461234a576040516369b562ff60e11b815260040160405180910390fd5b64ffffffffff811661236f576040516301924eab60e51b815260040160405180910390fd5b609e805464ffffffffff909216670100000000000000026bffffffffff0000000000000019909216919091179055565b336001600160a01b037f000000000000000000000000197d40b36677248e82939f96930bf4e7fe8ad1c216146123e8576040516369b562ff60e11b815260040160405180910390fd5b609e805491151566010000000000000266ff00000000000019909216919091179055565b60606037805461100e90615b9f565b6000610ffa6145a2565b60975460009081906001600160a01b031680156124a257806001600160a01b0316639b85961f6040518163ffffffff1660e01b81526004016040805180830381865afa158015612479573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061249d9190615d66565b6124a6565b6000805b92509250509091565b60006124c36097546001600160a01b031690565b6001600160a01b0316141561258a577f000000000000000000000000197d40b36677248e82939f96930bf4e7fe8ad1c26001600160a01b031663b80ec4c76040518163ffffffff1660e01b8152600401602060405180830381865afa158015612530573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906125549190615b82565b6001600160a01b0316336001600160a01b0316146125855760405163574d01c560e11b815260040160405180910390fd5b6126c9565b6097546001600160a01b03166001600160a01b031663e7f43c686040518163ffffffff1660e01b8152600401602060405180830381865afa1580156125d3573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906125f79190615b82565b6001600160a01b0316336001600160a01b0316141580156126ab57507f000000000000000000000000197d40b36677248e82939f96930bf4e7fe8ad1c26001600160a01b031663b80ec4c76040518163ffffffff1660e01b8152600401602060405180830381865afa158015612671573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906126959190615b82565b6001600160a01b0316336001600160a01b031614155b156126c95760405163574d01c560e11b815260040160405180910390fd5b6126d16136a6565b6097805460ff60a01b191690556040517f1ba9bbaac2497ed7a7c42445bdab75d210756e8147f5dc1796858f05d17d04b190600090a1610fcb6001606555565b609854600090610ffa90600190615c21565b3360008181526034602090815260408083206001600160a01b0387168452909152812054909190838110156127c05760405162461bcd60e51b815260206004820152602560248201527f45524332303a2064656372656173656420616c6c6f77616e63652062656c6f7760448201527f207a65726f0000000000000000000000000000000000000000000000000000006064820152608401612131565b611b5b8286868403613dc9565b600033611195818585613eed565b6000806000609885815481106127f3576127f3615d2b565b600091825260208083206002600390930201918201546001600160a01b03881684529082905260409092205460018201549193506fffffffffffffffffffffffffffffffff80841693600160801b90041691801561286657612856848383614662565b9650612863838383614662565b95505b50505050509250929050565b60006098828154811061288757612887615d2b565b9060005260206000209060030201600101549050919050565b60988054600091906128b490600190615c21565b815481106128c4576128c4615d2b565b906000526020600020906003020160010154905090565b336001600160a01b037f000000000000000000000000197d40b36677248e82939f96930bf4e7fe8ad1c21614612924576040516369b562ff60e11b815260040160405180910390fd5b80821115612945576040516309f0f20160e41b815260040160405180910390fd5b609c91909155609d55565b600061295a6136a6565b816001600160a01b038116612982576040516302b70c7960e21b815260040160405180910390fd5b83806129a15760405163ebca017960e01b815260040160405180910390fd5b6097546001600160a01b0316806129cb5760405163629c918560e01b815260040160405180910390fd5b6129d3613700565b6129de338288613eed565b6129f0336129eb88615d95565b613b9c565b6098546129ff90600190615c21565b9350600060988581548110612a1657612a16615d2b565b9060005260206000209060030201905086816001016000828254612a3a9190615bf0565b90915550506001600160a01b0386166000818152602083815260409182902080548b019055905189815287929133917f73da1edbee7a0ca003c721a21da1720c9f0128489276764d733e2fd53fda7f04910160405180910390a45050505061119b6001606555565b612aaa6136a6565b6097546001600160a01b031615801590612ac75750609b5460ff16155b15612ae5576040516394fd57bf60e01b815260040160405180910390fd5b612aed613700565b3360009081526033602052604090205480612b1b5760405163ebca017960e01b815260040160405180910390fd5b600080612b28600061351b565b915091506000612b3760355490565b90506000612b46848684614662565b90506000612b55848785614662565b9050612b64336129eb88615d95565b612b6e3387613f68565b8115612b9157612b9133833660011981013560f01c90036014013560601c611ffe565b8015612bb457612bb433823660011981013560f01c90036028013560601c611ffe565b604080518781526020810184905290810182905233907f5e51ee1d526b1e287667d5b5aa0f1595afa8e91197fb257f8351274f8fca50be9060600160405180910390a2505050505050610fcb6001606555565b336001600160a01b037f000000000000000000000000197d40b36677248e82939f96930bf4e7fe8ad1c21614612c50576040516369b562ff60e11b815260040160405180910390fd5b609b805460ff191690556040517f54fba861a001b606a3b53d3309e55be337ecd71afff2156d375f212fe0ddac2b90600090a1565b6000806000612c926136a6565b6001600160a01b037f000000000000000000000000039e2fb66102314ce7b64ce5ce3e5183bc94ad38163660011981013560f01c90036014013560601c14801590612d1857506001600160a01b037f000000000000000000000000039e2fb66102314ce7b64ce5ce3e5183bc94ad38163660011981013560f01c90036028013560601c14155b15612d36576040516330fcd9e760e01b815260040160405180910390fd5b3660011981013560f01c90036014810135606090811c9160280135901c7f000000000000000000000000039e2fb66102314ce7b64ce5ce3e5183bc94ad386001600160a01b0381168314808015612d8d5750348a14155b80612da1575080158015612da15750348914155b15612dbf57604051634314b40960e01b815260040160405180910390fd5b612dc7613700565b6000612dd38b8b613a58565b919a5098509650905088881015612dfd57604051630ad1667b60e41b815260040160405180910390fd5b612e073389613b9c565b60008215612e315750868615612e2c57612e2c6001600160a01b03861633848a613c54565b612e4f565b8715612e4c57612e4c6001600160a01b03871633848b613c54565b50855b8015612ebf57836001600160a01b031663d0e30db0826040518263ffffffff1660e01b81526004016000604051808303818588803b158015612e9057600080fd5b505af1158015612ea4573d6000803e3d6000fd5b50612ebf935050506001600160a01b03861690508383613f38565b80341115612ed357612ed33382340361468c565b505050505050610d5a6001606555565b600080612eee6136a6565b826001600160a01b038116612f16576040516302b70c7960e21b815260040160405180910390fd5b612f20858561409c565b90935091508215612f4857612f4884843660011981013560f01c90036014013560601c611ffe565b81156119675761196784833660011981013560f01c90036028013560601c611ffe565b6000612f7f6097546001600160a01b031690565b6001600160a01b03161415613046577f000000000000000000000000197d40b36677248e82939f96930bf4e7fe8ad1c26001600160a01b031663b80ec4c76040518163ffffffff1660e01b8152600401602060405180830381865afa158015612fec573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906130109190615b82565b6001600160a01b0316336001600160a01b0316146130415760405163574d01c560e11b815260040160405180910390fd5b613185565b6097546001600160a01b03166001600160a01b031663e7f43c686040518163ffffffff1660e01b8152600401602060405180830381865afa15801561308f573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906130b39190615b82565b6001600160a01b0316336001600160a01b03161415801561316757507f000000000000000000000000197d40b36677248e82939f96930bf4e7fe8ad1c26001600160a01b031663b80ec4c76040518163ffffffff1660e01b8152600401602060405180830381865afa15801561312d573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906131519190615b82565b6001600160a01b0316336001600160a01b031614155b156131855760405163574d01c560e11b815260040160405180910390fd5b6097546001600160a01b03166001600160a01b031663853828b66040518163ffffffff1660e01b8152600401600060405180830381600087803b1580156131cb57600080fd5b505af11580156131df573d6000803e3d6000fd5b5050609b805460ff1916600117905550506040517fabff7593ea272f2457671346fb8cd01c35939d15d915947810aeb81730ce9a9090600090a1565b336001600160a01b037f000000000000000000000000197d40b36677248e82939f96930bf4e7fe8ad1c21614613264576040516369b562ff60e11b815260040160405180910390fd5b609e805462ffffff92831663010000000265ffffffffffff199091169290931691909117919091179055565b6001600160a01b038116600090815260a56020526040812060a0548291908261332b6132c46097546001600160a01b031690565b6001600160a01b03166369940d796040518163ffffffff1660e01b8152600401602060405180830381865afa158015613301573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906133259190615b82565b30614700565b60a2549091508082148015906133435750600060a454115b1561337b5760006133548284615c21565b905060a4546133638260401b90565b61336d9190615dc8565b6133779085615bf0565b9350505b83546133885760006133ae565b600184015484546133a49061339e908690615ddc565b60401c90565b6133ae9190615c21565b60a15490965060006134086133cb6097546001600160a01b031690565b6001600160a01b0316635b41b35f6040518163ffffffff1660e01b8152600401602060405180830381865afa158015613301573d6000803e3d6000fd5b60a3549091508082148015906134205750600060a454115b156134585760006134318284615c21565b905060a4546134408260401b90565b61344a9190615dc8565b6134549085615bf0565b9350505b8654613465576000613485565b6002870154875461347b9061339e908690615ddc565b6134859190615c21565b975050505050505050915091565b336001600160a01b037f000000000000000000000000197d40b36677248e82939f96930bf4e7fe8ad1c216146134dc576040516369b562ff60e11b815260040160405180910390fd5b609b80546001600160a01b03909216610100027fffffffffffffffffffffff0000000000000000000000000000000000000000ff909216919091179055565b6000806001600160a01b0383161561359257826001600160a01b031662113e086040518163ffffffff1660e01b81526004016040805180830381865afa158015613569573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061358d9190615dfb565b610ff1565b6099543660011981013560f01c90036014013560601c6040516370a0823160e01b81523060048201526001600160a01b0391909116906370a0823190602401602060405180830381865afa1580156135ee573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906136129190615c08565b61361c9190615c21565b609a543660011981013560f01c90036028013560601c6040516370a0823160e01b81523060048201526001600160a01b0391909116906370a0823190602401602060405180830381865afa158015613678573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061369c9190615c08565b610ff19190615c21565b600260655414156136f95760405162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c006044820152606401612131565b6002606555565b60006137146097546001600160a01b031690565b6001600160a01b0316141561372557565b6097546001600160a01b03166001600160a01b031663ecf04e156040518163ffffffff1660e01b8152600401602060405180830381865afa15801561376e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906137929190615e1f565b156138405760a05460006137b16132c46097546001600160a01b031690565b60a2549091508082148015906137c95750600060a454115b1561383c5760006137da8284615c21565b9050600060a4546137eb8360401b90565b6137f59190615dc8565b6137ff9086615bf0565b60a081905560a2859055604051909150819042907f7fa9647ec1cc14e3822b46d05a2b9d4e019bde8875c0088c46b6503d71bf172290600090a350505b5050505b6097546001600160a01b03166001600160a01b0316631216bb956040518163ffffffff1660e01b8152600401602060405180830381865afa158015613889573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906138ad9190615e1f565b80156139a957506097546001600160a01b03166001600160a01b03166369940d796040518163ffffffff1660e01b8152600401602060405180830381865afa1580156138fd573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906139219190615b82565b6001600160a01b031661393c6097546001600160a01b031690565b6001600160a01b0316635b41b35f6040518163ffffffff1660e01b8152600401602060405180830381865afa158015613979573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061399d9190615b82565b6001600160a01b031614155b15610fcb5760a15460006139c86133cb6097546001600160a01b031690565b60a354909150808214806139dc575060a454155b156139e657505050565b60006139f28284615c21565b9050600060a454613a038360401b90565b613a0d9190615dc8565b613a179086615bf0565b60a181905560a3859055604051909150819042907f7fa9647ec1cc14e3822b46d05a2b9d4e019bde8875c0088c46b6503d71bf172290600090a35050505050565b600080600080609760149054906101000a900460ff1615613a8c5760405163572e1c0f60e11b815260040160405180910390fd5b85158015613a98575084155b15613ab657604051632837d7d960e11b815260040160405180910390fd5b6097546001600160a01b0316935083613ae25760405163629c918560e01b815260040160405180910390fd5b613aed8487876144e7565b9194509250905082613b125760405163ebca017960e01b815260040160405180910390fd5b603554613b4657613b256006600a615d1c565b613b2f9084615c21565b9250613b4630613b416006600a615d1c565b614793565b613b503384614793565b604080518381526020810183905290810184905233907f91ede45f04a37a7c170f5c1207df3b6bc748dc1e04ad5e917a241d0f52feada39060600160405180910390a292959194509250565b6001600160a01b038216600090815260a56020526040812090808312613bc25782613bcb565b613bcb83615d95565b90506000831315613c0d57613bdf84614854565b8154613bec908290615bf0565b825560a454613bfc908290615bf0565b60a455613c08826149de565b613c4e565b6000831215613c3c57613c1f84614854565b8154613c2c908290615c21565b825560a454613bfc908290615c21565b613c4584614854565b613c4e826149de565b50505050565b6040516001600160a01b0380851660248301528316604482015260648101829052613c4e9085906323b872dd60e01b906084015b60408051601f198184030181529190526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff0000000000000000000000000000000000000000000000000000000090931692909217909152614a0f565b6001606555565b60008083613d0657506000905080613d55565b82841115613d275760405163557452c160e01b815260040160405180910390fd5b600080613d338761351b565b9092509050613d43828787614662565b9350613d50818787614662565b925050505b935093915050565b609780547fffffffffffffffffffffffff0000000000000000000000000000000000000000166001600160a01b0383169081179091556040519081527fe70d79dad95c835bdd87e9cf4665651c9e5abb3b756e4fd2bf45f29c95c3aa409060200160405180910390a150565b6001600160a01b038316613e2b5760405162461bcd60e51b8152602060048201526024808201527f45524332303a20617070726f76652066726f6d20746865207a65726f206164646044820152637265737360e01b6064820152608401612131565b6001600160a01b038216613e8c5760405162461bcd60e51b815260206004820152602260248201527f45524332303a20617070726f766520746f20746865207a65726f206164647265604482015261737360f01b6064820152608401612131565b6001600160a01b0383811660008181526034602090815260408083209487168084529482529182902085905590518481527f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925910160405180910390a3505050565b613ef682614af4565b158015613f095750613f0783614af4565b155b15613f2d57613f16613700565b613f23836129eb83615d95565b613f2d8282613b9c565b6115e5838383614bb6565b6040516001600160a01b0383166024820152604481018290526115e590849063a9059cbb60e01b90606401613c88565b6001600160a01b038216613fc85760405162461bcd60e51b815260206004820152602160248201527f45524332303a206275726e2066726f6d20746865207a65726f206164647265736044820152607360f81b6064820152608401612131565b6001600160a01b0382166000908152603360205260409020548181101561403c5760405162461bcd60e51b815260206004820152602260248201527f45524332303a206275726e20616d6f756e7420657863656564732062616c616e604482015261636560f01b6064820152608401612131565b6001600160a01b03831660008181526033602090815260408083208686039055603580548790039055518581529192917fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef910160405180910390a3505050565b600080600060016098805490506140b39190615c21565b90508085106140d557604051632569b20f60e11b815260040160405180910390fd5b6000609886815481106140ea576140ea615d2b565b600091825260208083206001600160a01b03891684526003909202909101908190526040909120549091508061413357604051631d4e61bb60e11b815260040160405180910390fd5b6001600160a01b03861633148015906141755750336001600160a01b037f000000000000000000000000197d40b36677248e82939f96930bf4e7fe8ad1c21614155b1561419357604051637b9086ef60e01b815260040160405180910390fd5b60018201546001600160a01b03871660009081526020849052604081205560028301546141d3906fffffffffffffffffffffffffffffffff168383614662565b60028401549096506141ff90600160801b90046fffffffffffffffffffffffffffffffff168383614662565b94508515801561420d575084155b1561422b57604051632837d7d960e11b815260040160405180910390fd5b85156142495785609960008282546142439190615c21565b90915550505b84156142675784609a60008282546142619190615c21565b90915550505b604080518381526020810188905290810186905288906001600160a01b0389169033907ff7bca8bc4482b10f8c441e0662577734ab29da4c9ff67cfe1592a5d1a8db062a9060600160405180910390a4505050509250929050565b7f000000000000000000000000039e2fb66102314ce7b64ce5ce3e5183bc94ad386001600160a01b03848116908216141561435b57604051632e1a7d4d60e01b8152600481018390526001600160a01b03821690632e1a7d4d90602401600060405180830381600087803b15801561433957600080fd5b505af115801561434d573d6000803e3d6000fd5b50505050613c08838361468c565b613c4e6001600160a01b0385168484613f38565b6001600160a01b038381166000908152603460209081526040808320938616835292905220546000198114613c4e57818110156143ee5760405162461bcd60e51b815260206004820152601d60248201527f45524332303a20696e73756666696369656e7420616c6c6f77616e63650000006044820152606401612131565b613c4e8484848403613dc9565b600054610100900460ff166144665760405162461bcd60e51b815260206004820152602b60248201527f496e697469616c697a61626c653a20636f6e7472616374206973206e6f74206960448201526a6e697469616c697a696e6760a81b6064820152608401612131565b6144708282614d90565b5050565b600054610100900460ff166144df5760405162461bcd60e51b815260206004820152602b60248201527f496e697469616c697a61626c653a20636f6e7472616374206973206e6f74206960448201526a6e697469616c697a696e6760a81b6064820152608401612131565b610fcb614e22565b60008080841580156144f7575083155b1561450a57506000915081905080610d5a565b60006145146145a2565b905061451f81614e8d565b600061452a60355490565b90506000614539838989615142565b9050816145665761454c6006600a615d1c565b6145569082615ddc565b8888955095509550505050610d5a565b6000806145728b61351b565b915091506000614583868484615142565b9050614590848683614662565b9c9a9b50989998505050505050505050565b6000803660011981013560f01c9003603d013560f81c6145c390600a615d1c565b6145de3660011981013560f01c9003603e013560601c615166565b6145e89190615ddc565b905060003660011981013560f01c9003603c013560f81c61460a90600a615d1c565b6146253660011981013560f01c90036052013560601c615166565b61462f9190615ddc565b905061463d8260808361529f565b92508261465d576040516309f0f20160e41b815260040160405180910390fd5b505090565b600080600061467186866152c9565b9150915061468286868685856152e8565b9695505050505050565b6000826001600160a01b03168260405160006040518083038185875af1925050503d80600081146146d9576040519150601f19603f3d011682016040523d82523d6000602084013e6146de565b606091505b50509050806115e557604051633935f08760e01b815260040160405180910390fd5b60006001600160a01b03831615614780576040516370a0823160e01b81526001600160a01b0383811660048301528416906370a0823190602401602060405180830381865afa158015614757573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061477b9190615c08565b61478c565b816001600160a01b0316315b9392505050565b6001600160a01b0382166147e95760405162461bcd60e51b815260206004820152601f60248201527f45524332303a206d696e7420746f20746865207a65726f2061646472657373006044820152606401612131565b80603560008282546147fb9190615bf0565b90915550506001600160a01b0382166000818152603360209081526040808320805486019055518481527fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef910160405180910390a35050565b60006148686097546001600160a01b031690565b6001600160a01b0316141561487a5750565b6001600160a01b038116600090815260a560205260408120600181015460a05482549293926148ac9161339e91615ddc565b6148b69190615c21565b9050600082600201546148d460a154856000015461339e9190615ddc565b6148de9190615c21565b905061495f6148f56097546001600160a01b031690565b6001600160a01b03166369940d796040518163ffffffff1660e01b8152600401602060405180830381865afa158015614932573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906149569190615b82565b85846000615391565b613c4e6149746097546001600160a01b031690565b6001600160a01b0316635b41b35f6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156149b1573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906149d59190615b82565b85836001615391565b60a05481546149f09161339e91615ddc565b600182015560a1548154614a079161339e91615ddc565b600290910155565b6000614a64826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b03166153f69092919063ffffffff16565b8051909150156115e55780806020019051810190614a829190615e1f565b6115e55760405162461bcd60e51b815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e60448201527f6f742073756363656564000000000000000000000000000000000000000000006064820152608401612131565b6000614b086097546001600160a01b031690565b6001600160a01b0316826001600160a01b03161415614b2957506001919050565b7f000000000000000000000000197d40b36677248e82939f96930bf4e7fe8ad1c2604051630512582560e41b81526001600160a01b0384811660048301529190911690635125825090602401602060405180830381865afa158015614b92573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061119b9190615e1f565b6001600160a01b038316614c325760405162461bcd60e51b815260206004820152602560248201527f45524332303a207472616e736665722066726f6d20746865207a65726f20616460448201527f64726573730000000000000000000000000000000000000000000000000000006064820152608401612131565b6001600160a01b038216614c945760405162461bcd60e51b815260206004820152602360248201527f45524332303a207472616e7366657220746f20746865207a65726f206164647260448201526265737360e81b6064820152608401612131565b6001600160a01b03831660009081526033602052604090205481811015614d235760405162461bcd60e51b815260206004820152602660248201527f45524332303a207472616e7366657220616d6f756e742065786365656473206260448201527f616c616e636500000000000000000000000000000000000000000000000000006064820152608401612131565b6001600160a01b0380851660008181526033602052604080822086860390559286168082529083902080548601905591517fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef90614d839086815260200190565b60405180910390a3613c4e565b600054610100900460ff16614dfb5760405162461bcd60e51b815260206004820152602b60248201527f496e697469616c697a61626c653a20636f6e7472616374206973206e6f74206960448201526a6e697469616c697a696e6760a81b6064820152608401612131565b8151614e0e90603690602085019061578e565b5080516115e590603790602084019061578e565b600054610100900460ff16613cec5760405162461bcd60e51b815260206004820152602b60248201527f496e697469616c697a61626c653a20636f6e7472616374206973206e6f74206960448201526a6e697469616c697a696e6760a81b6064820152608401612131565b609e546601000000000000900460ff16614ea45750565b609e54600090614ec690670100000000000000900464ffffffffff1642615c21565b90504264ffffffffff80831690821611614ef3576040516301cd40d760e61b815260040160405180910390fd5b60003660011981013560f01c90033560601c6040516344a050b560e11b815264ffffffffff851660048201526001600160a01b039190911690638940a16a90602401606060405180830381865afa158015614f52573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190614f769190615e54565b505090506000614f93600119369081013560f01c90033560601c90565b6040516344a050b560e11b815264ffffffffff851660048201526001600160a01b039190911690638940a16a90602401606060405180830381865afa158015614fe0573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906150049190615e54565b50509050600084846150169190615e97565b9050600064ffffffffff821661502c8585615ebd565b6150369190615ede565b67ffffffffffffffff16905060003660011981013560f01c90033560601c604051634c7cffbd60e01b815262ffffff841660048201526001600160a01b039190911690634c7cffbd90602401602060405180830381865afa15801561509f573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906150c39190615c08565b90506064609f5460646150d69190615bf0565b6150e09083615ddc565b6150ea9190615dc8565b88118061511a57506064609f5460646151039190615c21565b61510d9083615ddc565b6151179190615dc8565b88105b1561513857604051630440f9d760e21b815260040160405180910390fd5b5050505050505050565b6000806151518585608061540d565b905061515d8382615bf0565b95945050505050565b600061517061547b565b600080836001600160a01b031663feaf968c6040518163ffffffff1660e01b815260040160a060405180830381865afa1580156151b1573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906151d59190615f1f565b5093505092505060006151f8603e600119369081013560f01c9003013560601c90565b6001600160a01b0316856001600160a01b03161461522457609e546301000000900462ffffff1661522d565b609e5462ffffff165b905081158061524957504261524762ffffff831684615bf0565b105b1561526757604051630ee735cf60e11b815260040160405180910390fd5b609c548310806152785750609d5483115b15615296576040516309f0f20160e41b815260040160405180910390fd5b50909392505050565b600060ff831684811b9061ffff6101008290031686901c906146829087906001901b8685856152e8565b6000806000198385098385029250828110838203039150509250929050565b600081615306578383816152fe576152fe615db2565b04905061515d565b838210615326576040516313eae71560e01b815260040160405180910390fd5b600084868809600186198101871660008190038190049091018683119095039490940294038390049390931760029290940460038102831880820284030280820284030280820284030280820284030280820284030290810290920390910292909202949350505050565b8161539b57613c4e565b60006153a78530614700565b905082818111156153b55750805b82156153d1578060a3546153c99190615c21565b60a3556153e3565b8060a2546153df9190615c21565b60a2555b6153ee868683615565565b505050505050565b60606154058484600085615601565b949350505050565b600080600061541c86866152c9565b9150915081600014615432578360ff1682901c92505b801561547257600160ff85161b811061545e57604051638e471a8960e01b815260040160405180910390fd5b8360ff166101000361ffff1681901b830192505b50509392505050565b609b5461010090046001600160a01b031661549257565b600080609b60019054906101000a90046001600160a01b03166001600160a01b031663feaf968c6040518163ffffffff1660e01b815260040160a060405180830381865afa1580156154e8573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061550c9190615f1f565b50919450925050821590508061553557604051636e1d410960e01b815260040160405180910390fd5b60006155418342615c21565b9050610e108111613c4e5760405163fe1a0def60e01b815260040160405180910390fd5b80156115e5576001600160a01b0383166155ed576000826001600160a01b03168260405160006040518083038185875af1925050503d80600081146155c6576040519150601f19603f3d011682016040523d82523d6000602084013e6155cb565b606091505b5050905080613c4e57604051636750787b60e11b815260040160405180910390fd5b6115e56001600160a01b0384168383613f38565b6060824710156156795760405162461bcd60e51b815260206004820152602660248201527f416464726573733a20696e73756666696369656e742062616c616e636520666f60448201527f722063616c6c00000000000000000000000000000000000000000000000000006064820152608401612131565b600080866001600160a01b031685876040516156959190615f6f565b60006040518083038185875af1925050503d80600081146156d2576040519150601f19603f3d011682016040523d82523d6000602084013e6156d7565b606091505b50915091506156e8878383876156f3565b979650505050505050565b6060831561575f578251615758576001600160a01b0385163b6157585760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152606401612131565b5081615405565b61540583838151156157745781518083602001fd5b8060405162461bcd60e51b81526004016121319190615898565b82805461579a90615b9f565b90600052602060002090601f0160209004810192826157bc5760008555615802565b82601f106157d557805160ff1916838001178555615802565b82800160010185558215615802579182015b828111156158025782518255916020019190600101906157e7565b5061580e929150615812565b5090565b5b8082111561580e5760008155600101615813565b60008060006060848603121561583c57600080fd5b505081359360208301359350604090920135919050565b60006020828403121561586557600080fd5b5035919050565b60005b8381101561588757818101518382015260200161586f565b83811115613c4e5750506000910152565b60208152600082518060208401526158b781604085016020870161586c565b601f01601f19169190910160400192915050565b6001600160a01b038116811461204757600080fd5b600080604083850312156158f357600080fd5b82356158fe816158cb565b946020939093013593505050565b60008060006060848603121561592157600080fd5b833561592c816158cb565b9250602084013561593c816158cb565b929592945050506040919091013590565b6000806040838503121561596057600080fd5b823591506020830135615972816158cb565b809150509250929050565b60006020828403121561598f57600080fd5b813561478c816158cb565b634e487b7160e01b600052604160045260246000fd5b600082601f8301126159c157600080fd5b813567ffffffffffffffff808211156159dc576159dc61599a565b604051601f8301601f19908116603f01168101908282118183101715615a0457615a0461599a565b81604052838152866020858801011115615a1d57600080fd5b836020870160208301376000602085830101528094505050505092915050565b60008060408385031215615a5057600080fd5b823567ffffffffffffffff80821115615a6857600080fd5b615a74868387016159b0565b93506020850135915080821115615a8a57600080fd5b50615a97858286016159b0565b9150509250929050565b60008060408385031215615ab457600080fd5b50508035926020909101359150565b600060208284031215615ad557600080fd5b813564ffffffffff8116811461478c57600080fd5b801515811461204757600080fd5b600060208284031215615b0a57600080fd5b813561478c81615aea565b60008060408385031215615b2857600080fd5b8235615b33816158cb565b91506020830135615972816158cb565b62ffffff8116811461204757600080fd5b60008060408385031215615b6757600080fd5b8235615b7281615b43565b9150602083013561597281615b43565b600060208284031215615b9457600080fd5b815161478c816158cb565b600181811c90821680615bb357607f821691505b60208210811415615bd457634e487b7160e01b600052602260045260246000fd5b50919050565b634e487b7160e01b600052601160045260246000fd5b60008219821115615c0357615c03615bda565b500190565b600060208284031215615c1a57600080fd5b5051919050565b600082821015615c3357615c33615bda565b500390565b600181815b80851115615c73578160001904821115615c5957615c59615bda565b80851615615c6657918102915b93841c9390800290615c3d565b509250929050565b600082615c8a5750600161119b565b81615c975750600061119b565b8160018114615cad5760028114615cb757615cd3565b600191505061119b565b60ff841115615cc857615cc8615bda565b50506001821b61119b565b5060208310610133831016604e8410600b8410161715615cf6575081810a61119b565b615d008383615c38565b8060001904821115615d1457615d14615bda565b029392505050565b600061478c60ff841683615c7b565b634e487b7160e01b600052603260045260246000fd5b600060ff821660ff84168060ff03821115615d5e57615d5e615bda565b019392505050565b60008060408385031215615d7957600080fd5b8251615d8481615b43565b602084015190925061597281615b43565b6000600160ff1b821415615dab57615dab615bda565b5060000390565b634e487b7160e01b600052601260045260246000fd5b600082615dd757615dd7615db2565b500490565b6000816000190483118215151615615df657615df6615bda565b500290565b60008060408385031215615e0e57600080fd5b505080516020909101519092909150565b600060208284031215615e3157600080fd5b815161478c81615aea565b805167ffffffffffffffff81168114611ab857600080fd5b600080600060608486031215615e6957600080fd5b615e7284615e3c565b9250615e8060208501615e3c565b9150615e8e60408501615e3c565b90509250925092565b600064ffffffffff83811690831681811015615eb557615eb5615bda565b039392505050565b600067ffffffffffffffff83811690831681811015615eb557615eb5615bda565b600067ffffffffffffffff80841680615ef957615ef9615db2565b92169190910492915050565b805169ffffffffffffffffffff81168114611ab857600080fd5b600080600080600060a08688031215615f3757600080fd5b615f4086615f05565b9450602086015193506040860151925060608601519150615f6360808701615f05565b90509295509295909350565b60008251615f8181846020870161586c565b919091019291505056fea26469706673582212209898d1f8c076df8e394a428deab122eb8e24d72880347a339085abbcd9defe3264736f6c634300080a0033

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

000000000000000000000000197d40b36677248e82939f96930bf4e7fe8ad1c2

-----Decoded View---------------
Arg [0] : factory (address): 0x197d40B36677248E82939f96930bf4E7Fe8aD1c2

-----Encoded View---------------
1 Constructor Arguments found :
Arg [0] : 000000000000000000000000197d40b36677248e82939f96930bf4e7fe8ad1c2


Block Transaction Gas Used Reward
view all blocks produced

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

Validator Index Block Amount
View All Withdrawals

Transaction Hash Block Value Eth2 PubKey Valid
View All Deposits

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.