ETH Price: $3,617.93 (+4.61%)

Contract

0xb5296A1D8fC57Bd0af3375C68d48114E91f3B1aD

Overview

ETH Balance

0 ETH

ETH Value

$0.00

Token Holdings

More Info

Private Name Tags

Multichain Info

No addresses found
Transaction Hash
Method
Block
From
To
Earn Preparation3987847832025-11-10 12:36:521 hr ago1762778212IN
0xb5296A1D...E91f3B1aD
0 ETH0.000001290.01
Earn3987847162025-11-10 12:36:351 hr ago1762778195IN
0xb5296A1D...E91f3B1aD
0 ETH0.00000090.01
Earn3987846762025-11-10 12:36:251 hr ago1762778185IN
0xb5296A1D...E91f3B1aD
0 ETH0.000008680.01
Earn3987846362025-11-10 12:36:151 hr ago1762778175IN
0xb5296A1D...E91f3B1aD
0 ETH0.00000860.01
Earn3987845912025-11-10 12:36:041 hr ago1762778164IN
0xb5296A1D...E91f3B1aD
0 ETH0.000008880.01
Earn Preparation3987845852025-11-10 12:36:021 hr ago1762778162IN
0xb5296A1D...E91f3B1aD
0 ETH0.000009130.01
Collect Rewards3987845492025-11-10 12:35:531 hr ago1762778153IN
0xb5296A1D...E91f3B1aD
0 ETH0.00000260.01
Earn3987845072025-11-10 12:35:431 hr ago1762778143IN
0xb5296A1D...E91f3B1aD
0 ETH0.000002060.01
Earn3987844672025-11-10 12:35:331 hr ago1762778133IN
0xb5296A1D...E91f3B1aD
0 ETH0.00000770.01
Earn3987844292025-11-10 12:35:231 hr ago1762778123IN
0xb5296A1D...E91f3B1aD
0 ETH0.000007960.01
Earn Preparation3987844232025-11-10 12:35:221 hr ago1762778122IN
0xb5296A1D...E91f3B1aD
0 ETH0.000008240.01
Collect Rewards3987843872025-11-10 12:35:131 hr ago1762778113IN
0xb5296A1D...E91f3B1aD
0 ETH0.00000240.01
Earn3987843452025-11-10 12:35:021 hr ago1762778102IN
0xb5296A1D...E91f3B1aD
0 ETH0.000003740.01
Earn3987843052025-11-10 12:34:521 hr ago1762778092IN
0xb5296A1D...E91f3B1aD
0 ETH0.000011780.01
Earn3987842652025-11-10 12:34:421 hr ago1762778082IN
0xb5296A1D...E91f3B1aD
0 ETH0.000011780.01
Earn3987842252025-11-10 12:34:321 hr ago1762778072IN
0xb5296A1D...E91f3B1aD
0 ETH0.000011780.01
Earn3987841852025-11-10 12:34:221 hr ago1762778062IN
0xb5296A1D...E91f3B1aD
0 ETH0.000011950.01
Earn3987841452025-11-10 12:34:121 hr ago1762778052IN
0xb5296A1D...E91f3B1aD
0 ETH0.000012040.01
Earn Preparation3987841392025-11-10 12:34:101 hr ago1762778050IN
0xb5296A1D...E91f3B1aD
0 ETH0.000012290.01
Collect Rewards3987841032025-11-10 12:34:011 hr ago1762778041IN
0xb5296A1D...E91f3B1aD
0 ETH0.000001920.01
Earn3987840612025-11-10 12:33:511 hr ago1762778031IN
0xb5296A1D...E91f3B1aD
0 ETH0.000004040.01
Earn3987840212025-11-10 12:33:411 hr ago1762778021IN
0xb5296A1D...E91f3B1aD
0 ETH0.00001710.01
Earn3987839822025-11-10 12:33:311 hr ago1762778011IN
0xb5296A1D...E91f3B1aD
0 ETH0.00001690.01
Earn3987839432025-11-10 12:33:211 hr ago1762778001IN
0xb5296A1D...E91f3B1aD
0 ETH0.000017120.01
Earn3987839032025-11-10 12:33:111 hr ago1762777991IN
0xb5296A1D...E91f3B1aD
0 ETH0.000017120.01
View all transactions

Parent Transaction Hash Block From To
View All Internal Transactions

Cross-Chain Transactions
Loading...
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
Controller

Compiler Version
v0.8.18+commit.87f61d96

Optimization Enabled:
Yes with 200 runs

Other Settings:
default evmVersion
File 1 of 27 : Controller.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

import "./interfaces/IFundManagerVault.sol";
import "./interfaces/IRescaleTickBoundaryCalculator.sol";
import "./interfaces/IController.sol";
import "./interfaces/IControllerEvent.sol";
import "./interfaces/IStrategyInfo.sol";
import "./interfaces/IStrategy.sol";
import "./interfaces/IStrategySetter.sol";
import "./libraries/constants/Constants.sol";
import "./libraries/LiquidityNftHelper.sol";
import "./libraries/ParameterVerificationHelper.sol";
import "@openzeppelin/contracts/access/AccessControl.sol";
import "@openzeppelin/contracts/utils/math/SafeMath.sol";

