Contract 0xe9426fCF504D448CC2e39783f1D1111DC0d8E4E0

 
Txn Hash Method
Block
From
To
Value [Txn Fee]
0x9acdc1b96afef7936d323392beff5c40cae6341fa0ebbb6708ae28354c5f5e85Create Farm478786022022-12-21 18:51:07348 days 6 hrs ago0x82429089e7c86b7047b793a9e7e7311c93d2b7a6 IN  0xe9426fcf504d448cc2e39783f1d1111dc0d8e4e00 ETH0.00012289 0.1
0xf30ad52c4d8012e68264bfd115a69f3cf62e93eaeb051bd881e945022adec8daCreate Farm412646182022-11-26 4:10:23373 days 21 hrs ago0x56cf8b0f423eac5fb50a252619fa683ae95ef497 IN  0xe9426fcf504d448cc2e39783f1d1111dc0d8e4e00 ETH0.00007538 0.1
0x490694808076b2381fdbd0c76da19f89b9b3a34dee32c901a60b1b548f43693cCreate Farm400159752022-11-21 17:54:24378 days 7 hrs ago0xd8fbd4b5fe30459e2025b0465c46b7815b689c25 IN  0xe9426fcf504d448cc2e39783f1d1111dc0d8e4e00 ETH0.00012023 0.1
0x6e31fda8a3a8ad0fe92b9e77aab89b34e80571e34e719dc106585a0c23730d1fCreate Farm384314262022-11-16 2:13:19383 days 23 hrs agoLayer2DAO: Deployer IN  0xe9426fcf504d448cc2e39783f1d1111dc0d8e4e00 ETH0.00008817 0.1
0xb97c25c92e6cfe111c2990664a9778db30cb17f482414c83abcfa178a8c80f79Create Farm381715002022-11-15 1:51:37384 days 23 hrs ago0x420220b72bbd307db8615e7aa0eadca399cf2fc0 IN  0xe9426fcf504d448cc2e39783f1d1111dc0d8e4e00 ETH0.00011791 0.1
0xbd47fc062375e284b228fc8fd91f73d93d38d1370266b42e78459f19a4a191ebCreate Farm372889232022-11-11 18:28:05388 days 7 hrs agoLayer2DAO: Deployer IN  0xe9426fcf504d448cc2e39783f1d1111dc0d8e4e00 ETH0.00012271 0.1
0x2d0f33ab70ba6b9825e74e0c0cc71130edb573bbe7d922c20fe8b22cc8868fd1Create Farm372851992022-11-11 18:09:31388 days 7 hrs ago0xb44f710054aabe793a14125646badfd43791a5c4 IN  0xe9426fcf504d448cc2e39783f1d1111dc0d8e4e00 ETH0.00012741 0.1
0xd1c0a5567cfc25bc5f44acdf8450031217dc206d0804fe1963d521c2e3a65984Create Farm372751462022-11-11 17:20:53388 days 8 hrs ago0x5cc44a5531946e3d399c9a45cf87ce6e9e742549 IN  0xe9426fcf504d448cc2e39783f1d1111dc0d8e4e00 ETH0.00016603 0.1
0x173b259167138d04e55cd7ad2d6f2af6230a5039d601aff5e725f328be36516fCreate Farm372719332022-11-11 17:05:55388 days 8 hrs ago0x82429089e7c86b7047b793a9e7e7311c93d2b7a6 IN  0xe9426fcf504d448cc2e39783f1d1111dc0d8e4e00 ETH0.00018256 0.1
0xca6607cc185df3c83074de9347ee51c6129d693e312438436bf26716ee3169c9Transfer Ownersh...330776602022-10-27 15:10:09403 days 10 hrs agoSperax: Deployer 1 IN  0xe9426fcf504d448cc2e39783f1d1111dc0d8e4e00 ETH0.000023140.1
0x5f7423064bc99a77427935386d2ccf8ec977212f352d7d25fd9c933dc6d9bed80x60806040330776202022-10-27 15:09:58403 days 10 hrs agoSperax: Deployer 1 IN  Create: UniswapFarmV1Deployer0 ETH0.002471910.1
[ Download CSV Export 
Latest 25 internal transaction
Parent Txn Hash Block From To Value
0x9acdc1b96afef7936d323392beff5c40cae6341fa0ebbb6708ae28354c5f5e85478786022022-12-21 18:51:07348 days 6 hrs ago 0xe9426fcf504d448cc2e39783f1d1111dc0d8e4e0 0xc4fb09e0cd212367642974f6ba81d8e23780a6590 ETH
0x9acdc1b96afef7936d323392beff5c40cae6341fa0ebbb6708ae28354c5f5e85478786022022-12-21 18:51:07348 days 6 hrs ago 0xe9426fcf504d448cc2e39783f1d1111dc0d8e4e0 Sperax: USDs Token0 ETH
0x9acdc1b96afef7936d323392beff5c40cae6341fa0ebbb6708ae28354c5f5e85478786022022-12-21 18:51:07348 days 6 hrs ago 0xe9426fcf504d448cc2e39783f1d1111dc0d8e4e0 0xc4fb09e0cd212367642974f6ba81d8e23780a6590 ETH
0x9acdc1b96afef7936d323392beff5c40cae6341fa0ebbb6708ae28354c5f5e85478786022022-12-21 18:51:07348 days 6 hrs ago 0xe9426fcf504d448cc2e39783f1d1111dc0d8e4e0 0x99d408010f9660cfb17aa743c546fc3f279df6870 ETH
0x9acdc1b96afef7936d323392beff5c40cae6341fa0ebbb6708ae28354c5f5e85478786022022-12-21 18:51:07348 days 6 hrs ago 0xe9426fcf504d448cc2e39783f1d1111dc0d8e4e0 0x99d408010f9660cfb17aa743c546fc3f279df6870 ETH
0x9acdc1b96afef7936d323392beff5c40cae6341fa0ebbb6708ae28354c5f5e85478786022022-12-21 18:51:07348 days 6 hrs ago 0xe9426fcf504d448cc2e39783f1d1111dc0d8e4e0  Contract Creation0 ETH
0xe5cc4175f747da7c8deb351eb54c906e98e2027ca4c14d774aa8dc11e22fd92c430486212022-12-02 17:55:19367 days 7 hrs ago 0x6d5240f086637fb408c7f727010a10cf57d51b62 0xe9426fcf504d448cc2e39783f1d1111dc0d8e4e00 ETH
0xf30ad52c4d8012e68264bfd115a69f3cf62e93eaeb051bd881e945022adec8da412646182022-11-26 4:10:23373 days 21 hrs ago 0xe9426fcf504d448cc2e39783f1d1111dc0d8e4e0 0xc4fb09e0cd212367642974f6ba81d8e23780a6590 ETH
0xf30ad52c4d8012e68264bfd115a69f3cf62e93eaeb051bd881e945022adec8da412646182022-11-26 4:10:23373 days 21 hrs ago 0xe9426fcf504d448cc2e39783f1d1111dc0d8e4e0 Sperax: USDs Token0 ETH
0xf30ad52c4d8012e68264bfd115a69f3cf62e93eaeb051bd881e945022adec8da412646182022-11-26 4:10:23373 days 21 hrs ago 0xe9426fcf504d448cc2e39783f1d1111dc0d8e4e0 0xc4fb09e0cd212367642974f6ba81d8e23780a6590 ETH
0xf30ad52c4d8012e68264bfd115a69f3cf62e93eaeb051bd881e945022adec8da412646182022-11-26 4:10:23373 days 21 hrs ago 0xe9426fcf504d448cc2e39783f1d1111dc0d8e4e0 0xfbdbba0aa4229f8253efb921e43b02b5c8fc43790 ETH
0xf30ad52c4d8012e68264bfd115a69f3cf62e93eaeb051bd881e945022adec8da412646182022-11-26 4:10:23373 days 21 hrs ago 0xe9426fcf504d448cc2e39783f1d1111dc0d8e4e0 0xfbdbba0aa4229f8253efb921e43b02b5c8fc43790 ETH
0xf30ad52c4d8012e68264bfd115a69f3cf62e93eaeb051bd881e945022adec8da412646182022-11-26 4:10:23373 days 21 hrs ago 0xe9426fcf504d448cc2e39783f1d1111dc0d8e4e0  Contract Creation0 ETH
0x490694808076b2381fdbd0c76da19f89b9b3a34dee32c901a60b1b548f43693c400159752022-11-21 17:54:24378 days 7 hrs ago 0xe9426fcf504d448cc2e39783f1d1111dc0d8e4e0 0xc4fb09e0cd212367642974f6ba81d8e23780a6590 ETH
0x490694808076b2381fdbd0c76da19f89b9b3a34dee32c901a60b1b548f43693c400159752022-11-21 17:54:24378 days 7 hrs ago 0xe9426fcf504d448cc2e39783f1d1111dc0d8e4e0 Sperax: USDs Token0 ETH
0x490694808076b2381fdbd0c76da19f89b9b3a34dee32c901a60b1b548f43693c400159752022-11-21 17:54:24378 days 7 hrs ago 0xe9426fcf504d448cc2e39783f1d1111dc0d8e4e0 0xc4fb09e0cd212367642974f6ba81d8e23780a6590 ETH
0x490694808076b2381fdbd0c76da19f89b9b3a34dee32c901a60b1b548f43693c400159752022-11-21 17:54:24378 days 7 hrs ago 0xe9426fcf504d448cc2e39783f1d1111dc0d8e4e0 0x876af63f316442a40d344a2eddc8916f2a37e76a0 ETH
0x490694808076b2381fdbd0c76da19f89b9b3a34dee32c901a60b1b548f43693c400159752022-11-21 17:54:24378 days 7 hrs ago 0xe9426fcf504d448cc2e39783f1d1111dc0d8e4e0 0x876af63f316442a40d344a2eddc8916f2a37e76a0 ETH
0x490694808076b2381fdbd0c76da19f89b9b3a34dee32c901a60b1b548f43693c400159752022-11-21 17:54:24378 days 7 hrs ago 0xe9426fcf504d448cc2e39783f1d1111dc0d8e4e0  Contract Creation0 ETH
0x6e31fda8a3a8ad0fe92b9e77aab89b34e80571e34e719dc106585a0c23730d1f384314262022-11-16 2:13:19383 days 23 hrs ago 0xe9426fcf504d448cc2e39783f1d1111dc0d8e4e0 0xc4fb09e0cd212367642974f6ba81d8e23780a6590 ETH
0x6e31fda8a3a8ad0fe92b9e77aab89b34e80571e34e719dc106585a0c23730d1f384314262022-11-16 2:13:19383 days 23 hrs ago 0xe9426fcf504d448cc2e39783f1d1111dc0d8e4e0 Sperax: USDs Token0 ETH
0x6e31fda8a3a8ad0fe92b9e77aab89b34e80571e34e719dc106585a0c23730d1f384314262022-11-16 2:13:19383 days 23 hrs ago 0xe9426fcf504d448cc2e39783f1d1111dc0d8e4e0 0xc4fb09e0cd212367642974f6ba81d8e23780a6590 ETH
0x6e31fda8a3a8ad0fe92b9e77aab89b34e80571e34e719dc106585a0c23730d1f384314262022-11-16 2:13:19383 days 23 hrs ago 0xe9426fcf504d448cc2e39783f1d1111dc0d8e4e0 0x9870c94ca99b8e15db7164fd623555ef64af8a940 ETH
0x6e31fda8a3a8ad0fe92b9e77aab89b34e80571e34e719dc106585a0c23730d1f384314262022-11-16 2:13:19383 days 23 hrs ago 0xe9426fcf504d448cc2e39783f1d1111dc0d8e4e0 0x9870c94ca99b8e15db7164fd623555ef64af8a940 ETH
0x6e31fda8a3a8ad0fe92b9e77aab89b34e80571e34e719dc106585a0c23730d1f384314262022-11-16 2:13:19383 days 23 hrs ago 0xe9426fcf504d448cc2e39783f1d1111dc0d8e4e0  Contract Creation0 ETH
[ Download CSV Export 
Loading
This contract may be a proxy contract. Click on More Options and select Is this a proxy? to confirm and enable the "Read as Proxy" & "Write as Proxy" tabs.

Contract Source Code Verified (Exact Match)

Contract Name:
UniswapFarmV1Deployer

Compiler Version
v0.8.10+commit.fc410830

Optimization Enabled:
Yes with 200 runs

Other Settings:
default evmVersion, MIT license

Contract Source Code (Solidity)

/**
 *Submitted for verification at Arbiscan.io on 2022-10-27
*/

// File: @openzeppelin/contracts/proxy/Clones.sol


// OpenZeppelin Contracts (last updated v4.7.0) (proxy/Clones.sol)

pragma solidity ^0.8.0;

/**
 * @dev https://eips.ethereum.org/EIPS/eip-1167[EIP 1167] is a standard for
 * deploying minimal proxy contracts, also known as "clones".
 *
 * > To simply and cheaply clone contract functionality in an immutable way, this standard specifies
 * > a minimal bytecode implementation that delegates all calls to a known, fixed address.
 *
 * The library includes functions to deploy a proxy using either `create` (traditional deployment) or `create2`
 * (salted deterministic deployment). It also includes functions to predict the addresses of clones deployed using the
 * deterministic method.
 *
 * _Available since v3.4._
 */
library Clones {
    /**
     * @dev Deploys and returns the address of a clone that mimics the behaviour of `implementation`.
     *
     * This function uses the create opcode, which should never revert.
     */
    function clone(address implementation) internal returns (address instance) {
        /// @solidity memory-safe-assembly
        assembly {
            let ptr := mload(0x40)
            mstore(ptr, 0x3d602d80600a3d3981f3363d3d373d3d3d363d73000000000000000000000000)
            mstore(add(ptr, 0x14), shl(0x60, implementation))
            mstore(add(ptr, 0x28), 0x5af43d82803e903d91602b57fd5bf30000000000000000000000000000000000)
            instance := create(0, ptr, 0x37)
        }
        require(instance != address(0), "ERC1167: create failed");
    }

    /**
     * @dev Deploys and returns the address of a clone that mimics the behaviour of `implementation`.
     *
     * This function uses the create2 opcode and a `salt` to deterministically deploy
     * the clone. Using the same `implementation` and `salt` multiple time will revert, since
     * the clones cannot be deployed twice at the same address.
     */
    function cloneDeterministic(address implementation, bytes32 salt) internal returns (address instance) {
        /// @solidity memory-safe-assembly
        assembly {
            let ptr := mload(0x40)
            mstore(ptr, 0x3d602d80600a3d3981f3363d3d373d3d3d363d73000000000000000000000000)
            mstore(add(ptr, 0x14), shl(0x60, implementation))
            mstore(add(ptr, 0x28), 0x5af43d82803e903d91602b57fd5bf30000000000000000000000000000000000)
            instance := create2(0, ptr, 0x37, salt)
        }
        require(instance != address(0), "ERC1167: create2 failed");
    }

    /**
     * @dev Computes the address of a clone deployed using {Clones-cloneDeterministic}.
     */
    function predictDeterministicAddress(
        address implementation,
        bytes32 salt,
        address deployer
    ) internal pure returns (address predicted) {
        /// @solidity memory-safe-assembly
        assembly {
            let ptr := mload(0x40)
            mstore(ptr, 0x3d602d80600a3d3981f3363d3d373d3d3d363d73000000000000000000000000)
            mstore(add(ptr, 0x14), shl(0x60, implementation))
            mstore(add(ptr, 0x28), 0x5af43d82803e903d91602b57fd5bf3ff00000000000000000000000000000000)
            mstore(add(ptr, 0x38), shl(0x60, deployer))
            mstore(add(ptr, 0x4c), salt)
            mstore(add(ptr, 0x6c), keccak256(ptr, 0x37))
            predicted := keccak256(add(ptr, 0x37), 0x55)
        }
    }

    /**
     * @dev Computes the address of a clone deployed using {Clones-cloneDeterministic}.
     */
    function predictDeterministicAddress(address implementation, bytes32 salt)
        internal
        view
        returns (address predicted)
    {
        return predictDeterministicAddress(implementation, salt, address(this));
    }
}

// File: @openzeppelin/contracts/utils/introspection/IERC165.sol


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

// File: @openzeppelin/contracts/token/ERC721/IERC721.sol


// OpenZeppelin Contracts (last updated v4.7.0) (token/ERC721/IERC721.sol)

pragma solidity ^0.8.0;


/**
 * @dev Required interface of an ERC721 compliant contract.
 */
interface IERC721 is IERC165 {
    /**
     * @dev Emitted when `tokenId` token is transferred from `from` to `to`.
     */
    event Transfer(address indexed from, address indexed to, uint256 indexed tokenId);

    /**
     * @dev Emitted when `owner` enables `approved` to manage the `tokenId` token.
     */
    event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId);

    /**
     * @dev Emitted when `owner` enables or disables (`approved`) `operator` to manage all of its assets.
     */
    event ApprovalForAll(address indexed owner, address indexed operator, bool approved);

    /**
     * @dev Returns the number of tokens in ``owner``'s account.
     */
    function balanceOf(address owner) external view returns (uint256 balance);

    /**
     * @dev Returns the owner of the `tokenId` token.
     *
     * Requirements:
     *
     * - `tokenId` must exist.
     */
    function ownerOf(uint256 tokenId) external view returns (address owner);

    /**
     * @dev Safely transfers `tokenId` token from `from` to `to`.
     *
     * Requirements:
     *
     * - `from` cannot be the zero address.
     * - `to` cannot be the zero address.
     * - `tokenId` token must exist and be owned by `from`.
     * - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}.
     * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
     *
     * Emits a {Transfer} event.
     */
    function safeTransferFrom(
        address from,
        address to,
        uint256 tokenId,
        bytes calldata data
    ) external;

    /**
     * @dev Safely transfers `tokenId` token from `from` to `to`, checking first that contract recipients
     * are aware of the ERC721 protocol to prevent tokens from being forever locked.
     *
     * Requirements:
     *
     * - `from` cannot be the zero address.
     * - `to` cannot be the zero address.
     * - `tokenId` token must exist and be owned by `from`.
     * - If the caller is not `from`, it must have been allowed to move this token by either {approve} or {setApprovalForAll}.
     * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
     *
     * Emits a {Transfer} event.
     */
    function safeTransferFrom(
        address from,
        address to,
        uint256 tokenId
    ) external;

    /**
     * @dev Transfers `tokenId` token from `from` to `to`.
     *
     * WARNING: Usage of this method is discouraged, use {safeTransferFrom} whenever possible.
     *
     * Requirements:
     *
     * - `from` cannot be the zero address.
     * - `to` cannot be the zero address.
     * - `tokenId` token must be owned by `from`.
     * - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}.
     *
     * Emits a {Transfer} event.
     */
    function transferFrom(
        address from,
        address to,
        uint256 tokenId
    ) external;

    /**
     * @dev Gives permission to `to` to transfer `tokenId` token to another account.
     * The approval is cleared when the token is transferred.
     *
     * Only a single account can be approved at a time, so approving the zero address clears previous approvals.
     *
     * Requirements:
     *
     * - The caller must own the token or be an approved operator.
     * - `tokenId` must exist.
     *
     * Emits an {Approval} event.
     */
    function approve(address to, uint256 tokenId) external;

    /**
     * @dev Approve or remove `operator` as an operator for the caller.
     * Operators can call {transferFrom} or {safeTransferFrom} for any token owned by the caller.
     *
     * Requirements:
     *
     * - The `operator` cannot be the caller.
     *
     * Emits an {ApprovalForAll} event.
     */
    function setApprovalForAll(address operator, bool _approved) external;

    /**
     * @dev Returns the account approved for `tokenId` token.
     *
     * Requirements:
     *
     * - `tokenId` must exist.
     */
    function getApproved(uint256 tokenId) external view returns (address operator);

    /**
     * @dev Returns if the `operator` is allowed to manage all of the assets of `owner`.
     *
     * See {setApprovalForAll}
     */
    function isApprovedForAll(address owner, address operator) external view returns (bool);
}

// File: interfaces/UniswapV3.sol


pragma solidity 0.8.10;


/// @title Non-fungible token for positions
/// @notice Wraps Uniswap V3 positions in a non-fungible token interface which allows for them to be transferred
/// and authorized.

interface IPoolInitializer {
    /// @notice Creates a new pool if it does not exist, then initializes if not initialized
    /// @dev This method can be bundled with others via IMulticall for the first action (e.g. mint) performed against a pool
    /// @param token0 The contract address of token0 of the pool
    /// @param token1 The contract address of token1 of the pool
    /// @param fee The fee amount of the v3 pool for the specified token pair
    /// @param sqrtPriceX96 The initial square root price of the pool as a Q64.96 value
    /// @return pool Returns the pool address based on the pair of tokens and fee, will return the newly created pool address if necessary
    function createAndInitializePoolIfNecessary(
        address token0,
        address token1,
        uint24 fee,
        uint160 sqrtPriceX96
    ) external payable returns (address pool);
}

interface INFPM is IPoolInitializer, IERC721 {
    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
        );

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

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

interface IUniswapV3TickSpacing {
    function tickSpacing() external view returns (int24);
}

// File: @openzeppelin/contracts/security/ReentrancyGuard.sol


// OpenZeppelin Contracts v4.4.1 (security/ReentrancyGuard.sol)

pragma solidity ^0.8.0;

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

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

    uint256 private _status;

    constructor() {
        _status = _NOT_ENTERED;
    }

    /**
     * @dev Prevents a contract from calling itself, directly or indirectly.
     * Calling a `nonReentrant` function from another `nonReentrant`
     * function is not supported. It is possible to prevent this from happening
     * by making the `nonReentrant` function external, and making it call a
     * `private` function that does the actual work.
     */
    modifier nonReentrant() {
        // On the first call to nonReentrant, _notEntered will be true
        require(_status != _ENTERED, "ReentrancyGuard: reentrant call");

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

        _;

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

// File: @openzeppelin/contracts/token/ERC721/IERC721Receiver.sol


// OpenZeppelin Contracts (last updated v4.6.0) (token/ERC721/IERC721Receiver.sol)

pragma solidity ^0.8.0;

/**
 * @title ERC721 token receiver interface
 * @dev Interface for any contract that wants to support safeTransfers
 * from ERC721 asset contracts.
 */
interface IERC721Receiver {
    /**
     * @dev Whenever an {IERC721} `tokenId` token is transferred to this contract via {IERC721-safeTransferFrom}
     * by `operator` from `from`, this function is called.
     *
     * It must return its Solidity selector to confirm the token transfer.
     * If any other value is returned or the interface is not implemented by the recipient, the transfer will be reverted.
     *
     * The selector can be obtained in Solidity with `IERC721Receiver.onERC721Received.selector`.
     */
    function onERC721Received(
        address operator,
        address from,
        uint256 tokenId,
        bytes calldata data
    ) external returns (bytes4);
}

// File: @openzeppelin/contracts/utils/Context.sol


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

// File: @openzeppelin/contracts/access/Ownable.sol


// OpenZeppelin Contracts (last updated v4.7.0) (access/Ownable.sol)

pragma solidity ^0.8.0;


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

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

    /**
     * @dev Initializes the contract setting the deployer as the initial owner.
     */
    constructor() {
        _transferOwnership(_msgSender());
    }

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

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

    /**
     * @dev Throws if the sender is not the owner.
     */
    function _checkOwner() internal view virtual {
        require(owner() == _msgSender(), "Ownable: caller is not the owner");
    }

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

    /**
     * @dev Transfers ownership of the contract to a new account (`newOwner`).
     * Can only be called by the current owner.
     */
    function transferOwnership(address newOwner) public virtual onlyOwner {
        require(newOwner != address(0), "Ownable: new owner is the zero address");
        _transferOwnership(newOwner);
    }

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

// File: @openzeppelin/contracts/utils/Address.sol


// OpenZeppelin Contracts (last updated v4.7.0) (utils/Address.sol)

pragma solidity ^0.8.1;

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

        return account.code.length > 0;
    }

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

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

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

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

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

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

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

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

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

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

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

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

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

    /**
     * @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the
     * revert reason using the provided one.
     *
     * _Available since v4.3._
     */
    function verifyCallResult(
        bool success,
        bytes memory returndata,
        string memory errorMessage
    ) internal pure returns (bytes memory) {
        if (success) {
            return returndata;
        } else {
            // Look for revert reason and bubble it up if present
            if (returndata.length > 0) {
                // The easiest way to bubble the revert reason is using memory via assembly
                /// @solidity memory-safe-assembly
                assembly {
                    let returndata_size := mload(returndata)
                    revert(add(32, returndata), returndata_size)
                }
            } else {
                revert(errorMessage);
            }
        }
    }
}

// File: @openzeppelin/contracts/proxy/utils/Initializable.sol


// OpenZeppelin Contracts (last updated v4.7.0) (proxy/utils/Initializable.sol)

pragma solidity ^0.8.2;


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

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

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

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

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

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

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

// File: @openzeppelin/contracts/token/ERC20/extensions/draft-IERC20Permit.sol


// OpenZeppelin Contracts v4.4.1 (token/ERC20/extensions/draft-IERC20Permit.sol)

pragma solidity ^0.8.0;

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

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

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

// File: @openzeppelin/contracts/token/ERC20/IERC20.sol


// OpenZeppelin Contracts (last updated v4.6.0) (token/ERC20/IERC20.sol)

pragma solidity ^0.8.0;

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

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

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

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

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

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

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

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

// File: @openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol


// OpenZeppelin Contracts (last updated v4.7.0) (token/ERC20/utils/SafeERC20.sol)

pragma solidity ^0.8.0;




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

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

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

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

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

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

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

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

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

// File: contracts/UniswapFarmV1.sol

pragma solidity 0.8.10;

//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@//
//@@@@@@@@&....(@@@@@@@@@@@@@..../@@@@@@@@@//
//@@@@@@........../@@@@@@@........../@@@@@@//
//@@@@@............(@@@@@............(@@@@@//
//@@@@@(............@@@@@(...........&@@@@@//
//@@@@@@@...........&@@@@@@.........@@@@@@@//
//@@@@@@@@@@@@@@%..../@@@@@@@@@@@@@@@@@@@@@//
//@@@@@@@@@@@@@@@@@@@...@@@@@@@@@@@@@@@@@@@//
//@@@@@@@@@@@@@@@@@@@@@......(&@@@@@@@@@@@@//
//@@@@@@#.........@@@@@@#...........@@@@@@@//
//@@@@@/...........%@@@@@............%@@@@@//
//@@@@@............#@@@@@............%@@@@@//
//@@@@@@..........#@@@@@@@/.........#@@@@@@//
//@@@@@@@@@&/.(@@@@@@@@@@@@@@&/.(&@@@@@@@@@//
//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@//







// Defines the Uniswap pool init data for constructor.
// tokenA - Address of tokenA
// tokenB - Address of tokenB
// feeTier - Fee tier for the Uniswap pool
// tickLowerAllowed - Lower bound of the tick range for farm
// tickUpperAllowed - Upper bound of the tick range for farm
struct UniswapPoolData {
    address tokenA;
    address tokenB;
    uint24 feeTier;
    int24 tickLowerAllowed;
    int24 tickUpperAllowed;
}

// Defines the reward data for constructor.
// token - Address of the token
// tknManager - Authority to update rewardToken related Params.
struct RewardTokenData {
    address token;
    address tknManager;
}