/// @dev verified, private contract
/// @dev only owner or operator/backend callable
contract Controller is AccessControl, IControllerEvent, IController {
    using SafeMath for uint256;

    bytes32 public constant EXECUTOR_ROLE = keccak256("EXECUTOR_ROLE");
    mapping(address => int24) public tickSpreadUpper; // strategy => tickSpreadUpper
    mapping(address => int24) public tickSpreadLower; // strategy => tickSpreadLower
    mapping(address => int24) public tickGapUpper; // strategy => tickGapUpper
    mapping(address => int24) public tickGapLower; // strategy => tickGapLower
    mapping(address => int24) public tickUpperBoundaryOffset; // strategy => tickUpperBoundaryOffset
    mapping(address => int24) public tickLowerBoundaryOffset; // strategy => tickLowerBoundaryOffset
    mapping(address => int24) public rescaleTickUpperBoundaryOffset; // strategy => rescaleTickUpperBoundaryOffset
    mapping(address => int24) public rescaleTickLowerBoundaryOffset; // strategy => rescaleTickLowerBoundaryOffset
    mapping(address => int24) public lastRescaleTick; // strategy => lastRescaleTick
    mapping(address => int24) public rescaleTickTolerance; // strategy => rescaleTickTolerance

    constructor(address _executor) {
        // set deployer as EXECUTOR_ROLE roleAdmin
        _setRoleAdmin(EXECUTOR_ROLE, DEFAULT_ADMIN_ROLE);
        _setupRole(DEFAULT_ADMIN_ROLE, msg.sender);
        // set EXECUTOR_ROLE memebers
        _setupRole(EXECUTOR_ROLE, _executor);
    }

    /// @dev backend get function support
    function isNftWithinRange(address _strategyContract) public view returns (bool isWithinRange) {
        uint256 liquidityNftId = IStrategyInfo(_strategyContract).liquidityNftId();

        require(liquidityNftId != 0, "not allow calling when liquidityNftId is 0");

        (isWithinRange,) = LiquidityNftHelper.verifyCurrentPriceInLiquidityNftRange(
            liquidityNftId, Constants.UNISWAP_V3_FACTORY_ADDRESS, Constants.NONFUNGIBLE_POSITION_MANAGER_ADDRESS
        );
    }

    function getEarningFlag(address _strategyContract) public view returns (bool isEarningFlag) {
        return IStrategyInfo(_strategyContract).isEarning();
    }

    function getRescalingFlag(address _strategyContract) public view returns (bool isRescalingFlag) {
        uint256 liquidityNftId = IStrategyInfo(_strategyContract).liquidityNftId();

        if (liquidityNftId == 0) {
            return true;
        } else {
            return false;
        }
    }

    function getRemainingEarnCountDown(address _strategyContract) public view returns (uint256 remainingEarn) {
        bool isEarningFlag = getEarningFlag(_strategyContract);
        if (isEarningFlag == false) {
            return 0;
        }

        uint256 userListLength = IStrategyInfo(_strategyContract).getAllUsersInUserList().length;
        uint256 earnLoopStartIndex = IStrategyInfo(_strategyContract).earnLoopStartIndex();

        uint256 remainingUserNumber = userListLength.sub(earnLoopStartIndex);
        uint256 earnLoopSegmentSize = IStrategyInfo(_strategyContract).earnLoopSegmentSize();

        (uint256 quotient, uint256 remainder) = calculateQuotientAndRemainder(remainingUserNumber, earnLoopSegmentSize);

        return remainder > 0 ? (quotient.add(1)) : quotient;
    }

    function calculateQuotientAndRemainder(uint256 dividend, uint256 divisor)
        internal
        pure
        returns (uint256 quotient, uint256 remainder)
    {
        quotient = dividend.div(divisor);
        remainder = dividend.mod(divisor);
    }

    function getAllFundManagers(address _fundManagerVaultContract)
        public
        view
        returns (IFundManagerVault.FundManager[8] memory)
    {
        return IFundManagerVault(_fundManagerVaultContract).getAllFundManagers();
    }

    /// @dev transactionDeadlineDuration setter
    function setTransactionDeadlineDuration(address _strategyContract, uint256 _transactionDeadlineDuration)
        public
        onlyRole(EXECUTOR_ROLE)
    {
        IStrategySetter(_strategyContract).setTransactionDeadlineDuration(_transactionDeadlineDuration);

        emit SetTransactionDeadlineDuration(_strategyContract, msg.sender, _transactionDeadlineDuration);
    }

    /// @dev tickSpreadUpper setter
    function setTickSpreadUpper(address _strategyContract, int24 _tickSpreadUpper) public onlyRole(EXECUTOR_ROLE) {
        // parameter verification
        ParameterVerificationHelper.verifyGreaterThanOrEqualToZero(_tickSpreadUpper);

        // update tickSpreadUpper
        tickSpreadUpper[_strategyContract] = _tickSpreadUpper;

        // emit SetTickSpreadUpper event
        emit SetTickSpreadUpper(_strategyContract, msg.sender, _tickSpreadUpper);
    }

    /// @dev tickSpreadLower setter
    function setTickSpreadLower(address _strategyContract, int24 _tickSpreadLower) public onlyRole(EXECUTOR_ROLE) {
        // parameter verification
        ParameterVerificationHelper.verifyGreaterThanOrEqualToZero(_tickSpreadLower);

        // update tickSpreadLower
        tickSpreadLower[_strategyContract] = _tickSpreadLower;

        // emit SetTickSpreadLower event
        emit SetTickSpreadLower(_strategyContract, msg.sender, _tickSpreadLower);
    }

    /// @dev tickGapUpper setter
    function setTickGapUpper(address _strategyContract, int24 _tickGapUpper) public onlyRole(EXECUTOR_ROLE) {
        // parameter verification
        ParameterVerificationHelper.verifyGreaterThanOne(_tickGapUpper);

        // update tickGapUpper
        tickGapUpper[_strategyContract] = _tickGapUpper;

        // emit SetTickGapUpper event
        emit SetTickGapUpper(_strategyContract, msg.sender, _tickGapUpper);
    }

    /// @dev tickGapLower setter
    function setTickGapLower(address _strategyContract, int24 _tickGapLower) public onlyRole(EXECUTOR_ROLE) {
        // parameter verification
        ParameterVerificationHelper.verifyGreaterThanOne(_tickGapLower);

        // update tickGapLower
        tickGapLower[_strategyContract] = _tickGapLower;

        // emit SetTickGapLower event
        emit SetTickGapLower(_strategyContract, msg.sender, _tickGapLower);
    }

    /// @dev buyBackToken setter
    function setBuyBackToken(address _strategyContract, address _buyBackToken) public onlyRole(EXECUTOR_ROLE) {
        IStrategySetter(_strategyContract).setBuyBackToken(_buyBackToken);

        emit SetBuyBackToken(_strategyContract, msg.sender, _buyBackToken);
    }

    /// @dev buyBackNumerator setter
    function setBuyBackNumerator(address _strategyContract, uint24 _buyBackNumerator) public onlyRole(EXECUTOR_ROLE) {
        IStrategySetter(_strategyContract).setBuyBackNumerator(_buyBackNumerator);

        emit SetBuyBackNumerator(_strategyContract, msg.sender, _buyBackNumerator);
    }

    /// @dev fundManagerVault setter
    function setFundManagerVaultByIndex(
        address _strategyContract,
        uint256 _index,
        address _fundManagerVaultAddress,
        uint24 _fundManagerProfitVaultNumerator
    ) public onlyRole(EXECUTOR_ROLE) {
        IStrategySetter(_strategyContract).setFundManagerVaultByIndex(
            _index, _fundManagerVaultAddress, _fundManagerProfitVaultNumerator
        );

        emit SetFundManagerVaultByIndex(
            _strategyContract, msg.sender, _index, _fundManagerVaultAddress, _fundManagerProfitVaultNumerator
        );
    }

    /// @dev fundManager setter
    function setFundManagerByIndex(
        address _fundManagerVaultContract,
        uint256 _index,
        address _fundManagerAddress,
        uint24 _fundManagerProfitNumerator
    ) public onlyRole(EXECUTOR_ROLE) {
        IFundManagerVault(_fundManagerVaultContract).setFundManagerByIndex(
            _index, _fundManagerAddress, _fundManagerProfitNumerator
        );

        emit SetFundManagerByIndex(
            _fundManagerVaultContract, msg.sender, _index, _fundManagerAddress, _fundManagerProfitNumerator
        );
    }

    /// @dev earnLoopSegmentSize setter
    function setEarnLoopSegmentSize(address _strategyContract, uint256 _earnLoopSegmentSize)
        public
        onlyRole(EXECUTOR_ROLE)
    {
        IStrategySetter(_strategyContract).setEarnLoopSegmentSize(_earnLoopSegmentSize);

        emit SetEarnLoopSegmentSize(_strategyContract, msg.sender, _earnLoopSegmentSize);
    }
    /// @dev tickBoundaryOffset setter

    function setTickUpperBoundaryOffset(address _strategyContract, int24 _tickUpperBoundaryOffset)
        public
        onlyRole(EXECUTOR_ROLE)
    {
        // parameter verification
        ParameterVerificationHelper.verifyGreaterThanOrEqualToZero(_tickUpperBoundaryOffset);

        // update tickBoundaryOffset
        tickUpperBoundaryOffset[_strategyContract] = _tickUpperBoundaryOffset;

        // emit SetTickUpperBoundaryOffset event
        emit SetTickUpperBoundaryOffset(_strategyContract, msg.sender, _tickUpperBoundaryOffset);
    }

    function setTickLowerBoundaryOffset(address _strategyContract, int24 _tickLowerBoundaryOffset)
        public
        onlyRole(EXECUTOR_ROLE)
    {
        // parameter verification
        ParameterVerificationHelper.verifyGreaterThanOrEqualToZero(_tickLowerBoundaryOffset);

        // update tickBoundaryOffset
        tickLowerBoundaryOffset[_strategyContract] = _tickLowerBoundaryOffset;

        // emit SetTickLowerBoundaryOffset event
        emit SetTickLowerBoundaryOffset(_strategyContract, msg.sender, _tickLowerBoundaryOffset);
    }

    /// @dev rescaleTickBoundaryOffset setter
    function setRescaleTickUpperBoundaryOffset(address _strategyContract, int24 _rescaleTickUpperBoundaryOffset)
        public
        onlyRole(EXECUTOR_ROLE)
    {
        // parameter verification
        ParameterVerificationHelper.verifyGreaterThanOrEqualToZero(_rescaleTickUpperBoundaryOffset);

        // update rescaleTickBoundaryOffset
        rescaleTickUpperBoundaryOffset[_strategyContract] = _rescaleTickUpperBoundaryOffset;

        // emit SetRescaleTickUpperBoundaryOffset event
        emit SetRescaleTickUpperBoundaryOffset(_strategyContract, msg.sender, _rescaleTickUpperBoundaryOffset);
    }

    function setRescaleTickLowerBoundaryOffset(address _strategyContract, int24 _rescaleTickLowerBoundaryOffset)
        public
        onlyRole(EXECUTOR_ROLE)
    {
        // parameter verification
        ParameterVerificationHelper.verifyGreaterThanOrEqualToZero(_rescaleTickLowerBoundaryOffset);

        // update rescaleTickBoundaryOffset
        rescaleTickLowerBoundaryOffset[_strategyContract] = _rescaleTickLowerBoundaryOffset;

        // emit SetRescaleTickLowerBoundaryOffset event
        emit SetRescaleTickLowerBoundaryOffset(_strategyContract, msg.sender, _rescaleTickLowerBoundaryOffset);
    }

    /// @dev rescaleTickTolerance setter
    function setRescaleTickTolerance(address _strategyContract, int24 _rescaleTickTolerance)
        public
        onlyRole(EXECUTOR_ROLE)
    {
        // parameter verification
        ParameterVerificationHelper.verifyGreaterThanOrEqualToZero(_rescaleTickTolerance);

        // update rescaleTickTolerance
        rescaleTickTolerance[_strategyContract] = _rescaleTickTolerance;

        // emit SetRescaleTickTolerance event
        emit SetRescaleTickTolerance(_strategyContract, msg.sender, _rescaleTickTolerance);
    }

    /// @dev earn related
    function collectRewards(address _strategyContract) public onlyRole(EXECUTOR_ROLE) {
        IStrategy(_strategyContract).collectRewards();

        emit CollectRewards(
            _strategyContract,
            msg.sender,
            IStrategyInfo(_strategyContract).liquidityNftId(),
            IStrategyInfo(_strategyContract).rewardToken0Amount(),
            IStrategyInfo(_strategyContract).rewardToken1Amount(),
            IStrategyInfo(_strategyContract).rewardWbtcAmount()
        );
    }

    function earnPreparation(
        address _strategyContract,
        uint256 _minimumToken0SwapOutAmount,
        uint256 _minimumToken1SwapOutAmount,
        uint256 _minimumBuybackSwapOutAmount
    ) public onlyRole(EXECUTOR_ROLE) {
        IStrategy(_strategyContract).earnPreparation(
            _minimumToken0SwapOutAmount, _minimumToken1SwapOutAmount, _minimumBuybackSwapOutAmount
        );
        require(getEarningFlag(_strategyContract) == true, "earn prep error");
        //todo: add the totalSwapOutWbtc,rewardWbtcAmount,fundManagerVaultUsedWbtc
        emit EarnPreparation(
            _strategyContract,
            msg.sender,
            IStrategyInfo(_strategyContract).liquidityNftId(),
            IStrategyInfo(_strategyContract).rewardWbtcAmount(),
            getRemainingEarnCountDown(_strategyContract)
        );
    }

    function earn(address _strategyContract) public onlyRole(EXECUTOR_ROLE) {
        IStrategy(_strategyContract).earn();

        emit Earn(
            _strategyContract,
            msg.sender,
            IStrategyInfo(_strategyContract).liquidityNftId(),
            getRemainingEarnCountDown(_strategyContract)
        );
    }

    function earnPreparation(address _fundManagerVaultContract) public onlyRole(EXECUTOR_ROLE) {
        distribute(_fundManagerVaultContract);
    }

    function allocate(address _fundManagerVaultContract) public onlyRole(EXECUTOR_ROLE) {
        distribute(_fundManagerVaultContract);
    }

    function distribute(address _fundManagerVaultContract) private {
        uint256 wbtcBeforeTx = IFundManagerVault(_fundManagerVaultContract).getWbtcBalance();

        IFundManagerVault(_fundManagerVaultContract).allocate();

        uint256 wbtcAfterTx = IFundManagerVault(_fundManagerVaultContract).getWbtcBalance();

        emit Allocate(_fundManagerVaultContract, msg.sender, wbtcBeforeTx.sub(wbtcAfterTx), wbtcAfterTx);
    }

    /// @dev rescale related
    /// @param _strategyContract strategy contract address
    /// @param _wasInRange true if lastRescaleTick was in the currentTickUpper and currentTickLower before
    function rescale(
        address _strategyContract,
        address _rescaleTickBoundaryCalculatorContract,
        bool _wasInRange,
        int24 _tickBeforeRescale
    ) public onlyRole(EXECUTOR_ROLE) {
        (bool allowRescale, int24 newTickUpper, int24 newTickLower) = IRescaleTickBoundaryCalculator(
            _rescaleTickBoundaryCalculatorContract
        ).verifyAndGetNewRescaleTickBoundary(
            _wasInRange, lastRescaleTick[_strategyContract], _strategyContract, address(this)
        );
        require(allowRescale, "current condition not allow rescale");

        triggerRescaleStartEvent(_strategyContract, _wasInRange, _tickBeforeRescale);

        (int24 currentTick,,) = LiquidityNftHelper.getTickInfo(
            IStrategyInfo(_strategyContract).liquidityNftId(),
            Constants.UNISWAP_V3_FACTORY_ADDRESS,
            Constants.NONFUNGIBLE_POSITION_MANAGER_ADDRESS
        );

        require(
            currentTick <= (_tickBeforeRescale + rescaleTickTolerance[_strategyContract])
                && currentTick >= (_tickBeforeRescale - rescaleTickTolerance[_strategyContract]),
            "out of tick tolerance range"
        );

        IStrategy(_strategyContract).rescale(newTickUpper, newTickLower);

        lastRescaleTick[_strategyContract] = currentTick;

        triggerRescaleEndEvent(_strategyContract, newTickUpper, newTickLower);
    }

    //! the RescaleStart event need deduct one field, or it will occur stack too deep error
    function triggerRescaleStartEvent(address _strategyContract, bool _wasInRange, int24 _tickBeforeRescale) internal {
        (int24 currentTick, int24 originalTickLower, int24 originalTickUpper) = LiquidityNftHelper.getTickInfo(
            IStrategyInfo(_strategyContract).liquidityNftId(),
            Constants.UNISWAP_V3_FACTORY_ADDRESS,
            Constants.NONFUNGIBLE_POSITION_MANAGER_ADDRESS
        );

        emit RescaleStart(
            _strategyContract,
            _wasInRange,
            IStrategyInfo(_strategyContract).tickSpacing(),
            tickGapUpper[_strategyContract],
            tickGapLower[_strategyContract],
            tickUpperBoundaryOffset[_strategyContract],
            tickLowerBoundaryOffset[_strategyContract],
            lastRescaleTick[_strategyContract],
            _tickBeforeRescale,
            currentTick,
            originalTickUpper,
            originalTickLower
        );
    }

    function triggerRescaleEndEvent(address _strategyContract, int24 newTickUpper, int24 newTickLower) internal {
        (int24 currentTick,,) = LiquidityNftHelper.getTickInfo(
            IStrategyInfo(_strategyContract).liquidityNftId(),
            Constants.UNISWAP_V3_FACTORY_ADDRESS,
            Constants.NONFUNGIBLE_POSITION_MANAGER_ADDRESS
        );

        emit RescaleEnd(
            _strategyContract,
            msg.sender,
            IStrategyInfo(_strategyContract).dustToken0Amount(),
            IStrategyInfo(_strategyContract).dustToken1Amount(),
            IStrategyInfo(_strategyContract).tickSpacing(),
            currentTick,
            tickSpreadUpper[_strategyContract],
            tickSpreadLower[_strategyContract],
            rescaleTickUpperBoundaryOffset[_strategyContract],
            rescaleTickLowerBoundaryOffset[_strategyContract],
            newTickUpper,
            newTickLower
        );
    }

    /// @dev deposit dust token related
    function depositDustToken(address _strategyContract, bool _depositDustToken0) public onlyRole(EXECUTOR_ROLE) {
        (uint256 increasedToken0Amount, uint256 increasedToken1Amount) =
            IStrategy(_strategyContract).depositDustToken(_depositDustToken0);

        emit DepositDustToken(
            _strategyContract,
            msg.sender,
            IStrategyInfo(_strategyContract).liquidityNftId(),
            _depositDustToken0,
            increasedToken0Amount,
            increasedToken1Amount,
            IStrategyInfo(_strategyContract).dustToken0Amount(),
            IStrategyInfo(_strategyContract).dustToken1Amount()
        );
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (access/AccessControl.sol)

pragma solidity ^0.8.0;

import "./IAccessControl.sol";
import "../utils/Context.sol";
import "../utils/Strings.sol";
import "../utils/introspection/ERC165.sol";

/**
 * @dev Contract module that allows children to implement role-based access
 * control mechanisms. This is a lightweight version that doesn't allow enumerating role
 * members except through off-chain means by accessing the contract event logs. Some
 * applications may benefit from on-chain enumerability, for those cases see
 * {AccessControlEnumerable}.
 *
 * Roles are referred to by their `bytes32` identifier. These should be exposed
 * in the external API and be unique. The best way to achieve this is by
 * using `public constant` hash digests:
 *
 * ```solidity
 * bytes32 public constant MY_ROLE = keccak256("MY_ROLE");
 * ```
 *
 * Roles can be used to represent a set of permissions. To restrict access to a
 * function call, use {hasRole}:
 *
 * ```solidity
 * function foo() public {
 *     require(hasRole(MY_ROLE, msg.sender));
 *     ...
 * }
 * ```
 *
 * Roles can be granted and revoked dynamically via the {grantRole} and
 * {revokeRole} functions. Each role has an associated admin role, and only
 * accounts that have a role's admin role can call {grantRole} and {revokeRole}.
 *
 * By default, the admin role for all roles is `DEFAULT_ADMIN_ROLE`, which means
 * that only accounts with this role will be able to grant or revoke other
 * roles. More complex role relationships can be created by using
 * {_setRoleAdmin}.
 *
 * WARNING: The `DEFAULT_ADMIN_ROLE` is also its own admin: it has permission to
 * grant and revoke this role. Extra precautions should be taken to secure
 * accounts that have been granted it. We recommend using {AccessControlDefaultAdminRules}
 * to enforce additional security measures for this role.
 */
abstract contract AccessControl is Context, IAccessControl, ERC165 {
    struct RoleData {
        mapping(address => bool) members;
        bytes32 adminRole;
    }

    mapping(bytes32 => RoleData) private _roles;

    bytes32 public constant DEFAULT_ADMIN_ROLE = 0x00;

    /**
     * @dev Modifier that checks that an account has a specific role. Reverts
     * with a standardized message including the required role.
     *
     * The format of the revert reason is given by the following regular expression:
     *
     *  /^AccessControl: account (0x[0-9a-f]{40}) is missing role (0x[0-9a-f]{64})$/
     *
     * _Available since v4.1._
     */
    modifier onlyRole(bytes32 role) {
        _checkRole(role);
        _;
    }

    /**
     * @dev See {IERC165-supportsInterface}.
     */
    function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
        return interfaceId == type(IAccessControl).interfaceId || super.supportsInterface(interfaceId);
    }

    /**
     * @dev Returns `true` if `account` has been granted `role`.
     */
    function hasRole(bytes32 role, address account) public view virtual override returns (bool) {
        return _roles[role].members[account];
    }

    /**
     * @dev Revert with a standard message if `_msgSender()` is missing `role`.
     * Overriding this function changes the behavior of the {onlyRole} modifier.
     *
     * Format of the revert message is described in {_checkRole}.
     *
     * _Available since v4.6._
     */
    function _checkRole(bytes32 role) internal view virtual {
        _checkRole(role, _msgSender());
    }

    /**
     * @dev Revert with a standard message if `account` is missing `role`.
     *
     * The format of the revert reason is given by the following regular expression:
     *
     *  /^AccessControl: account (0x[0-9a-f]{40}) is missing role (0x[0-9a-f]{64})$/
     */
    function _checkRole(bytes32 role, address account) internal view virtual {
        if (!hasRole(role, account)) {
            revert(
                string(
                    abi.encodePacked(
                        "AccessControl: account ",
                        Strings.toHexString(account),
                        " is missing role ",
                        Strings.toHexString(uint256(role), 32)
                    )
                )
            );
        }
    }

    /**
     * @dev Returns the admin role that controls `role`. See {grantRole} and
     * {revokeRole}.
     *
     * To change a role's admin, use {_setRoleAdmin}.
     */
    function getRoleAdmin(bytes32 role) public view virtual override returns (bytes32) {
        return _roles[role].adminRole;
    }

    /**
     * @dev Grants `role` to `account`.
     *
     * If `account` had not been already granted `role`, emits a {RoleGranted}
     * event.
     *
     * Requirements:
     *
     * - the caller must have ``role``'s admin role.
     *
     * May emit a {RoleGranted} event.
     */
    function grantRole(bytes32 role, address account) public virtual override onlyRole(getRoleAdmin(role)) {
        _grantRole(role, account);
    }

    /**
     * @dev Revokes `role` from `account`.
     *
     * If `account` had been granted `role`, emits a {RoleRevoked} event.
     *
     * Requirements:
     *
     * - the caller must have ``role``'s admin role.
     *
     * May emit a {RoleRevoked} event.
     */
    function revokeRole(bytes32 role, address account) public virtual override onlyRole(getRoleAdmin(role)) {
        _revokeRole(role, account);
    }

    /**
     * @dev Revokes `role` from the calling account.
     *
     * Roles are often managed via {grantRole} and {revokeRole}: this function's
     * purpose is to provide a mechanism for accounts to lose their privileges
     * if they are compromised (such as when a trusted device is misplaced).
     *
     * If the calling account had been revoked `role`, emits a {RoleRevoked}
     * event.
     *
     * Requirements:
     *
     * - the caller must be `account`.
     *
     * May emit a {RoleRevoked} event.
     */
    function renounceRole(bytes32 role, address account) public virtual override {
        require(account == _msgSender(), "AccessControl: can only renounce roles for self");

        _revokeRole(role, account);
    }

    /**
     * @dev Grants `role` to `account`.
     *
     * If `account` had not been already granted `role`, emits a {RoleGranted}
     * event. Note that unlike {grantRole}, this function doesn't perform any
     * checks on the calling account.
     *
     * May emit a {RoleGranted} event.
     *
     * [WARNING]
     * ====
     * This function should only be called from the constructor when setting
     * up the initial roles for the system.
     *
     * Using this function in any other way is effectively circumventing the admin
     * system imposed by {AccessControl}.
     * ====
     *
     * NOTE: This function is deprecated in favor of {_grantRole}.
     */
    function _setupRole(bytes32 role, address account) internal virtual {
        _grantRole(role, account);
    }

    /**
     * @dev Sets `adminRole` as ``role``'s admin role.
     *
     * Emits a {RoleAdminChanged} event.
     */
    function _setRoleAdmin(bytes32 role, bytes32 adminRole) internal virtual {
        bytes32 previousAdminRole = getRoleAdmin(role);
        _roles[role].adminRole = adminRole;
        emit RoleAdminChanged(role, previousAdminRole, adminRole);
    }

    /**
     * @dev Grants `role` to `account`.
     *
     * Internal function without access restriction.
     *
     * May emit a {RoleGranted} event.
     */
    function _grantRole(bytes32 role, address account) internal virtual {
        if (!hasRole(role, account)) {
            _roles[role].members[account] = true;
            emit RoleGranted(role, account, _msgSender());
        }
    }

    /**
     * @dev Revokes `role` from `account`.
     *
     * Internal function without access restriction.
     *
     * May emit a {RoleRevoked} event.
     */
    function _revokeRole(bytes32 role, address account) internal virtual {
        if (hasRole(role, account)) {
            _roles[role].members[account] = false;
            emit RoleRevoked(role, account, _msgSender());
        }
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (access/IAccessControl.sol)

pragma solidity ^0.8.0;

/**
 * @dev External interface of AccessControl declared to support ERC165 detection.
 */
interface IAccessControl {
    /**
     * @dev Emitted when `newAdminRole` is set as ``role``'s admin role, replacing `previousAdminRole`
     *
     * `DEFAULT_ADMIN_ROLE` is the starting admin for all roles, despite
     * {RoleAdminChanged} not being emitted signaling this.
     *
     * _Available since v3.1._
     */
    event RoleAdminChanged(bytes32 indexed role, bytes32 indexed previousAdminRole, bytes32 indexed newAdminRole);

    /**
     * @dev Emitted when `account` is granted `role`.
     *
     * `sender` is the account that originated the contract call, an admin role
     * bearer except when using {AccessControl-_setupRole}.
     */
    event RoleGranted(bytes32 indexed role, address indexed account, address indexed sender);

    /**
     * @dev Emitted when `account` is revoked `role`.
     *
     * `sender` is the account that originated the contract call:
     *   - if using `revokeRole`, it is the admin role bearer
     *   - if using `renounceRole`, it is the role bearer (i.e. `account`)
     */
    event RoleRevoked(bytes32 indexed role, address indexed account, address indexed sender);

    /**
     * @dev Returns `true` if `account` has been granted `role`.
     */
    function hasRole(bytes32 role, address account) external view returns (bool);

    /**
     * @dev Returns the admin role that controls `role`. See {grantRole} and
     * {revokeRole}.
     *
     * To change a role's admin, use {AccessControl-_setRoleAdmin}.
     */
    function getRoleAdmin(bytes32 role) external view returns (bytes32);

    /**
     * @dev Grants `role` to `account`.
     *
     * If `account` had not been already granted `role`, emits a {RoleGranted}
     * event.
     *
     * Requirements:
     *
     * - the caller must have ``role``'s admin role.
     */
    function grantRole(bytes32 role, address account) external;

    /**
     * @dev Revokes `role` from `account`.
     *
     * If `account` had been granted `role`, emits a {RoleRevoked} event.
     *
     * Requirements:
     *
     * - the caller must have ``role``'s admin role.
     */
    function revokeRole(bytes32 role, address account) external;

    /**
     * @dev Revokes `role` from the calling account.
     *
     * Roles are often managed via {grantRole} and {revokeRole}: this function's
     * purpose is to provide a mechanism for accounts to lose their privileges
     * if they are compromised (such as when a trusted device is misplaced).
     *
     * If the calling account had been granted `role`, emits a {RoleRevoked}
     * event.
     *
     * Requirements:
     *
     * - the caller must be `account`.
     */
    function renounceRole(bytes32 role, address account) external;
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.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);
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/Context.sol)

pragma solidity ^0.8.0;

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

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

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/introspection/ERC165.sol)

pragma solidity ^0.8.0;

import "./IERC165.sol";

/**
 * @dev Implementation of the {IERC165} interface.
 *
 * Contracts that want to implement ERC165 should inherit from this contract and override {supportsInterface} to check
 * for the additional interface id that will be supported. For example:
 *
 * ```solidity
 * function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
 *     return interfaceId == type(MyInterface).interfaceId || super.supportsInterface(interfaceId);
 * }
 * ```
 *
 * Alternatively, {ERC165Storage} provides an easier to use but more expensive implementation.
 */
abstract contract ERC165 is IERC165 {
    /**
     * @dev See {IERC165-supportsInterface}.
     */
    function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
        return interfaceId == type(IERC165).interfaceId;
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/introspection/IERC165.sol)

pragma solidity ^0.8.0;

/**
 * @dev Interface of the ERC165 standard, as defined in the
 * https://eips.ethereum.org/EIPS/eip-165[EIP].
 *
 * Implementers can declare support of contract interfaces, which can then be
 * queried by others ({ERC165Checker}).
 *
 * For an implementation, see {ERC165}.
 */
interface IERC165 {
    /**
     * @dev Returns true if this contract implements the interface defined by
     * `interfaceId`. See the corresponding
     * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section]
     * to learn more about how these ids are created.
     *
     * This function call must use less than 30 000 gas.
     */
    function supportsInterface(bytes4 interfaceId) external view returns (bool);
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (utils/math/Math.sol)

pragma solidity ^0.8.0;

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

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

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

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

    /**
     * @dev Returns the ceiling of the division of two numbers.
     *
     * This differs from standard division with `/` in that it rounds up instead
     * of rounding down.
     */
    function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {
        // (a + b - 1) / b can overflow on addition, so we distribute.
        return a == 0 ? 0 : (a - 1) / b + 1;
    }

    /**
     * @notice Calculates floor(x * y / denominator) with full precision. Throws if result overflows a uint256 or denominator == 0
     * @dev Original credit to Remco Bloemen under MIT license (https://xn--2-umb.com/21/muldiv)
     * with further edits by Uniswap Labs also under MIT license.
     */
    function mulDiv(uint256 x, uint256 y, uint256 denominator) internal pure returns (uint256 result) {
        unchecked {
            // 512-bit multiply [prod1 prod0] = x * y. Compute the product mod 2^256 and mod 2^256 - 1, then use
            // use the Chinese Remainder Theorem to reconstruct the 512 bit result. The result is stored in two 256
            // variables such that product = prod1 * 2^256 + prod0.
            uint256 prod0; // Least significant 256 bits of the product
            uint256 prod1; // Most significant 256 bits of the product
            assembly {
                let mm := mulmod(x, y, not(0))
                prod0 := mul(x, y)
                prod1 := sub(sub(mm, prod0), lt(mm, prod0))
            }

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

            // Make sure the result is less than 2^256. Also prevents denominator == 0.
            require(denominator > prod1, "Math: mulDiv overflow");

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

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

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

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

            // Does not overflow because the denominator cannot be zero at this stage in the function.
            uint256 twos = denominator & (~denominator + 1);
            assembly {
                // Divide denominator by twos.
                denominator := div(denominator, twos)

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

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

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

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

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

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

    /**
     * @notice Calculates x * y / denominator with full precision, following the selected rounding direction.
     */
    function mulDiv(uint256 x, uint256 y, uint256 denominator, Rounding rounding) internal pure returns (uint256) {
        uint256 result = mulDiv(x, y, denominator);
        if (rounding == Rounding.Up && mulmod(x, y, denominator) > 0) {
            result += 1;
        }
        return result;
    }

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

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

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

    /**
     * @notice Calculates sqrt(a), following the selected rounding direction.
     */
    function sqrt(uint256 a, Rounding rounding) internal pure returns (uint256) {
        unchecked {
            uint256 result = sqrt(a);
            return result + (rounding == Rounding.Up && result * result < a ? 1 : 0);
        }
    }

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

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

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

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

    /**
     * @dev Return the log in base 256, rounded down, of a positive value.
     * Returns 0 if given 0.
     *
     * Adding one to the result gives the number of pairs of hex symbols needed to represent `value` as a hex string.
     */
    function log256(uint256 value) internal pure returns (uint256) {
        uint256 result = 0;
        unchecked {
            if (value >> 128 > 0) {
                value >>= 128;
                result += 16;
            }
            if (value >> 64 > 0) {
                value >>= 64;
                result += 8;
            }
            if (value >> 32 > 0) {
                value >>= 32;
                result += 4;
            }
            if (value >> 16 > 0) {
                value >>= 16;
                result += 2;
            }
            if (value >> 8 > 0) {
                result += 1;
            }
        }
        return result;
    }

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

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (utils/math/SafeMath.sol)

pragma solidity ^0.8.0;

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

/**
 * @dev Wrappers over Solidity's arithmetic operations.
 *
 * NOTE: `SafeMath` is generally not needed starting with Solidity 0.8, since the compiler
 * now has built in overflow checking.
 */
library SafeMath {
    /**
     * @dev Returns the addition of two unsigned integers, with an overflow flag.
     *
     * _Available since v3.4._
     */
    function tryAdd(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        unchecked {
            uint256 c = a + b;
            if (c < a) return (false, 0);
            return (true, c);
        }
    }

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

    /**
     * @dev Returns the multiplication of two unsigned integers, with an overflow flag.
     *
     * _Available since v3.4._
     */
    function tryMul(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        unchecked {
            // Gas optimization: this is cheaper than requiring 'a' not being zero, but the
            // benefit is lost if 'b' is also tested.
            // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522
            if (a == 0) return (true, 0);
            uint256 c = a * b;
            if (c / a != b) return (false, 0);
            return (true, c);
        }
    }

    /**
     * @dev Returns the division of two unsigned integers, with a division by zero flag.
     *
     * _Available since v3.4._
     */
    function tryDiv(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        unchecked {
            if (b == 0) return (false, 0);
            return (true, a / b);
        }
    }

    /**
     * @dev Returns the remainder of dividing two unsigned integers, with a division by zero flag.
     *
     * _Available since v3.4._
     */
    function tryMod(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        unchecked {
            if (b == 0) return (false, 0);
            return (true, a % b);
        }
    }

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

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

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

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

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

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

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

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

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.0) (utils/math/SignedMath.sol)

pragma solidity ^0.8.0;

/**
 * @dev Standard signed math utilities missing in the Solidity language.
 */
library SignedMath {
    /**
     * @dev Returns the largest of two signed numbers.
     */
    function max(int256 a, int256 b) internal pure returns (int256) {
        return a > b ? a : b;
    }

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

    /**
     * @dev Returns the average of two signed numbers without overflow.
     * The result is rounded towards zero.
     */
    function average(int256 a, int256 b) internal pure returns (int256) {
        // Formula from the book "Hacker's Delight"
        int256 x = (a & b) + ((a ^ b) >> 1);
        return x + (int256(uint256(x) >> 255) & (a ^ b));
    }

    /**
     * @dev Returns the absolute unsigned value of a signed value.
     */
    function abs(int256 n) internal pure returns (uint256) {
        unchecked {
            // must be unchecked in order to support `n = type(int256).min`
            return uint256(n >= 0 ? n : -n);
        }
    }
}

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

pragma solidity ^0.8.0;

import "./math/Math.sol";
import "./math/SignedMath.sol";

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

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

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

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

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

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

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

// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

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

interface IERC20Querier is IERC20 {
    function decimals() external view returns (uint256);

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

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

// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

interface IController {
    function tickSpreadUpper(address strategyAddress) external view returns (int24);

    function tickSpreadLower(address strategyAddress) external view returns (int24);

    function tickGapUpper(address strategyAddress) external view returns (int24);

    function tickGapLower(address strategyAddress) external view returns (int24);

    function tickUpperBoundaryOffset(address strategyAddress) external view returns (int24);

    function tickLowerBoundaryOffset(address strategyAddress) external view returns (int24);

    function rescaleTickUpperBoundaryOffset(address strategyAddress) external view returns (int24);

    function rescaleTickLowerBoundaryOffset(address strategyAddress) external view returns (int24);

    function lastRescaleTick(address strategyAddress) external view returns (int24);
}

File 14 of 27 : IControllerEvent.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

interface IControllerEvent {
    event SetTransactionDeadlineDuration(
        address indexed strategyContract, address indexed executorAddress, uint256 transactionDeadlineDuration
    );

    event SetTickSpreadUpper(address indexed strategyContract, address indexed executorAddress, int24 tickSpreadUpper);

    event SetTickSpreadLower(address indexed strategyContract, address indexed executorAddress, int24 tickSpreadLower);

    event SetTickGapUpper(address indexed strategyContract, address indexed executorAddress, int24 tickGapUpper);

    event SetTickGapLower(address indexed strategyContract, address indexed executorAddress, int24 tickGapLower);

    event SetBuyBackToken(address indexed strategyContract, address indexed executorAddress, address buyBackToken);

    event SetBuyBackNumerator(
        address indexed strategyContract, address indexed executorAddress, uint24 buyBackNumerator
    );

    event SetFundManagerVaultByIndex(
        address indexed strategyContract,
        address indexed executorAddress,
        uint256 index,
        address fundManagerVaultAddress,
        uint24 fundManagerProfitVaultNumerator
    );

    event SetFundManagerByIndex(
        address indexed fundManagerVaultAddress,
        address indexed executorAddress,
        uint256 index,
        address fundManagerAddress,
        uint24 fundManagerProfitNumerator
    );

    event SetEarnLoopSegmentSize(
        address indexed strategyContract, address indexed executorAddress, uint256 earnLoopSegmentSize
    );

    event SetTickUpperBoundaryOffset(
        address indexed strategyContract, address indexed executorAddress, int24 tickUpperBoundaryOffset
    );

    event SetTickLowerBoundaryOffset(
        address indexed strategyContract, address indexed executorAddress, int24 tickLowerBoundaryOffset
    );

    event SetRescaleTickUpperBoundaryOffset(
        address indexed strategyContract, address indexed executorAddress, int24 rescaleTickUpperBoundaryOffset
    );

    event SetRescaleTickLowerBoundaryOffset(
        address indexed strategyContract, address indexed executorAddress, int24 rescaleTickLowerBoundaryOffset
    );

    event SetRescaleTickTolerance(
        address indexed strategyContract, address indexed executorAddress, int24 rescaleTickTolerance
    );

    event CollectRewards(
        address indexed strategyContract,
        address indexed executorAddress,
        uint256 indexed liquidityNftId,
        uint256 rewardToken0Amount,
        uint256 rewardToken1Amount,
        uint256 rewardWbtcAmount
    );

    event EarnPreparation(
        address indexed strategyContract,
        address indexed executorAddress,
        uint256 indexed liquidityNftId,
        uint256 rewardWbtcAmount,
        uint256 remainingEarnCountDown
    );

    event Earn(
        address indexed strategyContract,
        address indexed executorAddress,
        uint256 indexed liquidityNftId,
        uint256 remainingEarnCountDown
    );

    event Allocate(
        address indexed fundManagerVaultAddress,
        address indexed executorAddress,
        uint256 allocatedWbtcAmount,
        uint256 remainingWbtcAmount
    );

    event RescaleStart(
        address indexed strategyContract,
        bool wasInRange,
        int24 tickSpacing,
        int24 tickGapUpper,
        int24 tickGapLower,
        int24 tickUpperBoundaryOffset,
        int24 tickLowerBoundaryOffset,
        int24 lastRescaleTick,
        int24 tickBeforeRescale,
        int24 thisRescaleTick,
        int24 originalTickUpper,
        int24 originalTickLower
    );

    event RescaleEnd(
        address indexed strategyContract,
        address indexed executorAddress,
        uint256 dustToken0Amount,
        uint256 dustToken1Amount,
        int24 tickSpacing,
        int24 thisRescaleTick,
        int24 tickSpreadUpper,
        int24 tickSpreadLower,
        int24 rescaleTickUpperBoundaryOffset,
        int24 rescaleTickLowerBoundaryOffset,
        int24 newTickUpper,
        int24 newTickLower
    );

    event DepositDustToken(
        address indexed strategyContract,
        address indexed executorAddress,
        uint256 indexed liquidityNftId,
        bool depositDustToken0,
        uint256 increasedToken0Amount,
        uint256 increasedToken1Amount,
        uint256 dustToken0Amount,
        uint256 dustToken1Amount
    );
}

// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

interface IFundManagerVault {
    struct FundManager {
        address fundManagerAddress;
        uint256 fundManagerProfitNumerator;
    }

    function getWbtcBalance() external view returns (uint256);

    function getAllFundManagers() external view returns (FundManager[8] memory);

    function setFundManagerByIndex(
        uint256 index,
        address fundManagerAddress,
        uint24 fundManagerProfitNumerator
    ) external;

    function allocate() external;
}

// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

interface IRescaleTickBoundaryCalculator {
    function verifyAndGetNewRescaleTickBoundary(
        bool wasInRange,
        int24 lastRescaleTick,
        address strategyAddress,
        address controllerAddress
    )
        external
        view
        returns (bool allowRescale, int24 newTickUpper, int24 newTickLower);
}

// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

interface IStrategy {
    function depositLiquidity(
        bool isETH,
        address userAddress,
        address inputToken,
        uint256 inputAmount,
        uint256 swapInAmount,
        uint256 minimumSwapOutAmount
    )
        external
        payable
        returns (
            uint256 increasedToken0Amount,
            uint256 increasedToken1Amount,
            uint256 sendBackToken0Amount,
            uint256 sendBackToken1Amount
        );

    function withdrawLiquidity(
        address userAddress,
        uint256 withdrawShares
    )
        external
        returns (
            uint256 userReceivedToken0Amount,
            uint256 userReceivedToken1Amount
        );

    function collectRewards() external;

    function earnPreparation(
        uint256 minimumToken0SwapOutAmount,
        uint256 minimumToken1SwapOutAmount,
        uint256 minimumBuybackSwapOutAmount
    ) external;

    function earn() external;

    function claimReward(address userAddress) external;

    function rescale(int24 newTickUpper, int24 newTickLower) external;

    function depositDustToken(
        bool depositDustToken0
    )
        external
        returns (uint256 increasedToken0Amount, uint256 increasedToken1Amount);
}

// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

interface IStrategyInfo {
    /// @dev Uniswap-Transaction-related Variable
    function transactionDeadlineDuration() external view returns (uint256);

    /// @dev get Liquidity-NFT-related Variable
    function liquidityNftId() external view returns (uint256);

    function tickSpacing() external view returns (int24);

    /// @dev get Pool-related Variable
    function poolAddress() external view returns (address);

    function poolFee() external view returns (uint24);

    function token0Address() external view returns (address);

    function token1Address() external view returns (address);

    /// @dev get Tracker-Token-related Variable
    function trackerTokenAddress() external view returns (address);

    /// @dev get User-Management-related Variable
    function isInUserList(address userAddress) external view returns (bool);

    function userIndex(address userAddress) external view returns (uint256);

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

    /// @dev get User-Share-Management-related Variable
    function userShare(address userAddress) external view returns (uint256);

    function totalUserShare() external view returns (uint256);

    /// @dev get Reward-Management-related Variable
    function rewardToken0Amount() external view returns (uint256);

    function rewardToken1Amount() external view returns (uint256);

    function rewardWbtcAmount() external view returns (uint256);

    /// @dev get User-Reward-Management-related Variable
    function userWbtcReward(
        address userAddress
    ) external view returns (uint256);

    function totalUserWbtcReward() external view returns (uint256);

    /// @dev get Buyback-related Variable
    function buyBackToken() external view returns (address);

    function buyBackNumerator() external view returns (uint24);

    /// @dev get Fund-Manager-related Variable
    struct FundManagerVault {
        address fundManagerVaultAddress;
        uint256 fundManagerProfitVaultNumerator;
    }

    function getAllFundManagerVaults()
        external
        view
        returns (FundManagerVault[3] memory);

    /// @dev get Earn-Loop-Control-related Variable
    function earnLoopSegmentSize() external view returns (uint256);

    function earnLoopDistributedAmount() external view returns (uint256);

    function earnLoopStartIndex() external view returns (uint256);

    function isEarning() external view returns (bool);

    /// @dev get Rescale-related Variable
    function dustToken0Amount() external view returns (uint256);

    function dustToken1Amount() external view returns (uint256);

    /// @dev get Constant Variable
    function getBuyBackDenominator() external pure returns (uint24);

    function getFundManagerProfitVaultDenominator()
        external
        pure
        returns (uint24);

    function getFarmAddress() external pure returns (address);

    function getControllerAddress() external pure returns (address);

    function getSwapAmountCalculatorAddress() external pure returns (address);

    function getZapAddress() external pure returns (address);
}

// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

interface IStrategySetter {
    function setTransactionDeadlineDuration(
        uint256 _transactionDeadlineDuration
    ) external;

    function setBuyBackToken(address _buyBackToken) external;

    function setBuyBackNumerator(uint24 _buyBackNumerator) external;

    function setFundManagerVaultByIndex(
        uint256 index,
        address _fundManagerVaultAddress,
        uint24 _fundManagerProfitVaultNumerator
    ) external;

    function setEarnLoopSegmentSize(uint256 _earnLoopSegmentSize) external;
}

// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.7.5;
pragma abicoder v2;

interface INonfungiblePositionManager {
    /// @notice Returns the position information associated with a given token ID.
    /// @dev Throws if the token ID is not valid.
    /// @param tokenId The ID of the token that represents the position
    /// @return nonce The nonce for permits
    /// @return operator The address that is approved for spending
    /// @return token0 The address of the token0 for a specific pool
    /// @return token1 The address of the token1 for a specific pool
    /// @return fee The fee associated with the pool
    /// @return tickLower The lower end of the tick range for the position
    /// @return tickUpper The higher end of the tick range for the position
    /// @return liquidity The liquidity of the position
    /// @return feeGrowthInside0LastX128 The fee growth of token0 as of the last action on the individual position
    /// @return feeGrowthInside1LastX128 The fee growth of token1 as of the last action on the individual position
    /// @return tokensOwed0 The uncollected amount of token0 owed to the position as of the last computation
    /// @return tokensOwed1 The uncollected amount of token1 owed to the position as of the last computation
    function positions(
        uint256 tokenId
    )
        external
        view
        returns (
            uint96 nonce,
            address operator,
            address token0,
            address token1,
            uint24 fee,
            int24 tickLower,
            int24 tickUpper,
            uint128 liquidity,
            uint256 feeGrowthInside0LastX128,
            uint256 feeGrowthInside1LastX128,
            uint128 tokensOwed0,
            uint128 tokensOwed1
        );

    /// @notice Refunds any ETH balance held by this contract to the `msg.sender`
    /// @dev Useful for bundling with mint or increase liquidity that uses ether, or exact output swaps
    /// that use ether for the input amount
    function refundETH() external payable;

    struct MintParams {
        address token0;
        address token1;
        uint24 fee;
        int24 tickLower;
        int24 tickUpper;
        uint256 amount0Desired;
        uint256 amount1Desired;
        uint256 amount0Min;
        uint256 amount1Min;
        address recipient;
        uint256 deadline;
    }

    /// @notice Creates a new position wrapped in a NFT
    /// @dev Call this when the pool does exist and is initialized. Note that if the pool is created but not initialized
    /// a method does not exist, i.e. the pool is assumed to be initialized.
    /// @param params The params necessary to mint a position, encoded as `MintParams` in calldata
    /// @return tokenId The ID of the token that represents the minted position
    /// @return liquidity The amount of liquidity for this position
    /// @return amount0 The amount of token0
    /// @return amount1 The amount of token1
    function mint(
        MintParams calldata params
    )
        external
        payable
        returns (
            uint256 tokenId,
            uint128 liquidity,
            uint256 amount0,
            uint256 amount1
        );

    struct IncreaseLiquidityParams {
        uint256 tokenId;
        uint256 amount0Desired;
        uint256 amount1Desired;
        uint256 amount0Min;
        uint256 amount1Min;
        uint256 deadline;
    }

    /// @notice Increases the amount of liquidity in a position, with tokens paid by the `msg.sender`
    /// @param params tokenId The ID of the token for which liquidity is being increased,
    /// amount0Desired The desired amount of token0 to be spent,
    /// amount1Desired The desired amount of token1 to be spent,
    /// amount0Min The minimum amount of token0 to spend, which serves as a slippage check,
    /// amount1Min The minimum amount of token1 to spend, which serves as a slippage check,
    /// deadline The time by which the transaction must be included to effect the change
    /// @return liquidity The new liquidity amount as a result of the increase
    /// @return amount0 The amount of token0 to acheive resulting liquidity
    /// @return amount1 The amount of token1 to acheive resulting liquidity
    function increaseLiquidity(
        IncreaseLiquidityParams calldata params
    )
        external
        payable
        returns (uint128 liquidity, uint256 amount0, uint256 amount1);

    struct DecreaseLiquidityParams {
        uint256 tokenId;
        uint128 liquidity;
        uint256 amount0Min;
        uint256 amount1Min;
        uint256 deadline;
    }

    /// @notice Decreases the amount of liquidity in a position and accounts it to the position
    /// @param params tokenId The ID of the token for which liquidity is being decreased,
    /// amount The amount by which liquidity will be decreased,
    /// amount0Min The minimum amount of token0 that should be accounted for the burned liquidity,
    /// amount1Min The minimum amount of token1 that should be accounted for the burned liquidity,
    /// deadline The time by which the transaction must be included to effect the change
    /// @return amount0 The amount of token0 accounted to the position's tokens owed
    /// @return amount1 The amount of token1 accounted to the position's tokens owed
    function decreaseLiquidity(
        DecreaseLiquidityParams calldata params
    ) external payable returns (uint256 amount0, uint256 amount1);

    struct CollectParams {
        uint256 tokenId;
        address recipient;
        uint128 amount0Max;
        uint128 amount1Max;
    }

    /// @notice Collects up to a maximum amount of fees owed to a specific position to the recipient
    /// @param params tokenId The ID of the NFT for which tokens are being collected,
    /// recipient The account that should receive the tokens,
    /// amount0Max The maximum amount of token0 to collect,
    /// amount1Max The maximum amount of token1 to collect
    /// @return amount0 The amount of fees collected in token0
    /// @return amount1 The amount of fees collected in token1
    function collect(
        CollectParams calldata params
    ) external payable returns (uint256 amount0, uint256 amount1);

    /// @notice Burns a token ID, which deletes it from the NFT contract. The token must have 0 liquidity and all tokens
    /// must be collected first.
    /// @param tokenId The ID of the token that is being burned
    function burn(uint256 tokenId) external payable;
}

File 21 of 27 : IUniswapV3Factory.sol
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.5.0;

interface IUniswapV3Factory {
    /// @notice Returns the pool address for a given pair of tokens and a fee, or address 0 if it does not exist
    /// @dev tokenA and tokenB may be passed in either token0/token1 or token1/token0 order
    /// @param tokenA The contract address of either token0 or token1
    /// @param tokenB The contract address of the other token
    /// @param fee The fee collected upon every swap in the pool, denominated in hundredths of a bip
    /// @return pool The pool address
    function getPool(
        address tokenA,
        address tokenB,
        uint24 fee
    ) external view returns (address pool);
}

// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.5.0;

interface IUniswapV3Pool {
    /// @notice The 0th storage slot in the pool stores many values, and is exposed as a single method to save gas
    /// when accessed externally.
    /// @return sqrtPriceX96 The current price of the pool as a sqrt(token1/token0) Q64.96 value
    /// tick The current tick of the pool, i.e. according to the last tick transition that was run.
    /// This value may not always be equal to SqrtTickMath.getTickAtSqrtRatio(sqrtPriceX96) if the price is on a tick
    /// boundary.
    /// observationIndex The index of the last oracle observation that was written,
    /// observationCardinality The current maximum number of observations stored in the pool,
    /// observationCardinalityNext The next maximum number of observations, to be updated when the observation.
    /// feeProtocol The protocol fee for both tokens of the pool.
    /// Encoded as two 4 bit values, where the protocol fee of token1 is shifted 4 bits and the protocol fee of token0
    /// is the lower 4 bits. Used as the denominator of a fraction of the swap fee, e.g. 4 means 1/4th of the swap fee.
    /// unlocked Whether the pool is currently locked to reentrancy
    function slot0()
        external
        view
        returns (
            uint160 sqrtPriceX96,
            int24 tick,
            uint16 observationIndex,
            uint16 observationCardinality,
            uint16 observationCardinalityNext,
            uint8 feeProtocol,
            bool unlocked
        );

    /// @notice The first of the two tokens of the pool, sorted by address
    /// @return The token contract address
    function token0() external view returns (address);

    /// @notice The second of the two tokens of the pool, sorted by address
    /// @return The token contract address
    function token1() external view returns (address);

    /// @notice The pool's fee in hundredths of a bip, i.e. 1e-6
    /// @return The fee
    function fee() external view returns (uint24);

    /// @notice The pool tick spacing
    /// @dev Ticks can only be used at multiples of this value, minimum of 1 and always positive
    /// e.g.: a tickSpacing of 3 means ticks can be initialized every 3rd tick, i.e., ..., -6, -3, 0, 3, 6, ...
    /// This value is an int24 to avoid casting even though it is always positive.
    /// @return The tick spacing
    function tickSpacing() external view returns (int24);
}

File 23 of 27 : Constants.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

library Constants {
    /// @dev ArbiturmOne & Goerli uniswap V3
    address public constant UNISWAP_V3_FACTORY_ADDRESS =
        address(0x1F98431c8aD98523631AE4a59f267346ea31F984);
    address public constant NONFUNGIBLE_POSITION_MANAGER_ADDRESS =
        address(0xC36442b4a4522E871399CD717aBDD847Ab11FE88);
    address public constant SWAP_ROUTER_ADDRESS =
        address(0xE592427A0AEce92De3Edee1F18E0157C05861564);

    /// @dev ArbiturmOne token address
    address public constant WETH_ADDRESS =
        address(0x82aF49447D8a07e3bd95BD0d56f35241523fBab1);
    address public constant ARB_ADDRESS =
        address(0x912CE59144191C1204E64559FE8253a0e49E6548);
    address public constant WBTC_ADDRESS =
        address(0x2f2a2543B76A4166549F7aaB2e75Bef0aefC5B0f);
    address public constant USDC_ADDRESS =
        address(0xaf88d065e77c8cC2239327C5EDb3A432268e5831);
    address public constant USDCE_ADDRESS =
        address(0xFF970A61A04b1cA14834A43f5dE4533eBDDB5CC8);
    address public constant USDT_ADDRESS =
        address(0xFd086bC7CD5C481DCC9C85ebE478A1C0b69FCbb9);
    address public constant RDNT_ADDRESS =
        address(0x3082CC23568eA640225c2467653dB90e9250AaA0);
    address public constant LINK_ADDRESS =
        address(0xf97f4df75117a78c1A5a0DBb814Af92458539FB4);

    /// @dev black hole address
    address public constant BLACK_HOLE_ADDRESS =
        address(0x000000000000000000000000000000000000dEaD);
}

// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

import "../interfaces/uniswapV3/INonfungiblePositionManager.sol";
import "./uniswapV3/TickMath.sol";
import "./PoolHelper.sol";
import "@openzeppelin/contracts/utils/math/SafeMath.sol";

library LiquidityNftHelper {
    using SafeMath for uint256;

    function getLiquidityAmountByNftId(
        uint256 liquidityNftId,
        address nonfungiblePositionManagerAddress
    ) internal view returns (uint128 liquidity) {
        (, , , , , , , liquidity, , , , ) = INonfungiblePositionManager(
            nonfungiblePositionManagerAddress
        ).positions(liquidityNftId);
    }

    function getPoolInfoByLiquidityNft(
        uint256 liquidityNftId,
        address uniswapV3FactoryAddress,
        address nonfungiblePositionManagerAddress
    )
        internal
        view
        returns (
            address poolAddress,
            int24 tick,
            uint160 sqrtPriceX96,
            uint256 decimal0,
            uint256 decimal1
        )
    {
        (
            address token0,
            address token1,
            uint24 poolFee,
            ,
            ,
            ,

        ) = getLiquidityNftPositionsInfo(
                liquidityNftId,
                nonfungiblePositionManagerAddress
            );
        poolAddress = PoolHelper.getPoolAddress(
            uniswapV3FactoryAddress,
            token0,
            token1,
            poolFee
        );
        (, , , tick, sqrtPriceX96, decimal0, decimal1) = PoolHelper.getPoolInfo(
            poolAddress
        );
    }

    function getLiquidityNftPositionsInfo(
        uint256 liquidityNftId,
        address nonfungiblePositionManagerAddress
    )
        internal
        view
        returns (
            address token0,
            address token1,
            uint24 poolFee,
            int24 tickLower,
            int24 tickUpper,
            uint160 sqrtRatioAX96,
            uint160 sqrtRatioBX96
        )
    {
        (
            ,
            ,
            token0,
            token1,
            poolFee,
            tickLower,
            tickUpper,
            ,
            ,
            ,
            ,

        ) = INonfungiblePositionManager(nonfungiblePositionManagerAddress)
            .positions(liquidityNftId);
        sqrtRatioAX96 = TickMath.getSqrtRatioAtTick(tickLower);
        sqrtRatioBX96 = TickMath.getSqrtRatioAtTick(tickUpper);
    }

    /// @dev formula explanation
    /*
    [Original formula (without decimal precision)]
    tickUpper -> sqrtRatioBX96
    tickLower -> sqrtRatioAX96
    tick      -> sqrtPriceX96
    (token1 * (10^decimal1)) / (token0 * (10^decimal0)) = 
        (sqrtPriceX96 * sqrtRatioBX96 * (sqrtPriceX96 - sqrtRatioAX96))
            / ((2^192) * (sqrtRatioBX96 - sqrtPriceX96))
    
    [Formula with decimal precision & decimal adjustment]
    liquidityRatioWithDecimalAdj = liquidityRatio * (10^decimalPrecision)
        = (sqrtPriceX96 * (10^decimalPrecision) / (2^96))
            * (sqrtPriceBX96 * (10^decimalPrecision) / (2^96))
            * (sqrtPriceX96 - sqrtRatioAX96)
            / ((sqrtRatioBX96 - sqrtPriceX96) * (10^(decimalPrecision + decimal1 - decimal0)))
    */
    function getInRangeLiquidityRatioWithDecimals(
        uint256 liquidityNftId,
        uint256 decimalPrecision,
        address uniswapV3FactoryAddress,
        address nonfungiblePositionManagerAddress
    ) internal view returns (uint256 liquidityRatioWithDecimals) {
        // get sqrtPrice of tickUpper, tick, tickLower
        (
            ,
            ,
            ,
            ,
            ,
            uint160 sqrtRatioAX96,
            uint160 sqrtRatioBX96
        ) = getLiquidityNftPositionsInfo(
                liquidityNftId,
                nonfungiblePositionManagerAddress
            );
        (
            ,
            ,
            uint160 sqrtPriceX96,
            uint256 decimal0,
            uint256 decimal1
        ) = getPoolInfoByLiquidityNft(
                liquidityNftId,
                uniswapV3FactoryAddress,
                nonfungiblePositionManagerAddress
            );

        // when decimalPrecision is 18,
        // calculation restriction: 79228162514264337594 <= sqrtPriceX96 <= type(uint160).max
        uint256 scaledPriceX96 = uint256(sqrtPriceX96)
            .mul(10 ** decimalPrecision)
            .div(2 ** 96);
        uint256 scaledPriceBX96 = uint256(sqrtRatioBX96)
            .mul(10 ** decimalPrecision)
            .div(2 ** 96);

        uint256 decimalAdj = decimalPrecision.add(decimal1).sub(decimal0);
        uint256 preLiquidityRatioWithDecimals = scaledPriceX96
            .mul(scaledPriceBX96)
            .div(10 ** decimalAdj);

        liquidityRatioWithDecimals = preLiquidityRatioWithDecimals
            .mul(uint256(sqrtPriceX96).sub(sqrtRatioAX96))
            .div(uint256(sqrtRatioBX96).sub(sqrtPriceX96));
    }

    function verifyInputTokenIsLiquidityNftTokenPair(
        uint256 liquidityNftId,
        address inputToken,
        address nonfungiblePositionManagerAddress
    ) internal view {
        (
            address token0,
            address token1,
            ,
            ,
            ,
            ,

        ) = getLiquidityNftPositionsInfo(
                liquidityNftId,
                nonfungiblePositionManagerAddress
            );
        require(
            inputToken == token0 || inputToken == token1,
            "inputToken not in token pair"
        );
    }

    function verifyCurrentPriceInLiquidityNftRange(
        uint256 liquidityNftId,
        address uniswapV3FactoryAddress,
        address nonfungiblePositionManagerAddress
    ) internal view returns (bool isInRange, address liquidity0Token) {
        (, int24 tick, , , ) = getPoolInfoByLiquidityNft(
            liquidityNftId,
            uniswapV3FactoryAddress,
            nonfungiblePositionManagerAddress
        );
        (
            address token0,
            address token1,
            ,
            int24 tickLower,
            int24 tickUpper,
            ,

        ) = getLiquidityNftPositionsInfo(
                liquidityNftId,
                nonfungiblePositionManagerAddress
            );

        // tick out of range, tick <= tickLower left token0
        if (tick <= tickLower) {
            return (false, token0);

            // tick in range, tickLower < tick < tickUpper
        } else if (tick < tickUpper) {
            return (true, address(0));

            // tick out of range, tick >= tickUpper left token1
        } else {
            return (false, token1);
        }
    }

    function getTickInfo(
        uint256 liquidityNftId,
        address uniswapV3FactoryAddress,
        address nonfungiblePositionManagerAddress
    ) internal view returns (int24 tick, int24 tickLower, int24 tickUpper) {
        (, tick, , , ) = getPoolInfoByLiquidityNft(
            liquidityNftId,
            uniswapV3FactoryAddress,
            nonfungiblePositionManagerAddress
        );
        (, , , tickLower, tickUpper, , ) = getLiquidityNftPositionsInfo(
            liquidityNftId,
            nonfungiblePositionManagerAddress
        );
    }

    function verifyNoDuplicateTickAndTickLower(
        uint256 liquidityNftId,
        address uniswapV3FactoryAddress,
        address nonfungiblePositionManagerAddress
    ) internal view {
        (int24 tick, int24 tickLower, ) = getTickInfo(
            liquidityNftId,
            uniswapV3FactoryAddress,
            nonfungiblePositionManagerAddress
        );

        require(tickLower != tick, "tickLower == tick");
    }

    // for general condition (except tickSpacing 1 condition)
    function calculateInitTickBoundary(
        address poolAddress,
        int24 tickSpread,
        int24 tickSpacing
    ) internal view returns (int24 tickLower, int24 tickUpper) {
        require(tickSpacing != 1, "tickSpacing == 1");

        // Get current tick
        (, , , int24 currentTick, , , ) = PoolHelper.getPoolInfo(poolAddress);

        // Calculate the floor tick value
        int24 tickFloor = floorTick(currentTick, tickSpacing);

        // Calculate the tickLower & tickToTickLower value
        tickLower = tickFloor - tickSpacing * tickSpread;
        int24 tickToTickLower = currentTick - tickLower;

        // Calculate the tickUpper & tickUpperToTick value
        tickUpper = floorTick((currentTick + tickToTickLower), tickSpacing);
        int24 tickUpperToTick = tickUpper - currentTick;

        // Check
        // if the tickSpacing is greater than 1
        // and
        // if the (tickToTickLower - tickUpperToTick) is greater than or equal to (tickSpacing / 2)
        if (
            tickSpacing > 1 &&
            (tickToTickLower - tickUpperToTick) >= (tickSpacing / 2)
        ) {
            // Increment the tickUpper by the tickSpacing
            tickUpper += tickSpacing;
        }
    }

    function floorTick(
        int24 tick,
        int24 tickSpacing
    ) internal pure returns (int24) {
        int24 baseFloor = tick / tickSpacing;

        if (tick < 0 && tick % tickSpacing != 0) {
            return (baseFloor - 1) * tickSpacing;
        }
        return baseFloor * tickSpacing;
    }

    function ceilingTick(
        int24 tick,
        int24 tickSpacing
    ) internal pure returns (int24) {
        int24 baseFloor = tick / tickSpacing;

        if (tick > 0 && tick % tickSpacing != 0) {
            return (baseFloor + 1) * tickSpacing;
        }
        return baseFloor * tickSpacing;
    }
}

// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

library ParameterVerificationHelper {
    function verifyNotZeroAddress(address inputAddress) internal pure {
        require(inputAddress != address(0), "input zero address");
    }

    function verifyGreaterThanZero(uint256 inputNumber) internal pure {
        require(inputNumber > 0, "input 0");
    }

    function verifyGreaterThanZero(int24 inputNumber) internal pure {
        require(inputNumber > 0, "input 0");
    }

    function verifyGreaterThanOne(int24 inputNumber) internal pure {
        require(inputNumber > 1, "input <= 1");
    }

    function verifyGreaterThanOrEqualToZero(int24 inputNumber) internal pure {
        require(inputNumber >= 0, "input less than 0");
    }

    function verifyPairTokensHaveWeth(
        address token0Address,
        address token1Address,
        address wethAddress
    ) internal pure {
        require(
            token0Address == wethAddress || token1Address == wethAddress,
            "pair token not have WETH"
        );
    }

    function verifyMsgValueEqualsInputAmount(
        uint256 inputAmount
    ) internal view {
        require(msg.value == inputAmount, "msg.value != inputAmount");
    }

    function verifyPairTokensHaveInputToken(
        address token0Address,
        address token1Address,
        address inputToken
    ) internal pure {
        require(
            token0Address == inputToken || token1Address == inputToken,
            "pair token not have inputToken"
        );
    }
}

// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

import "../interfaces/external/IERC20Querier.sol";
import "../interfaces/uniswapV3/IUniswapV3Factory.sol";
import "../interfaces/uniswapV3/IUniswapV3Pool.sol";
import "@openzeppelin/contracts/utils/math/SafeMath.sol";

library PoolHelper {
    using SafeMath for uint256;

    function getPoolAddress(
        address uniswapV3FactoryAddress,
        address tokenA,
        address tokenB,
        uint24 poolFee
    ) internal view returns (address poolAddress) {
        return
            IUniswapV3Factory(uniswapV3FactoryAddress).getPool(
                tokenA,
                tokenB,
                poolFee
            );
    }

    function getPoolInfo(
        address poolAddress
    )
        internal
        view
        returns (
            address token0,
            address token1,
            uint24 poolFee,
            int24 tick,
            uint160 sqrtPriceX96,
            uint256 decimal0,
            uint256 decimal1
        )
    {
        (sqrtPriceX96, tick, , , , , ) = IUniswapV3Pool(poolAddress).slot0();
        token0 = IUniswapV3Pool(poolAddress).token0();
        token1 = IUniswapV3Pool(poolAddress).token1();
        poolFee = IUniswapV3Pool(poolAddress).fee();
        decimal0 = IERC20Querier(token0).decimals();
        decimal1 = IERC20Querier(token1).decimals();
    }

    /// @dev formula explanation
    /*
    [Original formula (without decimal precision)]
    (token1 * (10^decimal1)) / (token0 * (10^decimal0)) = (sqrtPriceX96 / (2^96))^2   
    tokenPrice = token1/token0 = (sqrtPriceX96 / (2^96))^2 * (10^decimal0) / (10^decimal1)

    [Formula with decimal precision & decimal adjustment]
    tokenPriceWithDecimalAdj = tokenPrice * (10^decimalPrecision)
        = (sqrtPriceX96 * (10^decimalPrecision) / (2^96))^2 
            / 10^(decimalPrecision + decimal1 - decimal0)
    */
    function getTokenPriceWithDecimalsByPool(
        address poolAddress,
        uint256 decimalPrecision
    ) internal view returns (uint256 tokenPriceWithDecimals) {
        (
            ,
            ,
            ,
            ,
            uint160 sqrtPriceX96,
            uint256 decimal0,
            uint256 decimal1
        ) = getPoolInfo(poolAddress);

        // when decimalPrecision is 18,
        // calculation restriction: 79228162514264337594 <= sqrtPriceX96 <= type(uint160).max
        uint256 scaledPriceX96 = uint256(sqrtPriceX96)
            .mul(10 ** decimalPrecision)
            .div(2 ** 96);
        uint256 tokenPriceWithoutDecimalAdj = scaledPriceX96.mul(
            scaledPriceX96
        );
        uint256 decimalAdj = decimalPrecision.add(decimal1).sub(decimal0);
        uint256 result = tokenPriceWithoutDecimalAdj.div(10 ** decimalAdj);
        require(result > 0, "token price too small");
        tokenPriceWithDecimals = result;
    }

    function getTokenDecimalAdjustment(
        address token
    ) internal view returns (uint256 decimalAdjustment) {
        uint256 tokenDecimalStandard = 18;
        uint256 decimal = IERC20Querier(token).decimals();
        return tokenDecimalStandard.sub(decimal);
    }
}

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

/// @title Math library for computing sqrt prices from ticks and vice versa
/// @notice Computes sqrt price for ticks of size 1.0001, i.e. sqrt(1.0001^tick) as fixed point Q64.96 numbers. Supports
/// prices between 2**-128 and 2**128
library TickMath {
    /// @dev The minimum tick that may be passed to #getSqrtRatioAtTick computed from log base 1.0001 of 2**-128
    int24 internal constant MIN_TICK = -887272;
    /// @dev The maximum tick that may be passed to #getSqrtRatioAtTick computed from log base 1.0001 of 2**128
    int24 internal constant MAX_TICK = -MIN_TICK;

    /// @dev The minimum value that can be returned from #getSqrtRatioAtTick. Equivalent to getSqrtRatioAtTick(MIN_TICK)
    uint160 internal constant MIN_SQRT_RATIO = 4295128739;
    /// @dev The maximum value that can be returned from #getSqrtRatioAtTick. Equivalent to getSqrtRatioAtTick(MAX_TICK)
    uint160 internal constant MAX_SQRT_RATIO =
        1461446703485210103287273052203988822378723970342;

    /// @notice Calculates sqrt(1.0001^tick) * 2^96
    /// @dev Throws if |tick| > max tick
    /// @param tick The input tick for the above formula
    /// @return sqrtPriceX96 A Fixed point Q64.96 number representing the sqrt of the ratio of the two assets (token1/token0)
    /// at the given tick
    function getSqrtRatioAtTick(
        int24 tick
    ) internal pure returns (uint160 sqrtPriceX96) {
        uint256 absTick = tick < 0
            ? uint256(-int256(tick))
            : uint256(int256(tick));
        require(absTick <= uint256(int256(MAX_TICK)), "T");

        uint256 ratio = absTick & 0x1 != 0
            ? 0xfffcb933bd6fad37aa2d162d1a594001
            : 0x100000000000000000000000000000000;
        if (absTick & 0x2 != 0)
            ratio = (ratio * 0xfff97272373d413259a46990580e213a) >> 128;
        if (absTick & 0x4 != 0)
            ratio = (ratio * 0xfff2e50f5f656932ef12357cf3c7fdcc) >> 128;
        if (absTick & 0x8 != 0)
            ratio = (ratio * 0xffe5caca7e10e4e61c3624eaa0941cd0) >> 128;
        if (absTick & 0x10 != 0)
            ratio = (ratio * 0xffcb9843d60f6159c9db58835c926644) >> 128;
        if (absTick & 0x20 != 0)
            ratio = (ratio * 0xff973b41fa98c081472e6896dfb254c0) >> 128;
        if (absTick & 0x40 != 0)
            ratio = (ratio * 0xff2ea16466c96a3843ec78b326b52861) >> 128;
        if (absTick & 0x80 != 0)
            ratio = (ratio * 0xfe5dee046a99a2a811c461f1969c3053) >> 128;
        if (absTick & 0x100 != 0)
            ratio = (ratio * 0xfcbe86c7900a88aedcffc83b479aa3a4) >> 128;
        if (absTick & 0x200 != 0)
            ratio = (ratio * 0xf987a7253ac413176f2b074cf7815e54) >> 128;
        if (absTick & 0x400 != 0)
            ratio = (ratio * 0xf3392b0822b70005940c7a398e4b70f3) >> 128;
        if (absTick & 0x800 != 0)
            ratio = (ratio * 0xe7159475a2c29b7443b29c7fa6e889d9) >> 128;
        if (absTick & 0x1000 != 0)
            ratio = (ratio * 0xd097f3bdfd2022b8845ad8f792aa5825) >> 128;
        if (absTick & 0x2000 != 0)
            ratio = (ratio * 0xa9f746462d870fdf8a65dc1f90e061e5) >> 128;
        if (absTick & 0x4000 != 0)
            ratio = (ratio * 0x70d869a156d2a1b890bb3df62baf32f7) >> 128;
        if (absTick & 0x8000 != 0)
            ratio = (ratio * 0x31be135f97d08fd981231505542fcfa6) >> 128;
        if (absTick & 0x10000 != 0)
            ratio = (ratio * 0x9aa508b5b7a84e1c677de54f3e99bc9) >> 128;
        if (absTick & 0x20000 != 0)
            ratio = (ratio * 0x5d6af8dedb81196699c329225ee604) >> 128;
        if (absTick & 0x40000 != 0)
            ratio = (ratio * 0x2216e584f5fa1ea926041bedfe98) >> 128;
        if (absTick & 0x80000 != 0)
            ratio = (ratio * 0x48a170391f7dc42444e8fa2) >> 128;

        if (tick > 0) ratio = type(uint256).max / ratio;

        // this divides by 1<<32 rounding up to go from a Q128.128 to a Q128.96.
        // we then downcast because we know the result always fits within 160 bits due to our tick input constraint
        // we round up in the division so getTickAtSqrtRatio of the output price is always consistent
        sqrtPriceX96 = uint160(
            (ratio >> 32) + (ratio % (1 << 32) == 0 ? 0 : 1)
        );
    }
}

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

Contract Security Audit

Contract ABI

API
[{"inputs":[{"internalType":"address","name":"_executor","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"fundManagerVaultAddress","type":"address"},{"indexed":true,"internalType":"address","name":"executorAddress","type":"address"},{"indexed":false,"internalType":"uint256","name":"allocatedWbtcAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"remainingWbtcAmount","type":"uint256"}],"name":"Allocate","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"strategyContract","type":"address"},{"indexed":true,"internalType":"address","name":"executorAddress","type":"address"},{"indexed":true,"internalType":"uint256","name":"liquidityNftId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"rewardToken0Amount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"rewardToken1Amount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"rewardWbtcAmount","type":"uint256"}],"name":"CollectRewards","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"strategyContract","type":"address"},{"indexed":true,"internalType":"address","name":"executorAddress","type":"address"},{"indexed":true,"internalType":"uint256","name":"liquidityNftId","type":"uint256"},{"indexed":false,"internalType":"bool","name":"depositDustToken0","type":"bool"},{"indexed":false,"internalType":"uint256","name":"increasedToken0Amount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"increasedToken1Amount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"dustToken0Amount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"dustToken1Amount","type":"uint256"}],"name":"DepositDustToken","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"strategyContract","type":"address"},{"indexed":true,"internalType":"address","name":"executorAddress","type":"address"},{"indexed":true,"internalType":"uint256","name":"liquidityNftId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"remainingEarnCountDown","type":"uint256"}],"name":"Earn","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"strategyContract","type":"address"},{"indexed":true,"internalType":"address","name":"executorAddress","type":"address"},{"indexed":true,"internalType":"uint256","name":"liquidityNftId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"rewardWbtcAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"remainingEarnCountDown","type":"uint256"}],"name":"EarnPreparation","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"strategyContract","type":"address"},{"indexed":true,"internalType":"address","name":"executorAddress","type":"address"},{"indexed":false,"internalType":"uint256","name":"dustToken0Amount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"dustToken1Amount","type":"uint256"},{"indexed":false,"internalType":"int24","name":"tickSpacing","type":"int24"},{"indexed":false,"internalType":"int24","name":"thisRescaleTick","type":"int24"},{"indexed":false,"internalType":"int24","name":"tickSpreadUpper","type":"int24"},{"indexed":false,"internalType":"int24","name":"tickSpreadLower","type":"int24"},{"indexed":false,"internalType":"int24","name":"rescaleTickUpperBoundaryOffset","type":"int24"},{"indexed":false,"internalType":"int24","name":"rescaleTickLowerBoundaryOffset","type":"int24"},{"indexed":false,"internalType":"int24","name":"newTickUpper","type":"int24"},{"indexed":false,"internalType":"int24","name":"newTickLower","type":"int24"}],"name":"RescaleEnd","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"strategyContract","type":"address"},{"indexed":false,"internalType":"bool","name":"wasInRange","type":"bool"},{"indexed":false,"internalType":"int24","name":"tickSpacing","type":"int24"},{"indexed":false,"internalType":"int24","name":"tickGapUpper","type":"int24"},{"indexed":false,"internalType":"int24","name":"tickGapLower","type":"int24"},{"indexed":false,"internalType":"int24","name":"tickUpperBoundaryOffset","type":"int24"},{"indexed":false,"internalType":"int24","name":"tickLowerBoundaryOffset","type":"int24"},{"indexed":false,"internalType":"int24","name":"lastRescaleTick","type":"int24"},{"indexed":false,"internalType":"int24","name":"tickBeforeRescale","type":"int24"},{"indexed":false,"internalType":"int24","name":"thisRescaleTick","type":"int24"},{"indexed":false,"internalType":"int24","name":"originalTickUpper","type":"int24"},{"indexed":false,"internalType":"int24","name":"originalTickLower","type":"int24"}],"name":"RescaleStart","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"role","type":"bytes32"},{"indexed":true,"internalType":"bytes32","name":"previousAdminRole","type":"bytes32"},{"indexed":true,"internalType":"bytes32","name":"newAdminRole","type":"bytes32"}],"name":"RoleAdminChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"role","type":"bytes32"},{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":true,"internalType":"address","name":"sender","type":"address"}],"name":"RoleGranted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"role","type":"bytes32"},{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":true,"internalType":"address","name":"sender","type":"address"}],"name":"RoleRevoked","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"strategyContract","type":"address"},{"indexed":true,"internalType":"address","name":"executorAddress","type":"address"},{"indexed":false,"internalType":"uint24","name":"buyBackNumerator","type":"uint24"}],"name":"SetBuyBackNumerator","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"strategyContract","type":"address"},{"indexed":true,"internalType":"address","name":"executorAddress","type":"address"},{"indexed":false,"internalType":"address","name":"buyBackToken","type":"address"}],"name":"SetBuyBackToken","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"strategyContract","type":"address"},{"indexed":true,"internalType":"address","name":"executorAddress","type":"address"},{"indexed":false,"internalType":"uint256","name":"earnLoopSegmentSize","type":"uint256"}],"name":"SetEarnLoopSegmentSize","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"fundManagerVaultAddress","type":"address"},{"indexed":true,"internalType":"address","name":"executorAddress","type":"address"},{"indexed":false,"internalType":"uint256","name":"index","type":"uint256"},{"indexed":false,"internalType":"address","name":"fundManagerAddress","type":"address"},{"indexed":false,"internalType":"uint24","name":"fundManagerProfitNumerator","type":"uint24"}],"name":"SetFundManagerByIndex","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"strategyContract","type":"address"},{"indexed":true,"internalType":"address","name":"executorAddress","type":"address"},{"indexed":false,"internalType":"uint256","name":"index","type":"uint256"},{"indexed":false,"internalType":"address","name":"fundManagerVaultAddress","type":"address"},{"indexed":false,"internalType":"uint24","name":"fundManagerProfitVaultNumerator","type":"uint24"}],"name":"SetFundManagerVaultByIndex","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"strategyContract","type":"address"},{"indexed":true,"internalType":"address","name":"executorAddress","type":"address"},{"indexed":false,"internalType":"int24","name":"rescaleTickLowerBoundaryOffset","type":"int24"}],"name":"SetRescaleTickLowerBoundaryOffset","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"strategyContract","type":"address"},{"indexed":true,"internalType":"address","name":"executorAddress","type":"address"},{"indexed":false,"internalType":"int24","name":"rescaleTickTolerance","type":"int24"}],"name":"SetRescaleTickTolerance","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"strategyContract","type":"address"},{"indexed":true,"internalType":"address","name":"executorAddress","type":"address"},{"indexed":false,"internalType":"int24","name":"rescaleTickUpperBoundaryOffset","type":"int24"}],"name":"SetRescaleTickUpperBoundaryOffset","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"strategyContract","type":"address"},{"indexed":true,"internalType":"address","name":"executorAddress","type":"address"},{"indexed":false,"internalType":"int24","name":"tickGapLower","type":"int24"}],"name":"SetTickGapLower","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"strategyContract","type":"address"},{"indexed":true,"internalType":"address","name":"executorAddress","type":"address"},{"indexed":false,"internalType":"int24","name":"tickGapUpper","type":"int24"}],"name":"SetTickGapUpper","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"strategyContract","type":"address"},{"indexed":true,"internalType":"address","name":"executorAddress","type":"address"},{"indexed":false,"internalType":"int24","name":"tickLowerBoundaryOffset","type":"int24"}],"name":"SetTickLowerBoundaryOffset","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"strategyContract","type":"address"},{"indexed":true,"internalType":"address","name":"executorAddress","type":"address"},{"indexed":false,"internalType":"int24","name":"tickSpreadLower","type":"int24"}],"name":"SetTickSpreadLower","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"strategyContract","type":"address"},{"indexed":true,"internalType":"address","name":"executorAddress","type":"address"},{"indexed":false,"internalType":"int24","name":"tickSpreadUpper","type":"int24"}],"name":"SetTickSpreadUpper","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"strategyContract","type":"address"},{"indexed":true,"internalType":"address","name":"executorAddress","type":"address"},{"indexed":false,"internalType":"int24","name":"tickUpperBoundaryOffset","type":"int24"}],"name":"SetTickUpperBoundaryOffset","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"strategyContract","type":"address"},{"indexed":true,"internalType":"address","name":"executorAddress","type":"address"},{"indexed":false,"internalType":"uint256","name":"transactionDeadlineDuration","type":"uint256"}],"name":"SetTransactionDeadlineDuration","type":"event"},{"inputs":[],"name":"DEFAULT_ADMIN_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"EXECUTOR_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_fundManagerVaultContract","type":"address"}],"name":"allocate","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_strategyContract","type":"address"}],"name":"collectRewards","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_strategyContract","type":"address"},{"internalType":"bool","name":"_depositDustToken0","type":"bool"}],"name":"depositDustToken","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_strategyContract","type":"address"}],"name":"earn","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_strategyContract","type":"address"},{"internalType":"uint256","name":"_minimumToken0SwapOutAmount","type":"uint256"},{"internalType":"uint256","name":"_minimumToken1SwapOutAmount","type":"uint256"},{"internalType":"uint256","name":"_minimumBuybackSwapOutAmount","type":"uint256"}],"name":"earnPreparation","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_fundManagerVaultContract","type":"address"}],"name":"earnPreparation","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_fundManagerVaultContract","type":"address"}],"name":"getAllFundManagers","outputs":[{"components":[{"internalType":"address","name":"fundManagerAddress","type":"address"},{"internalType":"uint256","name":"fundManagerProfitNumerator","type":"uint256"}],"internalType":"struct IFundManagerVault.FundManager[8]","name":"","type":"tuple[8]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_strategyContract","type":"address"}],"name":"getEarningFlag","outputs":[{"internalType":"bool","name":"isEarningFlag","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_strategyContract","type":"address"}],"name":"getRemainingEarnCountDown","outputs":[{"internalType":"uint256","name":"remainingEarn","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_strategyContract","type":"address"}],"name":"getRescalingFlag","outputs":[{"internalType":"bool","name":"isRescalingFlag","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"}],"name":"getRoleAdmin","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"grantRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"hasRole","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_strategyContract","type":"address"}],"name":"isNftWithinRange","outputs":[{"internalType":"bool","name":"isWithinRange","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"lastRescaleTick","outputs":[{"internalType":"int24","name":"","type":"int24"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"renounceRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_strategyContract","type":"address"},{"internalType":"address","name":"_rescaleTickBoundaryCalculatorContract","type":"address"},{"internalType":"bool","name":"_wasInRange","type":"bool"},{"internalType":"int24","name":"_tickBeforeRescale","type":"int24"}],"name":"rescale","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"rescaleTickLowerBoundaryOffset","outputs":[{"internalType":"int24","name":"","type":"int24"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"rescaleTickTolerance","outputs":[{"internalType":"int24","name":"","type":"int24"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"rescaleTickUpperBoundaryOffset","outputs":[{"internalType":"int24","name":"","type":"int24"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"revokeRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_strategyContract","type":"address"},{"internalType":"uint24","name":"_buyBackNumerator","type":"uint24"}],"name":"setBuyBackNumerator","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_strategyContract","type":"address"},{"internalType":"address","name":"_buyBackToken","type":"address"}],"name":"setBuyBackToken","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_strategyContract","type":"address"},{"internalType":"uint256","name":"_earnLoopSegmentSize","type":"uint256"}],"name":"setEarnLoopSegmentSize","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_fundManagerVaultContract","type":"address"},{"internalType":"uint256","name":"_index","type":"uint256"},{"internalType":"address","name":"_fundManagerAddress","type":"address"},{"internalType":"uint24","name":"_fundManagerProfitNumerator","type":"uint24"}],"name":"setFundManagerByIndex","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_strategyContract","type":"address"},{"internalType":"uint256","name":"_index","type":"uint256"},{"internalType":"address","name":"_fundManagerVaultAddress","type":"address"},{"internalType":"uint24","name":"_fundManagerProfitVaultNumerator","type":"uint24"}],"name":"setFundManagerVaultByIndex","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_strategyContract","type":"address"},{"internalType":"int24","name":"_rescaleTickLowerBoundaryOffset","type":"int24"}],"name":"setRescaleTickLowerBoundaryOffset","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_strategyContract","type":"address"},{"internalType":"int24","name":"_rescaleTickTolerance","type":"int24"}],"name":"setRescaleTickTolerance","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_strategyContract","type":"address"},{"internalType":"int24","name":"_rescaleTickUpperBoundaryOffset","type":"int24"}],"name":"setRescaleTickUpperBoundaryOffset","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_strategyContract","type":"address"},{"internalType":"int24","name":"_tickGapLower","type":"int24"}],"name":"setTickGapLower","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_strategyContract","type":"address"},{"internalType":"int24","name":"_tickGapUpper","type":"int24"}],"name":"setTickGapUpper","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_strategyContract","type":"address"},{"internalType":"int24","name":"_tickLowerBoundaryOffset","type":"int24"}],"name":"setTickLowerBoundaryOffset","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_strategyContract","type":"address"},{"internalType":"int24","name":"_tickSpreadLower","type":"int24"}],"name":"setTickSpreadLower","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_strategyContract","type":"address"},{"internalType":"int24","name":"_tickSpreadUpper","type":"int24"}],"name":"setTickSpreadUpper","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_strategyContract","type":"address"},{"internalType":"int24","name":"_tickUpperBoundaryOffset","type":"int24"}],"name":"setTickUpperBoundaryOffset","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_strategyContract","type":"address"},{"internalType":"uint256","name":"_transactionDeadlineDuration","type":"uint256"}],"name":"setTransactionDeadlineDuration","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes4","name":"interfaceId","type":"bytes4"}],"name":"supportsInterface","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"tickGapLower","outputs":[{"internalType":"int24","name":"","type":"int24"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"tickGapUpper","outputs":[{"internalType":"int24","name":"","type":"int24"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"tickLowerBoundaryOffset","outputs":[{"internalType":"int24","name":"","type":"int24"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"tickSpreadLower","outputs":[{"internalType":"int24","name":"","type":"int24"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"tickSpreadUpper","outputs":[{"internalType":"int24","name":"","type":"int24"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"tickUpperBoundaryOffset","outputs":[{"internalType":"int24","name":"","type":"int24"}],"stateMutability":"view","type":"function"}]

60806040523480156200001157600080fd5b5060405162003d9938038062003d9983398101604081905262000034916200017a565b6200005060008051602062003d7983398151915260006200007f565b6200005d600033620000ca565b6200007860008051602062003d7983398151915282620000ca565b50620001ac565b600082815260208190526040808220600101805490849055905190918391839186917fbd79b86ffe0ab8e8776151514217cd7cacd52c909f66475c3af44e129f0b00ff9190a4505050565b620000d68282620000da565b5050565b6000828152602081815260408083206001600160a01b038516845290915290205460ff16620000d6576000828152602081815260408083206001600160a01b03851684529091529020805460ff19166001179055620001363390565b6001600160a01b0316816001600160a01b0316837f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d60405160405180910390a45050565b6000602082840312156200018d57600080fd5b81516001600160a01b0381168114620001a557600080fd5b9392505050565b613bbd80620001bc6000396000f3fe608060405234801561001057600080fd5b506004361061027f5760003560e01c8063751681ca1161015c578063a702332d116100ce578063e38eb66c11610087578063e38eb66c14610637578063e89d66a81461064a578063f43d71991461066d578063f824c56614610690578063fdb5fefc146106a3578063ffd7d983146105eb57600080fd5b8063a702332d146105c5578063a9ed8882146105d8578063d2dc0e22146105eb578063d547741f146105fe578063da31500714610611578063dafe6db01461062457600080fd5b806391d148541161012057806391d148541461054e57806396adee011461056157806396db06f114610574578063a0f1fb1114610587578063a217fddf146105aa578063a4b3dfc4146105b257600080fd5b8063751681ca146104ef57806377977072146105025780637898e5cd146105155780637ac041ed146105285780637eebde771461053b57600080fd5b8063385892c6116101f557806357c13aaa116101b957806357c13aaa1461044e578063581c1597146104735780635a013639146104865780635ccc5ad4146104a657806361621153146104b95780636ced8c06146104cc57600080fd5b8063385892c6146103df57806338734882146103f2578063470def1414610405578063561fc351146104185780635763dbd01461043b57600080fd5b80632130f31f116102475780632130f31f1461033d578063248a9ca31461035057806328922086146103735780632f2ff15d1461039657806336568abe146103a95780633737bb22146103bc57600080fd5b806301ffc9a71461028457806305cf450a146102ac57806307bd0265146102e25780630b548482146103055780631c6e760414610328575b600080fd5b6102976102923660046131ae565b6106b6565b60405190151581526020015b60405180910390f35b6102cf6102ba3660046131ed565b60016020526000908152604090205460020b81565b60405160029190910b81526020016102a3565b6102f7600080516020613b6883398151915281565b6040519081526020016102a3565b6102cf6103133660046131ed565b60076020526000908152604090205460020b81565b61033b610336366004613219565b6106ed565b005b61033b61034b366004613252565b610778565b6102f761035e36600461328d565b60009081526020819052604090206001015490565b6102cf6103813660046131ed565b60066020526000908152604090205460020b81565b61033b6103a43660046132a6565b61096b565b61033b6103b73660046132a6565b610995565b6102cf6103ca3660046131ed565b60086020526000908152604090205460020b81565b61033b6103ed366004613219565b610a13565b61033b6104003660046132dc565b610a95565b61033b61041336600461330a565b610b4d565b6102cf6104263660046131ed565b60056020526000908152604090205460020b81565b61033b6104493660046131ed565b610bff565b6102cf61045c3660046131ed565b600260208190526000918252604090912054900b81565b6102f76104813660046131ed565b610e4a565b6104996104943660046131ed565b610fe7565b6040516102a39190613338565b61033b6104b436600461337f565b611052565b61033b6104c73660046133ab565b611101565b6102cf6104da3660046131ed565b60046020526000908152604090205460020b81565b61033b6104fd366004613219565b6111d1565b61033b6105103660046133ab565b611253565b61033b610523366004613219565b611314565b61033b610536366004613219565b611396565b61033b610549366004613219565b611417565b61029761055c3660046132a6565b611499565b61033b61056f366004613219565b6114c2565b61033b610582366004613219565b611544565b6102cf6105953660046131ed565b600a6020526000908152604090205460020b81565b6102f7600081565b6102976105c03660046131ed565b6115c5565b61033b6105d3366004613219565b611629565b6102976105e63660046131ed565b6116ab565b61033b6105f93660046131ed565b6117b0565b61033b61060c3660046132a6565b6117d1565b61029761061f3660046131ed565b6117f6565b61033b61063236600461340c565b611877565b61033b61064536600461337f565b611a8b565b6102cf6106583660046131ed565b60036020526000908152604090205460020b81565b6102cf61067b3660046131ed565b60096020526000908152604090205460020b81565b61033b61069e36600461343a565b611b3a565b61033b6106b13660046131ed565b611e47565b60006001600160e01b03198216637965db0b60e01b14806106e757506301ffc9a760e01b6001600160e01b03198316145b92915050565b600080516020613b6883398151915261070581611f57565b61070e82611f64565b6001600160a01b038316600081815260056020908152604091829020805462ffffff191662ffffff87161790559051600285900b81523392917f39c3e68e2a6a52a106264d6bb8eb9172b37c359aabe84a3e5a44165be1d53baa91015b60405180910390a3505050565b600080516020613b6883398151915261079081611f57565b6040516376a8e9ff60e11b81526004810185905260248101849052604481018390526001600160a01b0386169063ed51d3fe90606401600060405180830381600087803b1580156107e057600080fd5b505af11580156107f4573d6000803e3d6000fd5b50505050610801856115c5565b15156001146108495760405162461bcd60e51b815260206004820152600f60248201526e32b0b93710383932b81032b93937b960891b60448201526064015b60405180910390fd5b846001600160a01b031663ead892be6040518163ffffffff1660e01b8152600401602060405180830381865afa158015610887573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906108ab919061348b565b336001600160a01b0316866001600160a01b03167f305bc1ca7106255b17dbf66922802940d373f07b2c8810a76cee84700eb8c46c886001600160a01b0316636a185d5f6040518163ffffffff1660e01b8152600401602060405180830381865afa15801561091e573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610942919061348b565b61094b8a610e4a565b604080519283526020830191909152015b60405180910390a45050505050565b60008281526020819052604090206001015461098681611f57565b6109908383611fac565b505050565b6001600160a01b0381163314610a055760405162461bcd60e51b815260206004820152602f60248201527f416363657373436f6e74726f6c3a2063616e206f6e6c792072656e6f756e636560448201526e103937b632b9903337b91039b2b63360891b6064820152608401610840565b610a0f8282612030565b5050565b600080516020613b68833981519152610a2b81611f57565b610a3482612095565b6001600160a01b038316600081815260046020908152604091829020805462ffffff191662ffffff87161790559051600285900b81523392917fb76904a8ba51dc05f78444581f265d5e71c3783225c473e9620d4c09799df5bc910161076b565b600080516020613b68833981519152610aad81611f57565b60405163088d621d60e31b815262ffffff831660048201526001600160a01b0384169063446b10e890602401600060405180830381600087803b158015610af357600080fd5b505af1158015610b07573d6000803e3d6000fd5b505060405162ffffff851681523392506001600160a01b03861691507f8ab763dee50895615d0bb8306cdddbc0c1a6258f62aacbac6edc6be8c14ad8339060200161076b565b600080516020613b68833981519152610b6581611f57565b604051630c9590fd60e41b81526001600160a01b03838116600483015284169063c9590fd090602401600060405180830381600087803b158015610ba857600080fd5b505af1158015610bbc573d6000803e3d6000fd5b50506040516001600160a01b038581168252339350861691507f45f9e54d9f5cd84815f0bebc99eab35ed7008a0d9751ccd3c19807c4b331078a9060200161076b565b600080516020613b68833981519152610c1781611f57565b816001600160a01b03166370bb45b36040518163ffffffff1660e01b8152600401600060405180830381600087803b158015610c5257600080fd5b505af1158015610c66573d6000803e3d6000fd5b50505050816001600160a01b031663ead892be6040518163ffffffff1660e01b8152600401602060405180830381865afa158015610ca8573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610ccc919061348b565b336001600160a01b0316836001600160a01b03167f5d0927b601ecc1d0a4bedb898b2beff630177e857cecab540ba9b9136b302626856001600160a01b031663314e61ca6040518163ffffffff1660e01b8152600401602060405180830381865afa158015610d3f573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610d63919061348b565b866001600160a01b0316631a536a326040518163ffffffff1660e01b8152600401602060405180830381865afa158015610da1573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610dc5919061348b565b876001600160a01b0316636a185d5f6040518163ffffffff1660e01b8152600401602060405180830381865afa158015610e03573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610e27919061348b565b604080519384526020840192909252908201526060015b60405180910390a45050565b600080610e56836115c5565b9050801515600003610e6b5750600092915050565b6000836001600160a01b03166370cb00df6040518163ffffffff1660e01b8152600401600060405180830381865afa158015610eab573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052610ed39190810190613548565b5190506000846001600160a01b031663a142492d6040518163ffffffff1660e01b8152600401602060405180830381865afa158015610f16573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610f3a919061348b565b90506000610f4883836120d5565b90506000866001600160a01b031663234c4cce6040518163ffffffff1660e01b8152600401602060405180830381865afa158015610f8a573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610fae919061348b565b9050600080610fbd84846120e8565b9150915060008111610fcf5781610fda565b610fda82600161210a565b9998505050505050505050565b610fef613174565b816001600160a01b031663cbff66e26040518163ffffffff1660e01b815260040161020060405180830381865afa15801561102e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906106e791906135fa565b600080516020613b6883398151915261106a81611f57565b604051630f8a517f60e41b8152600481018390526001600160a01b0384169063f8a517f090602401600060405180830381600087803b1580156110ac57600080fd5b505af11580156110c0573d6000803e3d6000fd5b50506040518481523392506001600160a01b03861691507f6c58dea38c1fef4e97173e19196cf983e5a8f566fdf04367343c63b8abaa1be99060200161076b565b600080516020613b6883398151915261111981611f57565b6040516307795f5760e41b81526001600160a01b03861690637795f570906111499087908790879060040161368b565b600060405180830381600087803b15801561116357600080fd5b505af1158015611177573d6000803e3d6000fd5b50505050336001600160a01b0316856001600160a01b03167f08e0badddf13ca7fc84b48e3f0b89b0c276e9ae5e9497643f367fd2f240fdf998686866040516111c29392919061368b565b60405180910390a35050505050565b600080516020613b688339815191526111e981611f57565b6111f282611f64565b6001600160a01b038316600081815260076020908152604091829020805462ffffff191662ffffff87161790559051600285900b81523392917fe796bc4f3ce03bd32fb0e15586b34a308fc7bfa8c3c5485e92cd577b1bb9390a910161076b565b600080516020613b6883398151915261126b81611f57565b604051634bd613c960e11b81526001600160a01b038616906397ac27929061129b9087908790879060040161368b565b600060405180830381600087803b1580156112b557600080fd5b505af11580156112c9573d6000803e3d6000fd5b50505050336001600160a01b0316856001600160a01b03167f5b48ee2dc08a8c37693631d459c19db8be2f161266f3ae57cda789130f6ed53b8686866040516111c29392919061368b565b600080516020613b6883398151915261132c81611f57565b61133582611f64565b6001600160a01b038316600081815260066020908152604091829020805462ffffff191662ffffff87161790559051600285900b81523392917f4d5ea40657b0a60f4e1adf5562de4fc7b431b1087121d60e006af1b4dd80700c910161076b565b600080516020613b688339815191526113ae81611f57565b6113b782611f64565b6001600160a01b0383166000818152600a6020908152604091829020805462ffffff191662ffffff87161790559051600285900b81523392917e16e8ec2d70d4a15a6124477bef7aee3fd4b10b52f714c937f219ad3d4ecae5910161076b565b600080516020613b6883398151915261142f81611f57565b61143882611f64565b6001600160a01b038316600081815260086020908152604091829020805462ffffff191662ffffff87161790559051600285900b81523392917fd1c1e360f9477b31cc4bd12a26e42e098d96e90f614e2fcc621dea418ae0dd5f910161076b565b6000918252602082815260408084206001600160a01b0393909316845291905290205460ff1690565b600080516020613b688339815191526114da81611f57565b6114e382611f64565b6001600160a01b038316600081815260016020908152604091829020805462ffffff191662ffffff87161790559051600285900b81523392917f2447a43b2c31ed21e468726ba53813b663d414569fdfda6f648a646537d19bff910161076b565b600080516020613b6883398151915261155c81611f57565b61156582611f64565b6001600160a01b038316600081815260026020818152604092839020805462ffffff191662ffffff881617905591519085900b81523392917f5d87708daffcb58a78b045d80a978ca2200af1de98d942f0fb5674e08dc14fb0910161076b565b6000816001600160a01b0316638a1966e36040518163ffffffff1660e01b8152600401602060405180830381865afa158015611605573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906106e791906136af565b600080516020613b6883398151915261164181611f57565b61164a82612095565b6001600160a01b038316600081815260036020908152604091829020805462ffffff191662ffffff87161790559051600285900b81523392917fc1d8cd1a0ed04005edcfd0819fd55a04044b95ffbca6ad9f9c56bc0d69e06870910161076b565b600080826001600160a01b031663ead892be6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156116ec573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611710919061348b565b9050806000036117755760405162461bcd60e51b815260206004820152602a60248201527f6e6f7420616c6c6f772063616c6c696e67207768656e206c697175696469747960448201526904e6674496420697320360b41b6064820152608401610840565b6117a881731f98431c8ad98523631ae4a59f267346ea31f98473c36442b4a4522e871399cd717abdd847ab11fe88612116565b509392505050565b600080516020613b688339815191526117c881611f57565b610a0f82612199565b6000828152602081905260409020600101546117ec81611f57565b6109908383612030565b600080826001600160a01b031663ead892be6040518163ffffffff1660e01b8152600401602060405180830381865afa158015611837573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061185b919061348b565b90508060000361186e5750600192915050565b50600092915050565b600080516020613b6883398151915261188f81611f57565b6040516338ddeee160e11b8152821515600482015260009081906001600160a01b038616906371bbddc29060240160408051808303816000875af11580156118db573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906118ff91906136cc565b91509150846001600160a01b031663ead892be6040518163ffffffff1660e01b8152600401602060405180830381865afa158015611941573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611965919061348b565b336001600160a01b0316866001600160a01b03167f6f039c89811a62a23aa18d16914e04bb7bf86e124afcbf5f81bd141148e4ba7c8786868b6001600160a01b0316634f89b24d6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156119db573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906119ff919061348b565b8c6001600160a01b031663bf8895ea6040518163ffffffff1660e01b8152600401602060405180830381865afa158015611a3d573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611a61919061348b565b6040805195151586526020860194909452928401919091526060830152608082015260a00161095c565b600080516020613b68833981519152611aa381611f57565b6040516302ef42c760e11b8152600481018390526001600160a01b038416906305de858e90602401600060405180830381600087803b158015611ae557600080fd5b505af1158015611af9573d6000803e3d6000fd5b50506040518481523392506001600160a01b03861691507f8d35dc65727d53b4f7a4bdebeaa6c8b7b6ee8e6d772e6c35a4bbc087aa19e7ac9060200161076b565b600080516020613b68833981519152611b5281611f57565b6001600160a01b03858116600081815260096020526040808220549051631695590760e31b8152871515600482015260029190910b60248201526044810192909252306064830152918291829188169063b4aac83890608401606060405180830381865afa158015611bc8573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611bec91906136fb565b92509250925082611c4b5760405162461bcd60e51b815260206004820152602360248201527f63757272656e7420636f6e646974696f6e206e6f7420616c6c6f772072657363604482015262616c6560e81b6064820152608401610840565b611c56888787612302565b6000611cec896001600160a01b031663ead892be6040518163ffffffff1660e01b8152600401602060405180830381865afa158015611c99573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611cbd919061348b565b731f98431c8ad98523631ae4a59f267346ea31f98473c36442b4a4522e871399cd717abdd847ab11fe88612487565b50506001600160a01b038a166000908152600a6020526040902054909150611d179060020b8761375e565b60020b8160020b13158015611d5757506001600160a01b0389166000908152600a6020526040902054611d4d9060020b87613783565b60020b8160020b12155b611da35760405162461bcd60e51b815260206004820152601b60248201527f6f7574206f66207469636b20746f6c6572616e63652072616e676500000000006044820152606401610840565b60405163646ea14160e11b8152600284810b600483015283900b60248201526001600160a01b038a169063c8dd428290604401600060405180830381600087803b158015611df057600080fd5b505af1158015611e04573d6000803e3d6000fd5b505050506001600160a01b0389166000908152600960205260409020805462ffffff191662ffffff8316179055611e3c8984846124bd565b505050505050505050565b600080516020613b68833981519152611e5f81611f57565b816001600160a01b031663d389800f6040518163ffffffff1660e01b8152600401600060405180830381600087803b158015611e9a57600080fd5b505af1158015611eae573d6000803e3d6000fd5b50505050816001600160a01b031663ead892be6040518163ffffffff1660e01b8152600401602060405180830381865afa158015611ef0573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611f14919061348b565b336001600160a01b0384167f024aa4dfbe44229e3ca86f829e80c3a1b19d5e5d39962eae78b417b805178453611f4986610e4a565b604051908152602001610e3e565b611f6181336126f1565b50565b60008160020b1215611f615760405162461bcd60e51b81526020600482015260116024820152700696e707574206c657373207468616e203607c1b6044820152606401610840565b611fb68282611499565b610a0f576000828152602081815260408083206001600160a01b03851684529091529020805460ff19166001179055611fec3390565b6001600160a01b0316816001600160a01b0316837f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d60405160405180910390a45050565b61203a8282611499565b15610a0f576000828152602081815260408083206001600160a01b0385168085529252808320805460ff1916905551339285917ff6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171b9190a45050565b60018160020b13611f615760405162461bcd60e51b815260206004820152600a602482015269696e707574203c3d203160b01b6044820152606401610840565b60006120e182846137a8565b9392505050565b6000806120f5848461274a565b91506121018484612756565b90509250929050565b60006120e182846137bb565b6000806000612126868686612762565b50505091505060008060008061213c8a896127c9565b50509450945050935093508160020b8560020b1361216557600084965096505050505050612191565b8060020b8560020b12156121855760016000965096505050505050612191565b50600095509093505050505b935093915050565b6000816001600160a01b031663c221b7ec6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156121d9573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906121fd919061348b565b9050816001600160a01b031663abaa99166040518163ffffffff1660e01b8152600401600060405180830381600087803b15801561223a57600080fd5b505af115801561224e573d6000803e3d6000fd5b505050506000826001600160a01b031663c221b7ec6040518163ffffffff1660e01b8152600401602060405180830381865afa158015612292573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906122b6919061348b565b9050336001600160a01b0384167f5b9d56b28a7cde78f80ab41749028c18864c008e0e3722985c63ea258111b7eb6122ee85856120d5565b60408051918252602082018690520161076b565b6000806000612348866001600160a01b031663ead892be6040518163ffffffff1660e01b8152600401602060405180830381865afa158015611c99573d6000803e3d6000fd5b925092509250856001600160a01b03167f7a73c0af961631015e379691e5a22dadd4d0fa4c85b6c6c5b36af570aa537aa086886001600160a01b031663d0c93a7c6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156123b8573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906123dc91906137ce565b6001600160a01b038a1660009081526003602090815260408083205460048352818420546005845282852054600685528386205460098652958490205484519815158952600297880b9589019590955291860b87840152850b6060870152840b608086015291830b60a0850152820b60c084015288820b60e084015287820b61010084015285820b6101208401529086900b61014083015251908190036101600190a2505050505050565b6000806000612497868686612762565b509195506124ab92508891508690506127c9565b50979b919a5098509650505050505050565b6000612500846001600160a01b031663ead892be6040518163ffffffff1660e01b8152600401602060405180830381865afa158015611c99573d6000803e3d6000fd5b50509050336001600160a01b0316846001600160a01b03167fc6d664f645086a1c833d57b521e95bb8362087047154cdaf636f6228c6940ee7866001600160a01b0316634f89b24d6040518163ffffffff1660e01b8152600401602060405180830381865afa158015612577573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061259b919061348b565b876001600160a01b031663bf8895ea6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156125d9573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906125fd919061348b565b886001600160a01b031663d0c93a7c6040518163ffffffff1660e01b8152600401602060405180830381865afa15801561263b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061265f91906137ce565b6001600160a01b038a1660009081526001602090815260408083205460028084528285205460078552838620546008865295849020548451998a529489019790975294850b878301528a850b6060880152840b608087015293830b60a086015290820b60c0850152810b60e084015288810b61010084015287900b61012083015251908190036101400190a350505050565b6126fb8282611499565b610a0f57612708816128a0565b6127138360206128b2565b60405160200161272492919061380f565b60408051601f198184030181529082905262461bcd60e51b825261084091600401613884565b60006120e182846138cd565b60006120e182846138e1565b6000806000806000806000806127788b8a6127c9565b5050505092509250925061278e8a848484612a4e565b975061279988612ad8565b909192939495509091929394509091929350809750819850829950839a5050505050505050939792965093509350565b6000806000806000806000876001600160a01b03166399fbab888a6040518263ffffffff1660e01b815260040161280291815260200190565b61018060405180830381865afa158015612820573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906128449190613920565b909192939495969798999a509091929394959697989950909192935090919250909150905050809750819850829950839a50849b50505050505061288784612d54565b915061289283612d54565b905092959891949750929550565b60606106e76001600160a01b03831660145b606060006128c1836002613a01565b6128cc9060026137bb565b67ffffffffffffffff8111156128e4576128e46134a4565b6040519080825280601f01601f19166020018201604052801561290e576020820181803683370190505b509050600360fc1b8160008151811061292957612929613a18565b60200101906001600160f81b031916908160001a905350600f60fb1b8160018151811061295857612958613a18565b60200101906001600160f81b031916908160001a905350600061297c846002613a01565b6129879060016137bb565b90505b60018111156129ff576f181899199a1a9b1b9c1cb0b131b232b360811b85600f16601081106129bb576129bb613a18565b1a60f81b8282815181106129d1576129d1613a18565b60200101906001600160f81b031916908160001a90535060049490941c936129f881613a2e565b905061298a565b5083156120e15760405162461bcd60e51b815260206004820181905260248201527f537472696e67733a20686578206c656e67746820696e73756666696369656e746044820152606401610840565b604051630b4c774160e11b81526001600160a01b038481166004830152838116602483015262ffffff8316604483015260009190861690631698ee8290606401602060405180830381865afa158015612aab573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612acf9190613a45565b95945050505050565b6000806000806000806000876001600160a01b0316633850c7bd6040518163ffffffff1660e01b815260040160e060405180830381865afa158015612b21573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612b459190613a74565b505060408051630dfe168160e01b8152905194995094975050506001600160a01b038b1692630dfe168192600480830193506020928290030181865afa158015612b93573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612bb79190613a45565b9650876001600160a01b031663d21220a76040518163ffffffff1660e01b8152600401602060405180830381865afa158015612bf7573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612c1b9190613a45565b9550876001600160a01b031663ddca3f436040518163ffffffff1660e01b8152600401602060405180830381865afa158015612c5b573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612c7f9190613b0c565b9450866001600160a01b031663313ce5676040518163ffffffff1660e01b8152600401602060405180830381865afa158015612cbf573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612ce3919061348b565b9150856001600160a01b031663313ce5676040518163ffffffff1660e01b8152600401602060405180830381865afa158015612d23573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612d47919061348b565b9050919395979092949650565b60008060008360020b12612d6b578260020b612d78565b8260020b612d7890613b29565b9050612d87620d89e719613b45565b60020b811115612dbd5760405162461bcd60e51b81526020600482015260016024820152601560fa1b6044820152606401610840565b600081600116600003612dd457600160801b612de6565b6ffffcb933bd6fad37aa2d162d1a5940015b70ffffffffffffffffffffffffffffffffff1690506002821615612e25576080612e20826ffff97272373d413259a46990580e213a613a01565b901c90505b6004821615612e4f576080612e4a826ffff2e50f5f656932ef12357cf3c7fdcc613a01565b901c90505b6008821615612e79576080612e74826fffe5caca7e10e4e61c3624eaa0941cd0613a01565b901c90505b6010821615612ea3576080612e9e826fffcb9843d60f6159c9db58835c926644613a01565b901c90505b6020821615612ecd576080612ec8826fff973b41fa98c081472e6896dfb254c0613a01565b901c90505b6040821615612ef7576080612ef2826fff2ea16466c96a3843ec78b326b52861613a01565b901c90505b6080821615612f21576080612f1c826ffe5dee046a99a2a811c461f1969c3053613a01565b901c90505b610100821615612f4c576080612f47826ffcbe86c7900a88aedcffc83b479aa3a4613a01565b901c90505b610200821615612f77576080612f72826ff987a7253ac413176f2b074cf7815e54613a01565b901c90505b610400821615612fa2576080612f9d826ff3392b0822b70005940c7a398e4b70f3613a01565b901c90505b610800821615612fcd576080612fc8826fe7159475a2c29b7443b29c7fa6e889d9613a01565b901c90505b611000821615612ff8576080612ff3826fd097f3bdfd2022b8845ad8f792aa5825613a01565b901c90505b61200082161561302357608061301e826fa9f746462d870fdf8a65dc1f90e061e5613a01565b901c90505b61400082161561304e576080613049826f70d869a156d2a1b890bb3df62baf32f7613a01565b901c90505b618000821615613079576080613074826f31be135f97d08fd981231505542fcfa6613a01565b901c90505b620100008216156130a55760806130a0826f09aa508b5b7a84e1c677de54f3e99bc9613a01565b901c90505b620200008216156130d05760806130cb826e5d6af8dedb81196699c329225ee604613a01565b901c90505b620400008216156130fa5760806130f5826d2216e584f5fa1ea926041bedfe98613a01565b901c90505b6208000082161561312257608061311d826b048a170391f7dc42444e8fa2613a01565b901c90505b60008460020b131561313d5761313a816000196138cd565b90505b61314c640100000000826138e1565b1561315857600161315b565b60005b61316c9060ff16602083901c6137bb565b949350505050565b6040518061010001604052806008905b60408051808201909152600080825260208201528152602001906001900390816131845790505090565b6000602082840312156131c057600080fd5b81356001600160e01b0319811681146120e157600080fd5b6001600160a01b0381168114611f6157600080fd5b6000602082840312156131ff57600080fd5b81356120e1816131d8565b8060020b8114611f6157600080fd5b6000806040838503121561322c57600080fd5b8235613237816131d8565b915060208301356132478161320a565b809150509250929050565b6000806000806080858703121561326857600080fd5b8435613273816131d8565b966020860135965060408601359560600135945092505050565b60006020828403121561329f57600080fd5b5035919050565b600080604083850312156132b957600080fd5b823591506020830135613247816131d8565b62ffffff81168114611f6157600080fd5b600080604083850312156132ef57600080fd5b82356132fa816131d8565b91506020830135613247816132cb565b6000806040838503121561331d57600080fd5b8235613328816131d8565b91506020830135613247816131d8565b6102008101818360005b600881101561337657815180516001600160a01b031684526020908101518185015260409093019290910190600101613342565b50505092915050565b6000806040838503121561339257600080fd5b823561339d816131d8565b946020939093013593505050565b600080600080608085870312156133c157600080fd5b84356133cc816131d8565b93506020850135925060408501356133e3816131d8565b915060608501356133f3816132cb565b939692955090935050565b8015158114611f6157600080fd5b6000806040838503121561341f57600080fd5b823561342a816131d8565b91506020830135613247816133fe565b6000806000806080858703121561345057600080fd5b843561345b816131d8565b9350602085013561346b816131d8565b9250604085013561347b816133fe565b915060608501356133f38161320a565b60006020828403121561349d57600080fd5b5051919050565b634e487b7160e01b600052604160045260246000fd5b604051610100810167ffffffffffffffff811182821017156134de576134de6134a4565b60405290565b6040805190810167ffffffffffffffff811182821017156134de576134de6134a4565b604051601f8201601f1916810167ffffffffffffffff81118282101715613530576135306134a4565b604052919050565b8051613543816131d8565b919050565b6000602080838503121561355b57600080fd5b825167ffffffffffffffff8082111561357357600080fd5b818501915085601f83011261358757600080fd5b815181811115613599576135996134a4565b8060051b91506135aa848301613507565b81815291830184019184810190888411156135c457600080fd5b938501935b838510156135ee57845192506135de836131d8565b82825293850193908501906135c9565b98975050505050505050565b600061020080838503121561360e57600080fd5b83601f84011261361d57600080fd5b6136256134ba565b90830190808583111561363757600080fd5b845b8381101561368157604081880312156136525760008081fd5b61365a6134e4565b8151613665816131d8565b8152602082810151818301529084529290920191604001613639565b5095945050505050565b9283526001600160a01b0391909116602083015262ffffff16604082015260600190565b6000602082840312156136c157600080fd5b81516120e1816133fe565b600080604083850312156136df57600080fd5b505080516020909101519092909150565b80516135438161320a565b60008060006060848603121561371057600080fd5b835161371b816133fe565b602085015190935061372c8161320a565b604085015190925061373d8161320a565b809150509250925092565b634e487b7160e01b600052601160045260246000fd5b600281810b9083900b01627fffff8113627fffff19821217156106e7576106e7613748565b600282810b9082900b03627fffff198112627fffff821317156106e7576106e7613748565b818103818111156106e7576106e7613748565b808201808211156106e7576106e7613748565b6000602082840312156137e057600080fd5b81516120e18161320a565b60005b838110156138065781810151838201526020016137ee565b50506000910152565b7f416363657373436f6e74726f6c3a206163636f756e74200000000000000000008152600083516138478160178501602088016137eb565b7001034b99036b4b9b9b4b733903937b6329607d1b60179184019182015283516138788160288401602088016137eb565b01602801949350505050565b60208152600082518060208401526138a38160408501602087016137eb565b601f01601f19169190910160400192915050565b634e487b7160e01b600052601260045260246000fd5b6000826138dc576138dc6138b7565b500490565b6000826138f0576138f06138b7565b500690565b8051613543816132cb565b80516fffffffffffffffffffffffffffffffff8116811461354357600080fd5b6000806000806000806000806000806000806101808d8f03121561394357600080fd5b8c516bffffffffffffffffffffffff8116811461395f57600080fd5b9b5061396d60208e01613538565b9a5061397b60408e01613538565b995061398960608e01613538565b985061399760808e016138f5565b97506139a560a08e016136f0565b96506139b360c08e016136f0565b95506139c160e08e01613900565b94506101008d015193506101208d015192506139e06101408e01613900565b91506139ef6101608e01613900565b90509295989b509295989b509295989b565b80820281158282048414176106e7576106e7613748565b634e487b7160e01b600052603260045260246000fd5b600081613a3d57613a3d613748565b506000190190565b600060208284031215613a5757600080fd5b81516120e1816131d8565b805161ffff8116811461354357600080fd5b600080600080600080600060e0888a031215613a8f57600080fd5b8751613a9a816131d8565b6020890151909750613aab8161320a565b9550613ab960408901613a62565b9450613ac760608901613a62565b9350613ad560808901613a62565b925060a088015160ff81168114613aeb57600080fd5b60c0890151909250613afc816133fe565b8091505092959891949750929550565b600060208284031215613b1e57600080fd5b81516120e1816132cb565b6000600160ff1b8201613b3e57613b3e613748565b5060000390565b60008160020b627fffff198103613b5e57613b5e613748565b6000039291505056fed8aa0f3194971a2a116679f7c2090f6939c8d4e01a2a8d7e41d55e5351469e63a26469706673582212209c1709a30e73d7637741745e751e0f1b427fb91d397ff1a6d027113a4ff68e5364736f6c63430008120033d8aa0f3194971a2a116679f7c2090f6939c8d4e01a2a8d7e41d55e5351469e63000000000000000000000000b60adcf4afda59132407c3b3e8b85b3c33bcfe7c

Deployed Bytecode

0x608060405234801561001057600080fd5b506004361061027f5760003560e01c8063751681ca1161015c578063a702332d116100ce578063e38eb66c11610087578063e38eb66c14610637578063e89d66a81461064a578063f43d71991461066d578063f824c56614610690578063fdb5fefc146106a3578063ffd7d983146105eb57600080fd5b8063a702332d146105c5578063a9ed8882146105d8578063d2dc0e22146105eb578063d547741f146105fe578063da31500714610611578063dafe6db01461062457600080fd5b806391d148541161012057806391d148541461054e57806396adee011461056157806396db06f114610574578063a0f1fb1114610587578063a217fddf146105aa578063a4b3dfc4146105b257600080fd5b8063751681ca146104ef57806377977072146105025780637898e5cd146105155780637ac041ed146105285780637eebde771461053b57600080fd5b8063385892c6116101f557806357c13aaa116101b957806357c13aaa1461044e578063581c1597146104735780635a013639146104865780635ccc5ad4146104a657806361621153146104b95780636ced8c06146104cc57600080fd5b8063385892c6146103df57806338734882146103f2578063470def1414610405578063561fc351146104185780635763dbd01461043b57600080fd5b80632130f31f116102475780632130f31f1461033d578063248a9ca31461035057806328922086146103735780632f2ff15d1461039657806336568abe146103a95780633737bb22146103bc57600080fd5b806301ffc9a71461028457806305cf450a146102ac57806307bd0265146102e25780630b548482146103055780631c6e760414610328575b600080fd5b6102976102923660046131ae565b6106b6565b60405190151581526020015b60405180910390f35b6102cf6102ba3660046131ed565b60016020526000908152604090205460020b81565b60405160029190910b81526020016102a3565b6102f7600080516020613b6883398151915281565b6040519081526020016102a3565b6102cf6103133660046131ed565b60076020526000908152604090205460020b81565b61033b610336366004613219565b6106ed565b005b61033b61034b366004613252565b610778565b6102f761035e36600461328d565b60009081526020819052604090206001015490565b6102cf6103813660046131ed565b60066020526000908152604090205460020b81565b61033b6103a43660046132a6565b61096b565b61033b6103b73660046132a6565b610995565b6102cf6103ca3660046131ed565b60086020526000908152604090205460020b81565b61033b6103ed366004613219565b610a13565b61033b6104003660046132dc565b610a95565b61033b61041336600461330a565b610b4d565b6102cf6104263660046131ed565b60056020526000908152604090205460020b81565b61033b6104493660046131ed565b610bff565b6102cf61045c3660046131ed565b600260208190526000918252604090912054900b81565b6102f76104813660046131ed565b610e4a565b6104996104943660046131ed565b610fe7565b6040516102a39190613338565b61033b6104b436600461337f565b611052565b61033b6104c73660046133ab565b611101565b6102cf6104da3660046131ed565b60046020526000908152604090205460020b81565b61033b6104fd366004613219565b6111d1565b61033b6105103660046133ab565b611253565b61033b610523366004613219565b611314565b61033b610536366004613219565b611396565b61033b610549366004613219565b611417565b61029761055c3660046132a6565b611499565b61033b61056f366004613219565b6114c2565b61033b610582366004613219565b611544565b6102cf6105953660046131ed565b600a6020526000908152604090205460020b81565b6102f7600081565b6102976105c03660046131ed565b6115c5565b61033b6105d3366004613219565b611629565b6102976105e63660046131ed565b6116ab565b61033b6105f93660046131ed565b6117b0565b61033b61060c3660046132a6565b6117d1565b61029761061f3660046131ed565b6117f6565b61033b61063236600461340c565b611877565b61033b61064536600461337f565b611a8b565b6102cf6106583660046131ed565b60036020526000908152604090205460020b81565b6102cf61067b3660046131ed565b60096020526000908152604090205460020b81565b61033b61069e36600461343a565b611b3a565b61033b6106b13660046131ed565b611e47565b60006001600160e01b03198216637965db0b60e01b14806106e757506301ffc9a760e01b6001600160e01b03198316145b92915050565b600080516020613b6883398151915261070581611f57565b61070e82611f64565b6001600160a01b038316600081815260056020908152604091829020805462ffffff191662ffffff87161790559051600285900b81523392917f39c3e68e2a6a52a106264d6bb8eb9172b37c359aabe84a3e5a44165be1d53baa91015b60405180910390a3505050565b600080516020613b6883398151915261079081611f57565b6040516376a8e9ff60e11b81526004810185905260248101849052604481018390526001600160a01b0386169063ed51d3fe90606401600060405180830381600087803b1580156107e057600080fd5b505af11580156107f4573d6000803e3d6000fd5b50505050610801856115c5565b15156001146108495760405162461bcd60e51b815260206004820152600f60248201526e32b0b93710383932b81032b93937b960891b60448201526064015b60405180910390fd5b846001600160a01b031663ead892be6040518163ffffffff1660e01b8152600401602060405180830381865afa158015610887573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906108ab919061348b565b336001600160a01b0316866001600160a01b03167f305bc1ca7106255b17dbf66922802940d373f07b2c8810a76cee84700eb8c46c886001600160a01b0316636a185d5f6040518163ffffffff1660e01b8152600401602060405180830381865afa15801561091e573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610942919061348b565b61094b8a610e4a565b604080519283526020830191909152015b60405180910390a45050505050565b60008281526020819052604090206001015461098681611f57565b6109908383611fac565b505050565b6001600160a01b0381163314610a055760405162461bcd60e51b815260206004820152602f60248201527f416363657373436f6e74726f6c3a2063616e206f6e6c792072656e6f756e636560448201526e103937b632b9903337b91039b2b63360891b6064820152608401610840565b610a0f8282612030565b5050565b600080516020613b68833981519152610a2b81611f57565b610a3482612095565b6001600160a01b038316600081815260046020908152604091829020805462ffffff191662ffffff87161790559051600285900b81523392917fb76904a8ba51dc05f78444581f265d5e71c3783225c473e9620d4c09799df5bc910161076b565b600080516020613b68833981519152610aad81611f57565b60405163088d621d60e31b815262ffffff831660048201526001600160a01b0384169063446b10e890602401600060405180830381600087803b158015610af357600080fd5b505af1158015610b07573d6000803e3d6000fd5b505060405162ffffff851681523392506001600160a01b03861691507f8ab763dee50895615d0bb8306cdddbc0c1a6258f62aacbac6edc6be8c14ad8339060200161076b565b600080516020613b68833981519152610b6581611f57565b604051630c9590fd60e41b81526001600160a01b03838116600483015284169063c9590fd090602401600060405180830381600087803b158015610ba857600080fd5b505af1158015610bbc573d6000803e3d6000fd5b50506040516001600160a01b038581168252339350861691507f45f9e54d9f5cd84815f0bebc99eab35ed7008a0d9751ccd3c19807c4b331078a9060200161076b565b600080516020613b68833981519152610c1781611f57565b816001600160a01b03166370bb45b36040518163ffffffff1660e01b8152600401600060405180830381600087803b158015610c5257600080fd5b505af1158015610c66573d6000803e3d6000fd5b50505050816001600160a01b031663ead892be6040518163ffffffff1660e01b8152600401602060405180830381865afa158015610ca8573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610ccc919061348b565b336001600160a01b0316836001600160a01b03167f5d0927b601ecc1d0a4bedb898b2beff630177e857cecab540ba9b9136b302626856001600160a01b031663314e61ca6040518163ffffffff1660e01b8152600401602060405180830381865afa158015610d3f573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610d63919061348b565b866001600160a01b0316631a536a326040518163ffffffff1660e01b8152600401602060405180830381865afa158015610da1573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610dc5919061348b565b876001600160a01b0316636a185d5f6040518163ffffffff1660e01b8152600401602060405180830381865afa158015610e03573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610e27919061348b565b604080519384526020840192909252908201526060015b60405180910390a45050565b600080610e56836115c5565b9050801515600003610e6b5750600092915050565b6000836001600160a01b03166370cb00df6040518163ffffffff1660e01b8152600401600060405180830381865afa158015610eab573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052610ed39190810190613548565b5190506000846001600160a01b031663a142492d6040518163ffffffff1660e01b8152600401602060405180830381865afa158015610f16573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610f3a919061348b565b90506000610f4883836120d5565b90506000866001600160a01b031663234c4cce6040518163ffffffff1660e01b8152600401602060405180830381865afa158015610f8a573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610fae919061348b565b9050600080610fbd84846120e8565b9150915060008111610fcf5781610fda565b610fda82600161210a565b9998505050505050505050565b610fef613174565b816001600160a01b031663cbff66e26040518163ffffffff1660e01b815260040161020060405180830381865afa15801561102e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906106e791906135fa565b600080516020613b6883398151915261106a81611f57565b604051630f8a517f60e41b8152600481018390526001600160a01b0384169063f8a517f090602401600060405180830381600087803b1580156110ac57600080fd5b505af11580156110c0573d6000803e3d6000fd5b50506040518481523392506001600160a01b03861691507f6c58dea38c1fef4e97173e19196cf983e5a8f566fdf04367343c63b8abaa1be99060200161076b565b600080516020613b6883398151915261111981611f57565b6040516307795f5760e41b81526001600160a01b03861690637795f570906111499087908790879060040161368b565b600060405180830381600087803b15801561116357600080fd5b505af1158015611177573d6000803e3d6000fd5b50505050336001600160a01b0316856001600160a01b03167f08e0badddf13ca7fc84b48e3f0b89b0c276e9ae5e9497643f367fd2f240fdf998686866040516111c29392919061368b565b60405180910390a35050505050565b600080516020613b688339815191526111e981611f57565b6111f282611f64565b6001600160a01b038316600081815260076020908152604091829020805462ffffff191662ffffff87161790559051600285900b81523392917fe796bc4f3ce03bd32fb0e15586b34a308fc7bfa8c3c5485e92cd577b1bb9390a910161076b565b600080516020613b6883398151915261126b81611f57565b604051634bd613c960e11b81526001600160a01b038616906397ac27929061129b9087908790879060040161368b565b600060405180830381600087803b1580156112b557600080fd5b505af11580156112c9573d6000803e3d6000fd5b50505050336001600160a01b0316856001600160a01b03167f5b48ee2dc08a8c37693631d459c19db8be2f161266f3ae57cda789130f6ed53b8686866040516111c29392919061368b565b600080516020613b6883398151915261132c81611f57565b61133582611f64565b6001600160a01b038316600081815260066020908152604091829020805462ffffff191662ffffff87161790559051600285900b81523392917f4d5ea40657b0a60f4e1adf5562de4fc7b431b1087121d60e006af1b4dd80700c910161076b565b600080516020613b688339815191526113ae81611f57565b6113b782611f64565b6001600160a01b0383166000818152600a6020908152604091829020805462ffffff191662ffffff87161790559051600285900b81523392917e16e8ec2d70d4a15a6124477bef7aee3fd4b10b52f714c937f219ad3d4ecae5910161076b565b600080516020613b6883398151915261142f81611f57565b61143882611f64565b6001600160a01b038316600081815260086020908152604091829020805462ffffff191662ffffff87161790559051600285900b81523392917fd1c1e360f9477b31cc4bd12a26e42e098d96e90f614e2fcc621dea418ae0dd5f910161076b565b6000918252602082815260408084206001600160a01b0393909316845291905290205460ff1690565b600080516020613b688339815191526114da81611f57565b6114e382611f64565b6001600160a01b038316600081815260016020908152604091829020805462ffffff191662ffffff87161790559051600285900b81523392917f2447a43b2c31ed21e468726ba53813b663d414569fdfda6f648a646537d19bff910161076b565b600080516020613b6883398151915261155c81611f57565b61156582611f64565b6001600160a01b038316600081815260026020818152604092839020805462ffffff191662ffffff881617905591519085900b81523392917f5d87708daffcb58a78b045d80a978ca2200af1de98d942f0fb5674e08dc14fb0910161076b565b6000816001600160a01b0316638a1966e36040518163ffffffff1660e01b8152600401602060405180830381865afa158015611605573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906106e791906136af565b600080516020613b6883398151915261164181611f57565b61164a82612095565b6001600160a01b038316600081815260036020908152604091829020805462ffffff191662ffffff87161790559051600285900b81523392917fc1d8cd1a0ed04005edcfd0819fd55a04044b95ffbca6ad9f9c56bc0d69e06870910161076b565b600080826001600160a01b031663ead892be6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156116ec573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611710919061348b565b9050806000036117755760405162461bcd60e51b815260206004820152602a60248201527f6e6f7420616c6c6f772063616c6c696e67207768656e206c697175696469747960448201526904e6674496420697320360b41b6064820152608401610840565b6117a881731f98431c8ad98523631ae4a59f267346ea31f98473c36442b4a4522e871399cd717abdd847ab11fe88612116565b509392505050565b600080516020613b688339815191526117c881611f57565b610a0f82612199565b6000828152602081905260409020600101546117ec81611f57565b6109908383612030565b600080826001600160a01b031663ead892be6040518163ffffffff1660e01b8152600401602060405180830381865afa158015611837573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061185b919061348b565b90508060000361186e5750600192915050565b50600092915050565b600080516020613b6883398151915261188f81611f57565b6040516338ddeee160e11b8152821515600482015260009081906001600160a01b038616906371bbddc29060240160408051808303816000875af11580156118db573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906118ff91906136cc565b91509150846001600160a01b031663ead892be6040518163ffffffff1660e01b8152600401602060405180830381865afa158015611941573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611965919061348b565b336001600160a01b0316866001600160a01b03167f6f039c89811a62a23aa18d16914e04bb7bf86e124afcbf5f81bd141148e4ba7c8786868b6001600160a01b0316634f89b24d6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156119db573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906119ff919061348b565b8c6001600160a01b031663bf8895ea6040518163ffffffff1660e01b8152600401602060405180830381865afa158015611a3d573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611a61919061348b565b6040805195151586526020860194909452928401919091526060830152608082015260a00161095c565b600080516020613b68833981519152611aa381611f57565b6040516302ef42c760e11b8152600481018390526001600160a01b038416906305de858e90602401600060405180830381600087803b158015611ae557600080fd5b505af1158015611af9573d6000803e3d6000fd5b50506040518481523392506001600160a01b03861691507f8d35dc65727d53b4f7a4bdebeaa6c8b7b6ee8e6d772e6c35a4bbc087aa19e7ac9060200161076b565b600080516020613b68833981519152611b5281611f57565b6001600160a01b03858116600081815260096020526040808220549051631695590760e31b8152871515600482015260029190910b60248201526044810192909252306064830152918291829188169063b4aac83890608401606060405180830381865afa158015611bc8573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611bec91906136fb565b92509250925082611c4b5760405162461bcd60e51b815260206004820152602360248201527f63757272656e7420636f6e646974696f6e206e6f7420616c6c6f772072657363604482015262616c6560e81b6064820152608401610840565b611c56888787612302565b6000611cec896001600160a01b031663ead892be6040518163ffffffff1660e01b8152600401602060405180830381865afa158015611c99573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611cbd919061348b565b731f98431c8ad98523631ae4a59f267346ea31f98473c36442b4a4522e871399cd717abdd847ab11fe88612487565b50506001600160a01b038a166000908152600a6020526040902054909150611d179060020b8761375e565b60020b8160020b13158015611d5757506001600160a01b0389166000908152600a6020526040902054611d4d9060020b87613783565b60020b8160020b12155b611da35760405162461bcd60e51b815260206004820152601b60248201527f6f7574206f66207469636b20746f6c6572616e63652072616e676500000000006044820152606401610840565b60405163646ea14160e11b8152600284810b600483015283900b60248201526001600160a01b038a169063c8dd428290604401600060405180830381600087803b158015611df057600080fd5b505af1158015611e04573d6000803e3d6000fd5b505050506001600160a01b0389166000908152600960205260409020805462ffffff191662ffffff8316179055611e3c8984846124bd565b505050505050505050565b600080516020613b68833981519152611e5f81611f57565b816001600160a01b031663d389800f6040518163ffffffff1660e01b8152600401600060405180830381600087803b158015611e9a57600080fd5b505af1158015611eae573d6000803e3d6000fd5b50505050816001600160a01b031663ead892be6040518163ffffffff1660e01b8152600401602060405180830381865afa158015611ef0573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611f14919061348b565b336001600160a01b0384167f024aa4dfbe44229e3ca86f829e80c3a1b19d5e5d39962eae78b417b805178453611f4986610e4a565b604051908152602001610e3e565b611f6181336126f1565b50565b60008160020b1215611f615760405162461bcd60e51b81526020600482015260116024820152700696e707574206c657373207468616e203607c1b6044820152606401610840565b611fb68282611499565b610a0f576000828152602081815260408083206001600160a01b03851684529091529020805460ff19166001179055611fec3390565b6001600160a01b0316816001600160a01b0316837f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d60405160405180910390a45050565b61203a8282611499565b15610a0f576000828152602081815260408083206001600160a01b0385168085529252808320805460ff1916905551339285917ff6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171b9190a45050565b60018160020b13611f615760405162461bcd60e51b815260206004820152600a602482015269696e707574203c3d203160b01b6044820152606401610840565b60006120e182846137a8565b9392505050565b6000806120f5848461274a565b91506121018484612756565b90509250929050565b60006120e182846137bb565b6000806000612126868686612762565b50505091505060008060008061213c8a896127c9565b50509450945050935093508160020b8560020b1361216557600084965096505050505050612191565b8060020b8560020b12156121855760016000965096505050505050612191565b50600095509093505050505b935093915050565b6000816001600160a01b031663c221b7ec6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156121d9573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906121fd919061348b565b9050816001600160a01b031663abaa99166040518163ffffffff1660e01b8152600401600060405180830381600087803b15801561223a57600080fd5b505af115801561224e573d6000803e3d6000fd5b505050506000826001600160a01b031663c221b7ec6040518163ffffffff1660e01b8152600401602060405180830381865afa158015612292573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906122b6919061348b565b9050336001600160a01b0384167f5b9d56b28a7cde78f80ab41749028c18864c008e0e3722985c63ea258111b7eb6122ee85856120d5565b60408051918252602082018690520161076b565b6000806000612348866001600160a01b031663ead892be6040518163ffffffff1660e01b8152600401602060405180830381865afa158015611c99573d6000803e3d6000fd5b925092509250856001600160a01b03167f7a73c0af961631015e379691e5a22dadd4d0fa4c85b6c6c5b36af570aa537aa086886001600160a01b031663d0c93a7c6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156123b8573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906123dc91906137ce565b6001600160a01b038a1660009081526003602090815260408083205460048352818420546005845282852054600685528386205460098652958490205484519815158952600297880b9589019590955291860b87840152850b6060870152840b608086015291830b60a0850152820b60c084015288820b60e084015287820b61010084015285820b6101208401529086900b61014083015251908190036101600190a2505050505050565b6000806000612497868686612762565b509195506124ab92508891508690506127c9565b50979b919a5098509650505050505050565b6000612500846001600160a01b031663ead892be6040518163ffffffff1660e01b8152600401602060405180830381865afa158015611c99573d6000803e3d6000fd5b50509050336001600160a01b0316846001600160a01b03167fc6d664f645086a1c833d57b521e95bb8362087047154cdaf636f6228c6940ee7866001600160a01b0316634f89b24d6040518163ffffffff1660e01b8152600401602060405180830381865afa158015612577573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061259b919061348b565b876001600160a01b031663bf8895ea6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156125d9573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906125fd919061348b565b886001600160a01b031663d0c93a7c6040518163ffffffff1660e01b8152600401602060405180830381865afa15801561263b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061265f91906137ce565b6001600160a01b038a1660009081526001602090815260408083205460028084528285205460078552838620546008865295849020548451998a529489019790975294850b878301528a850b6060880152840b608087015293830b60a086015290820b60c0850152810b60e084015288810b61010084015287900b61012083015251908190036101400190a350505050565b6126fb8282611499565b610a0f57612708816128a0565b6127138360206128b2565b60405160200161272492919061380f565b60408051601f198184030181529082905262461bcd60e51b825261084091600401613884565b60006120e182846138cd565b60006120e182846138e1565b6000806000806000806000806127788b8a6127c9565b5050505092509250925061278e8a848484612a4e565b975061279988612ad8565b909192939495509091929394509091929350809750819850829950839a5050505050505050939792965093509350565b6000806000806000806000876001600160a01b03166399fbab888a6040518263ffffffff1660e01b815260040161280291815260200190565b61018060405180830381865afa158015612820573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906128449190613920565b909192939495969798999a509091929394959697989950909192935090919250909150905050809750819850829950839a50849b50505050505061288784612d54565b915061289283612d54565b905092959891949750929550565b60606106e76001600160a01b03831660145b606060006128c1836002613a01565b6128cc9060026137bb565b67ffffffffffffffff8111156128e4576128e46134a4565b6040519080825280601f01601f19166020018201604052801561290e576020820181803683370190505b509050600360fc1b8160008151811061292957612929613a18565b60200101906001600160f81b031916908160001a905350600f60fb1b8160018151811061295857612958613a18565b60200101906001600160f81b031916908160001a905350600061297c846002613a01565b6129879060016137bb565b90505b60018111156129ff576f181899199a1a9b1b9c1cb0b131b232b360811b85600f16601081106129bb576129bb613a18565b1a60f81b8282815181106129d1576129d1613a18565b60200101906001600160f81b031916908160001a90535060049490941c936129f881613a2e565b905061298a565b5083156120e15760405162461bcd60e51b815260206004820181905260248201527f537472696e67733a20686578206c656e67746820696e73756666696369656e746044820152606401610840565b604051630b4c774160e11b81526001600160a01b038481166004830152838116602483015262ffffff8316604483015260009190861690631698ee8290606401602060405180830381865afa158015612aab573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612acf9190613a45565b95945050505050565b6000806000806000806000876001600160a01b0316633850c7bd6040518163ffffffff1660e01b815260040160e060405180830381865afa158015612b21573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612b459190613a74565b505060408051630dfe168160e01b8152905194995094975050506001600160a01b038b1692630dfe168192600480830193506020928290030181865afa158015612b93573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612bb79190613a45565b9650876001600160a01b031663d21220a76040518163ffffffff1660e01b8152600401602060405180830381865afa158015612bf7573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612c1b9190613a45565b9550876001600160a01b031663ddca3f436040518163ffffffff1660e01b8152600401602060405180830381865afa158015612c5b573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612c7f9190613b0c565b9450866001600160a01b031663313ce5676040518163ffffffff1660e01b8152600401602060405180830381865afa158015612cbf573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612ce3919061348b565b9150856001600160a01b031663313ce5676040518163ffffffff1660e01b8152600401602060405180830381865afa158015612d23573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612d47919061348b565b9050919395979092949650565b60008060008360020b12612d6b578260020b612d78565b8260020b612d7890613b29565b9050612d87620d89e719613b45565b60020b811115612dbd5760405162461bcd60e51b81526020600482015260016024820152601560fa1b6044820152606401610840565b600081600116600003612dd457600160801b612de6565b6ffffcb933bd6fad37aa2d162d1a5940015b70ffffffffffffffffffffffffffffffffff1690506002821615612e25576080612e20826ffff97272373d413259a46990580e213a613a01565b901c90505b6004821615612e4f576080612e4a826ffff2e50f5f656932ef12357cf3c7fdcc613a01565b901c90505b6008821615612e79576080612e74826fffe5caca7e10e4e61c3624eaa0941cd0613a01565b901c90505b6010821615612ea3576080612e9e826fffcb9843d60f6159c9db58835c926644613a01565b901c90505b6020821615612ecd576080612ec8826fff973b41fa98c081472e6896dfb254c0613a01565b901c90505b6040821615612ef7576080612ef2826fff2ea16466c96a3843ec78b326b52861613a01565b901c90505b6080821615612f21576080612f1c826ffe5dee046a99a2a811c461f1969c3053613a01565b901c90505b610100821615612f4c576080612f47826ffcbe86c7900a88aedcffc83b479aa3a4613a01565b901c90505b610200821615612f77576080612f72826ff987a7253ac413176f2b074cf7815e54613a01565b901c90505b610400821615612fa2576080612f9d826ff3392b0822b70005940c7a398e4b70f3613a01565b901c90505b610800821615612fcd576080612fc8826fe7159475a2c29b7443b29c7fa6e889d9613a01565b901c90505b611000821615612ff8576080612ff3826fd097f3bdfd2022b8845ad8f792aa5825613a01565b901c90505b61200082161561302357608061301e826fa9f746462d870fdf8a65dc1f90e061e5613a01565b901c90505b61400082161561304e576080613049826f70d869a156d2a1b890bb3df62baf32f7613a01565b901c90505b618000821615613079576080613074826f31be135f97d08fd981231505542fcfa6613a01565b901c90505b620100008216156130a55760806130a0826f09aa508b5b7a84e1c677de54f3e99bc9613a01565b901c90505b620200008216156130d05760806130cb826e5d6af8dedb81196699c329225ee604613a01565b901c90505b620400008216156130fa5760806130f5826d2216e584f5fa1ea926041bedfe98613a01565b901c90505b6208000082161561312257608061311d826b048a170391f7dc42444e8fa2613a01565b901c90505b60008460020b131561313d5761313a816000196138cd565b90505b61314c640100000000826138e1565b1561315857600161315b565b60005b61316c9060ff16602083901c6137bb565b949350505050565b6040518061010001604052806008905b60408051808201909152600080825260208201528152602001906001900390816131845790505090565b6000602082840312156131c057600080fd5b81356001600160e01b0319811681146120e157600080fd5b6001600160a01b0381168114611f6157600080fd5b6000602082840312156131ff57600080fd5b81356120e1816131d8565b8060020b8114611f6157600080fd5b6000806040838503121561322c57600080fd5b8235613237816131d8565b915060208301356132478161320a565b809150509250929050565b6000806000806080858703121561326857600080fd5b8435613273816131d8565b966020860135965060408601359560600135945092505050565b60006020828403121561329f57600080fd5b5035919050565b600080604083850312156132b957600080fd5b823591506020830135613247816131d8565b62ffffff81168114611f6157600080fd5b600080604083850312156132ef57600080fd5b82356132fa816131d8565b91506020830135613247816132cb565b6000806040838503121561331d57600080fd5b8235613328816131d8565b91506020830135613247816131d8565b6102008101818360005b600881101561337657815180516001600160a01b031684526020908101518185015260409093019290910190600101613342565b50505092915050565b6000806040838503121561339257600080fd5b823561339d816131d8565b946020939093013593505050565b600080600080608085870312156133c157600080fd5b84356133cc816131d8565b93506020850135925060408501356133e3816131d8565b915060608501356133f3816132cb565b939692955090935050565b8015158114611f6157600080fd5b6000806040838503121561341f57600080fd5b823561342a816131d8565b91506020830135613247816133fe565b6000806000806080858703121561345057600080fd5b843561345b816131d8565b9350602085013561346b816131d8565b9250604085013561347b816133fe565b915060608501356133f38161320a565b60006020828403121561349d57600080fd5b5051919050565b634e487b7160e01b600052604160045260246000fd5b604051610100810167ffffffffffffffff811182821017156134de576134de6134a4565b60405290565b6040805190810167ffffffffffffffff811182821017156134de576134de6134a4565b604051601f8201601f1916810167ffffffffffffffff81118282101715613530576135306134a4565b604052919050565b8051613543816131d8565b919050565b6000602080838503121561355b57600080fd5b825167ffffffffffffffff8082111561357357600080fd5b818501915085601f83011261358757600080fd5b815181811115613599576135996134a4565b8060051b91506135aa848301613507565b81815291830184019184810190888411156135c457600080fd5b938501935b838510156135ee57845192506135de836131d8565b82825293850193908501906135c9565b98975050505050505050565b600061020080838503121561360e57600080fd5b83601f84011261361d57600080fd5b6136256134ba565b90830190808583111561363757600080fd5b845b8381101561368157604081880312156136525760008081fd5b61365a6134e4565b8151613665816131d8565b8152602082810151818301529084529290920191604001613639565b5095945050505050565b9283526001600160a01b0391909116602083015262ffffff16604082015260600190565b6000602082840312156136c157600080fd5b81516120e1816133fe565b600080604083850312156136df57600080fd5b505080516020909101519092909150565b80516135438161320a565b60008060006060848603121561371057600080fd5b835161371b816133fe565b602085015190935061372c8161320a565b604085015190925061373d8161320a565b809150509250925092565b634e487b7160e01b600052601160045260246000fd5b600281810b9083900b01627fffff8113627fffff19821217156106e7576106e7613748565b600282810b9082900b03627fffff198112627fffff821317156106e7576106e7613748565b818103818111156106e7576106e7613748565b808201808211156106e7576106e7613748565b6000602082840312156137e057600080fd5b81516120e18161320a565b60005b838110156138065781810151838201526020016137ee565b50506000910152565b7f416363657373436f6e74726f6c3a206163636f756e74200000000000000000008152600083516138478160178501602088016137eb565b7001034b99036b4b9b9b4b733903937b6329607d1b60179184019182015283516138788160288401602088016137eb565b01602801949350505050565b60208152600082518060208401526138a38160408501602087016137eb565b601f01601f19169190910160400192915050565b634e487b7160e01b600052601260045260246000fd5b6000826138dc576138dc6138b7565b500490565b6000826138f0576138f06138b7565b500690565b8051613543816132cb565b80516fffffffffffffffffffffffffffffffff8116811461354357600080fd5b6000806000806000806000806000806000806101808d8f03121561394357600080fd5b8c516bffffffffffffffffffffffff8116811461395f57600080fd5b9b5061396d60208e01613538565b9a5061397b60408e01613538565b995061398960608e01613538565b985061399760808e016138f5565b97506139a560a08e016136f0565b96506139b360c08e016136f0565b95506139c160e08e01613900565b94506101008d015193506101208d015192506139e06101408e01613900565b91506139ef6101608e01613900565b90509295989b509295989b509295989b565b80820281158282048414176106e7576106e7613748565b634e487b7160e01b600052603260045260246000fd5b600081613a3d57613a3d613748565b506000190190565b600060208284031215613a5757600080fd5b81516120e1816131d8565b805161ffff8116811461354357600080fd5b600080600080600080600060e0888a031215613a8f57600080fd5b8751613a9a816131d8565b6020890151909750613aab8161320a565b9550613ab960408901613a62565b9450613ac760608901613a62565b9350613ad560808901613a62565b925060a088015160ff81168114613aeb57600080fd5b60c0890151909250613afc816133fe565b8091505092959891949750929550565b600060208284031215613b1e57600080fd5b81516120e1816132cb565b6000600160ff1b8201613b3e57613b3e613748565b5060000390565b60008160020b627fffff198103613b5e57613b5e613748565b6000039291505056fed8aa0f3194971a2a116679f7c2090f6939c8d4e01a2a8d7e41d55e5351469e63a26469706673582212209c1709a30e73d7637741745e751e0f1b427fb91d397ff1a6d027113a4ff68e5364736f6c63430008120033

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

000000000000000000000000b60adcf4afda59132407c3b3e8b85b3c33bcfe7c

-----Decoded View---------------
Arg [0] : _executor (address): 0xB60adcf4aFDA59132407C3b3e8b85B3C33bcfe7c

-----Encoded View---------------
1 Constructor Arguments found :
Arg [0] : 000000000000000000000000b60adcf4afda59132407c3b3e8b85b3c33bcfe7c


Block Transaction Difficulty Gas Used Reward
View All Blocks Produced

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

Validator Index Block Amount
View All Withdrawals

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

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.