contract UniswapFarmV1 is
    Ownable,
    ReentrancyGuard,
    Initializable,
    IERC721Receiver
{
    using SafeERC20 for IERC20;

    // Defines the reward funds for the farm
    // totalLiquidity - amount of liquidity sharing the rewards in the fund
    // rewardsPerSec - the emission rate of the fund
    // accRewardPerShare - the accumulated reward per share
    struct RewardFund {
        uint256 totalLiquidity;
        uint256[] rewardsPerSec;
        uint256[] accRewardPerShare;
    }

    // Keeps track of a deposit's share in a reward fund.
    // fund id - id of the subscribed reward fund
    // rewardDebt - rewards claimed for a deposit corresponding to
    //              latest accRewardPerShare value of the budget
    // rewardClaimed - rewards claimed for a deposit from the reward fund
    struct Subscription {
        uint8 fundId;
        uint256[] rewardDebt;
        uint256[] rewardClaimed;
    }

    // Deposit information
    // locked - determines if the deposit is locked or not
    // liquidity - amount of liquidity in the deposit
    // tokenId - maps to uniswap NFT token id
    // startTime - time of deposit
    // expiryDate - expiry time (if deposit is locked)
    // totalRewardsClaimed - total rewards claimed for the deposit
    struct Deposit {
        bool locked;
        uint256 liquidity;
        uint256 tokenId;
        uint256 startTime;
        uint256 expiryDate;
        uint256[] totalRewardsClaimed;
    }

    // Reward token related information
    // tknManager Address that manages the rewardToken.
    // accRewardBal The rewards accrued but pending to be claimed.
    struct RewardData {
        address tknManager;
        uint8 id;
        uint256 accRewardBal;
    }

    // constants
    address public constant SPA = 0x5575552988A3A80504bBaeB1311674fCFd40aD4B;
    address public constant SPA_TOKEN_MANAGER =
        0x6d5240f086637fb408c7F727010A10cf57D51B62;
    address public constant NFPM = 0xC36442b4a4522E871399CD717aBDD847Ab11FE88;
    address public constant UNIV3_FACTORY =
        0x1F98431c8aD98523631AE4a59f267346ea31F984;
    uint8 public constant COMMON_FUND_ID = 0;
    uint8 public constant LOCKUP_FUND_ID = 1;
    uint256 public constant PREC = 1e18;
    uint256 public constant MIN_COOLDOWN_PERIOD = 1; // In days
    uint256 public constant MAX_NUM_REWARDS = 4;

    // Global Params
    bool public isPaused;
    bool public isClosed;

    // UniswapV3 params
    int24 public tickLowerAllowed;
    int24 public tickUpperAllowed;
    address public uniswapPool;

    uint256 public cooldownPeriod;
    uint256 public lastFundUpdateTime;
    uint256 public farmStartTime;

    // Reward info
    RewardFund[] public rewardFunds;
    address[] public rewardTokens;
    mapping(address => RewardData) public rewardData;
    mapping(address => Deposit[]) public deposits;
    mapping(uint256 => Subscription[]) public subscriptions;

    event Deposited(
        address indexed account,
        bool locked,
        uint256 tokenId,
        uint256 liquidity
    );
    event CooldownInitiated(
        address indexed account,
        uint256 tokenId,
        uint256 expiryDate
    );
    event DepositWithdrawn(
        address indexed account,
        uint256 tokenId,
        uint256 startTime,
        uint256 liquidity,
        uint256[] totalRewardsClaimed
    );
    event RewardsClaimed(
        address indexed account,
        uint8 fundId,
        uint256 tokenId,
        uint256 liquidity,
        uint256 fundLiquidity,
        uint256[] rewardAmount
    );
    event PoolUnsubscribed(
        address indexed account,
        uint8 fundId,
        uint256 depositId,
        uint256 startTime,
        uint256[] totalRewardsClaimed
    );
    event FarmStartTimeUpdated(uint256 newStartTime);
    event CooldownPeriodUpdated(
        uint256 oldCooldownPeriod,
        uint256 newCooldownPeriod
    );
    event RewardRateUpdated(
        address rwdToken,
        uint256[] oldRewardRate,
        uint256[] newRewardRate
    );
    event RewardAdded(address rwdToken, uint256 amount);
    event FarmClosed();
    event RecoveredERC20(address token, uint256 amount);
    event FundsRecovered(
        address indexed account,
        address rwdToken,
        uint256 amount
    );
    event TokenManagerUpdated(
        address rwdToken,
        address oldTokenManager,
        address newTokenManager
    );
    event RewardTokenAdded(address rwdToken, address rwdTokenManager);
    event FarmPaused(bool paused);

    modifier notPaused() {
        require(!isPaused, "Farm is paused");
        _;
    }

    modifier farmNotClosed() {
        require(!isClosed, "Farm closed");
        _;
    }

    modifier isTokenManager(address _rwdToken) {
        require(
            msg.sender == rewardData[_rwdToken].tknManager,
            "Not the token manager"
        );
        _;
    }

    // Disallow initialization of a implementation contract
    constructor() {
        _disableInitializers();
    }

    /// @notice constructor
    /// @param _farmStartTime - time of farm start
    /// @param _cooldownPeriod - cooldown period for locked deposits in days
    /// @dev _cooldownPeriod = 0 Disables lockup functionality for the farm.
    /// @param _uniswapPoolData - init data for UniswapV3 pool
    /// @param _rwdTokenData - init data for reward tokens
    function initialize(
        uint256 _farmStartTime,
        uint256 _cooldownPeriod,
        UniswapPoolData memory _uniswapPoolData,
        RewardTokenData[] memory _rwdTokenData
    ) external initializer {
        require(_farmStartTime >= block.timestamp, "Invalid farm startTime");
        _transferOwnership(msg.sender);
        // Initialize farm global params
        lastFundUpdateTime = _farmStartTime;
        farmStartTime = _farmStartTime;
        isPaused = false;
        isClosed = false;

        // initialize uniswap related data
        uniswapPool = IUniswapV3Factory(UNIV3_FACTORY).getPool(
            _uniswapPoolData.tokenB,
            _uniswapPoolData.tokenA,
            _uniswapPoolData.feeTier
        );
        require(uniswapPool != address(0), "Invalid uniswap pool config");
        _validateTickRange(
            _uniswapPoolData.tickLowerAllowed,
            _uniswapPoolData.tickUpperAllowed
        );
        tickLowerAllowed = _uniswapPoolData.tickLowerAllowed;
        tickUpperAllowed = _uniswapPoolData.tickUpperAllowed;

        // Check for lockup functionality
        // @dev If _cooldownPeriod is 0, then the lockup functionality is disabled for
        // the farm.
        uint8 numFunds = 1;
        if (_cooldownPeriod > 0) {
            require(
                _cooldownPeriod > MIN_COOLDOWN_PERIOD,
                "Cooldown < MinCooldownPeriod"
            );
            cooldownPeriod = _cooldownPeriod;
            numFunds = 2;
        }
        _setupFarm(numFunds, _rwdTokenData);
    }

    /// @notice Function is called when user transfers the NFT to the contract.
    /// @param _from The address of the owner.
    /// @param _tokenId nft Id generated by uniswap v3.
    /// @param _data The data should be the lockup flag (bool).
    function onERC721Received(
        address, // unused variable. not named
        address _from,
        uint256 _tokenId,
        bytes calldata _data
    ) external override notPaused returns (bytes4) {
        require(msg.sender == NFPM, "onERC721Received: not a univ3 nft");

        require(_data.length > 0, "onERC721Received: no data");

        bool lockup = abi.decode(_data, (bool));
        if (cooldownPeriod == 0) {
            require(!lockup, "Lockup functionality is disabled");
        }

        // update the reward funds
        _updateFarmRewardData();

        // Validate the position and get the liquidity
        uint256 liquidity = _getLiquidity(_tokenId);

        // Prepare data to be stored.
        Deposit memory userDeposit = Deposit({
            locked: lockup,
            tokenId: _tokenId,
            startTime: block.timestamp,
            expiryDate: 0,
            totalRewardsClaimed: new uint256[](rewardTokens.length),
            liquidity: liquidity
        });

        // @dev Add the deposit to the user's deposit list
        deposits[_from].push(userDeposit);
        // Add common fund subscription to the user's deposit
        _subscribeRewardFund(COMMON_FUND_ID, _tokenId, liquidity);

        if (lockup) {
            // Add lockup fund subscription to the user's deposit
            _subscribeRewardFund(LOCKUP_FUND_ID, _tokenId, liquidity);
        }

        emit Deposited(_from, lockup, _tokenId, liquidity);
        return this.onERC721Received.selector;
    }

    /// @notice Function to lock a staked deposit
    /// @param _depositId The id of the deposit to be locked
    /// @dev _depositId is corresponding to the user's deposit
    function initiateCooldown(uint256 _depositId)
        external
        notPaused
        nonReentrant
    {
        address account = msg.sender;
        _isValidDeposit(account, _depositId);
        Deposit storage userDeposit = deposits[account][_depositId];

        // validate if the deposit is in locked state
        require(userDeposit.locked, "Can not initiate cooldown");

        // update the deposit expiry time & lock status
        userDeposit.expiryDate = block.timestamp + (cooldownPeriod * 1 days);
        userDeposit.locked = false;

        // claim the pending rewards for the user
        _claimRewards(account, _depositId);

        // Unsubscribe the deposit from the lockup reward fund
        _unsubscribeRewardFund(LOCKUP_FUND_ID, account, _depositId);

        emit CooldownInitiated(
            account,
            userDeposit.tokenId,
            userDeposit.expiryDate
        );
    }

    /// @notice Function to withdraw a deposit from the farm.
    /// @param _depositId The id of the deposit to be withdrawn
    function withdraw(uint256 _depositId) external nonReentrant {
        address account = msg.sender;
        _isValidDeposit(account, _depositId);
        Deposit memory userDeposit = deposits[account][_depositId];

        // Check for the withdrawal criteria
        // Note: If farm is paused, skip the cooldown check
        if (!isPaused) {
            require(!userDeposit.locked, "Please initiate cooldown");
            if (userDeposit.expiryDate > 0) {
                // Cooldown is initiated for the user
                require(
                    userDeposit.expiryDate <= block.timestamp,
                    "Deposit is in cooldown"
                );
            }
        }

        // Compute the user's unclaimed rewards
        _claimRewards(account, _depositId);

        // Store the total rewards earned
        uint256[] memory totalRewards = deposits[account][_depositId]
            .totalRewardsClaimed;

        // unsubscribe the user from the common reward fund
        _unsubscribeRewardFund(COMMON_FUND_ID, account, _depositId);

        if (subscriptions[userDeposit.tokenId].length > 0) {
            // To handle a lockup withdraw without cooldown (during farmPause)
            _unsubscribeRewardFund(LOCKUP_FUND_ID, account, _depositId);
        }

        // Update the user's deposit list
        deposits[account][_depositId] = deposits[account][
            deposits[account].length - 1
        ];
        deposits[account].pop();

        // Transfer the nft back to the user.
        INFPM(NFPM).safeTransferFrom(
            address(this),
            account,
            userDeposit.tokenId
        );

        emit DepositWithdrawn(
            account,
            userDeposit.tokenId,
            userDeposit.startTime,
            userDeposit.liquidity,
            totalRewards
        );
    }

    /// @notice Claim rewards for the user.
    /// @param _account The user's address
    /// @param _depositId The id of the deposit
    /// @dev Anyone can call this function to claim rewards for the user
    function claimRewards(address _account, uint256 _depositId)
        external
        farmNotClosed
        nonReentrant
    {
        _isValidDeposit(_account, _depositId);
        _claimRewards(_account, _depositId);
    }

    /// @notice Claim rewards for the user.
    /// @param _depositId The id of the deposit
    function claimRewards(uint256 _depositId)
        external
        farmNotClosed
        nonReentrant
    {
        address account = msg.sender;
        _isValidDeposit(account, _depositId);
        _claimRewards(account, _depositId);
    }

    /// @notice Add rewards to the farm.
    /// @param _rwdToken the reward token's address.
    /// @param _amount the amount of reward tokens to add.
    function addRewards(address _rwdToken, uint256 _amount)
        external
        nonReentrant
    {
        require(
            rewardData[_rwdToken].tknManager != address(0),
            "Invalid reward token"
        );
        _updateFarmRewardData();
        IERC20(_rwdToken).safeTransferFrom(msg.sender, address(this), _amount);
        emit RewardAdded(_rwdToken, _amount);
    }

    // --------------------- Admin  Functions ---------------------
    /// @notice Update the cooldown period
    /// @param _newCooldownPeriod The new cooldown period (in days)
    function updateCooldownPeriod(uint256 _newCooldownPeriod)
        external
        onlyOwner
    {
        require(cooldownPeriod != 0, "Farm does not support lockup");
        require(
            _newCooldownPeriod > MIN_COOLDOWN_PERIOD,
            "Cooldown period too low"
        );
        emit CooldownPeriodUpdated(cooldownPeriod, _newCooldownPeriod);
        cooldownPeriod = _newCooldownPeriod;
    }

    /// @notice Update the farm start time.
    /// @dev Can be updated only before the farm start
    ///      New start time should be in future.
    /// @param _newStartTime The new farm start time.
    function updateFarmStartTime(uint256 _newStartTime) external onlyOwner {
        require(block.timestamp < farmStartTime, "Farm already started");
        require(_newStartTime >= block.timestamp, "Time < now");
        farmStartTime = _newStartTime;
        lastFundUpdateTime = _newStartTime;

        emit FarmStartTimeUpdated(_newStartTime);
    }

    /// @notice Pause / UnPause the deposit
    function farmPauseSwitch(bool _isPaused) external onlyOwner farmNotClosed {
        require(isPaused != _isPaused, "Farm already in required state");
        _updateFarmRewardData();
        isPaused = _isPaused;
        emit FarmPaused(isPaused);
    }

    /// @notice Recover rewardToken from the farm in case of EMERGENCY
    /// @dev Shuts down the farm completely
    function closeFarm() external onlyOwner nonReentrant {
        _updateFarmRewardData();
        cooldownPeriod = 0;
        isPaused = true;
        isClosed = true;
        for (uint8 iRwd = 0; iRwd < rewardTokens.length; ++iRwd) {
            _recoverRewardFunds(rewardTokens[iRwd], type(uint256).max);
        }
        emit FarmClosed();
    }

    /// @notice Recover erc20 tokens other than the reward Tokens.
    /// @param _token Address of token to be recovered
    function recoverERC20(address _token) external onlyOwner nonReentrant {
        require(
            rewardData[_token].tknManager == address(0),
            "Can't withdraw rewardToken"
        );

        uint256 balance = IERC20(_token).balanceOf(address(this));
        require(balance > 0, "Can't withdraw 0 amount");

        IERC20(_token).safeTransfer(owner(), balance);
        emit RecoveredERC20(_token, balance);
    }

    // --------------------- Token Manager Functions ---------------------
    /// @notice Get the remaining balance out of the  farm
    /// @param _rwdToken The reward token's address
    /// @param _amount The amount of the reward token to be withdrawn
    /// @dev Function recovers minOf(_amount, rewardsLeft)
    function recoverRewardFunds(address _rwdToken, uint256 _amount)
        external
        isTokenManager(_rwdToken)
        nonReentrant
    {
        _updateFarmRewardData();
        _recoverRewardFunds(_rwdToken, _amount);
    }

    /// @notice Function to update reward params for a fund.
    /// @param _rwdToken The reward token's address
    /// @param _newRewardRates The new reward rate for the fund (includes the precision)
    function setRewardRate(address _rwdToken, uint256[] memory _newRewardRates)
        external
        isTokenManager(_rwdToken)
    {
        _updateFarmRewardData();
        _setRewardRate(_rwdToken, _newRewardRates);
    }

    /// @notice Transfer the tokenManagerRole to other user.
    /// @dev Only the existing tokenManager for a reward can call this function.
    /// @param _rwdToken The reward token's address.
    /// @param _newTknManager Address of the new token manager.
    function updateTokenManager(address _rwdToken, address _newTknManager)
        external
        isTokenManager(_rwdToken)
    {
        _isNonZeroAddr(_newTknManager);
        rewardData[_rwdToken].tknManager = _newTknManager;
        emit TokenManagerUpdated(_rwdToken, msg.sender, _newTknManager);
    }

    /// @notice Function to compute the total accrued rewards for a deposit
    /// @param _account The user's address
    /// @param _depositId The id of the deposit
    /// @return rewards The total accrued rewards for the deposit (uint256[])
    function computeRewards(address _account, uint256 _depositId)
        external
        view
        returns (uint256[] memory rewards)
    {
        _isValidDeposit(_account, _depositId);
        Deposit memory userDeposit = deposits[_account][_depositId];
        Subscription[] memory depositSubs = subscriptions[userDeposit.tokenId];
        RewardFund[] memory funds = rewardFunds;
        uint256 numRewards = rewardTokens.length;
        rewards = new uint256[](numRewards);

        uint256 time = 0;
        // In case the reward is not updated
        if (block.timestamp > lastFundUpdateTime) {
            time = block.timestamp - lastFundUpdateTime;
        }

        // Update the two reward funds.
        for (uint8 iSub = 0; iSub < depositSubs.length; ++iSub) {
            uint8 fundId = depositSubs[iSub].fundId;
            for (uint8 iRwd = 0; iRwd < numRewards; ++iRwd) {
                if (funds[fundId].totalLiquidity > 0) {
                    uint256 accRewards = _getAccRewards(iRwd, fundId, time);
                    // update the accRewardPerShare for delta time.
                    funds[fundId].accRewardPerShare[iRwd] +=
                        (accRewards * PREC) /
                        funds[fundId].totalLiquidity;
                }
                rewards[iRwd] +=
                    ((userDeposit.liquidity *
                        funds[fundId].accRewardPerShare[iRwd]) / PREC) -
                    depositSubs[iSub].rewardDebt[iRwd];
            }
        }
        return rewards;
    }

    /// @notice get number of deposits for an account
    /// @param _account The user's address
    function getNumDeposits(address _account) external view returns (uint256) {
        return deposits[_account].length;
    }

    /// @notice get deposit info for an account
    /// @notice _account The user's address
    /// @notice _depositId The id of the deposit
    function getDeposit(address _account, uint256 _depositId)
        external
        view
        returns (Deposit memory)
    {
        return deposits[_account][_depositId];
    }

    /// @notice get number of deposits for an account
    /// @param _tokenId The token's id
    function getNumSubscriptions(uint256 _tokenId)
        external
        view
        returns (uint256)
    {
        return subscriptions[_tokenId].length;
    }

    /// @notice get subscription stats for a deposit.
    /// @param _tokenId The token's id
    /// @param _subscriptionId The subscription's id
    function getSubscriptionInfo(uint256 _tokenId, uint256 _subscriptionId)
        external
        view
        returns (Subscription memory)
    {
        require(
            _subscriptionId < subscriptions[_tokenId].length,
            "Subscription does not exist"
        );
        return subscriptions[_tokenId][_subscriptionId];
    }

    /// @notice get reward rates for a rewardToken.
    /// @param _rwdToken The reward token's address
    /// @return The reward rates for the reward token (uint256[])
    function getRewardRates(address _rwdToken)
        external
        view
        returns (uint256[] memory)
    {
        uint256 numFunds = rewardFunds.length;
        uint256[] memory rates = new uint256[](numFunds);
        uint8 id = rewardData[_rwdToken].id;
        for (uint8 iFund = 0; iFund < numFunds; ++iFund) {
            rates[iFund] = rewardFunds[iFund].rewardsPerSec[id];
        }
        return rates;
    }

    /// @notice Get list of reward tokens.
    /// @return The list of reward tokens.
    function getRewardTokens() external view returns (address[] memory) {
        return rewardTokens;
    }

    /// @notice get farm reward fund info.
    /// @param _fundId The fund's id
    function getRewardFundInfo(uint8 _fundId)
        external
        view
        returns (RewardFund memory)
    {
        require(_fundId < rewardFunds.length, "Reward fund does not exist");
        return rewardFunds[_fundId];
    }

    /// @notice Get the remaining reward balance for the farm.
    /// @param _rwdToken The reward token's address
    function getRewardBalance(address _rwdToken) public view returns (uint256) {
        uint256 rwdId = rewardData[_rwdToken].id;
        require(rewardTokens[rwdId] == _rwdToken, "Invalid _rwdToken");

        uint256 numFunds = rewardFunds.length;
        uint256 rewardsAcc = rewardData[_rwdToken].accRewardBal;
        uint256 supply = IERC20(_rwdToken).balanceOf(address(this));
        if (block.timestamp > lastFundUpdateTime) {
            uint256 time = block.timestamp - lastFundUpdateTime;
            // Compute the accrued reward balance for time
            for (uint8 iFund = 0; iFund < numFunds; ++iFund) {
                if (rewardFunds[iFund].totalLiquidity > 0) {
                    rewardsAcc +=
                        rewardFunds[iFund].rewardsPerSec[rwdId] *
                        time;
                }
            }
        }
        if (rewardsAcc >= supply) {
            return 0;
        }
        return (supply - rewardsAcc);
    }

    /// @notice Claim rewards for the user.
    /// @param _account The user's address
    /// @param _depositId The id of the deposit
    /// @dev NOTE: any function calling this private
    ///     function should be marked as non-reentrant
    function _claimRewards(address _account, uint256 _depositId) private {
        _updateFarmRewardData();

        Deposit storage userDeposit = deposits[_account][_depositId];
        Subscription[] storage depositSubs = subscriptions[userDeposit.tokenId];

        uint256 numRewards = rewardTokens.length;
        uint256 numSubs = depositSubs.length;
        uint256[] memory totalRewards = new uint256[](numRewards);
        // Compute the rewards for each subscription.
        for (uint8 iSub = 0; iSub < numSubs; ++iSub) {
            uint8 fundId = depositSubs[iSub].fundId;
            uint256[] memory rewards = new uint256[](numRewards);
            for (uint256 iRwd = 0; iRwd < numRewards; ++iRwd) {
                // rewards = (liquidity * accRewardPerShare) / PREC - rewardDebt
                uint256 accRewards = (userDeposit.liquidity *
                    rewardFunds[fundId].accRewardPerShare[iRwd]) / PREC;
                rewards[iRwd] = accRewards - depositSubs[iSub].rewardDebt[iRwd];
                depositSubs[iSub].rewardClaimed[iRwd] += rewards[iRwd];
                totalRewards[iRwd] += rewards[iRwd];

                // Update userRewardDebt for the subscriptions
                // rewardDebt = liquidity * accRewardPerShare
                depositSubs[iSub].rewardDebt[iRwd] = accRewards;
            }

            emit RewardsClaimed(
                _account,
                fundId,
                userDeposit.tokenId,
                userDeposit.liquidity,
                rewardFunds[fundId].totalLiquidity,
                rewards
            );
        }

        // Transfer the claimed rewards to the User if any.
        for (uint8 iRwd = 0; iRwd < numRewards; ++iRwd) {
            if (totalRewards[iRwd] > 0) {
                rewardData[rewardTokens[iRwd]].accRewardBal -= totalRewards[
                    iRwd
                ];
                // Update the total rewards earned for the deposit
                userDeposit.totalRewardsClaimed[iRwd] += totalRewards[iRwd];
                IERC20(rewardTokens[iRwd]).safeTransfer(
                    _account,
                    totalRewards[iRwd]
                );
            }
        }
    }

    /// @notice Get the remaining balance out of the  farm
    /// @param _rwdToken The reward token's address
    /// @param _amount The amount of the reward token to be withdrawn
    /// @dev Function recovers minOf(_amount, rewardsLeft)
    /// @dev In case of partial withdraw of funds, the reward rate has to be set manually again.
    function _recoverRewardFunds(address _rwdToken, uint256 _amount) private {
        address emergencyRet = rewardData[_rwdToken].tknManager;
        uint256 rewardsLeft = getRewardBalance(_rwdToken);
        uint256 amountToRecover = _amount;
        if (_amount >= rewardsLeft) {
            amountToRecover = rewardsLeft;
        }
        if (amountToRecover > 0) {
            IERC20(_rwdToken).safeTransfer(emergencyRet, amountToRecover);
            emit FundsRecovered(emergencyRet, _rwdToken, amountToRecover);
        }
    }

    /// @notice Function to update reward params for a fund.
    /// @param _rwdToken The reward token's address
    /// @param _newRewardRates The new reward rate for the fund (includes the precision)
    function _setRewardRate(address _rwdToken, uint256[] memory _newRewardRates)
        private
    {
        uint8 id = rewardData[_rwdToken].id;
        uint256 numFunds = rewardFunds.length;
        require(
            _newRewardRates.length == numFunds,
            "Invalid reward rates length"
        );
        uint256[] memory oldRewardRates = new uint256[](numFunds);
        // Update the reward rate
        for (uint8 iFund = 0; iFund < numFunds; ++iFund) {
            oldRewardRates[iFund] = rewardFunds[iFund].rewardsPerSec[id];
            rewardFunds[iFund].rewardsPerSec[id] = _newRewardRates[iFund];
        }
        emit RewardRateUpdated(_rwdToken, oldRewardRates, _newRewardRates);
    }

    /// @notice Add subscription to the reward fund for a deposit
    /// @param _tokenId The tokenId of the deposit
    /// @param _fundId The reward fund id
    /// @param _liquidity The liquidity of the deposit
    function _subscribeRewardFund(
        uint8 _fundId,
        uint256 _tokenId,
        uint256 _liquidity
    ) private {
        require(_fundId < rewardFunds.length, "Invalid fund id");
        // Subscribe to the reward fund
        uint256 numRewards = rewardTokens.length;
        subscriptions[_tokenId].push(
            Subscription({
                fundId: _fundId,
                rewardDebt: new uint256[](numRewards),
                rewardClaimed: new uint256[](numRewards)
            })
        );
        uint256 subId = subscriptions[_tokenId].length - 1;

        // initialize user's reward debt
        for (uint8 iRwd = 0; iRwd < numRewards; ++iRwd) {
            subscriptions[_tokenId][subId].rewardDebt[iRwd] =
                (_liquidity * rewardFunds[_fundId].accRewardPerShare[iRwd]) /
                PREC;
        }
        // Update the totalLiquidity for the fund
        rewardFunds[_fundId].totalLiquidity += _liquidity;
    }

    /// @notice Unsubscribe a reward fund from a deposit
    /// @param _fundId The reward fund id
    /// @param _account The user's address
    /// @param _depositId The deposit id corresponding to the user
    /// @dev The rewards claimed from the reward fund is persisted in the event
    function _unsubscribeRewardFund(
        uint8 _fundId,
        address _account,
        uint256 _depositId
    ) private {
        require(_fundId < rewardFunds.length, "Invalid fund id");
        Deposit memory userDeposit = deposits[_account][_depositId];
        uint256 numRewards = rewardTokens.length;

        // Unsubscribe from the reward fund
        Subscription[] storage depositSubs = subscriptions[userDeposit.tokenId];
        uint256 numSubs = depositSubs.length;
        for (uint256 iSub = 0; iSub < numSubs; ++iSub) {
            if (depositSubs[iSub].fundId == _fundId) {
                // Persist the reward information
                uint256[] memory rewardClaimed = new uint256[](numRewards);

                for (uint8 iRwd = 0; iRwd < numRewards; ++iRwd) {
                    rewardClaimed[iRwd] = depositSubs[iSub].rewardClaimed[iRwd];
                }

                // Delete the subscription from the list
                depositSubs[iSub] = depositSubs[numSubs - 1];
                depositSubs.pop();

                // Remove the liquidity from the reward fund
                rewardFunds[_fundId].totalLiquidity -= userDeposit.liquidity;

                emit PoolUnsubscribed(
                    _account,
                    _fundId,
                    userDeposit.tokenId,
                    userDeposit.startTime,
                    rewardClaimed
                );

                break;
            }
        }
    }

    /// @notice Function to update the FarmRewardData for all funds
    function _updateFarmRewardData() private {
        if (block.timestamp > lastFundUpdateTime) {
            // if farm is paused don't accrue any rewards.
            // only update the lastFundUpdateTime.
            if (!isPaused) {
                uint256 time = block.timestamp - lastFundUpdateTime;
                uint256 numRewards = rewardTokens.length;
                // Update the reward funds.
                for (uint8 iFund = 0; iFund < rewardFunds.length; ++iFund) {
                    RewardFund memory fund = rewardFunds[iFund];
                    if (fund.totalLiquidity > 0) {
                        for (uint8 iRwd = 0; iRwd < numRewards; ++iRwd) {
                            // Get the accrued rewards for the time.
                            uint256 accRewards = _getAccRewards(
                                iRwd,
                                iFund,
                                time
                            );
                            rewardData[rewardTokens[iRwd]]
                                .accRewardBal += accRewards;
                            fund.accRewardPerShare[iRwd] +=
                                (accRewards * PREC) /
                                fund.totalLiquidity;
                        }
                    }
                    rewardFunds[iFund] = fund;
                }
            }
            lastFundUpdateTime = block.timestamp;
        }
    }

    /// @notice Function to setup the reward funds during construction.
    /// @param _numFunds - Number of reward funds to setup.
    /// @param _rwdTokenData - Reward data for each reward token.
    function _setupFarm(uint8 _numFunds, RewardTokenData[] memory _rwdTokenData)
        private
    {
        // Setup reward related information.
        uint256 numRewards = _rwdTokenData.length;
        require(numRewards <= MAX_NUM_REWARDS - 1, "Invalid reward data");

        // Initialize fund storage
        for (uint8 i = 0; i < _numFunds; ++i) {
            RewardFund memory _rewardFund = RewardFund({
                totalLiquidity: 0,
                rewardsPerSec: new uint256[](numRewards + 1),
                accRewardPerShare: new uint256[](numRewards + 1)
            });
            rewardFunds.push(_rewardFund);
        }

        // Add SPA as default reward token in the farm
        _addRewardData(SPA, SPA_TOKEN_MANAGER);

        // Initialize reward Data
        for (uint8 iRwd = 0; iRwd < numRewards; ++iRwd) {
            _addRewardData(
                _rwdTokenData[iRwd].token,
                _rwdTokenData[iRwd].tknManager
            );
        }
    }

    /// @notice Adds new reward token to the farm
    /// @param _token Address of the reward token to be added.
    /// @param _tknManager Address of the reward token Manager.
    function _addRewardData(address _token, address _tknManager) private {
        // Validate if addresses are correct
        _isNonZeroAddr(_token);
        _isNonZeroAddr(_tknManager);

        require(
            rewardData[_token].tknManager == address(0),
            "Reward token already added"
        );

        rewardData[_token] = RewardData({
            id: uint8(rewardTokens.length),
            tknManager: _tknManager,
            accRewardBal: 0
        });

        // Add reward token in the list
        rewardTokens.push(_token);

        emit RewardTokenAdded(_token, _tknManager);
    }

    /// @notice Computes the accrued reward for a given fund id and time interval.
    /// @param _rwdId Id of the reward token.
    /// @param _fundId Id of the reward fund.
    /// @param _time Time interval for the reward computation.
    function _getAccRewards(
        uint8 _rwdId,
        uint8 _fundId,
        uint256 _time
    ) private view returns (uint256) {
        RewardFund memory fund = rewardFunds[_fundId];
        if (fund.rewardsPerSec[_rwdId] == 0) {
            return 0;
        }
        address rwdToken = rewardTokens[_rwdId];
        uint256 rwdSupply = IERC20(rwdToken).balanceOf(address(this));
        uint256 rwdAccrued = rewardData[rwdToken].accRewardBal;

        uint256 rwdBal = 0;
        // Calculate the available reward funds in the farm.
        if (rwdSupply > rwdAccrued) {
            rwdBal = rwdSupply - rwdAccrued;
        }
        // Calculate the rewards accrued in time.
        uint256 accRewards = fund.rewardsPerSec[_rwdId] * _time;
        // Cap the reward with the available balance.
        if (accRewards > rwdBal) {
            accRewards = rwdBal;
        }
        return accRewards;
    }

    /// @notice Validate the position for the pool and get Liquidity
    /// @param _tokenId The tokenId of the position
    /// @dev the position must adhere to the price ranges
    /// @dev Only allow specific pool token to be staked.
    function _getLiquidity(uint256 _tokenId) private view returns (uint256) {
        /// @dev Get the info of the required token
        (
            ,
            ,
            address token0,
            address token1,
            uint24 fee,
            int24 tickLower,
            int24 tickUpper,
            uint128 liquidity,
            ,
            ,
            ,

        ) = INFPM(NFPM).positions(_tokenId);

        /// @dev Check if the token belongs to correct pool
        require(
            uniswapPool ==
                IUniswapV3Factory(UNIV3_FACTORY).getPool(token0, token1, fee),
            "Incorrect pool token"
        );

        /// @dev Check if the token adheres to the tick range
        require(
            tickLower == tickLowerAllowed && tickUpper == tickUpperAllowed,
            "Incorrect tick range"
        );

        return uint256(liquidity);
    }

    function _validateTickRange(int24 _tickLower, int24 _tickUpper)
        private
        view
    {
        int24 spacing = IUniswapV3TickSpacing(uniswapPool).tickSpacing();
        require(
            _tickLower < _tickUpper &&
                _tickLower >= -887272 &&
                _tickLower % spacing == 0 &&
                _tickUpper <= 887272 &&
                _tickUpper % spacing == 0,
            "Invalid tick range"
        );
    }

    /// @notice Validate the deposit for account
    function _isValidDeposit(address _account, uint256 _depositId)
        private
        view
    {
        require(
            _depositId < deposits[_account].length,
            "Deposit does not exist"
        );
    }

    /// @notice Validate address
    function _isNonZeroAddr(address _addr) private pure {
        require(_addr != address(0), "Invalid address");
    }
}

// File: contracts/BaseFarmDeployer.sol

pragma solidity 0.8.10;



abstract contract BaseFarmDeployer is Ownable {
    address public constant SPA = 0x5575552988A3A80504bBaeB1311674fCFd40aD4B;
    address public constant USDs = 0xD74f5255D557944cf7Dd0E45FF521520002D5748;
    address public factory;
    // Stores the address of farmImplementation.
    address public farmImplementation;

    event FarmCreated(address farm, address creator, address indexed admin);
    event FeeCollected(
        address indexed creator,
        address token,
        uint256 amount,
        bool indexed claimable
    );

    /// @notice A function to calculate fees based on the tokens
    /// @param tokenA One token of the pool
    /// @param tokenB Other token of the pool
    /// @dev return feeReceiver, feeToken, feeAmount, bool claimable
    function calculateFees(address tokenA, address tokenB)
        external
        view
        virtual
        returns (
            address feeReceiver,
            address feeToken,
            uint256 feeAmount,
            bool claimable
        );

    /// @notice A function to collect fees from the creator of the farm
    /// @param tokenA One token of the pool
    /// @param tokenB Other token of the pool
    /// @dev Transfer fees from msg.sender to feeReceiver from FarmFactory in this function
    function _collectFee(address tokenA, address tokenB) internal virtual;

    /// @notice Validate address
    function _isNonZeroAddr(address _addr) internal pure {
        require(_addr != address(0), "Invalid address");
    }
}

// File: contracts/interfaces/IFarmFactory.sol

pragma solidity 0.8.10;

interface IFarmFactory {
    function registerFarm(address farm, address creator) external;

    function getFeeParams()
        external
        view
        returns (
            address feeFeceiver,
            address feeToken,
            uint256 feeAmount
        );
}

// File: contracts/UniswapFarmV1Deployer.sol

pragma solidity 0.8.10;






contract UniswapFarmV1Deployer is BaseFarmDeployer, ReentrancyGuard {
    using SafeERC20 for IERC20;
    // farmAdmin - Address to which ownership of farm is transferred to post deployment
    // farmStartTime - Time after which the rewards start accruing for the deposits in the farm.
    // cooldownPeriod -  cooldown period for locked deposits (in days)
    //                   make cooldownPeriod = 0 for disabling lockup functionality of the farm.
    // uniswapPoolData - Init data for UniswapV3 pool.
    //                  (tokenA, tokenB, feeTier, tickLower, tickUpper)
    // rewardTokenData - [(rewardTokenAddress, tknManagerAddress), ... ]
    struct FarmData {
        address farmAdmin;
        uint256 farmStartTime;
        uint256 cooldownPeriod;
        UniswapPoolData uniswapPoolData;
        RewardTokenData[] rewardData;
    }

    string public constant DEPLOYER_NAME = "UniswapV3FarmDeployer";
    uint256 public discountedFee;
    // List of deployers for which fee won't be charged.
    mapping(address => bool) public isPrivilegedDeployer;

    event PrivilegeUpdated(address deployer, bool privilege);
    event DiscountedFeeUpdated(
        uint256 oldDiscountedFee,
        uint256 newDiscountedFee
    );

    constructor(address _factory) {
        _isNonZeroAddr(_factory);
        factory = _factory;
        discountedFee = 100e18; // 100 USDs
        farmImplementation = address(new UniswapFarmV1());
    }

    /// @notice Deploys a new UniswapV3 farm.
    /// @param _data data for deployment.
    function createFarm(FarmData memory _data)
        external
        nonReentrant
        returns (address)
    {
        _isNonZeroAddr(_data.farmAdmin);
        UniswapFarmV1 farmInstance = UniswapFarmV1(
            Clones.clone(farmImplementation)
        );
        farmInstance.initialize(
            _data.farmStartTime,
            _data.cooldownPeriod,
            _data.uniswapPoolData,
            _data.rewardData
        );
        farmInstance.transferOwnership(_data.farmAdmin);
        address farm = address(farmInstance);
        // Calculate and collect fee if required
        _collectFee(_data.uniswapPoolData.tokenA, _data.uniswapPoolData.tokenB);
        IFarmFactory(factory).registerFarm(farm, msg.sender);
        emit FarmCreated(farm, msg.sender, _data.farmAdmin);
        return farm;
    }

    /// @notice A function to add/ remove privileged deployer
    /// @param _deployer Deployer(address) to add to privileged deployers list
    /// @param _privilege Privilege(bool) whether true or false
    /// @dev to be only called by owner
    function updatePrivilege(address _deployer, bool _privilege)
        external
        onlyOwner
    {
        require(
            isPrivilegedDeployer[_deployer] != _privilege,
            "Privilege is same as desired"
        );
        isPrivilegedDeployer[_deployer] = _privilege;
        emit PrivilegeUpdated(_deployer, _privilege);
    }

    /// @notice An external function to update discountOnSpaUSDsFarms
    /// @param _discountedFee New desired discount on Spa/ USDs farms
    /// @dev _discountedFee cannot be more than 100
    function updateDiscountedFee(uint256 _discountedFee) external onlyOwner {
        emit DiscountedFeeUpdated(discountedFee, _discountedFee);
        discountedFee = _discountedFee;
    }

    /// @notice A public view function to calculate fees
    /// @param _tokenA address of token A
    /// @param _tokenB address of token B
    /// @notice Order does not matter
    /// @return Fees to be paid in feeToken set in FarmFactory (mostly USDs)
    function calculateFees(address _tokenA, address _tokenB)
        external
        view
        override
        returns (
            address,
            address,
            uint256,
            bool
        )
    {
        _isNonZeroAddr(_tokenA);
        _isNonZeroAddr(_tokenB);
        require(_tokenA != _tokenB, "Invalid token pair");
        return _calculateFees(_tokenA, _tokenB);
    }

    /// @notice Collect fee and transfer it to feeReceiver.
    /// @dev Function fetches all the fee params from farmFactory.
    function _collectFee(address _tokenA, address _tokenB) internal override {
        (
            address feeReceiver,
            address feeToken,
            uint256 feeAmount,
            bool claimable
        ) = _calculateFees(_tokenA, _tokenB);
        if (feeAmount > 0) {
            IERC20(feeToken).safeTransferFrom(
                msg.sender,
                feeReceiver,
                feeAmount
            );
            emit FeeCollected(msg.sender, feeToken, feeAmount, claimable);
        }
    }

    /// @notice An internal function to calculate fees
    /// @notice and return feeReceiver, feeToken, feeAmount and claimable
    function _calculateFees(address _tokenA, address _tokenB)
        internal
        view
        returns (
            address,
            address,
            uint256,
            bool
        )
    {
        (
            address feeReceiver,
            address feeToken,
            uint256 feeAmount
        ) = IFarmFactory(factory).getFeeParams();
        if (isPrivilegedDeployer[msg.sender]) {
            // No fees for privileged deployers
            feeAmount = 0;
            return (feeReceiver, feeToken, feeAmount, false);
        }
        if (!_validateToken(_tokenA) && !_validateToken(_tokenB)) {
            // No discount because neither of the token is SPA or USDs
            return (feeReceiver, feeToken, feeAmount, false);
        } else {
            // DiscountedFee if either of the token is SPA or USDs
            // This fees is claimable
            return (feeReceiver, feeToken, discountedFee, true);
        }
    }

    /// @notice Validate if a token is either SPA | USDs.
    /// @param _token Address of the desired token.
    function _validateToken(address _token) private pure returns (bool) {
        return _token == SPA || _token == USDs;
    }
}

Contract ABI

[{"inputs":[{"internalType":"address","name":"_factory","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"oldDiscountedFee","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"newDiscountedFee","type":"uint256"}],"name":"DiscountedFeeUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"farm","type":"address"},{"indexed":false,"internalType":"address","name":"creator","type":"address"},{"indexed":true,"internalType":"address","name":"admin","type":"address"}],"name":"FarmCreated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"creator","type":"address"},{"indexed":false,"internalType":"address","name":"token","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":true,"internalType":"bool","name":"claimable","type":"bool"}],"name":"FeeCollected","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"deployer","type":"address"},{"indexed":false,"internalType":"bool","name":"privilege","type":"bool"}],"name":"PrivilegeUpdated","type":"event"},{"inputs":[],"name":"DEPLOYER_NAME","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"SPA","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"USDs","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_tokenA","type":"address"},{"internalType":"address","name":"_tokenB","type":"address"}],"name":"calculateFees","outputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"components":[{"internalType":"address","name":"farmAdmin","type":"address"},{"internalType":"uint256","name":"farmStartTime","type":"uint256"},{"internalType":"uint256","name":"cooldownPeriod","type":"uint256"},{"components":[{"internalType":"address","name":"tokenA","type":"address"},{"internalType":"address","name":"tokenB","type":"address"},{"internalType":"uint24","name":"feeTier","type":"uint24"},{"internalType":"int24","name":"tickLowerAllowed","type":"int24"},{"internalType":"int24","name":"tickUpperAllowed","type":"int24"}],"internalType":"struct UniswapPoolData","name":"uniswapPoolData","type":"tuple"},{"components":[{"internalType":"address","name":"token","type":"address"},{"internalType":"address","name":"tknManager","type":"address"}],"internalType":"struct RewardTokenData[]","name":"rewardData","type":"tuple[]"}],"internalType":"struct UniswapFarmV1Deployer.FarmData","name":"_data","type":"tuple"}],"name":"createFarm","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"discountedFee","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"factory","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"farmImplementation","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"isPrivilegedDeployer","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_discountedFee","type":"uint256"}],"name":"updateDiscountedFee","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_deployer","type":"address"},{"internalType":"bool","name":"_privilege","type":"bool"}],"name":"updatePrivilege","outputs":[],"stateMutability":"nonpayable","type":"function"}]

60806040523480156200001157600080fd5b506040516200691538038062006915833981016040819052620000349162000178565b6200003f33620000ca565b60016003556200004f816200011a565b600180546001600160a01b0319166001600160a01b03831617905568056bc75e2d6310000060045560405162000085906200016a565b604051809103906000f080158015620000a2573d6000803e3d6000fd5b50600280546001600160a01b0319166001600160a01b039290921691909117905550620001aa565b600080546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b6001600160a01b038116620001675760405162461bcd60e51b815260206004820152600f60248201526e496e76616c6964206164647265737360881b604482015260640160405180910390fd5b50565b61555680620013bf83390190565b6000602082840312156200018b57600080fd5b81516001600160a01b0381168114620001a357600080fd5b9392505050565b61120580620001ba6000396000f3fe608060405234801561001057600080fd5b50600436106100ea5760003560e01c806379a416501161008c5780639a49a505116100665780639a49a50514610219578063c3f9cfdf14610259578063c45a015514610274578063f2fde38b1461028757600080fd5b806379a41650146101de5780638da5cb5b146101f157806392d055ee1461020257600080fd5b806350967558116100c85780635096755814610147578063538daac41461017a5780636d93de7d14610195578063715018a6146101d657600080fd5b8063125c67dd146100ef5780631ba236ef1461011f5780632c30cd9114610134575b600080fd5b600254610102906001600160a01b031681565b6040516001600160a01b0390911681526020015b60405180910390f35b61013261012d366004610d34565b61029a565b005b610132610142366004610d6d565b610379565b61016a610155366004610d86565b60056020526000908152604090205460ff1681565b6040519015158152602001610116565b610102735575552988a3a80504bbaeb1311674fcfd40ad4b81565b6101c9604051806040016040528060158152602001742ab734b9bbb0b82b19a330b936a232b83637bcb2b960591b81525081565b6040516101169190610dcf565b6101326103c2565b6101026101ec366004610f5d565b6103d6565b6000546001600160a01b0316610102565b61020b60045481565b604051908152602001610116565b61022c610227366004611078565b6105f5565b604080516001600160a01b0395861681529490931660208501529183015215156060820152608001610116565b61010273d74f5255d557944cf7dd0e45ff521520002d574881565b600154610102906001600160a01b031681565b610132610295366004610d86565b610680565b6102a26106f9565b6001600160a01b03821660009081526005602052604090205460ff16151581151514156103165760405162461bcd60e51b815260206004820152601c60248201527f50726976696c6567652069732073616d6520617320646573697265640000000060448201526064015b60405180910390fd5b6001600160a01b038216600081815260056020908152604091829020805460ff19168515159081179091558251938452908301527fa366e4d9fb2dc9821d016721b96bd4f57108f71ed3d167ec76ee851c3bdb4b71910160405180910390a15050565b6103816106f9565b60045460408051918252602082018390527f23ab621b3fc290514891dcbd1fc12299d286b975a7b54345fda5f828e6590ee2910160405180910390a1600455565b6103ca6106f9565b6103d46000610753565b565b60006002600354141561042b5760405162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c00604482015260640161030d565b6002600355815161043b906107a3565b600254600090610453906001600160a01b03166107eb565b9050806001600160a01b031663688cdfe984602001518560400151866060015187608001516040518563ffffffff1660e01b815260040161049794939291906110a6565b600060405180830381600087803b1580156104b157600080fd5b505af11580156104c5573d6000803e3d6000fd5b5050845160405163f2fde38b60e01b81526001600160a01b039182166004820152908416925063f2fde38b9150602401600060405180830381600087803b15801561050f57600080fd5b505af1158015610523573d6000803e3d6000fd5b5050505060608301518051602090910151829161053f91610888565b6001546040516268af8960e41b81526001600160a01b0383811660048301523360248301529091169063068af89090604401600060405180830381600087803b15801561058b57600080fd5b505af115801561059f573d6000803e3d6000fd5b50508551604080516001600160a01b0386811682523360208301528251931694507f6e766a2e8ac6e7ecbc0956ba73fdf9e546312daf00a15a6817b8ca66159bd7ab93508290030190a260016003559392505050565b600080600080610604866107a3565b61060d856107a3565b846001600160a01b0316866001600160a01b031614156106645760405162461bcd60e51b815260206004820152601260248201527124b73b30b634b2103a37b5b2b7103830b4b960711b604482015260640161030d565b61066e868661090f565b93509350935093505b92959194509250565b6106886106f9565b6001600160a01b0381166106ed5760405162461bcd60e51b815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201526564647265737360d01b606482015260840161030d565b6106f681610753565b50565b6000546001600160a01b031633146103d45760405162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604482015260640161030d565b600080546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b6001600160a01b0381166106f65760405162461bcd60e51b815260206004820152600f60248201526e496e76616c6964206164647265737360881b604482015260640161030d565b6000604051733d602d80600a3d3981f3363d3d373d3d3d363d7360601b81528260601b60148201526e5af43d82803e903d91602b57fd5bf360881b60288201526037816000f09150506001600160a01b0381166108835760405162461bcd60e51b8152602060048201526016602482015275115490cc4c4d8dce8818dc99585d194819985a5b195960521b604482015260640161030d565b919050565b600080600080610898868661090f565b93509350935093506000821115610907576108be6001600160a01b038416338685610a08565b604080516001600160a01b0385168152602081018490528215159133917f59d6713734b6d7abb3d28e0ecdab7c005f4438de560ac404ff25d175a822990b910160405180910390a35b505050505050565b6000806000806000806000600160009054906101000a90046001600160a01b03166001600160a01b031663be6fc1816040518163ffffffff1660e01b8152600401606060405180830381865afa15801561096d573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906109919190611153565b33600090815260056020526040902054929550909350915060ff16156109c35750909450925060009150819050610677565b6109cc89610a68565b1580156109df57506109dd88610a68565b155b156109f4579195509350915060009050610677565b506004549195509350915060019050610677565b604080516001600160a01b0385811660248301528416604482015260648082018490528251808303909101815260849091019091526020810180516001600160e01b03166323b872dd60e01b179052610a62908590610ab7565b50505050565b60006001600160a01b038216735575552988a3a80504bbaeb1311674fcfd40ad4b1480610ab157506001600160a01b03821673d74f5255d557944cf7dd0e45ff521520002d5748145b92915050565b6000610b0c826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b0316610b8e9092919063ffffffff16565b805190915015610b895780806020019051810190610b2a9190611196565b610b895760405162461bcd60e51b815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e6044820152691bdd081cdd58d8d9595960b21b606482015260840161030d565b505050565b6060610b9d8484600085610ba7565b90505b9392505050565b606082471015610c085760405162461bcd60e51b815260206004820152602660248201527f416464726573733a20696e73756666696369656e742062616c616e636520666f6044820152651c8818d85b1b60d21b606482015260840161030d565b6001600160a01b0385163b610c5f5760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e7472616374000000604482015260640161030d565b600080866001600160a01b03168587604051610c7b91906111b3565b60006040518083038185875af1925050503d8060008114610cb8576040519150601f19603f3d011682016040523d82523d6000602084013e610cbd565b606091505b5091509150610ccd828286610cd8565b979650505050505050565b60608315610ce7575081610ba0565b825115610cf75782518084602001fd5b8160405162461bcd60e51b815260040161030d9190610dcf565b6001600160a01b03811681146106f657600080fd5b80151581146106f657600080fd5b60008060408385031215610d4757600080fd5b8235610d5281610d11565b91506020830135610d6281610d26565b809150509250929050565b600060208284031215610d7f57600080fd5b5035919050565b600060208284031215610d9857600080fd5b8135610ba081610d11565b60005b83811015610dbe578181015183820152602001610da6565b83811115610a625750506000910152565b6020815260008251806020840152610dee816040850160208701610da3565b601f01601f19169190910160400192915050565b634e487b7160e01b600052604160045260246000fd5b6040805190810167ffffffffffffffff81118282101715610e3b57610e3b610e02565b60405290565b60405160a0810167ffffffffffffffff81118282101715610e3b57610e3b610e02565b604051601f8201601f1916810167ffffffffffffffff81118282101715610e8d57610e8d610e02565b604052919050565b8035600281900b811461088357600080fd5b600082601f830112610eb857600080fd5b8135602067ffffffffffffffff821115610ed457610ed4610e02565b610ee2818360051b01610e64565b82815260069290921b84018101918181019086841115610f0157600080fd5b8286015b84811015610f525760408189031215610f1e5760008081fd5b610f26610e18565b8135610f3181610d11565b815281850135610f4081610d11565b81860152835291830191604001610f05565b509695505050505050565b600060208284031215610f6f57600080fd5b813567ffffffffffffffff80821115610f8757600080fd5b90830190818503610120811215610f9d57600080fd5b610fa5610e41565b8335610fb081610d11565b8152602084810135908201526040808501359082015260a0605f1983011215610fd857600080fd5b610fe0610e41565b91506060840135610ff081610d11565b8252608084013561100081610d11565b602083015260a084013562ffffff8116811461101b57600080fd5b604083015261102c60c08501610e95565b606083015261103d60e08501610e95565b608083015281606082015261010084013591508282111561105d57600080fd5b61106987838601610ea7565b60808201529695505050505050565b6000806040838503121561108b57600080fd5b823561109681610d11565b91506020830135610d6281610d11565b600061010080830187845260208781860152604060018060a01b03808951168288015280838a015116606088015262ffffff828a0151166080880152606089015160020b60a0880152608089015160020b60c08801528460e0880152839450875180855261012088019550838901945060005b818110156111425785518051841688528501518316858801529583019594840194600101611119565b50949b9a5050505050505050505050565b60008060006060848603121561116857600080fd5b835161117381610d11565b602085015190935061118481610d11565b80925050604084015190509250925092565b6000602082840312156111a857600080fd5b8151610ba081610d26565b600082516111c5818460208701610da3565b919091019291505056fea2646970667358221220dd05e5636d26105955ccc8ac61577003ed6d7b8b3f094eb8ec5107717aa72b4d64736f6c634300080a003360806040523480156200001157600080fd5b506200001d3362000031565b600180556200002b62000081565b62000143565b600080546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b600254610100900460ff1615620000ee5760405162461bcd60e51b815260206004820152602760248201527f496e697469616c697a61626c653a20636f6e747261637420697320696e697469604482015266616c697a696e6760c81b606482015260840160405180910390fd5b60025460ff908116101562000141576002805460ff191660ff9081179091556040519081527f7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb38474024989060200160405180910390a15b565b61540380620001536000396000f3fe608060405234801561001057600080fd5b50600436106102bb5760003560e01c806388e69d9911610182578063c2b6b58c116100e9578063df064dd5116100a2578063f1ffc1781161007c578063f1ffc1781461073a578063f2fde38b1461074f578063f56e895d14610762578063fca267901461076a57600080fd5b8063df064dd514610721578063ec05d7c01461072a578063f019f6021461073257600080fd5b8063c2b6b58c1461066c578063c4f59f9b14610680578063d5a849e914610695578063d6d68177146106a8578063d6e91e91146106e5578063d8e573741461070e57600080fd5b80639e8c708e1161013b5780639e8c708e146105e1578063a74a8699146105f4578063a9fc507b14610614578063b17c29ad14610627578063b187bd261461062f578063bdd3d8251461065257600080fd5b806388e69d991461057c5780638da5cb5b1461058f578063923cfb48146105a05780639365fbbe146105b357806393c45462146105c65780639a99b4f0146105ce57600080fd5b806348e5d9f811610226578063715018a6116101df578063715018a6146105175780637a89cdd31461051f5780637bb7bed11461053a578063841654f01461054d578063861f6bfd14610556578063869d44a31461056957600080fd5b806348e5d9f81461042157806353089dd214610485578063538daac4146104a55780635fc41158146104c0578063681d8a7c146104e9578063688cdfe91461050457600080fd5b80632726b506116102785780632726b506146103635780632e1a7d4d146103835780632f28452c1461039657806331f0acdf146103c9578063365cfcbf146103e957806343d19ca91461040e57600080fd5b806303a35865146102c057806304646a49146102e95780630962ef7914610300578063136b621214610315578063150b7a02146103285780632282870e14610354575b600080fd5b6102d36102ce36600461496e565b61077d565b6040516102e091906149cb565b60405180910390f35b6102f260035481565b6040519081526020016102e0565b61031361030e366004614a1a565b610900565b005b610313610323366004614a1a565b61096f565b61033b610336366004614a48565b610b14565b6040516001600160e01b031990911681526020016102e0565b6102f2670de0b6b3a764000081565b610376610371366004614ae6565b610e16565b6040516102e09190614b12565b610313610391366004614a1a565b610f24565b6103b173c36442b4a4522e871399cd717abdd847ab11fe8881565b6040516001600160a01b0390911681526020016102e0565b6103dc6103d7366004614ae6565b6113d0565b6040516102e09190614b61565b6103fc6103f736600461496e565b611955565b60405160ff90911681526020016102e0565b61031361041c366004614ae6565b61198c565b61045e61042f366004614b74565b600860205260009081526040902080546001909101546001600160a01b03821691600160a01b900460ff169083565b604080516001600160a01b03909416845260ff9092166020840152908201526060016102e0565b6102f2610493366004614a1a565b6000908152600a602052604090205490565b6103b1735575552988a3a80504bbaeb1311674fcfd40ad4b81565b600280546104d691640100000000909104900b81565b60405160029190910b81526020016102e0565b6103b1736d5240f086637fb408c7f727010a10cf57d51b6281565b610313610512366004614d05565b611a0a565b610313611d73565b6103b1731f98431c8ad98523631ae4a59f267346ea31f98481565b6103b1610548366004614a1a565b611d87565b6102f260045481565b610313610564366004614a1a565b611db1565b610313610577366004614a1a565b611e99565b6103dc61058a366004614b74565b611f67565b6000546001600160a01b03166103b1565b6103136105ae366004614de0565b612065565b6103136105c1366004614dfd565b612152565b6103136121a4565b6103136105dc366004614ae6565b61227a565b6103136105ef366004614b74565b6122e0565b610607610602366004614ea2565b6124a6565b6040516102e09190614ec5565b610313610622366004614ae6565b61260e565b6102f2600481565b6002546106429062010000900460ff1681565b60405190151581526020016102e0565b6002546103b190600160501b90046001600160a01b031681565b600254610642906301000000900460ff1681565b6106886126f0565b6040516102e09190614eeb565b6102f26106a3366004614b74565b612752565b6106bb6106b6366004614ae6565b612953565b6040805195151586526020860194909452928401919091526060830152608082015260a0016102e0565b6102f26106f3366004614b74565b6001600160a01b031660009081526009602052604090205490565b61031361071c366004614f38565b6129a5565b6102f260055481565b6103fc600181565b6103fc600081565b600280546104d691600160381b909104900b81565b61031361075d366004614b74565b612a5c565b6102f2600181565b6102f2610778366004614a1a565b612ad5565b6107a46040518060600160405280600060ff16815260200160608152602001606081525090565b6000838152600a602052604090205482106108065760405162461bcd60e51b815260206004820152601b60248201527f537562736372697074696f6e20646f6573206e6f74206578697374000000000060448201526064015b60405180910390fd5b6000838152600a6020526040902080548390811061082657610826614f71565b6000918252602091829020604080516060810182526003909302909101805460ff16835260018101805483518187028101870190945280845293949193858301939283018282801561089757602002820191906000526020600020905b815481526020019060010190808311610883575b50505050508152602001600282018054806020026020016040519081016040528092919081815260200182805480156108ef57602002820191906000526020600020905b8154815260200190600101908083116108db575b505050505081525050905092915050565b6002546301000000900460ff161561092a5760405162461bcd60e51b81526004016107fd90614f87565b6002600154141561094d5760405162461bcd60e51b81526004016107fd90614fac565b60026001553361095d8183612afb565b6109678183612b5f565b505060018055565b60025462010000900460ff16156109b95760405162461bcd60e51b815260206004820152600e60248201526d11985c9b481a5cc81c185d5cd95960921b60448201526064016107fd565b600260015414156109dc5760405162461bcd60e51b81526004016107fd90614fac565b6002600155336109ec8183612afb565b6001600160a01b0381166000908152600960205260408120805484908110610a1657610a16614f71565b60009182526020909120600690910201805490915060ff16610a7a5760405162461bcd60e51b815260206004820152601960248201527f43616e206e6f7420696e69746961746520636f6f6c646f776e0000000000000060448201526064016107fd565b600354610a8a9062015180614ff9565b610a949042615018565b6004820155805460ff19168155610aab8284612b5f565b610ab760018385613062565b816001600160a01b03167f810500030f51f04e0a6a7c0323c84654a386b2572d248a7ae15432d4496cc9d182600201548360040154604051610b03929190918252602082015260400190565b60405180910390a250506001805550565b60025460009062010000900460ff1615610b615760405162461bcd60e51b815260206004820152600e60248201526d11985c9b481a5cc81c185d5cd95960921b60448201526064016107fd565b3373c36442b4a4522e871399cd717abdd847ab11fe8814610bce5760405162461bcd60e51b815260206004820152602160248201527f6f6e45524337323152656365697665643a206e6f74206120756e697633206e666044820152601d60fa1b60648201526084016107fd565b81610c1b5760405162461bcd60e51b815260206004820152601960248201527f6f6e45524337323152656365697665643a206e6f20646174610000000000000060448201526064016107fd565b6000610c2983850185614de0565b905060035460001415610c84578015610c845760405162461bcd60e51b815260206004820181905260248201527f4c6f636b75702066756e6374696f6e616c6974792069732064697361626c656460448201526064016107fd565b610c8c613415565b6000610c97866136a8565b905060006040518060c001604052808415158152602001838152602001888152602001428152602001600081526020016007805490506001600160401b03811115610ce457610ce4614b91565b604051908082528060200260200182016040528015610d0d578160200160208202803683370190505b5090526001600160a01b0389166000908152600960209081526040808320805460018082018355918552938390208551600690950201805460ff191694151594909417845584830151908401558301516002830155606083015160038301556080830151600483015560a083015180519394508493610d9292600585019201906148b4565b505050610da1600088846138be565b8215610db357610db3600188846138be565b604080518415158152602081018990529081018390526001600160a01b038916907fb08217ce123adc0cf2ed190b91bd4d1d6f87597fcd0c789711088e9f85fab41e9060600160405180910390a250630a85bd0160e11b98975050505050505050565b610e516040518060c0016040528060001515815260200160008152602001600081526020016000815260200160008152602001606081525090565b6001600160a01b0383166000908152600960205260409020805483908110610e7b57610e7b614f71565b60009182526020918290206040805160c0810182526006909302909101805460ff161515835260018101548385015260028101548383015260038101546060840152600481015460808401526005810180548351818702810187019094528084529394919360a0860193928301828280156108ef57602002820191906000526020600020908154815260200190600101908083116108db57505050505081525050905092915050565b60026001541415610f475760405162461bcd60e51b81526004016107fd90614fac565b600260015533610f578183612afb565b6001600160a01b0381166000908152600960205260408120805484908110610f8157610f81614f71565b60009182526020918290206040805160c0810182526006909302909101805460ff161515835260018101548385015260028101548383015260038101546060840152600481015460808401526005810180548351818702810187019094528084529394919360a08601939283018282801561101b57602002820191906000526020600020905b815481526020019060010190808311611007575b505050505081525050905060028054906101000a900460ff166110de578051156110875760405162461bcd60e51b815260206004820152601860248201527f506c6561736520696e69746961746520636f6f6c646f776e000000000000000060448201526064016107fd565b6080810151156110de5742816080015111156110de5760405162461bcd60e51b81526020600482015260166024820152752232b837b9b4ba1034b99034b71031b7b7b63237bbb760511b60448201526064016107fd565b6110e88284612b5f565b6001600160a01b038216600090815260096020526040812080548590811061111257611112614f71565b906000526020600020906006020160050180548060200260200160405190810160405280929190818152602001828054801561116d57602002820191906000526020600020905b815481526020019060010190808311611159575b5050505050905061118060008486613062565b6040808301516000908152600a6020522054156111a3576111a360018486613062565b6001600160a01b038316600090815260096020526040902080546111c990600190615030565b815481106111d9576111d9614f71565b906000526020600020906006020160096000856001600160a01b03166001600160a01b03168152602001908152602001600020858154811061121d5761121d614f71565b600091825260209091208254600690920201805460ff191660ff9092161515919091178155600180830154908201556002808301549082015560038083015490820155600480830154908201556005808301805461127e92840191906148ff565b5050506001600160a01b03831660009081526009602052604090208054806112a8576112a8615047565b600082815260208120600660001990930192830201805460ff1916815560018101829055600281018290556003810182905560048101829055906112ef600583018261493f565b505090556040828101519051632142170760e11b81523060048201526001600160a01b0385166024820152604481019190915273c36442b4a4522e871399cd717abdd847ab11fe88906342842e0e90606401600060405180830381600087803b15801561135b57600080fd5b505af115801561136f573d6000803e3d6000fd5b50505050826001600160a01b03167f763d7dfa8d94fbde5da5f6cb94c05ef14f357ca06c4c1a430b41dd86497d36c3836040015184606001518560200151856040516113be949392919061505d565b60405180910390a25050600180555050565b60606113dc8383612afb565b6001600160a01b038316600090815260096020526040812080548490811061140657611406614f71565b60009182526020918290206040805160c0810182526006909302909101805460ff161515835260018101548385015260028101548383015260038101546060840152600481015460808401526005810180548351818702810187019094528084529394919360a0860193928301828280156114a057602002820191906000526020600020905b81548152602001906001019080831161148c575b50505050508152505090506000600a600083604001518152602001908152602001600020805480602002602001604051908101604052809291908181526020016000905b828210156115cc5760008481526020908190206040805160608101825260038602909201805460ff16835260018101805483518187028101870190945280845293949193858301939283018282801561155c57602002820191906000526020600020905b815481526020019060010190808311611548575b50505050508152602001600282018054806020026020016040519081016040528092919081815260200182805480156115b457602002820191906000526020600020905b8154815260200190600101908083116115a0575b505050505081525050815260200190600101906114e4565b50505050905060006006805480602002602001604051908101604052809291908181526020016000905b828210156116e25783829060005260206000209060030201604051806060016040529081600082015481526020016001820180548060200260200160405190810160405280929190818152602001828054801561167257602002820191906000526020600020905b81548152602001906001019080831161165e575b50505050508152602001600282018054806020026020016040519081016040528092919081815260200182805480156116ca57602002820191906000526020600020905b8154815260200190600101908083116116b6575b505050505081525050815260200190600101906115f6565b5050600754929350829150506001600160401b0381111561170557611705614b91565b60405190808252806020026020018201604052801561172e578160200160208202803683370190505b509450600060045442111561174d5760045461174a9042615030565b90505b60005b84518160ff161015611949576000858260ff168151811061177357611773614f71565b602002602001015160000151905060005b848160ff161015611936576000868360ff16815181106117a6576117a6614f71565b60200260200101516000015111156118545760006117c5828487613b53565b9050868360ff16815181106117dc576117dc614f71565b602002602001015160000151670de0b6b3a7640000826117fc9190614ff9565b6118069190615098565b878460ff168151811061181b5761181b614f71565b6020026020010151604001518360ff168151811061183b5761183b614f71565b6020026020010181815161184f9190615018565b905250505b868360ff168151811061186957611869614f71565b6020026020010151602001518160ff168151811061188957611889614f71565b6020026020010151670de0b6b3a7640000878460ff16815181106118af576118af614f71565b6020026020010151604001518360ff16815181106118cf576118cf614f71565b60200260200101518a602001516118e69190614ff9565b6118f09190615098565b6118fa9190615030565b898260ff168151811061190f5761190f614f71565b602002602001018181516119239190615018565b90525061192f816150ac565b9050611784565b505080611942906150ac565b9050611750565b50505050505092915050565b600a602052816000526040600020818154811061197157600080fd5b600091825260209091206003909102015460ff169150829050565b6001600160a01b0382811660009081526008602052604090205483911633146119c75760405162461bcd60e51b81526004016107fd906150cc565b600260015414156119ea5760405162461bcd60e51b81526004016107fd90614fac565b60026001556119f7613415565b611a018383613d81565b50506001805550565b600254610100900460ff1615808015611a2a5750600254600160ff909116105b80611a445750303b158015611a44575060025460ff166001145b611aa75760405162461bcd60e51b815260206004820152602e60248201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160448201526d191e481a5b9a5d1a585b1a5e995960921b60648201526084016107fd565b6002805460ff191660011790558015611aca576002805461ff0019166101001790555b42851015611b135760405162461bcd60e51b8152602060048201526016602482015275496e76616c6964206661726d20737461727454696d6560501b60448201526064016107fd565b611b1c33613e1c565b600485815560058690556002805463ffff000019169055602084015184516040808701519051630b4c774160e11b81526001600160a01b03938416948101949094529116602483015262ffffff166044820152731f98431c8ad98523631ae4a59f267346ea31f98490631698ee8290606401602060405180830381865afa158015611bab573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611bcf919061510b565b600280547fffff0000000000000000000000000000000000000000ffffffffffffffffffff16600160501b6001600160a01b0393841681029190911791829055900416611c5e5760405162461bcd60e51b815260206004820152601b60248201527f496e76616c696420756e697377617020706f6f6c20636f6e666967000000000060448201526064016107fd565b611c7083606001518460800151613e6c565b606083015160028054608086015162ffffff908116600160381b0269ffffff000000000000001991909416640100000000021669ffffffffffff00000000199091161791909117905560018415611d1a5760018511611d115760405162461bcd60e51b815260206004820152601c60248201527f436f6f6c646f776e203c204d696e436f6f6c646f776e506572696f640000000060448201526064016107fd565b50600384905560025b611d248184613f82565b508015611d6c576002805461ff0019169055604051600181527f7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb3847402498906020015b60405180910390a15b5050505050565b611d7b6141e8565b611d856000613e1c565b565b60078181548110611d9757600080fd5b6000918252602090912001546001600160a01b0316905081565b611db96141e8565b600354611e085760405162461bcd60e51b815260206004820152601c60248201527f4661726d20646f6573206e6f7420737570706f7274206c6f636b75700000000060448201526064016107fd565b60018111611e585760405162461bcd60e51b815260206004820152601760248201527f436f6f6c646f776e20706572696f6420746f6f206c6f7700000000000000000060448201526064016107fd565b60035460408051918252602082018390527f98eaabfe135a9c40c420208962bf81e7926b4d6df3e23502164c0554b7b35224910160405180910390a1600355565b611ea16141e8565b6005544210611ee95760405162461bcd60e51b815260206004820152601460248201527311985c9b48185b1c9958591e481cdd185c9d195960621b60448201526064016107fd565b42811015611f265760405162461bcd60e51b815260206004820152600a60248201526954696d65203c206e6f7760b01b60448201526064016107fd565b600581905560048190556040518181527f130f25fa5bdf314167bf39be054338299d55a00b55e4033be861dbd24a633c14906020015b60405180910390a150565b6006546060906000816001600160401b03811115611f8757611f87614b91565b604051908082528060200260200182016040528015611fb0578160200160208202803683370190505b506001600160a01b038516600090815260086020526040812054919250600160a01b90910460ff16905b838160ff16101561205b5760068160ff1681548110611ffb57611ffb614f71565b90600052602060002090600302016001018260ff168154811061202057612020614f71565b9060005260206000200154838260ff168151811061204057612040614f71565b6020908102919091010152612054816150ac565b9050611fda565b5090949350505050565b61206d6141e8565b6002546301000000900460ff16156120975760405162461bcd60e51b81526004016107fd90614f87565b60025460ff6201000090910416151581151514156120f75760405162461bcd60e51b815260206004820152601e60248201527f4661726d20616c726561647920696e207265717569726564207374617465000060448201526064016107fd565b6120ff613415565b6002805462ff00001916620100008315158102919091179182905560405160ff9190920416151581527f6ff9bde12009774494378420e3298e7a01f52dd41f4f60e814eb683dec08846390602001611f5c565b6001600160a01b03828116600090815260086020526040902054839116331461218d5760405162461bcd60e51b81526004016107fd906150cc565b612195613415565b61219f8383614242565b505050565b6121ac6141e8565b600260015414156121cf5760405162461bcd60e51b81526004016107fd90614fac565b60026001556121dc613415565b600060038190556002805463ffff0000191663010100001790555b60075460ff8216101561224a5761223a60078260ff168154811061221d5761221d614f71565b6000918252602090912001546001600160a01b0316600019613d81565b612243816150ac565b90506121f7565b506040517f5e1f165b802e7ec1eb767f0054488b42a684b23139c36385321abeb2164cbf2490600090a160018055565b6002546301000000900460ff16156122a45760405162461bcd60e51b81526004016107fd90614f87565b600260015414156122c75760405162461bcd60e51b81526004016107fd90614fac565b60026001556122d68282612afb565b6109678282612b5f565b6122e86141e8565b6002600154141561230b5760405162461bcd60e51b81526004016107fd90614fac565b60026001556001600160a01b0381811660009081526008602052604090205416156123785760405162461bcd60e51b815260206004820152601a60248201527f43616e277420776974686472617720726577617264546f6b656e00000000000060448201526064016107fd565b6040516370a0823160e01b81523060048201526000906001600160a01b038316906370a0823190602401602060405180830381865afa1580156123bf573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906123e39190615128565b9050600081116124355760405162461bcd60e51b815260206004820152601760248201527f43616e2774207769746864726177203020616d6f756e7400000000000000000060448201526064016107fd565b61245b61244a6000546001600160a01b031690565b6001600160a01b038416908361441d565b604080516001600160a01b0384168152602081018390527f55350610fe57096d8c0ffa30beede987326bccfcb0b4415804164d0dd50ce8b191015b60405180910390a1505060018055565b6124ca60405180606001604052806000815260200160608152602001606081525090565b60065460ff83161061251e5760405162461bcd60e51b815260206004820152601a60248201527f5265776172642066756e6420646f6573206e6f7420657869737400000000000060448201526064016107fd565b60068260ff168154811061253457612534614f71565b906000526020600020906003020160405180606001604052908160008201548152602001600182018054806020026020016040519081016040528092919081815260200182805480156125a657602002820191906000526020600020905b815481526020019060010190808311612592575b50505050508152602001600282018054806020026020016040519081016040528092919081815260200182805480156125fe57602002820191906000526020600020905b8154815260200190600101908083116125ea575b5050505050815250509050919050565b600260015414156126315760405162461bcd60e51b81526004016107fd90614fac565b60026001556001600160a01b03828116600090815260086020526040902054166126945760405162461bcd60e51b815260206004820152601460248201527324b73b30b634b2103932bbb0b932103a37b5b2b760611b60448201526064016107fd565b61269c613415565b6126b16001600160a01b038316333084614480565b604080516001600160a01b0384168152602081018390527fac24935fd910bc682b5ccb1a07b718cadf8cf2f6d1404c4f3ddc3662dae40e299101612496565b6060600780548060200260200160405190810160405280929190818152602001828054801561274857602002820191906000526020600020905b81546001600160a01b0316815260019091019060200180831161272a575b5050505050905090565b6001600160a01b038116600081815260086020526040812054600780549293600160a01b90920460ff16928390811061278d5761278d614f71565b6000918252602090912001546001600160a01b0316146127e35760405162461bcd60e51b815260206004820152601160248201527024b73b30b634b2102fb93bb22a37b5b2b760791b60448201526064016107fd565b6006546001600160a01b0384166000818152600860205260408082206001015490516370a0823160e01b81523060048201529092906370a0823190602401602060405180830381865afa15801561283e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906128629190615128565b905060045442111561292c5760006004544261287e9190615030565b905060005b848160ff16101561292957600060068260ff16815481106128a6576128a6614f71565b9060005260206000209060030201600001541115612919578160068260ff16815481106128d5576128d5614f71565b906000526020600020906003020160010187815481106128f7576128f7614f71565b906000526020600020015461290c9190614ff9565b6129169085615018565b93505b612922816150ac565b9050612883565b50505b80821061293f5750600095945050505050565b6129498282615030565b9695505050505050565b6009602052816000526040600020818154811061296f57600080fd5b60009182526020909120600690910201805460018201546002830154600384015460049094015460ff9093169550909350919085565b6001600160a01b0382811660009081526008602052604090205483911633146129e05760405162461bcd60e51b81526004016107fd906150cc565b6129e9826144b8565b6001600160a01b0383811660008181526008602090815260409182902080546001600160a01b0319169487169485179055815192835233908301528101919091527f5c0557fb633aff9396d4a8ccdac0fb278766dbf365162825b0b593f520b3bfb29060600160405180910390a1505050565b612a646141e8565b6001600160a01b038116612ac95760405162461bcd60e51b815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201526564647265737360d01b60648201526084016107fd565b612ad281613e1c565b50565b60068181548110612ae557600080fd5b6000918252602090912060039091020154905081565b6001600160a01b0382166000908152600960205260409020548110612b5b5760405162461bcd60e51b815260206004820152601660248201527511195c1bdcda5d08191bd95cc81b9bdd08195e1a5cdd60521b60448201526064016107fd565b5050565b612b67613415565b6001600160a01b0382166000908152600960205260408120805483908110612b9157612b91614f71565b600091825260208083206002600690930201918201548352600a90526040822060075481549294509092909190826001600160401b03811115612bd657612bd6614b91565b604051908082528060200260200182016040528015612bff578160200160208202803683370190505b50905060005b828160ff161015612ef7576000858260ff1681548110612c2757612c27614f71565b6000918252602082206003909102015460ff169150856001600160401b03811115612c5457612c54614b91565b604051908082528060200260200182016040528015612c7d578160200160208202803683370190505b50905060005b86811015612e6b576000670de0b6b3a764000060068560ff1681548110612cac57612cac614f71565b90600052602060002090600302016002018381548110612cce57612cce614f71565b90600052602060002001548b60010154612ce89190614ff9565b612cf29190615098565b9050888560ff1681548110612d0957612d09614f71565b90600052602060002090600302016001018281548110612d2b57612d2b614f71565b906000526020600020015481612d419190615030565b838381518110612d5357612d53614f71565b602002602001018181525050828281518110612d7157612d71614f71565b6020026020010151898660ff1681548110612d8e57612d8e614f71565b90600052602060002090600302016002018381548110612db057612db0614f71565b906000526020600020016000828254612dc99190615018565b92505081905550828281518110612de257612de2614f71565b6020026020010151868381518110612dfc57612dfc614f71565b60200260200101818151612e109190615018565b905250885481908a9060ff8816908110612e2c57612e2c614f71565b90600052602060002090600302016001018381548110612e4e57612e4e614f71565b60009182526020909120015550612e6481615141565b9050612c83565b50896001600160a01b03167f1cae27cc7b140076d3acecff4a7b68d47935ad1ea499376ef4f72d52633d0b1d838a600201548b6001015460068760ff1681548110612eb857612eb8614f71565b90600052602060002090600302016000015486604051612edc95949392919061515c565b60405180910390a2505080612ef0906150ac565b9050612c05565b5060005b838160ff161015613058576000828260ff1681518110612f1d57612f1d614f71565b6020026020010151111561304857818160ff1681518110612f4057612f40614f71565b60200260200101516008600060078460ff1681548110612f6257612f62614f71565b60009182526020808320909101546001600160a01b0316835282019290925260400181206001018054909190612f99908490615030565b92505081905550818160ff1681518110612fb557612fb5614f71565b6020026020010151866005018260ff1681548110612fd557612fd5614f71565b906000526020600020016000828254612fee9190615018565b9250508190555061304888838360ff168151811061300e5761300e614f71565b602002602001015160078460ff168154811061302c5761302c614f71565b6000918252602090912001546001600160a01b0316919061441d565b613051816150ac565b9050612efb565b5050505050505050565b60065460ff8416106130a85760405162461bcd60e51b815260206004820152600f60248201526e125b9d985b1a5908199d5b99081a59608a1b60448201526064016107fd565b6001600160a01b03821660009081526009602052604081208054839081106130d2576130d2614f71565b60009182526020918290206040805160c0810182526006909302909101805460ff161515835260018101548385015260028101548383015260038101546060840152600481015460808401526005810180548351818702810187019094528084529394919360a08601939283018282801561316c57602002820191906000526020600020905b815481526020019060010190808311613158575b5050509190925250506007546040808401516000908152600a602052908120805494955091939192505b81811015613058578760ff168382815481106131b4576131b4614f71565b600091825260209091206003909102015460ff161415613405576000846001600160401b038111156131e8576131e8614b91565b604051908082528060200260200182016040528015613211578160200160208202803683370190505b50905060005b858160ff1610156132945784838154811061323457613234614f71565b90600052602060002090600302016002018160ff168154811061325957613259614f71565b9060005260206000200154828260ff168151811061327957613279614f71565b602090810291909101015261328d816150ac565b9050613217565b50836132a1600185615030565b815481106132b1576132b1614f71565b90600052602060002090600302018483815481106132d1576132d1614f71565b600091825260209091208254600390920201805460ff191660ff9092169190911781556001808301805461330892840191906148ff565b506002828101805461331d92840191906148ff565b509050508380548061333157613331615047565b600082815260208120600360001990930192830201805460ff191681559061335c600183018261493f565b61336a60028301600061493f565b50509055856020015160068a60ff168154811061338957613389614f71565b906000526020600020906003020160000160008282546133a99190615030565b92505081905550876001600160a01b03167f602bb7b33d527bb4aaea01da006083162e4055fa16af04ad5c33219400b790a28a88604001518960600151856040516133f7949392919061518a565b60405180910390a250613058565b61340e81615141565b9050613196565b600454421115611d855760025462010000900460ff166136a25760006004544261343f9190615030565b60075490915060005b60065460ff8216101561369e57600060068260ff168154811061346d5761346d614f71565b906000526020600020906003020160405180606001604052908160008201548152602001600182018054806020026020016040519081016040528092919081815260200182805480156134df57602002820191906000526020600020905b8154815260200190600101908083116134cb575b505050505081526020016002820180548060200260200160405190810160405280929190818152602001828054801561353757602002820191906000526020600020905b815481526020019060010190808311613523575b505050919092525050815191925050156136215760005b838160ff16101561361f576000613566828588613b53565b9050806008600060078560ff168154811061358357613583614f71565b60009182526020808320909101546001600160a01b03168352820192909252604001812060010180549091906135ba908490615018565b909155505082516135d3670de0b6b3a764000083614ff9565b6135dd9190615098565b83604001518360ff16815181106135f6576135f6614f71565b6020026020010181815161360a9190615018565b9052506136189050816150ac565b905061354e565b505b8060068360ff168154811061363857613638614f71565b906000526020600020906003020160008201518160000155602082015181600101908051906020019061366c9291906148b4565b50604082015180516136889160028401916020909101906148b4565b509050505080613697906150ac565b9050613448565b5050505b42600455565b600080600080600080600073c36442b4a4522e871399cd717abdd847ab11fe886001600160a01b03166399fbab88896040518263ffffffff1660e01b81526004016136f591815260200190565b61018060405180830381865afa158015613713573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061373791906151df565b5050604051630b4c774160e11b81526001600160a01b03808a1660048301528816602482015262ffffff87166044820152979f50959d50939b5091995097509550731f98431c8ad98523631ae4a59f267346ea31f9849450631698ee8293505060649091019050602060405180830381865afa1580156137bb573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906137df919061510b565b600254600160501b90046001600160a01b0390811691161461383a5760405162461bcd60e51b815260206004820152601460248201527324b731b7b93932b1ba103837b7b6103a37b5b2b760611b60448201526064016107fd565b600280546401000000009004810b9084900b148015613867575060028054600160381b9004810b9083900b145b6138aa5760405162461bcd60e51b8152602060048201526014602482015273496e636f7272656374207469636b2072616e676560601b60448201526064016107fd565b6001600160801b0316979650505050505050565b60065460ff8416106139045760405162461bcd60e51b815260206004820152600f60248201526e125b9d985b1a5908199d5b99081a59608a1b60448201526064016107fd565b6007546000838152600a602090815260409182902082516060810190935260ff8716835291908101836001600160401b0381111561394457613944614b91565b60405190808252806020026020018201604052801561396d578160200160208202803683370190505b508152602001836001600160401b0381111561398b5761398b614b91565b6040519080825280602002602001820160405280156139b4578160200160208202803683370190505b50905281546001808201845560009384526020938490208351600390930201805460ff191660ff90931692909217825582840151805193949293613a00939285019291909101906148b4565b5060408201518051613a1c9160028401916020909101906148b4565b5050506000838152600a6020526040812054613a3a90600190615030565b905060005b828160ff161015613b0f57670de0b6b3a764000060068760ff1681548110613a6957613a69614f71565b90600052602060002090600302016002018260ff1681548110613a8e57613a8e614f71565b906000526020600020015485613aa49190614ff9565b613aae9190615098565b6000868152600a60205260409020805484908110613ace57613ace614f71565b90600052602060002090600302016001018260ff1681548110613af357613af3614f71565b600091825260209091200155613b08816150ac565b9050613a3f565b508260068660ff1681548110613b2757613b27614f71565b90600052602060002090600302016000016000828254613b479190615018565b90915550505050505050565b60008060068460ff1681548110613b6c57613b6c614f71565b90600052602060002090600302016040518060600160405290816000820154815260200160018201805480602002602001604051908101604052809291908181526020018280548015613bde57602002820191906000526020600020905b815481526020019060010190808311613bca575b5050505050815260200160028201805480602002602001604051908101604052809291908181526020018280548015613c3657602002820191906000526020600020905b815481526020019060010190808311613c22575b505050505081525050905080602001518560ff1681518110613c5a57613c5a614f71565b602002602001015160001415613c74576000915050613d7a565b600060078660ff1681548110613c8c57613c8c614f71565b60009182526020822001546040516370a0823160e01b81523060048201526001600160a01b03909116925082906370a0823190602401602060405180830381865afa158015613cdf573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613d039190615128565b6001600160a01b03831660009081526008602052604081206001015491925081831115613d3757613d348284615030565b90505b60008786602001518b60ff1681518110613d5357613d53614f71565b6020026020010151613d659190614ff9565b905081811115613d725750805b955050505050505b9392505050565b6001600160a01b0380831660009081526008602052604081205490911690613da884612752565b905082818110613db55750805b8015611d6c57613dcf6001600160a01b038616848361441d565b604080516001600160a01b038781168252602082018490528516917f13e06184555481b6d2cb327155e8d2e1d0b1f0252a7fe6621e32cf9988148835910160405180910390a25050505050565b600080546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b60006002600a9054906101000a90046001600160a01b03166001600160a01b031663d0c93a7c6040518163ffffffff1660e01b8152600401602060405180830381865afa158015613ec1573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613ee591906152c0565b90508160020b8360020b128015613f035750620d89e7198360020b12155b8015613f195750613f1481846152dd565b60020b155b8015613f2b5750620d89e88260020b13155b8015613f415750613f3c81836152dd565b60020b155b61219f5760405162461bcd60e51b8152602060048201526012602482015271496e76616c6964207469636b2072616e676560701b60448201526064016107fd565b8051613f9060016004615030565b811115613fd55760405162461bcd60e51b8152602060048201526013602482015272496e76616c696420726577617264206461746160681b60448201526064016107fd565b60005b8360ff168160ff1610156141475760006040518060600160405280600081526020018460016140079190615018565b6001600160401b0381111561401e5761401e614b91565b604051908082528060200260200182016040528015614047578160200160208202803683370190505b508152602001614058856001615018565b6001600160401b0381111561406f5761406f614b91565b604051908082528060200260200182016040528015614098578160200160208202803683370190505b50905260068054600181018255600091909152815160039091027ff652222313e28459528d920b65115c16c04f3efc82aaedc97be59f3f377c0d3f810191825560208084015180519495508594614116937ff652222313e28459528d920b65115c16c04f3efc82aaedc97be59f3f377c0d40019291909101906148b4565b50604082015180516141329160028401916020909101906148b4565b5050505080614140906150ac565b9050613fd8565b5061417a735575552988a3a80504bbaeb1311674fcfd40ad4b736d5240f086637fb408c7f727010a10cf57d51b62614500565b60005b818160ff1610156141e2576141d2838260ff16815181106141a0576141a0614f71565b602002602001015160000151848360ff16815181106141c1576141c1614f71565b602002602001015160200151614500565b6141db816150ac565b905061417d565b50505050565b6000546001600160a01b03163314611d855760405162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e657260448201526064016107fd565b6001600160a01b0382166000908152600860205260409020546006548251600160a01b90920460ff169181146142ba5760405162461bcd60e51b815260206004820152601b60248201527f496e76616c696420726577617264207261746573206c656e677468000000000060448201526064016107fd565b6000816001600160401b038111156142d4576142d4614b91565b6040519080825280602002602001820160405280156142fd578160200160208202803683370190505b50905060005b828160ff1610156143e95760068160ff168154811061432457614324614f71565b90600052602060002090600302016001018460ff168154811061434957614349614f71565b9060005260206000200154828260ff168151811061436957614369614f71565b602002602001018181525050848160ff168151811061438a5761438a614f71565b602002602001015160068260ff16815481106143a8576143a8614f71565b90600052602060002090600302016001018560ff16815481106143cd576143cd614f71565b6000918252602090912001556143e2816150ac565b9050614303565b507f11bdba784675a1ae0fcb4a62299fe0409fdb243ebda575bd1f182747874d8861858286604051611d63939291906152ff565b6040516001600160a01b03831660248201526044810182905261219f90849063a9059cbb60e01b906064015b60408051601f198184030181529190526020810180516001600160e01b03166001600160e01b031990931692909217909152614661565b6040516001600160a01b03808516602483015283166044820152606481018290526141e29085906323b872dd60e01b90608401614449565b6001600160a01b038116612ad25760405162461bcd60e51b815260206004820152600f60248201526e496e76616c6964206164647265737360881b60448201526064016107fd565b614509826144b8565b614512816144b8565b6001600160a01b03828116600090815260086020526040902054161561457a5760405162461bcd60e51b815260206004820152601a60248201527f52657761726420746f6b656e20616c726561647920616464656400000000000060448201526064016107fd565b604080516060810182526001600160a01b038381168083526007805460ff908116602080870191825260008789018181528b8816808352600884528a832099518a54955199166001600160a81b031990951694909417600160a01b98909516979097029390931787559451600196870155825495860183559190527fa66cc928b5edb82af9bd49922954155ab7b0942694bea4ce44661d9a8736c68890930180546001600160a01b031916841790558351928352908201527f3344e0a0f48738979c56a1b9f2cd3425597f76766d53e83439cab3fc30b067c7910160405180910390a15050565b60006146b6826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b03166147339092919063ffffffff16565b80519091501561219f57808060200190518101906146d49190615335565b61219f5760405162461bcd60e51b815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e6044820152691bdd081cdd58d8d9595960b21b60648201526084016107fd565b6060614742848460008561474a565b949350505050565b6060824710156147ab5760405162461bcd60e51b815260206004820152602660248201527f416464726573733a20696e73756666696369656e742062616c616e636520666f6044820152651c8818d85b1b60d21b60648201526084016107fd565b6001600160a01b0385163b6148025760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e747261637400000060448201526064016107fd565b600080866001600160a01b0316858760405161481e919061537e565b60006040518083038185875af1925050503d806000811461485b576040519150601f19603f3d011682016040523d82523d6000602084013e614860565b606091505b509150915061487082828661487b565b979650505050505050565b6060831561488a575081613d7a565b82511561489a5782518084602001fd5b8160405162461bcd60e51b81526004016107fd919061539a565b8280548282559060005260206000209081019282156148ef579160200282015b828111156148ef5782518255916020019190600101906148d4565b506148fb929150614959565b5090565b8280548282559060005260206000209081019282156148ef5760005260206000209182015b828111156148ef578254825591600101919060010190614924565b5080546000825590600052602060002090810190612ad291905b5b808211156148fb576000815560010161495a565b6000806040838503121561498157600080fd5b50508035926020909101359150565b600081518084526020808501945080840160005b838110156149c0578151875295820195908201906001016149a4565b509495945050505050565b6020815260ff825116602082015260006020830151606060408401526149f46080840182614990565b90506040840151601f19848303016060850152614a118282614990565b95945050505050565b600060208284031215614a2c57600080fd5b5035919050565b6001600160a01b0381168114612ad257600080fd5b600080600080600060808688031215614a6057600080fd5b8535614a6b81614a33565b94506020860135614a7b81614a33565b93506040860135925060608601356001600160401b0380821115614a9e57600080fd5b818801915088601f830112614ab257600080fd5b813581811115614ac157600080fd5b896020828501011115614ad357600080fd5b9699959850939650602001949392505050565b60008060408385031215614af957600080fd5b8235614b0481614a33565b946020939093013593505050565b60208152815115156020820152602082015160408201526040820151606082015260608201516080820152608082015160a0820152600060a083015160c08084015261474260e0840182614990565b602081526000613d7a6020830184614990565b600060208284031215614b8657600080fd5b8135613d7a81614a33565b634e487b7160e01b600052604160045260246000fd5b604080519081016001600160401b0381118282101715614bc957614bc9614b91565b60405290565b60405160a081016001600160401b0381118282101715614bc957614bc9614b91565b604051601f8201601f191681016001600160401b0381118282101715614c1957614c19614b91565b604052919050565b62ffffff81168114612ad257600080fd5b8060020b8114612ad257600080fd5b60006001600160401b03821115614c5a57614c5a614b91565b5060051b60200190565b600082601f830112614c7557600080fd5b81356020614c8a614c8583614c41565b614bf1565b82815260069290921b84018101918181019086841115614ca957600080fd5b8286015b84811015614cfa5760408189031215614cc65760008081fd5b614cce614ba7565b8135614cd981614a33565b815281850135614ce881614a33565b81860152835291830191604001614cad565b509695505050505050565b600080600080848603610100811215614d1d57600080fd5b853594506020860135935060a0603f1982011215614d3a57600080fd5b50614d43614bcf565b6040860135614d5181614a33565b81526060860135614d6181614a33565b60208201526080860135614d7481614c21565b604082015260a0860135614d8781614c32565b606082015260c0860135614d9a81614c32565b6080820152915060e08501356001600160401b03811115614dba57600080fd5b614dc687828801614c64565b91505092959194509250565b8015158114612ad257600080fd5b600060208284031215614df257600080fd5b8135613d7a81614dd2565b60008060408385031215614e1057600080fd5b8235614e1b81614a33565b91506020838101356001600160401b03811115614e3757600080fd5b8401601f81018613614e4857600080fd5b8035614e56614c8582614c41565b81815260059190911b82018301908381019088831115614e7557600080fd5b928401925b82841015614e9357833582529284019290840190614e7a565b80955050505050509250929050565b600060208284031215614eb457600080fd5b813560ff81168114613d7a57600080fd5b602081528151602082015260006020830151606060408401526149f46080840182614990565b6020808252825182820181905260009190848201906040850190845b81811015614f2c5783516001600160a01b031683529284019291840191600101614f07565b50909695505050505050565b60008060408385031215614f4b57600080fd5b8235614f5681614a33565b91506020830135614f6681614a33565b809150509250929050565b634e487b7160e01b600052603260045260246000fd5b6020808252600b908201526a11985c9b4818db1bdcd95960aa1b604082015260600190565b6020808252601f908201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c00604082015260600190565b634e487b7160e01b600052601160045260246000fd5b600081600019048311821515161561501357615013614fe3565b500290565b6000821982111561502b5761502b614fe3565b500190565b60008282101561504257615042614fe3565b500390565b634e487b7160e01b600052603160045260246000fd5b8481528360208201528260408201526080606082015260006129496080830184614990565b634e487b7160e01b600052601260045260246000fd5b6000826150a7576150a7615082565b500490565b600060ff821660ff8114156150c3576150c3614fe3565b60010192915050565b6020808252601590820152742737ba103a3432903a37b5b2b71036b0b730b3b2b960591b604082015260600190565b805161510681614a33565b919050565b60006020828403121561511d57600080fd5b8151613d7a81614a33565b60006020828403121561513a57600080fd5b5051919050565b600060001982141561515557615155614fe3565b5060010190565b60ff8616815284602082015283604082015282606082015260a06080820152600061487060a0830184614990565b60ff851681528360208201528260408201526080606082015260006129496080830184614990565b805161510681614c21565b805161510681614c32565b80516001600160801b038116811461510657600080fd5b6000806000806000806000806000806000806101808d8f03121561520257600080fd5b8c516bffffffffffffffffffffffff8116811461521e57600080fd5b9b5061522c60208e016150fb565b9a5061523a60408e016150fb565b995061524860608e016150fb565b985061525660808e016151b2565b975061526460a08e016151bd565b965061527260c08e016151bd565b955061528060e08e016151c8565b94506101008d015193506101208d0151925061529f6101408e016151c8565b91506152ae6101608e016151c8565b90509295989b509295989b509295989b565b6000602082840312156152d257600080fd5b8151613d7a81614c32565b60008260020b806152f0576152f0615082565b808360020b0791505092915050565b6001600160a01b038416815260606020820181905260009061532390830185614990565b82810360408401526129498185614990565b60006020828403121561534757600080fd5b8151613d7a81614dd2565b60005b8381101561536d578181015183820152602001615355565b838111156141e25750506000910152565b60008251615390818460208701615352565b9190910192915050565b60208152600082518060208401526153b9816040850160208701615352565b601f01601f1916919091016040019291505056fea2646970667358221220f88813749133ce299e89049bd0083fbaf53d64fd7970387515c329f38cf607cc64736f6c634300080a0033000000000000000000000000c4fb09e0cd212367642974f6ba81d8e23780a659

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

000000000000000000000000c4fb09e0cd212367642974f6ba81d8e23780a659

-----Decoded View---------------
Arg [0] : _factory (address): 0xC4fb09E0CD212367642974F6bA81D8e23780A659

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


Deployed ByteCode Sourcemap

86712:6138:0:-:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;85000:33;;;;;-1:-1:-1;;;;;85000:33:0;;;;;;-1:-1:-1;;;;;178:32:1;;;160:51;;148:2;133:18;85000:33:0;;;;;;;;89387:355;;;;;;:::i;:::-;;:::i;:::-;;89945:188;;;;;;:::i;:::-;;:::i;87748:52::-;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;1470:14:1;;1463:22;1445:41;;1433:2;1418:18;87748:52:0;1305:187:1;84762:72:0;;84792:42;84762:72;;87586:62;;;;;;;;;;;;;;;-1:-1:-1;;;87586:62:0;;;;;;;;;;;;:::i;20991:103::-;;;:::i;88289:841::-;;;;;;:::i;:::-;;:::i;20343:87::-;20389:7;20416:6;-1:-1:-1;;;;;20416:6:0;20343:87;;87655:28;;;;;;;;;6154:25:1;;;6142:2;6127:18;87655:28:0;6008:177:1;90402:412:0;;;;;;:::i;:::-;;:::i;:::-;;;;-1:-1:-1;;;;;6864:15:1;;;6846:34;;6916:15;;;;6911:2;6896:18;;6889:43;6948:18;;;6941:34;7018:14;7011:22;7006:2;6991:18;;6984:50;6795:3;6780:19;90402:412:0;6583:457:1;84841:73:0;;84872:42;84841:73;;84921:22;;;;;-1:-1:-1;;;;;84921:22:0;;;21249:201;;;;;;:::i;:::-;;:::i;89387:355::-;20229:13;:11;:13::i;:::-;-1:-1:-1;;;;;89523:31:0;::::1;;::::0;;;:20:::1;:31;::::0;;;;;::::1;;:45;;::::0;::::1;;;;89501:123;;;::::0;-1:-1:-1;;;89501:123:0;;7247:2:1;89501:123:0::1;::::0;::::1;7229:21:1::0;7286:2;7266:18;;;7259:30;7325;7305:18;;;7298:58;7373:18;;89501:123:0::1;;;;;;;;;-1:-1:-1::0;;;;;89635:31:0;::::1;;::::0;;;:20:::1;:31;::::0;;;;;;;;:44;;-1:-1:-1;;89635:44:0::1;::::0;::::1;;::::0;;::::1;::::0;;;89695:39;;7570:51:1;;;7637:18;;;7630:50;89695:39:0::1;::::0;7543:18:1;89695:39:0::1;;;;;;;89387:355:::0;;:::o;89945:188::-;20229:13;:11;:13::i;:::-;90054::::1;::::0;90033:51:::1;::::0;;7865:25:1;;;7921:2;7906:18;;7899:34;;;90033:51:0::1;::::0;7838:18:1;90033:51:0::1;;;;;;;90095:13;:30:::0;89945:188::o;20991:103::-;20229:13;:11;:13::i;:::-;21056:30:::1;21083:1;21056:18;:30::i;:::-;20991:103::o:0;88289:841::-;88390:7;16214:1;16812:7;;:19;;16804:63;;;;-1:-1:-1;;;16804:63:0;;8146:2:1;16804:63:0;;;8128:21:1;8185:2;8165:18;;;8158:30;8224:33;8204:18;;;8197:61;8275:18;;16804:63:0;7944:355:1;16804:63:0;16214:1;16945:7;:18;88430:15;;88415:31:::1;::::0;:14:::1;:31::i;:::-;88527:18;::::0;88457:26:::1;::::0;88514:32:::1;::::0;-1:-1:-1;;;;;88527:18:0::1;88514:12;:32::i;:::-;88457:100;;88568:12;-1:-1:-1::0;;;;;88568:23:0::1;;88606:5;:19;;;88640:5;:20;;;88675:5;:21;;;88711:5;:16;;;88568:170;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;::::0;::::1;;;;;;;;;;;;::::0;::::1;;;;;-1:-1:-1::0;;88780:15:0;;88749:47:::1;::::0;-1:-1:-1;;;88749:47:0;;-1:-1:-1;;;;;178:32:1;;;88749:47:0::1;::::0;::::1;160:51:1::0;88749:30:0;;::::1;::::0;-1:-1:-1;88749:30:0::1;::::0;-1:-1:-1;133:18:1;;88749:47:0::1;;;;;;;;;;;;;;;;;::::0;::::1;;;;;;;;;;;;::::0;::::1;;;;;-1:-1:-1::0;;;;88916:21:0::1;::::0;::::1;::::0;:28;;88946::::1;::::0;;::::1;::::0;88830:12;;88904:71:::1;::::0;:11:::1;:71::i;:::-;88999:7;::::0;88986:52:::1;::::0;-1:-1:-1;;;88986:52:0;;-1:-1:-1;;;;;10012:15:1;;;88986:52:0::1;::::0;::::1;9994:34:1::0;89027:10:0::1;10044:18:1::0;;;10037:43;88999:7:0;;::::1;::::0;88986:34:::1;::::0;9929:18:1;;88986:52:0::1;;;;;;;;;;;;;;;;;::::0;::::1;;;;;;;;;;;;::::0;::::1;;;;;-1:-1:-1::0;;89084:15:0;;89054:46:::1;::::0;;-1:-1:-1;;;;;10012:15:1;;;9994:34;;89072:10:0::1;10059:2:1::0;10044:18;;10037:43;89054:46:0;;;::::1;::::0;-1:-1:-1;89054:46:0::1;::::0;-1:-1:-1;89054:46:0;;;;;::::1;16170:1:::0;17124:7;:22;89118:4;88289:841;-1:-1:-1;;;88289:841:0:o;90402:412::-;90541:7;90563;90585;90607:4;90639:23;90654:7;90639:14;:23::i;:::-;90673;90688:7;90673:14;:23::i;:::-;90726:7;-1:-1:-1;;;;;90715:18:0;:7;-1:-1:-1;;;;;90715:18:0;;;90707:49;;;;-1:-1:-1;;;90707:49:0;;10293:2:1;90707:49:0;;;10275:21:1;10332:2;10312:18;;;10305:30;-1:-1:-1;;;10351:18:1;;;10344:48;10409:18;;90707:49:0;10091:342:1;90707:49:0;90774:32;90789:7;90798;90774:14;:32::i;:::-;90767:39;;;;;;;;90402:412;;;;;;;;:::o;21249:201::-;20229:13;:11;:13::i;:::-;-1:-1:-1;;;;;21338:22:0;::::1;21330:73;;;::::0;-1:-1:-1;;;21330:73:0;;10640:2:1;21330:73:0::1;::::0;::::1;10622:21:1::0;10679:2;10659:18;;;10652:30;10718:34;10698:18;;;10691:62;-1:-1:-1;;;10769:18:1;;;10762:36;10815:19;;21330:73:0::1;10438:402:1::0;21330:73:0::1;21414:28;21433:8;21414:18;:28::i;:::-;21249:201:::0;:::o;20508:132::-;20389:7;20416:6;-1:-1:-1;;;;;20416:6:0;18974:10;20572:23;20564:68;;;;-1:-1:-1;;;20564:68:0;;11047:2:1;20564:68:0;;;11029:21:1;;;11066:18;;;11059:30;11125:34;11105:18;;;11098:62;11177:18;;20564:68:0;10845:356:1;21610:191:0;21684:16;21703:6;;-1:-1:-1;;;;;21720:17:0;;;-1:-1:-1;;;;;;21720:17:0;;;;;;21753:40;;21703:6;;;;;;;21753:40;;21684:16;21753:40;21673:128;21610:191;:::o;86135:119::-;-1:-1:-1;;;;;86207:19:0;;86199:47;;;;-1:-1:-1;;;86199:47:0;;11408:2:1;86199:47:0;;;11390:21:1;11447:2;11427:18;;;11420:30;-1:-1:-1;;;11466:18:1;;;11459:45;11521:18;;86199:47:0;11206:339:1;1019:568:0;1076:16;1190:4;1184:11;-1:-1:-1;;;1216:3:0;1209:79;1335:14;1329:4;1325:25;1318:4;1313:3;1309:14;1302:49;-1:-1:-1;;;1381:4:0;1376:3;1372:14;1365:90;1496:4;1491:3;1488:1;1481:20;1469:32;-1:-1:-1;;;;;;;1530:22:0;;1522:57;;;;-1:-1:-1;;;1522:57:0;;11752:2:1;1522:57:0;;;11734:21:1;11791:2;11771:18;;;11764:30;-1:-1:-1;;;11810:18:1;;;11803:52;11872:18;;1522:57:0;11550:346:1;1522:57:0;1019:568;;;:::o;90951:531::-;91050:19;91084:16;91115:17;91147:14;91175:32;91190:7;91199;91175:14;:32::i;:::-;91035:172;;;;;;;;91234:1;91222:9;:13;91218:257;;;91252:135;-1:-1:-1;;;;;91252:33:0;;91304:10;91333:11;91363:9;91252:33;:135::i;:::-;91407:56;;;-1:-1:-1;;;;;12093:32:1;;12075:51;;12157:2;12142:18;;12135:34;;;91407:56:0;;;;91420:10;;91407:56;;12048:18:1;91407:56:0;;;;;;;91218:257;91024:458;;;;90951:531;;:::o;91621:981::-;91743:7;91765;91787;91809:4;91856:19;91890:16;91921:17;91965:7;;;;;;;;;-1:-1:-1;;;;;91965:7:0;-1:-1:-1;;;;;91952:34:0;;:36;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;92024:10;92003:32;;;;:20;:32;;;;;;91841:147;;-1:-1:-1;91841:147:0;;-1:-1:-1;91841:147:0;-1:-1:-1;92003:32:0;;91999:190;;;-1:-1:-1;92137:11:0;;-1:-1:-1;92150:8:0;-1:-1:-1;92113:1:0;;-1:-1:-1;92113:1:0;;-1:-1:-1;92129:48:0;;91999:190;92204:23;92219:7;92204:14;:23::i;:::-;92203:24;:52;;;;;92232:23;92247:7;92232:14;:23::i;:::-;92231:24;92203:52;92199:396;;;92352:11;;-1:-1:-1;92365:8:0;-1:-1:-1;92375:9:0;-1:-1:-1;92386:5:0;;-1:-1:-1;92344:48:0;;92199:396;-1:-1:-1;92563:13:0;;92540:11;;-1:-1:-1;92553:8:0;-1:-1:-1;92563:13:0;-1:-1:-1;92578:4:0;;-1:-1:-1;92532:51:0;;42326:248;42497:68;;;-1:-1:-1;;;;;12889:15:1;;;42497:68:0;;;12871:34:1;12941:15;;12921:18;;;12914:43;12973:18;;;;12966:34;;;42497:68:0;;;;;;;;;;12806:18:1;;;;42497:68:0;;;;;;;;-1:-1:-1;;;;;42497:68:0;-1:-1:-1;;;42497:68:0;;;42470:96;;42490:5;;42470:19;:96::i;:::-;42326:248;;;;:::o;92722:125::-;92784:4;-1:-1:-1;;;;;92808:13:0;;84792:42;92808:13;;:31;;-1:-1:-1;;;;;;92825:14:0;;84872:42;92825:14;92808:31;92801:38;92722:125;-1:-1:-1;;92722:125:0:o;45174:716::-;45598:23;45624:69;45652:4;45624:69;;;;;;;;;;;;;;;;;45632:5;-1:-1:-1;;;;;45624:27:0;;;:69;;;;;:::i;:::-;45708:17;;45598:95;;-1:-1:-1;45708:21:0;45704:179;;45805:10;45794:30;;;;;;;;;;;;:::i;:::-;45786:85;;;;-1:-1:-1;;;45786:85:0;;13463:2:1;45786:85:0;;;13445:21:1;13502:2;13482:18;;;13475:30;13541:34;13521:18;;;13514:62;-1:-1:-1;;;13592:18:1;;;13585:40;13642:19;;45786:85:0;13261:406:1;45786:85:0;45244:646;45174:716;;:::o;25786:229::-;25923:12;25955:52;25977:6;25985:4;25991:1;25994:12;25955:21;:52::i;:::-;25948:59;;25786:229;;;;;;:::o;26906:510::-;27076:12;27134:5;27109:21;:30;;27101:81;;;;-1:-1:-1;;;27101:81:0;;13874:2:1;27101:81:0;;;13856:21:1;13913:2;13893:18;;;13886:30;13952:34;13932:18;;;13925:62;-1:-1:-1;;;14003:18:1;;;13996:36;14049:19;;27101:81:0;13672:402:1;27101:81:0;-1:-1:-1;;;;;23336:19:0;;;27193:60;;;;-1:-1:-1;;;27193:60:0;;14281:2:1;27193:60:0;;;14263:21:1;14320:2;14300:18;;;14293:30;14359:31;14339:18;;;14332:59;14408:18;;27193:60:0;14079:353:1;27193:60:0;27267:12;27281:23;27308:6;-1:-1:-1;;;;;27308:11:0;27327:5;27334:4;27308:31;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;27266:73;;;;27357:51;27374:7;27383:10;27395:12;27357:16;:51::i;:::-;27350:58;26906:510;-1:-1:-1;;;;;;;26906:510:0:o;29592:762::-;29742:12;29771:7;29767:580;;;-1:-1:-1;29802:10:0;29795:17;;29767:580;29916:17;;:21;29912:424;;30164:10;30158:17;30225:15;30212:10;30208:2;30204:19;30197:44;29912:424;30307:12;30300:20;;-1:-1:-1;;;30300:20:0;;;;;;;;:::i;222:131:1:-;-1:-1:-1;;;;;297:31:1;;287:42;;277:70;;343:1;340;333:12;358:118;444:5;437:13;430:21;423:5;420:32;410:60;;466:1;463;456:12;481:382;546:6;554;607:2;595:9;586:7;582:23;578:32;575:52;;;623:1;620;613:12;575:52;662:9;649:23;681:31;706:5;681:31;:::i;:::-;731:5;-1:-1:-1;788:2:1;773:18;;760:32;801:30;760:32;801:30;:::i;:::-;850:7;840:17;;;481:382;;;;;:::o;868:180::-;927:6;980:2;968:9;959:7;955:23;951:32;948:52;;;996:1;993;986:12;948:52;-1:-1:-1;1019:23:1;;868:180;-1:-1:-1;868:180:1:o;1053:247::-;1112:6;1165:2;1153:9;1144:7;1140:23;1136:32;1133:52;;;1181:1;1178;1171:12;1133:52;1220:9;1207:23;1239:31;1264:5;1239:31;:::i;1497:258::-;1569:1;1579:113;1593:6;1590:1;1587:13;1579:113;;;1669:11;;;1663:18;1650:11;;;1643:39;1615:2;1608:10;1579:113;;;1710:6;1707:1;1704:13;1701:48;;;-1:-1:-1;;1745:1:1;1727:16;;1720:27;1497:258::o;1760:383::-;1909:2;1898:9;1891:21;1872:4;1941:6;1935:13;1984:6;1979:2;1968:9;1964:18;1957:34;2000:66;2059:6;2054:2;2043:9;2039:18;2034:2;2026:6;2022:15;2000:66;:::i;:::-;2127:2;2106:15;-1:-1:-1;;2102:29:1;2087:45;;;;2134:2;2083:54;;1760:383;-1:-1:-1;;1760:383:1:o;2148:127::-;2209:10;2204:3;2200:20;2197:1;2190:31;2240:4;2237:1;2230:15;2264:4;2261:1;2254:15;2280:257;2352:4;2346:11;;;2384:17;;2431:18;2416:34;;2452:22;;;2413:62;2410:88;;;2478:18;;:::i;:::-;2514:4;2507:24;2280:257;:::o;2542:253::-;2614:2;2608:9;2656:4;2644:17;;2691:18;2676:34;;2712:22;;;2673:62;2670:88;;;2738:18;;:::i;2800:275::-;2871:2;2865:9;2936:2;2917:13;;-1:-1:-1;;2913:27:1;2901:40;;2971:18;2956:34;;2992:22;;;2953:62;2950:88;;;3018:18;;:::i;:::-;3054:2;3047:22;2800:275;;-1:-1:-1;2800:275:1:o;3080:160::-;3146:20;;3206:1;3195:20;;;3185:31;;3175:59;;3230:1;3227;3220:12;3245:1149;3314:5;3367:3;3360:4;3352:6;3348:17;3344:27;3334:55;;3385:1;3382;3375:12;3334:55;3421:6;3408:20;3447:4;3470:18;3466:2;3463:26;3460:52;;;3492:18;;:::i;:::-;3532:36;3564:2;3559;3556:1;3552:10;3548:19;3532:36;:::i;:::-;3602:15;;;3688:1;3684:10;;;;3672:23;;3668:32;;;3633:12;;;;3712:15;;;3709:35;;;3740:1;3737;3730:12;3709:35;3776:2;3768:6;3764:15;3788:577;3804:6;3799:3;3796:15;3788:577;;;3882:4;3876:3;3871;3867:13;3863:24;3860:114;;;3928:1;3957:2;3953;3946:14;3860:114;4000:22;;:::i;:::-;4063:3;4050:17;4080:33;4105:7;4080:33;:::i;:::-;4126:22;;4189:12;;;4176:26;4215:33;4176:26;4215:33;:::i;:::-;4268:14;;;4261:31;4305:18;;4343:12;;;;3830:4;3821:14;3788:577;;;-1:-1:-1;4383:5:1;3245:1149;-1:-1:-1;;;;;;3245:1149:1:o;4399:1604::-;4484:6;4537:2;4525:9;4516:7;4512:23;4508:32;4505:52;;;4553:1;4550;4543:12;4505:52;4593:9;4580:23;4622:18;4663:2;4655:6;4652:14;4649:34;;;4679:1;4676;4669:12;4649:34;4702:22;;;;4743:16;;;4779:6;4771:15;;4768:35;;;4799:1;4796;4789:12;4768:35;4825:22;;:::i;:::-;4884:2;4871:16;4896:33;4921:7;4896:33;:::i;:::-;4938:22;;5013:2;5005:11;;;4992:25;4976:14;;;4969:49;5071:2;5063:11;;;5050:25;5034:14;;;5027:49;5110:4;-1:-1:-1;;5092:16:1;;5088:27;5085:47;;;5128:1;5125;5118:12;5085:47;5156:22;;:::i;:::-;5141:37;;5223:2;5219;5215:11;5202:25;5236:33;5261:7;5236:33;:::i;:::-;5278:24;;5347:3;5339:12;;5326:26;5361:33;5326:26;5361:33;:::i;:::-;5423:2;5410:16;;5403:33;5481:4;5473:13;;5460:27;5531:8;5518:22;;5506:35;;5496:63;;5555:1;5552;5545:12;5496:63;5588:2;5575:16;;5568:33;5635:30;5660:3;5652:12;;5635:30;:::i;:::-;5630:2;5621:7;5617:16;5610:56;5701:30;5726:3;5722:2;5718:12;5701:30;:::i;:::-;5695:3;5686:7;5682:17;5675:57;5764:7;5759:2;5752:5;5748:14;5741:31;5818:3;5814:2;5810:12;5797:26;5781:42;;5848:2;5838:8;5835:16;5832:36;;;5864:1;5861;5854:12;5832:36;5901:71;5964:7;5953:8;5949:2;5945:17;5901:71;:::i;:::-;5895:3;5884:15;;5877:96;5888:5;4399:1604;-1:-1:-1;;;;;;4399:1604:1:o;6190:388::-;6258:6;6266;6319:2;6307:9;6298:7;6294:23;6290:32;6287:52;;;6335:1;6332;6325:12;6287:52;6374:9;6361:23;6393:31;6418:5;6393:31;:::i;:::-;6443:5;-1:-1:-1;6500:2:1;6485:18;;6472:32;6513:33;6472:32;6513:33;:::i;8304:1473::-;8662:4;8691:3;8732:2;8721:9;8717:18;8762:6;8751:9;8744:25;8788:2;8826:6;8821:2;8810:9;8806:18;8799:34;8852:2;8890:1;8886;8881:3;8877:11;8873:19;8947:2;8938:6;8932:13;8928:22;8923:2;8912:9;8908:18;8901:50;9015:2;9009;9001:6;8997:15;8991:22;8987:31;8982:2;8971:9;8967:18;8960:59;9084:8;9078:2;9070:6;9066:15;9060:22;9056:37;9050:3;9039:9;9035:19;9028:66;9163:2;9155:6;9151:15;9145:22;9142:1;9131:37;9125:3;9114:9;9110:19;9103:66;9238:3;9230:6;9226:16;9220:23;9217:1;9206:38;9200:3;9189:9;9185:19;9178:67;9282:2;9276:3;9265:9;9261:19;9254:31;9305:6;9294:17;;9340:6;9334:13;9371:6;9363;9356:22;9409:3;9398:9;9394:19;9387:26;;9448:2;9440:6;9436:15;9422:29;;9469:1;9479:272;9493:6;9490:1;9487:13;9479:272;;;9552:13;;9594:9;;9590:18;;9578:31;;9653:11;;9647:18;9643:27;;9629:12;;;9622:49;9691:12;;;;9726:15;;;;9515:1;9508:9;9479:272;;;-1:-1:-1;9768:3:1;;8304:1473;-1:-1:-1;;;;;;;;;;;8304:1473:1:o;12180:446::-;12268:6;12276;12284;12337:2;12325:9;12316:7;12312:23;12308:32;12305:52;;;12353:1;12350;12343:12;12305:52;12385:9;12379:16;12404:31;12429:5;12404:31;:::i;:::-;12504:2;12489:18;;12483:25;12454:5;;-1:-1:-1;12517:33:1;12483:25;12517:33;:::i;:::-;12569:7;12559:17;;;12616:2;12605:9;12601:18;12595:25;12585:35;;12180:446;;;;;:::o;13011:245::-;13078:6;13131:2;13119:9;13110:7;13106:23;13102:32;13099:52;;;13147:1;13144;13137:12;13099:52;13179:9;13173:16;13198:28;13220:5;13198:28;:::i;14437:274::-;14566:3;14604:6;14598:13;14620:53;14666:6;14661:3;14654:4;14646:6;14642:17;14620:53;:::i;:::-;14689:16;;;;;14437:274;-1:-1:-1;;14437:274:1:o

Metadata Hash

f88813749133ce299e89049bd0083fbaf53d64fd7970387515c329f38cf607cc
Block Transaction Gas Used Reward
Age Block Fee Address BC Fee Address Voting Power Jailed Incoming
Block Uncle Number Difficulty Gas Used Reward
Loading
Make sure to use the "Vote Down" button for any spammy posts, and the "Vote Up" for interesting conversations.