ETH Price: $3,519.87 (+2.53%)
Gas: 6 Gwei

Contract

0x8Ad3668B088Bf7abEE6488Da8F6570e8c3586c22
 

Multichain Info

No addresses found
Transaction Hash
Method
Block
From
To
Set Approval For...188851522023-12-28 15:59:23204 days ago1703779163IN
0x8Ad3668B...8c3586c22
0 ETH0.0015689433.97742581
Withdraw161567982022-12-10 20:49:23587 days ago1670705363IN
0x8Ad3668B...8c3586c22
0 ETH0.0004345112.97918466
Withdraw161567962022-12-10 20:48:59587 days ago1670705339IN
0x8Ad3668B...8c3586c22
0 ETH0.0015559312.09009759
Withdraw159825942022-11-16 12:29:59611 days ago1668601799IN
0x8Ad3668B...8c3586c22
0 ETH0.0060659114.73973899
Withdraw158965682022-11-04 12:11:47623 days ago1667563907IN
0x8Ad3668B...8c3586c22
0 ETH0.0017060513.40292033
Withdraw158488922022-10-28 20:16:47630 days ago1666988207IN
0x8Ad3668B...8c3586c22
0 ETH0.0022080824.19319074
Fill Order158456472022-10-28 9:25:35630 days ago1666949135IN
0x8Ad3668B...8c3586c22
0 ETH0.0017756810.93683537
Exercise158412582022-10-27 18:40:23631 days ago1666896023IN
0x8Ad3668B...8c3586c22
5.75 ETH0.0049893728.61077862
Withdraw158357472022-10-27 0:12:11631 days ago1666829531IN
0x8Ad3668B...8c3586c22
0 ETH0.0008530311.70947632
Withdraw158357462022-10-27 0:11:59631 days ago1666829519IN
0x8Ad3668B...8c3586c22
0 ETH0.0008644611.86631706
Withdraw158357452022-10-27 0:11:47631 days ago1666829507IN
0x8Ad3668B...8c3586c22
0 ETH0.0008323511.42747311
Withdraw158357442022-10-27 0:11:35631 days ago1666829495IN
0x8Ad3668B...8c3586c22
0 ETH0.0008540211.72496146
Withdraw158357422022-10-27 0:11:11631 days ago1666829471IN
0x8Ad3668B...8c3586c22
0 ETH0.0008288911.38003763
Withdraw158357412022-10-27 0:10:59631 days ago1666829459IN
0x8Ad3668B...8c3586c22
0 ETH0.0008489511.65541029
Withdraw158357402022-10-27 0:10:47631 days ago1666829447IN
0x8Ad3668B...8c3586c22
0 ETH0.0010598411.78417294
Exercise158338272022-10-26 17:45:35632 days ago1666806335IN
0x8Ad3668B...8c3586c22
1.04 ETH0.0038820922.81223207
Exercise158336752022-10-26 17:14:59632 days ago1666804499IN
0x8Ad3668B...8c3586c22
5.6 ETH0.0048411327.97177365
Exercise158334042022-10-26 16:20:35632 days ago1666801235IN
0x8Ad3668B...8c3586c22
0 ETH0.0047525225.73966727
Exercise158334012022-10-26 16:19:59632 days ago1666801199IN
0x8Ad3668B...8c3586c22
0 ETH0.0042767824.53070648
Exercise158333682022-10-26 16:13:23632 days ago1666800803IN
0x8Ad3668B...8c3586c22
0 ETH0.0036551621.31418574
Exercise158332982022-10-26 15:59:23632 days ago1666799963IN
0x8Ad3668B...8c3586c22
0 ETH0.0044470525.80428002
Exercise158332892022-10-26 15:57:35632 days ago1666799855IN
0x8Ad3668B...8c3586c22
0 ETH0.0050192127.18409906
Withdraw158331082022-10-26 15:21:11632 days ago1666797671IN
0x8Ad3668B...8c3586c22
0 ETH0.0039598544.03454972
Exercise158330652022-10-26 15:12:35632 days ago1666797155IN
0x8Ad3668B...8c3586c22
0 ETH0.0124921568.71223314
Withdraw158287552022-10-26 0:47:11632 days ago1666745231IN
0x8Ad3668B...8c3586c22
0 ETH0.0008449211.38824464
View all transactions

Latest 25 internal transactions (View All)

Advanced mode:
Parent Transaction Hash Block From To
158412582022-10-27 18:40:23631 days ago1666896023
0x8Ad3668B...8c3586c22
5.75 ETH
158338272022-10-26 17:45:35632 days ago1666806335
0x8Ad3668B...8c3586c22
1.04 ETH
158336752022-10-26 17:14:59632 days ago1666804499
0x8Ad3668B...8c3586c22
5.6 ETH
157970682022-10-21 14:26:47637 days ago1666362407
0x8Ad3668B...8c3586c22
1.9 ETH
157871682022-10-20 5:17:59638 days ago1666243079
0x8Ad3668B...8c3586c22
2.1 ETH
157871512022-10-20 5:14:35638 days ago1666242875
0x8Ad3668B...8c3586c22
2.1 ETH
157871452022-10-20 5:13:23638 days ago1666242803
0x8Ad3668B...8c3586c22
2.1 ETH
157836812022-10-19 17:35:35639 days ago1666200935
0x8Ad3668B...8c3586c22
0.96 ETH
157490162022-10-14 21:24:35644 days ago1665782675
0x8Ad3668B...8c3586c22
2.7 ETH
157111442022-10-09 14:30:35649 days ago1665325835
0x8Ad3668B...8c3586c22
0.7 ETH
156898102022-10-06 14:59:23652 days ago1665068363
0x8Ad3668B...8c3586c22
0.75 ETH
156894192022-10-06 13:40:35652 days ago1665063635
0x8Ad3668B...8c3586c22
1.04 ETH
156820462022-10-05 12:57:47653 days ago1664974667
0x8Ad3668B...8c3586c22
1.9 ETH
156817112022-10-05 11:49:59653 days ago1664970599
0x8Ad3668B...8c3586c22
0.7 ETH
156757882022-10-04 15:53:59654 days ago1664898839
0x8Ad3668B...8c3586c22
0.7 ETH
156757822022-10-04 15:52:47654 days ago1664898767
0x8Ad3668B...8c3586c22
0.7 ETH
156737582022-10-04 9:03:35654 days ago1664874215
0x8Ad3668B...8c3586c22
0.75 ETH
156720562022-10-04 3:20:23654 days ago1664853623
0x8Ad3668B...8c3586c22
0.002 ETH
156720232022-10-04 3:13:47654 days ago1664853227
0x8Ad3668B...8c3586c22
0.002 ETH
156720002022-10-04 3:08:59654 days ago1664852939
0x8Ad3668B...8c3586c22
0.001 ETH
156719692022-10-04 3:02:47654 days ago1664852567
0x8Ad3668B...8c3586c22
0.001 ETH
156705312022-10-03 22:13:11655 days ago1664835191
0x8Ad3668B...8c3586c22
0.7 ETH
156701012022-10-03 20:46:47655 days ago1664830007
0x8Ad3668B...8c3586c22
0.88 ETH
156700962022-10-03 20:45:47655 days ago1664829947
0x8Ad3668B...8c3586c22
0.6 ETH
156700912022-10-03 20:44:47655 days ago1664829887
0x8Ad3668B...8c3586c22
0.6 ETH
View All Internal Transactions
Loading...
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
PuttyV2

Compiler Version
v0.8.16+commit.07a7930e

Optimization Enabled:
Yes with 10000 runs

Other Settings:
default evmVersion
File 1 of 17 : PuttyV2.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.16;

/**
 * ██████╗ ██╗   ██╗████████╗████████╗██╗   ██╗    ██╗   ██╗██████╗
 * ██╔══██╗██║   ██║╚══██╔══╝╚══██╔══╝╚██╗ ██╔╝    ██║   ██║╚════██╗
 * ██████╔╝██║   ██║   ██║      ██║    ╚████╔╝     ██║   ██║ █████╔╝
 * ██╔═══╝ ██║   ██║   ██║      ██║     ╚██╔╝      ╚██╗ ██╔╝██╔═══╝
 * ██║     ╚██████╔╝   ██║      ██║      ██║        ╚████╔╝ ███████╗
 * ╚═╝      ╚═════╝    ╚═╝      ╚═╝      ╚═╝         ╚═══╝  ╚══════╝
 *
 *
 * _..._
 * .'     '.      _
 * /    .-""-\   _/ \
 * .-|   /:.   |  |   |   bussin
 * |  \  |:.   /.-'-./
 * | .-'-;:__.'    =/
 * .'=  *=|     _.='
 * /   _.  |    ;        minister you satoshi
 * ;-.-'|    \   |
 * /   | \    _\  _\
 * \__/'._;.  ==' ==\
 * \    \   |
 * /    /   /
 * /-._/-._/
 * jgs    \   `\  \
 * `-._/._/
 *
 *
 * this is a public good.
 * by out.eth and tamagoyaki
 */

import "./lib/IWETH.sol";

import "openzeppelin/utils/cryptography/SignatureChecker.sol";
import "openzeppelin/utils/cryptography/draft-EIP712.sol";
import "openzeppelin/utils/Strings.sol";
import "openzeppelin/utils/introspection/ERC165Checker.sol";
import "openzeppelin/access/Ownable.sol";
import "solmate/utils/SafeTransferLib.sol";
import "solmate/tokens/ERC721.sol";

import "./PuttyV2Nft.sol";
import "./PuttyV2Handler.sol";

/**
 * @title PuttyV2
 * @author out.eth
 * @notice An otc erc721 and erc20 option market.
 */
contract PuttyV2 is PuttyV2Nft, EIP712("Putty", "2.0"), ERC721TokenReceiver, Ownable {
    /* ~~~ TYPES ~~~ */

    using SafeTransferLib for ERC20;

    /**
     * @notice ERC20 asset details.
     * @param token The token address for the erc20 asset.
     * @param tokenAmount The amount of erc20 tokens.
     */
    struct ERC20Asset {
        address token;
        uint256 tokenAmount;
    }

    /**
     * @notice ERC721 asset details.
     * @param token The token address for the erc721 asset.
     * @param tokenId The token id of the erc721 assset.
     */
    struct ERC721Asset {
        address token;
        uint256 tokenId;
    }

    /**
     * @notice Order details.
     * @param maker The maker of the order.
     * @param isCall Whether or not the order is for a call or put option.
     * @param isLong Whether or not the order is long or short.
     * @param baseAsset The erc20 contract to use for the strike and premium.
     * @param strike The strike amount.
     * @param premium The premium amount.
     * @param duration The duration of the option contract (in seconds).
     * @param expiration The timestamp after which the order is no longer (unix).
     * @param nonce A random number for each order to prevent hash collisions and also check order validity.
     * @param whitelist A list of addresses that are allowed to fill this order - if empty then anyone can fill.
     * @param floorTokens A list of erc721 contract addresses for the underlying.
     * @param erc20Assets A list of erc20 assets for the underlying.
     * @param erc721Assets A list of erc721 assets for the underlying.
     */
    struct Order {
        address maker;
        bool isCall;
        bool isLong;
        address baseAsset;
        uint256 strike;
        uint256 premium;
        uint256 duration;
        uint256 expiration;
        uint256 nonce;
        address[] whitelist;
        address[] floorTokens;
        ERC20Asset[] erc20Assets;
        ERC721Asset[] erc721Assets;
    }

    /* ~~~ STATE VARIABLES ~~~ */

    /**
     * @dev ERC721Asset type hash used for EIP-712 encoding.
     */
    bytes32 public constant ERC721ASSET_TYPE_HASH = keccak256("ERC721Asset(address token,uint256 tokenId)");

    /**
     * @dev ERC20Asset type hash used for EIP-712 encoding.
     */
    bytes32 public constant ERC20ASSET_TYPE_HASH = keccak256("ERC20Asset(address token,uint256 tokenAmount)");

    /**
     * @dev ORDER_TYPE_HASH type hash used for EIP-712 encoding.
     */
    bytes32 public constant ORDER_TYPE_HASH = keccak256(
        "Order(" "address maker," "bool isCall," "bool isLong," "address baseAsset," "uint256 strike," "uint256 premium,"
        "uint256 duration," "uint256 expiration," "uint256 nonce," "address[] whitelist," "address[] floorTokens,"
        "ERC20Asset[] erc20Assets," "ERC721Asset[] erc721Assets" ")" "ERC20Asset(address token,uint256 tokenAmount)"
        "ERC721Asset(address token,uint256 tokenId)"
    );

    /**
     * @dev Contract address for Wrapped Ethereum.
     */
    address public immutable weth;

    /**
     * @dev baseURI used to generate the tokenURI for PuttyV2 NFTs.
     */
    string public baseURI;

    /**
     * @notice Fee rate that is applied on premiums.
     */
    uint256 public fee;

    /**
     * @notice Whether or not an order has been cancelled. Maps
     * from orderHash to isCancelled.
     */
    mapping(bytes32 => bool) public cancelledOrders;

    /**
     * @notice The current expiration timestamp of a position. Maps
     * from positionId to an expiration unix timestamp.
     */
    mapping(uint256 => uint256) public positionExpirations;

    /**
     * @notice Whether or not a position has been exercised. Maps
     * from positionId to isExercised.
     */
    mapping(uint256 => bool) public exercisedPositions;

    /**
     * @notice The floor asset token ids for a position. Maps from
     * positionId to floor asset token ids. This should only
     * be set for a long call position in `fillOrder`, or for
     * a short put position in `exercise`.
     */
    mapping(uint256 => uint256[]) public positionFloorAssetTokenIds;

    /**
     * @notice The total unclaimed premium fees for each asset.
     */
    mapping(address => uint256) public unclaimedFees;

    /**
     * @notice The minimum valid nonce for each address.
     */
    mapping(address => uint256) public minimumValidNonce;

    /* ~~~ EVENTS ~~~ */

    /**
     * @notice Emitted when a new base URI is set.
     * @param baseURI The new baseURI.
     */
    event NewBaseURI(string baseURI);

    /**
     * @notice Emitted when a new fee is set.
     * @param fee The new fee.
     */
    event NewFee(uint256 fee);

    /**
     * @notice Emitted when fees are withdrawn.
     * @param asset The asset which fees are being withdrawn for.
     * @param fees The amount of fees that are being withdrawn.
     * @param recipient The recipient address for the fees.
     */
    event WithdrewFees(address indexed asset, uint256 fees, address recipient);

    /**
     * @notice Emitted when an order is filled.
     * @param orderHash The hash of the order that was filled.
     * @param oppositeOrderHash The opposite hash of the order that was filled.
     * @param floorAssetTokenIds The floor asset token ids that were used.
     * @param order The order that was filled.
     */
    event FilledOrder(
        bytes32 indexed orderHash, bytes32 indexed oppositeOrderHash, uint256[] floorAssetTokenIds, Order order
    );

    /**
     * @notice Emitted when an order is exercised.
     * @param orderHash The hash of the order that was exercised.
     * @param floorAssetTokenIds The floor asset token ids that were used.
     * @param order The order that was exercised.
     */
    event ExercisedOrder(bytes32 indexed orderHash, uint256[] floorAssetTokenIds, Order order);

    /**
     * @notice Emitted when an order is withdrawn.
     * @param orderHash The hash of the order that was withdrawn.
     * @param order The order that was withdrawn.
     */
    event WithdrawOrder(bytes32 indexed orderHash, Order order);

    /**
     * @notice Emitted when an order is cancelled.
     * @param orderHash The hash of the order that was cancelled.
     * @param order The order that was cancelled.
     */
    event CancelledOrder(bytes32 indexed orderHash, Order order);

    /**
     * @notice Emitted when a user sets their minimum valid nonce.
     * @param minimumValidNonce The new minimum valid nonce.
     */
    event SetMinimumValidNonce(uint256 minimumValidNonce);

    constructor(string memory _baseURI, uint256 _fee, address _weth) {
        require(_weth != address(0), "Must set weth address");

        setBaseURI(_baseURI);
        setFee(_fee);
        weth = _weth;
    }

    /* ~~~ ADMIN FUNCTIONS ~~~ */

    /**
     * @notice Sets a new baseURI that is used in the construction
     * of the tokenURI for each NFT position. Admin/DAO only.
     * @param _baseURI The new base URI to use.
     */
    function setBaseURI(string memory _baseURI) public payable onlyOwner {
        baseURI = _baseURI;

        emit NewBaseURI(_baseURI);
    }

    /**
     * @notice Sets a new fee rate that is applied on premiums. The
     * fee has a precision of 1 decimal. e.g. 1000 = 100%,
     * 100 = 10%, 1 = 0.1%. Admin/DAO only.
     * @param _fee The new fee rate to use.
     */
    function setFee(uint256 _fee) public payable onlyOwner {
        require(_fee < 30, "fee must be less than 3%");

        fee = _fee;

        emit NewFee(_fee);
    }

    /**
     * @notice Withdraws the fees that have been collected from premiums for a particular asset.
     * @param asset The asset to collect fees for.
     * @param recipient The recipient address for the unclaimed fees.
     */
    function withdrawFees(address asset, address recipient) public payable onlyOwner {
        uint256 fees = unclaimedFees[asset];

        // reset the fees
        unclaimedFees[asset] = 0;

        emit WithdrewFees(asset, fees, recipient);

        // send the fees to the recipient
        ERC20(asset).safeTransfer(recipient, fees);
    }

    /*
        ~~~ MAIN LOGIC FUNCTIONS ~~~

        Standard lifecycle:
            [1] fillOrder()
            [2] exercise()
            [3] withdraw()

            * It is also possible to cancel() an order before fillOrder()
    */

    /**
     * @notice Fills an offchain order and settles it onchain. Mints two
     * NFTs that represent the long and short position for the order.
     * @param order The order to fill.
     * @param signature The signature for the order. Signature must recover to order.maker.
     * @param floorAssetTokenIds The floor asset token ids to use. Should only be set
     * when filling a long call order.
     * @return positionId The id of the position NFT that the msg.sender receives.
     */
    function fillOrder(Order memory order, bytes calldata signature, uint256[] memory floorAssetTokenIds)
        public
        payable
        returns (uint256 positionId)
    {
        /* ~~~ CHECKS ~~~ */

        bytes32 orderHash = hashOrder(order);

        // check signature is valid using EIP-712
        require(SignatureChecker.isValidSignatureNow(order.maker, orderHash, signature), "Invalid signature");

        // check order is not cancelled
        require(!cancelledOrders[orderHash], "Order has been cancelled");

        // check order nonce is valid
        require(order.nonce >= minimumValidNonce[order.maker], "Nonce is smaller than min");

        // check msg.sender is allowed to fill the order
        require(order.whitelist.length == 0 || isWhitelisted(order.whitelist, msg.sender), "Not whitelisted");

        // check duration is not too long
        require(order.duration <= 10_000 days, "Duration too long");

        // check duration is not too short
        require(order.duration >= 15 minutes, "Duration too short");

        // check order has not expired
        require(block.timestamp < order.expiration, "Order has expired");

        // check base asset exists
        require(order.baseAsset.code.length > 0, "baseAsset is not contract");

        // check short call doesn't have floor tokens
        if (order.isCall && !order.isLong) {
            require(order.floorTokens.length == 0, "Short call cant have floorTokens");
        }

        // check native eth is only used if baseAsset is weth
        require(msg.value == 0 || order.baseAsset == address(weth), "Cannot use native ETH");

        // check floor asset token ids length is 0 unless the order type is call and side is long
        order.isCall && order.isLong
            ? require(floorAssetTokenIds.length == order.floorTokens.length, "Wrong amount of floor tokenIds")
            : require(floorAssetTokenIds.length == 0, "Invalid floor tokens length");

        /*  ~~~ EFFECTS ~~~ */

        // create long/short position for maker
        _mint(order.maker, uint256(orderHash));

        // create opposite long/short position for taker
        bytes32 oppositeOrderHash = hashOppositeOrder(order);
        positionId = uint256(oppositeOrderHash);
        _mint(msg.sender, positionId);

        // save floorAssetTokenIds if filling a long call order
        if (order.isLong && order.isCall) {
            positionFloorAssetTokenIds[uint256(orderHash)] = floorAssetTokenIds;
        }

        // save the long position expiration
        positionExpirations[order.isLong ? uint256(orderHash) : positionId] = block.timestamp + order.duration;

        emit FilledOrder(orderHash, oppositeOrderHash, floorAssetTokenIds, order);

        /* ~~~ INTERACTIONS ~~~ */

        // calculate the fee amount
        uint256 feeAmount = 0;
        if (fee > 0) {
            feeAmount = (order.premium * fee) / 1000;
            unclaimedFees[order.baseAsset] += feeAmount;
        }

        // transfer premium to whoever is short from whomever is long
        if (order.premium > 0) {
            if (order.isLong) {
                // transfer premium to taker
                ERC20(order.baseAsset).safeTransferFrom(order.maker, msg.sender, order.premium - feeAmount);

                // collect fees
                if (feeAmount > 0) {
                    ERC20(order.baseAsset).safeTransferFrom(order.maker, address(this), feeAmount);
                }
            } else {
                // handle the case where the user uses native ETH instead of WETH to pay the premium
                if (msg.value > 0) {
                    // check enough ETH was sent to cover the premium
                    require(msg.value == order.premium, "Incorrect ETH amount sent");

                    // convert ETH to WETH and send premium to maker
                    // converting to WETH instead of forwarding native ETH to the maker has two benefits;
                    // 1) active market makers will mostly be using WETH not native ETH
                    // 2) attack surface for re-entrancy is reduced
                    IWETH(weth).deposit{value: order.premium}();

                    // collect fees and transfer to premium to maker
                    IWETH(weth).transfer(order.maker, order.premium - feeAmount);
                } else {
                    // transfer premium to maker
                    ERC20(order.baseAsset).safeTransferFrom(msg.sender, order.maker, order.premium - feeAmount);

                    // collect fees
                    if (feeAmount > 0) {
                        ERC20(order.baseAsset).safeTransferFrom(msg.sender, address(this), feeAmount);
                    }
                }
            }
        }

        if (!order.isLong && !order.isCall) {
            // filling short put: transfer strike from maker to contract
            ERC20(order.baseAsset).safeTransferFrom(order.maker, address(this), order.strike);
        } else if (order.isLong && !order.isCall) {
            // filling long put: transfer strike from taker to contract
            // handle the case where the taker uses native ETH instead of WETH to deposit the strike
            if (msg.value > 0) {
                // check enough ETH was sent to cover the strike
                require(msg.value == order.strike, "Incorrect ETH amount sent");

                // convert ETH to WETH
                // we convert the strike ETH to WETH so that the logic in exercise() works
                // - because exercise() assumes an ERC20 interface on the base asset.
                IWETH(weth).deposit{value: msg.value}();
            } else {
                ERC20(order.baseAsset).safeTransferFrom(msg.sender, address(this), order.strike);
            }
        } else if (!order.isLong && order.isCall) {
            // filling short call: transfer assets from maker to contract
            _transferERC20sIn(order.erc20Assets, order.maker);
            _transferERC721sIn(order.erc721Assets, order.maker);
        } else if (order.isLong && order.isCall) {
            // filling long call: transfer assets from taker to contract
            // long calls never need native ETH
            require(msg.value == 0, "Long call can't use native ETH");

            _transferERC20sIn(order.erc20Assets, msg.sender);
            _transferERC721sIn(order.erc721Assets, msg.sender);
            _transferFloorsIn(order.floorTokens, floorAssetTokenIds, msg.sender);
        }

        if (ERC165Checker.supportsInterface(order.maker, type(IPuttyV2Handler).interfaceId)) {
            // callback the maker with onFillOrder - save 15k gas in case of revert
            order.maker.call{gas: gasleft() - 15_000}(
                abi.encodeWithSelector(PuttyV2Handler.onFillOrder.selector, order, msg.sender, floorAssetTokenIds)
            );
        }
    }

    /**
     * @notice Exercises a long order and also burns the long position NFT which
     * represents it.
     * @param order The order of the position to exercise.
     * @param floorAssetTokenIds The floor asset token ids to use. Should only be set
     * when exercising a put order.
     */
    function exercise(Order memory order, uint256[] calldata floorAssetTokenIds) public payable {
        /* ~~~ CHECKS ~~~ */

        bytes32 orderHash = hashOrder(order);

        // check user owns the position
        require(ownerOf(uint256(orderHash)) == msg.sender, "Not owner");

        // check position is long
        require(order.isLong, "Can only exercise long positions");

        // check position has not expired
        require(block.timestamp < positionExpirations[uint256(orderHash)], "Position has expired");

        // check native eth is only used if baseAsset is weth
        require(msg.value == 0 || order.baseAsset == address(weth), "Cannot use native ETH");

        // check floor asset token ids length is 0 unless the position type is put
        !order.isCall
            ? require(floorAssetTokenIds.length == order.floorTokens.length, "Wrong amount of floor tokenIds")
            : require(floorAssetTokenIds.length == 0, "Invalid floor tokenIds length");

        /* ~~~ EFFECTS ~~~ */

        // send the long position to 0xdead.
        // instead of doing a standard burn by sending to 0x000...000, sending
        // to 0xdead ensures that the same position id cannot be minted again.
        transferFrom(msg.sender, address(0xdead), uint256(orderHash));

        // mark the position as exercised
        exercisedPositions[uint256(orderHash)] = true;

        emit ExercisedOrder(orderHash, floorAssetTokenIds, order);

        /* ~~~ INTERACTIONS ~~~ */

        uint256 shortPositionId = uint256(hashOppositeOrder(order));
        if (order.isCall) {
            // -- exercising a call option

            // transfer strike from exerciser to putty
            // handle the case where the taker uses native ETH instead of WETH to pay the strike
            if (order.strike > 0) {
                if (msg.value > 0) {
                    // check enough ETH was sent to cover the strike
                    require(msg.value == order.strike, "Incorrect ETH amount sent");

                    // convert ETH to WETH
                    // we convert the strike ETH to WETH so that the logic in withdraw() works
                    // - because withdraw() assumes an ERC20 interface on the base asset.
                    IWETH(weth).deposit{value: msg.value}();
                } else {
                    ERC20(order.baseAsset).safeTransferFrom(msg.sender, address(this), order.strike);
                }
            }

            // transfer assets from putty to exerciser
            _transferERC20sOut(order.erc20Assets);
            _transferERC721sOut(order.erc721Assets);
            _transferFloorsOut(order.floorTokens, positionFloorAssetTokenIds[uint256(orderHash)]);
        } else {
            // -- exercising a put option
            // exercising a put never needs native ETH
            require(msg.value == 0, "Puts can't use native ETH");

            // save the floor asset token ids to the short position
            positionFloorAssetTokenIds[shortPositionId] = floorAssetTokenIds;

            // transfer strike from putty to exerciser
            ERC20(order.baseAsset).safeTransfer(msg.sender, order.strike);

            // transfer assets from exerciser to putty
            _transferERC20sIn(order.erc20Assets, msg.sender);
            _transferERC721sIn(order.erc721Assets, msg.sender);
            _transferFloorsIn(order.floorTokens, floorAssetTokenIds, msg.sender);
        }

        // attempt call onExercise on the short position owner
        address shortOwner = ownerOf(shortPositionId);
        order.isLong = false;
        if (ERC165Checker.supportsInterface(shortOwner, type(IPuttyV2Handler).interfaceId)) {
            // callback the short owner with onExercise - save 15k gas in case of revert
            shortOwner.call{gas: gasleft() - 15_000}(
                abi.encodeWithSelector(PuttyV2Handler.onExercise.selector, order, msg.sender, floorAssetTokenIds)
            );
        }
    }

    /**
     * @notice Withdraws the assets from a short order and also burns the short position
     * that represents it. The assets that are withdrawn are dependent on whether
     * the order is exercised or expired and if the order is a put or call.
     * @param order The order to withdraw.
     */
    function withdraw(Order memory order) public {
        /* ~~~ CHECKS ~~~ */

        // check order is short
        require(!order.isLong, "Must be short position");

        bytes32 orderHash = hashOrder(order);

        // check msg.sender owns the position
        require(ownerOf(uint256(orderHash)) == msg.sender, "Not owner");

        uint256 longPositionId = uint256(hashOppositeOrder(order));
        bool isExercised = exercisedPositions[longPositionId];

        // check long position has either been exercised or is expired
        require(isExercised || block.timestamp > positionExpirations[longPositionId], "Must be exercised or expired");

        /* ~~~ EFFECTS ~~~ */

        // send the short position to 0xdead.
        // instead of doing a standard burn by sending to 0x000...000, sending
        // to 0xdead ensures that the same position id cannot be minted again.
        transferFrom(msg.sender, address(0xdead), uint256(orderHash));

        emit WithdrawOrder(orderHash, order);

        /* ~~~ INTERACTIONS ~~~ */

        // transfer strike to owner if put is expired or call is exercised
        if ((order.isCall && isExercised) || (!order.isCall && !isExercised)) {
            ERC20(order.baseAsset).safeTransfer(msg.sender, order.strike);
            return;
        }

        // transfer assets from putty to owner if put is exercised or call is expired
        if ((order.isCall && !isExercised) || (!order.isCall && isExercised)) {
            _transferERC20sOut(order.erc20Assets);
            _transferERC721sOut(order.erc721Assets);

            // for call options the floor token ids are saved in the long position in fillOrder(),
            // and for put options the floor tokens ids are saved in the short position in exercise()
            uint256 floorPositionId = order.isCall ? longPositionId : uint256(orderHash);
            _transferFloorsOut(order.floorTokens, positionFloorAssetTokenIds[floorPositionId]);

            return;
        }
    }

    /**
     * @notice Cancels an order which prevents it from being filled in the future.
     * @param order The order to cancel.
     */
    function cancel(Order memory order) public {
        require(msg.sender == order.maker, "Not your order");

        bytes32 orderHash = hashOrder(order);
        require(_ownerOf[uint256(orderHash)] == address(0), "Order already filled");

        // mark the order as cancelled
        cancelledOrders[orderHash] = true;

        emit CancelledOrder(orderHash, order);
    }

    /* ~~~ PERIPHERY LOGIC FUNCTIONS ~~~ */

    /**
     * @notice Batch fills multiple orders.
     * @dev Purposefully marked as non-payable otherwise the msg.value can be used multiple times in fillOrder.
     * @param orders The orders to fill.
     * @param signatures The signatures to use for each respective order.
     * @param floorAssetTokenIds The floorAssetTokenIds to use for each respective order.
     * @return positionIds The ids of the position NFT that the msg.sender receives.
     */
    function batchFillOrder(Order[] memory orders, bytes[] calldata signatures, uint256[][] memory floorAssetTokenIds)
        public
        returns (uint256[] memory positionIds)
    {
        require(
            orders.length == signatures.length && signatures.length == floorAssetTokenIds.length,
            "Length mismatch in input"
        );

        positionIds = new uint256[](orders.length);

        for (uint256 i = 0; i < orders.length; i++) {
            positionIds[i] = fillOrder(orders[i], signatures[i], floorAssetTokenIds[i]);
        }
    }

    /**
     * @notice Accepts a counter offer for an order. It cancels the original order that the counter
     * offer was made for and then it fills the counter offer.
     * @dev There is no need for floorTokenIds here because there is no situation in which
     * it makes sense to have them when accepting counter offers; When accepting a counter
     * offer for a short call order, the complementary long call order already knows what
     * tokenIds are used in the short call so floorTokens should always be empty.
     * @param order The counter offer to accept.
     * @param signature The signature for the counter offer.
     * @param originalOrder The original order that the counter was made for.
     * @return positionId The id of the position NFT that the msg.sender receives.
     */
    function acceptCounterOffer(Order memory order, bytes calldata signature, Order memory originalOrder)
        public
        payable
        returns (uint256 positionId)
    {
        // cancel the original order
        cancel(originalOrder);

        // accept the counter offer
        uint256[] memory floorAssetTokenIds = new uint256[](0);
        positionId = fillOrder(order, signature, floorAssetTokenIds);
    }

    /**
     * @notice Sets the minimum valid nonce for a user. Any unfilled orders with a nonce
     * smaller than this minimum will no longer be valid and will unable to be filled.
     * @param _minimumValidNonce The new minimum valid nonce.
     */
    function setMinimumValidNonce(uint256 _minimumValidNonce) public {
        require(_minimumValidNonce > minimumValidNonce[msg.sender], "Nonce should increase");
        minimumValidNonce[msg.sender] = _minimumValidNonce;

        emit SetMinimumValidNonce(_minimumValidNonce);
    }

    /* ~~~ HELPER FUNCTIONS ~~~ */

    /**
     * @notice Transfers an array of erc20s into the contract from an address.
     * @param assets The erc20 tokens and amounts to transfer in.
     * @param from Who to transfer the erc20 assets from.
     */
    function _transferERC20sIn(ERC20Asset[] memory assets, address from) internal {
        for (uint256 i = 0; i < assets.length; i++) {
            address token = assets[i].token;
            uint256 tokenAmount = assets[i].tokenAmount;

            require(token.code.length > 0, "ERC20: Token is not contract");

            if (tokenAmount > 0) {
                ERC20(token).safeTransferFrom(from, address(this), tokenAmount);
            }
        }
    }

    /**
     * @notice Transfers an array of erc721s into the contract from an address.
     * @param assets The erc721 tokens and token ids to transfer in.
     * @param from Who to transfer the erc721 assets from.
     */
    function _transferERC721sIn(ERC721Asset[] memory assets, address from) internal {
        for (uint256 i = 0; i < assets.length; i++) {
            ERC721(assets[i].token).safeTransferFrom(from, address(this), assets[i].tokenId);
        }
    }

    /**
     * @notice Transfers an array of erc721 floor tokens into the contract from an address.
     * @param floorTokens The contract addresses of each erc721.
     * @param floorTokenIds The token id of each erc721.
     * @param from Who to transfer the floor tokens from.
     */
    function _transferFloorsIn(address[] memory floorTokens, uint256[] memory floorTokenIds, address from) internal {
        for (uint256 i = 0; i < floorTokens.length; i++) {
            ERC721(floorTokens[i]).safeTransferFrom(from, address(this), floorTokenIds[i]);
        }
    }

    /**
     * @notice Transfers an array of erc20 tokens to the msg.sender.
     * @param assets The erc20 tokens and amounts to send.
     */
    function _transferERC20sOut(ERC20Asset[] memory assets) internal {
        for (uint256 i = 0; i < assets.length; i++) {
            if (assets[i].tokenAmount > 0) {
                ERC20(assets[i].token).safeTransfer(msg.sender, assets[i].tokenAmount);
            }
        }
    }

    /**
     * @notice Transfers an array of erc721 tokens to the msg.sender.
     * @param assets The erc721 tokens and token ids to send.
     */
    function _transferERC721sOut(ERC721Asset[] memory assets) internal {
        for (uint256 i = 0; i < assets.length; i++) {
            ERC721(assets[i].token).safeTransferFrom(address(this), msg.sender, assets[i].tokenId);
        }
    }

    /**
     * @notice Transfers an array of erc721 floor tokens to the msg.sender.
     * @param floorTokens The contract addresses for each floor token.
     * @param floorTokenIds The token id of each floor token.
     */
    function _transferFloorsOut(address[] memory floorTokens, uint256[] memory floorTokenIds) internal {
        for (uint256 i = 0; i < floorTokens.length; i++) {
            ERC721(floorTokens[i]).safeTransferFrom(address(this), msg.sender, floorTokenIds[i]);
        }
    }

    /**
     * @notice Checks whether or not an address exists in the whitelist.
     * @param whitelist The whitelist to check against.
     * @param target The target address to check.
     * @return If it exists in the whitelist or not.
     */
    function isWhitelisted(address[] memory whitelist, address target) public pure returns (bool) {
        for (uint256 i = 0; i < whitelist.length; i++) {
            if (target == whitelist[i]) {
                return true;
            }
        }

        return false;
    }

    /**
     * @notice Get the orderHash for a complementary short/long order - e.g for a long order,
     * this returns the hash of its opposite short order.
     * @param order The order to find the complementary long/short hash for.
     * @return orderHash The hash of the opposite order.
     */
    function hashOppositeOrder(Order memory order) public view returns (bytes32 orderHash) {
        // use decode/encode to get a copy instead of reference
        Order memory oppositeOrder = abi.decode(abi.encode(order), (Order));

        // get the opposite side of the order (short/long)
        oppositeOrder.isLong = !order.isLong;
        orderHash = hashOrder(oppositeOrder);
    }

    /* ~~~ EIP-712 HELPERS ~~~ */

    /**
     * @notice Hashes an order based on the eip-712 encoding scheme.
     * @param order The order to hash.
     * @return orderHash The eip-712 compliant hash of the order.
     */
    function hashOrder(Order memory order) public view returns (bytes32 orderHash) {
        orderHash = keccak256(
            abi.encode(
                ORDER_TYPE_HASH,
                order.maker,
                order.isCall,
                order.isLong,
                order.baseAsset,
                order.strike,
                order.premium,
                order.duration,
                order.expiration,
                order.nonce,
                keccak256(abi.encodePacked(order.whitelist)),
                keccak256(abi.encodePacked(order.floorTokens)),
                keccak256(encodeERC20Assets(order.erc20Assets)),
                keccak256(encodeERC721Assets(order.erc721Assets))
            )
        );

        orderHash = _hashTypedDataV4(orderHash);
    }

    /**
     * @notice Encodes an array of erc20 assets following the eip-712 encoding scheme.
     * @param arr Array of erc20 assets to hash.
     * @return encoded The eip-712 encoded array of erc20 assets.
     */
    function encodeERC20Assets(ERC20Asset[] memory arr) public pure returns (bytes memory encoded) {
        for (uint256 i = 0; i < arr.length; i++) {
            encoded =
                abi.encodePacked(encoded, keccak256(abi.encode(ERC20ASSET_TYPE_HASH, arr[i].token, arr[i].tokenAmount)));
        }
    }

    /**
     * @notice Encodes an array of erc721 assets following the eip-712 encoding scheme.
     * @param arr Array of erc721 assets to hash.
     * @return encoded The eip-712 encoded array of erc721 assets.
     */
    function encodeERC721Assets(ERC721Asset[] memory arr) public pure returns (bytes memory encoded) {
        for (uint256 i = 0; i < arr.length; i++) {
            encoded =
                abi.encodePacked(encoded, keccak256(abi.encode(ERC721ASSET_TYPE_HASH, arr[i].token, arr[i].tokenId)));
        }
    }

    /**
     * @return The domain separator used when calculating the eip-712 hash.
     */
    function domainSeparatorV4() public view returns (bytes32) {
        return _domainSeparatorV4();
    }

    /* ~~~ OVERRIDES ~~~ */

    /**
     * @notice Gets the token URI for an NFT.
     * @param id The id of the position NFT.
     * @return The tokenURI of the position NFT.
     */
    function tokenURI(uint256 id) public view override returns (string memory) {
        require(_ownerOf[id] != address(0), "URI query for NOT_MINTED token");

        return string.concat(baseURI, Strings.toString(id));
    }
}

File 2 of 17 : IWETH.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.16;

interface IWETH {
    event Approval(address indexed src, address indexed guy, uint256 wad);
    event Transfer(address indexed src, address indexed dst, uint256 wad);
    event Deposit(address indexed dst, uint256 wad);
    event Withdrawal(address indexed src, uint256 wad);

    function deposit() external payable;

    function withdraw(uint256 wad) external;

    function totalSupply() external view returns (uint256);

    function approve(address guy, uint256 wad) external returns (bool);

    function transfer(address dst, uint256 wad) external returns (bool);

    function transferFrom(address src, address dst, uint256 wad) external returns (bool);
}

File 3 of 17 : SignatureChecker.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.5.0) (utils/cryptography/SignatureChecker.sol)

pragma solidity ^0.8.0;

import "./ECDSA.sol";
import "../Address.sol";
import "../../interfaces/IERC1271.sol";

/**
 * @dev Signature verification helper that can be used instead of `ECDSA.recover` to seamlessly support both ECDSA
 * signatures from externally owned accounts (EOAs) as well as ERC1271 signatures from smart contract wallets like
 * Argent and Gnosis Safe.
 *
 * _Available since v4.1._
 */
library SignatureChecker {
    /**
     * @dev Checks if a signature is valid for a given signer and data hash. If the signer is a smart contract, the
     * signature is validated against that smart contract using ERC1271, otherwise it's validated using `ECDSA.recover`.
     *
     * NOTE: Unlike ECDSA signatures, contract signatures are revocable, and the outcome of this function can thus
     * change through time. It could return true at block N and false at block N+1 (or the opposite).
     */
    function isValidSignatureNow(
        address signer,
        bytes32 hash,
        bytes memory signature
    ) internal view returns (bool) {
        (address recovered, ECDSA.RecoverError error) = ECDSA.tryRecover(hash, signature);
        if (error == ECDSA.RecoverError.NoError && recovered == signer) {
            return true;
        }

        (bool success, bytes memory result) = signer.staticcall(
            abi.encodeWithSelector(IERC1271.isValidSignature.selector, hash, signature)
        );
        return (success &&
            result.length == 32 &&
            abi.decode(result, (bytes32)) == bytes32(IERC1271.isValidSignature.selector));
    }
}

File 4 of 17 : draft-EIP712.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/cryptography/draft-EIP712.sol)

pragma solidity ^0.8.0;

import "./ECDSA.sol";

/**
 * @dev https://eips.ethereum.org/EIPS/eip-712[EIP 712] is a standard for hashing and signing of typed structured data.
 *
 * The encoding specified in the EIP is very generic, and such a generic implementation in Solidity is not feasible,
 * thus this contract does not implement the encoding itself. Protocols need to implement the type-specific encoding
 * they need in their contracts using a combination of `abi.encode` and `keccak256`.
 *
 * This contract implements the EIP 712 domain separator ({_domainSeparatorV4}) that is used as part of the encoding
 * scheme, and the final step of the encoding to obtain the message digest that is then signed via ECDSA
 * ({_hashTypedDataV4}).
 *
 * The implementation of the domain separator was designed to be as efficient as possible while still properly updating
 * the chain id to protect against replay attacks on an eventual fork of the chain.
 *
 * NOTE: This contract implements the version of the encoding known as "v4", as implemented by the JSON RPC method
 * https://docs.metamask.io/guide/signing-data.html[`eth_signTypedDataV4` in MetaMask].
 *
 * _Available since v3.4._
 */
abstract contract EIP712 {
    /* solhint-disable var-name-mixedcase */
    // Cache the domain separator as an immutable value, but also store the chain id that it corresponds to, in order to
    // invalidate the cached domain separator if the chain id changes.
    bytes32 private immutable _CACHED_DOMAIN_SEPARATOR;
    uint256 private immutable _CACHED_CHAIN_ID;
    address private immutable _CACHED_THIS;

    bytes32 private immutable _HASHED_NAME;
    bytes32 private immutable _HASHED_VERSION;
    bytes32 private immutable _TYPE_HASH;

    /* solhint-enable var-name-mixedcase */

    /**
     * @dev Initializes the domain separator and parameter caches.
     *
     * The meaning of `name` and `version` is specified in
     * https://eips.ethereum.org/EIPS/eip-712#definition-of-domainseparator[EIP 712]:
     *
     * - `name`: the user readable name of the signing domain, i.e. the name of the DApp or the protocol.
     * - `version`: the current major version of the signing domain.
     *
     * NOTE: These parameters cannot be changed except through a xref:learn::upgrading-smart-contracts.adoc[smart
     * contract upgrade].
     */
    constructor(string memory name, string memory version) {
        bytes32 hashedName = keccak256(bytes(name));
        bytes32 hashedVersion = keccak256(bytes(version));
        bytes32 typeHash = keccak256(
            "EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"
        );
        _HASHED_NAME = hashedName;
        _HASHED_VERSION = hashedVersion;
        _CACHED_CHAIN_ID = block.chainid;
        _CACHED_DOMAIN_SEPARATOR = _buildDomainSeparator(typeHash, hashedName, hashedVersion);
        _CACHED_THIS = address(this);
        _TYPE_HASH = typeHash;
    }

    /**
     * @dev Returns the domain separator for the current chain.
     */
    function _domainSeparatorV4() internal view returns (bytes32) {
        if (address(this) == _CACHED_THIS && block.chainid == _CACHED_CHAIN_ID) {
            return _CACHED_DOMAIN_SEPARATOR;
        } else {
            return _buildDomainSeparator(_TYPE_HASH, _HASHED_NAME, _HASHED_VERSION);
        }
    }

    function _buildDomainSeparator(
        bytes32 typeHash,
        bytes32 nameHash,
        bytes32 versionHash
    ) private view returns (bytes32) {
        return keccak256(abi.encode(typeHash, nameHash, versionHash, block.chainid, address(this)));
    }

    /**
     * @dev Given an already https://eips.ethereum.org/EIPS/eip-712#definition-of-hashstruct[hashed struct], this
     * function returns the hash of the fully encoded EIP712 message for this domain.
     *
     * This hash can be used together with {ECDSA-recover} to obtain the signer of a message. For example:
     *
     * ```solidity
     * bytes32 digest = _hashTypedDataV4(keccak256(abi.encode(
     *     keccak256("Mail(address to,string contents)"),
     *     mailTo,
     *     keccak256(bytes(mailContents))
     * )));
     * address signer = ECDSA.recover(digest, signature);
     * ```
     */
    function _hashTypedDataV4(bytes32 structHash) internal view virtual returns (bytes32) {
        return ECDSA.toTypedDataHash(_domainSeparatorV4(), structHash);
    }
}

File 5 of 17 : Strings.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.0) (utils/Strings.sol)

pragma solidity ^0.8.0;

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

    /**
     * @dev Converts a `uint256` to its ASCII `string` decimal representation.
     */
    function toString(uint256 value) internal pure returns (string memory) {
        // Inspired by OraclizeAPI's implementation - MIT licence
        // https://github.com/oraclize/ethereum-api/blob/b42146b063c7d6ee1358846c198246239e9360e8/oraclizeAPI_0.4.25.sol

        if (value == 0) {
            return "0";
        }
        uint256 temp = value;
        uint256 digits;
        while (temp != 0) {
            digits++;
            temp /= 10;
        }
        bytes memory buffer = new bytes(digits);
        while (value != 0) {
            digits -= 1;
            buffer[digits] = bytes1(uint8(48 + uint256(value % 10)));
            value /= 10;
        }
        return string(buffer);
    }

    /**
     * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation.
     */
    function toHexString(uint256 value) internal pure returns (string memory) {
        if (value == 0) {
            return "0x00";
        }
        uint256 temp = value;
        uint256 length = 0;
        while (temp != 0) {
            length++;
            temp >>= 8;
        }
        return toHexString(value, length);
    }

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

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

File 6 of 17 : ERC165Checker.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/introspection/ERC165Checker.sol)

pragma solidity ^0.8.0;

import "./IERC165.sol";

/**
 * @dev Library used to query support of an interface declared via {IERC165}.
 *
 * Note that these functions return the actual result of the query: they do not
 * `revert` if an interface is not supported. It is up to the caller to decide
 * what to do in these cases.
 */
library ERC165Checker {
    // As per the EIP-165 spec, no interface should ever match 0xffffffff
    bytes4 private constant _INTERFACE_ID_INVALID = 0xffffffff;

    /**
     * @dev Returns true if `account` supports the {IERC165} interface,
     */
    function supportsERC165(address account) internal view returns (bool) {
        // Any contract that implements ERC165 must explicitly indicate support of
        // InterfaceId_ERC165 and explicitly indicate non-support of InterfaceId_Invalid
        return
            supportsERC165InterfaceUnchecked(account, type(IERC165).interfaceId) &&
            !supportsERC165InterfaceUnchecked(account, _INTERFACE_ID_INVALID);
    }

    /**
     * @dev Returns true if `account` supports the interface defined by
     * `interfaceId`. Support for {IERC165} itself is queried automatically.
     *
     * See {IERC165-supportsInterface}.
     */
    function supportsInterface(address account, bytes4 interfaceId) internal view returns (bool) {
        // query support of both ERC165 as per the spec and support of _interfaceId
        return supportsERC165(account) && supportsERC165InterfaceUnchecked(account, interfaceId);
    }

    /**
     * @dev Returns a boolean array where each value corresponds to the
     * interfaces passed in and whether they're supported or not. This allows
     * you to batch check interfaces for a contract where your expectation
     * is that some interfaces may not be supported.
     *
     * See {IERC165-supportsInterface}.
     *
     * _Available since v3.4._
     */
    function getSupportedInterfaces(address account, bytes4[] memory interfaceIds)
        internal
        view
        returns (bool[] memory)
    {
        // an array of booleans corresponding to interfaceIds and whether they're supported or not
        bool[] memory interfaceIdsSupported = new bool[](interfaceIds.length);

        // query support of ERC165 itself
        if (supportsERC165(account)) {
            // query support of each interface in interfaceIds
            for (uint256 i = 0; i < interfaceIds.length; i++) {
                interfaceIdsSupported[i] = supportsERC165InterfaceUnchecked(account, interfaceIds[i]);
            }
        }

        return interfaceIdsSupported;
    }

    /**
     * @dev Returns true if `account` supports all the interfaces defined in
     * `interfaceIds`. Support for {IERC165} itself is queried automatically.
     *
     * Batch-querying can lead to gas savings by skipping repeated checks for
     * {IERC165} support.
     *
     * See {IERC165-supportsInterface}.
     */
    function supportsAllInterfaces(address account, bytes4[] memory interfaceIds) internal view returns (bool) {
        // query support of ERC165 itself
        if (!supportsERC165(account)) {
            return false;
        }

        // query support of each interface in _interfaceIds
        for (uint256 i = 0; i < interfaceIds.length; i++) {
            if (!supportsERC165InterfaceUnchecked(account, interfaceIds[i])) {
                return false;
            }
        }

        // all interfaces supported
        return true;
    }

    /**
     * @notice Query if a contract implements an interface, does not check ERC165 support
     * @param account The address of the contract to query for support of an interface
     * @param interfaceId The interface identifier, as specified in ERC-165
     * @return true if the contract at account indicates support of the interface with
     * identifier interfaceId, false otherwise
     * @dev Assumes that account contains a contract that supports ERC165, otherwise
     * the behavior of this method is undefined. This precondition can be checked
     * with {supportsERC165}.
     * Interface identification is specified in ERC-165.
     */
    function supportsERC165InterfaceUnchecked(address account, bytes4 interfaceId) internal view returns (bool) {
        // prepare call
        bytes memory encodedParams = abi.encodeWithSelector(IERC165.supportsInterface.selector, interfaceId);

        // perform static call
        bool success;
        uint256 returnSize;
        uint256 returnValue;
        assembly {
            success := staticcall(30000, account, add(encodedParams, 0x20), mload(encodedParams), 0x00, 0x20)
            returnSize := returndatasize()
            returnValue := mload(0x00)
        }

        return success && returnSize >= 0x20 && returnValue > 0;
    }
}

File 7 of 17 : Ownable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.0) (access/Ownable.sol)

pragma solidity ^0.8.0;

import "../utils/Context.sol";

/**
 * @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 8 of 17 : SafeTransferLib.sol
// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity >=0.8.0;

import {ERC20} from "../tokens/ERC20.sol";

/// @notice Safe ETH and ERC20 transfer library that gracefully handles missing return values.
/// @author Solmate (https://github.com/Rari-Capital/solmate/blob/main/src/utils/SafeTransferLib.sol)
/// @dev Use with caution! Some functions in this library knowingly create dirty bits at the destination of the free memory pointer.
/// @dev Note that none of the functions in this library check that a token has code at all! That responsibility is delegated to the caller.
library SafeTransferLib {
    /*//////////////////////////////////////////////////////////////
                             ETH OPERATIONS
    //////////////////////////////////////////////////////////////*/

    function safeTransferETH(address to, uint256 amount) internal {
        bool success;

        assembly {
            // Transfer the ETH and store if it succeeded or not.
            success := call(gas(), to, amount, 0, 0, 0, 0)
        }

        require(success, "ETH_TRANSFER_FAILED");
    }

    /*//////////////////////////////////////////////////////////////
                            ERC20 OPERATIONS
    //////////////////////////////////////////////////////////////*/

    function safeTransferFrom(
        ERC20 token,
        address from,
        address to,
        uint256 amount
    ) internal {
        bool success;

        assembly {
            // Get a pointer to some free memory.
            let freeMemoryPointer := mload(0x40)

            // Write the abi-encoded calldata into memory, beginning with the function selector.
            mstore(freeMemoryPointer, 0x23b872dd00000000000000000000000000000000000000000000000000000000)
            mstore(add(freeMemoryPointer, 4), from) // Append the "from" argument.
            mstore(add(freeMemoryPointer, 36), to) // Append the "to" argument.
            mstore(add(freeMemoryPointer, 68), amount) // Append the "amount" argument.

            success := and(
                // Set success to whether the call reverted, if not we check it either
                // returned exactly 1 (can't just be non-zero data), or had no return data.
                or(and(eq(mload(0), 1), gt(returndatasize(), 31)), iszero(returndatasize())),
                // We use 100 because the length of our calldata totals up like so: 4 + 32 * 3.
                // We use 0 and 32 to copy up to 32 bytes of return data into the scratch space.
                // Counterintuitively, this call must be positioned second to the or() call in the
                // surrounding and() call or else returndatasize() will be zero during the computation.
                call(gas(), token, 0, freeMemoryPointer, 100, 0, 32)
            )
        }

        require(success, "TRANSFER_FROM_FAILED");
    }

    function safeTransfer(
        ERC20 token,
        address to,
        uint256 amount
    ) internal {
        bool success;

        assembly {
            // Get a pointer to some free memory.
            let freeMemoryPointer := mload(0x40)

            // Write the abi-encoded calldata into memory, beginning with the function selector.
            mstore(freeMemoryPointer, 0xa9059cbb00000000000000000000000000000000000000000000000000000000)
            mstore(add(freeMemoryPointer, 4), to) // Append the "to" argument.
            mstore(add(freeMemoryPointer, 36), amount) // Append the "amount" argument.

            success := and(
                // Set success to whether the call reverted, if not we check it either
                // returned exactly 1 (can't just be non-zero data), or had no return data.
                or(and(eq(mload(0), 1), gt(returndatasize(), 31)), iszero(returndatasize())),
                // We use 68 because the length of our calldata totals up like so: 4 + 32 * 2.
                // We use 0 and 32 to copy up to 32 bytes of return data into the scratch space.
                // Counterintuitively, this call must be positioned second to the or() call in the
                // surrounding and() call or else returndatasize() will be zero during the computation.
                call(gas(), token, 0, freeMemoryPointer, 68, 0, 32)
            )
        }

        require(success, "TRANSFER_FAILED");
    }

    function safeApprove(
        ERC20 token,
        address to,
        uint256 amount
    ) internal {
        bool success;

        assembly {
            // Get a pointer to some free memory.
            let freeMemoryPointer := mload(0x40)

            // Write the abi-encoded calldata into memory, beginning with the function selector.
            mstore(freeMemoryPointer, 0x095ea7b300000000000000000000000000000000000000000000000000000000)
            mstore(add(freeMemoryPointer, 4), to) // Append the "to" argument.
            mstore(add(freeMemoryPointer, 36), amount) // Append the "amount" argument.

            success := and(
                // Set success to whether the call reverted, if not we check it either
                // returned exactly 1 (can't just be non-zero data), or had no return data.
                or(and(eq(mload(0), 1), gt(returndatasize(), 31)), iszero(returndatasize())),
                // We use 68 because the length of our calldata totals up like so: 4 + 32 * 2.
                // We use 0 and 32 to copy up to 32 bytes of return data into the scratch space.
                // Counterintuitively, this call must be positioned second to the or() call in the
                // surrounding and() call or else returndatasize() will be zero during the computation.
                call(gas(), token, 0, freeMemoryPointer, 68, 0, 32)
            )
        }

        require(success, "APPROVE_FAILED");
    }
}

File 9 of 17 : ERC721.sol
// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity >=0.8.0;

/// @notice Modern, minimalist, and gas efficient ERC-721 implementation.
/// @author Solmate (https://github.com/Rari-Capital/solmate/blob/main/src/tokens/ERC721.sol)
abstract contract ERC721 {
    /*//////////////////////////////////////////////////////////////
                                 EVENTS
    //////////////////////////////////////////////////////////////*/

    event Transfer(address indexed from, address indexed to, uint256 indexed id);

    event Approval(address indexed owner, address indexed spender, uint256 indexed id);

    event ApprovalForAll(address indexed owner, address indexed operator, bool approved);

    /*//////////////////////////////////////////////////////////////
                         METADATA STORAGE/LOGIC
    //////////////////////////////////////////////////////////////*/

    string public name;

    string public symbol;

    function tokenURI(uint256 id) public view virtual returns (string memory);

    /*//////////////////////////////////////////////////////////////
                      ERC721 BALANCE/OWNER STORAGE
    //////////////////////////////////////////////////////////////*/

    mapping(uint256 => address) internal _ownerOf;

    mapping(address => uint256) internal _balanceOf;

    function ownerOf(uint256 id) public view virtual returns (address owner) {
        require((owner = _ownerOf[id]) != address(0), "NOT_MINTED");
    }

    function balanceOf(address owner) public view virtual returns (uint256) {
        require(owner != address(0), "ZERO_ADDRESS");

        return _balanceOf[owner];
    }

    /*//////////////////////////////////////////////////////////////
                         ERC721 APPROVAL STORAGE
    //////////////////////////////////////////////////////////////*/

    mapping(uint256 => address) public getApproved;

    mapping(address => mapping(address => bool)) public isApprovedForAll;

    /*//////////////////////////////////////////////////////////////
                               CONSTRUCTOR
    //////////////////////////////////////////////////////////////*/

    constructor(string memory _name, string memory _symbol) {
        name = _name;
        symbol = _symbol;
    }

    /*//////////////////////////////////////////////////////////////
                              ERC721 LOGIC
    //////////////////////////////////////////////////////////////*/

    function approve(address spender, uint256 id) public virtual {
        address owner = _ownerOf[id];

        require(msg.sender == owner || isApprovedForAll[owner][msg.sender], "NOT_AUTHORIZED");

        getApproved[id] = spender;

        emit Approval(owner, spender, id);
    }

    function setApprovalForAll(address operator, bool approved) public virtual {
        isApprovedForAll[msg.sender][operator] = approved;

        emit ApprovalForAll(msg.sender, operator, approved);
    }

    function transferFrom(
        address from,
        address to,
        uint256 id
    ) public virtual {
        require(from == _ownerOf[id], "WRONG_FROM");

        require(to != address(0), "INVALID_RECIPIENT");

        require(
            msg.sender == from || isApprovedForAll[from][msg.sender] || msg.sender == getApproved[id],
            "NOT_AUTHORIZED"
        );

        // Underflow of the sender's balance is impossible because we check for
        // ownership above and the recipient's balance can't realistically overflow.
        unchecked {
            _balanceOf[from]--;

            _balanceOf[to]++;
        }

        _ownerOf[id] = to;

        delete getApproved[id];

        emit Transfer(from, to, id);
    }

    function safeTransferFrom(
        address from,
        address to,
        uint256 id
    ) public virtual {
        transferFrom(from, to, id);

        require(
            to.code.length == 0 ||
                ERC721TokenReceiver(to).onERC721Received(msg.sender, from, id, "") ==
                ERC721TokenReceiver.onERC721Received.selector,
            "UNSAFE_RECIPIENT"
        );
    }

    function safeTransferFrom(
        address from,
        address to,
        uint256 id,
        bytes calldata data
    ) public virtual {
        transferFrom(from, to, id);

        require(
            to.code.length == 0 ||
                ERC721TokenReceiver(to).onERC721Received(msg.sender, from, id, data) ==
                ERC721TokenReceiver.onERC721Received.selector,
            "UNSAFE_RECIPIENT"
        );
    }

    /*//////////////////////////////////////////////////////////////
                              ERC165 LOGIC
    //////////////////////////////////////////////////////////////*/

    function supportsInterface(bytes4 interfaceId) public view virtual returns (bool) {
        return
            interfaceId == 0x01ffc9a7 || // ERC165 Interface ID for ERC165
            interfaceId == 0x80ac58cd || // ERC165 Interface ID for ERC721
            interfaceId == 0x5b5e139f; // ERC165 Interface ID for ERC721Metadata
    }

    /*//////////////////////////////////////////////////////////////
                        INTERNAL MINT/BURN LOGIC
    //////////////////////////////////////////////////////////////*/

    function _mint(address to, uint256 id) internal virtual {
        require(to != address(0), "INVALID_RECIPIENT");

        require(_ownerOf[id] == address(0), "ALREADY_MINTED");

        // Counter overflow is incredibly unrealistic.
        unchecked {
            _balanceOf[to]++;
        }

        _ownerOf[id] = to;

        emit Transfer(address(0), to, id);
    }

    function _burn(uint256 id) internal virtual {
        address owner = _ownerOf[id];

        require(owner != address(0), "NOT_MINTED");

        // Ownership check above ensures no underflow.
        unchecked {
            _balanceOf[owner]--;
        }

        delete _ownerOf[id];

        delete getApproved[id];

        emit Transfer(owner, address(0), id);
    }

    /*//////////////////////////////////////////////////////////////
                        INTERNAL SAFE MINT LOGIC
    //////////////////////////////////////////////////////////////*/

    function _safeMint(address to, uint256 id) internal virtual {
        _mint(to, id);

        require(
            to.code.length == 0 ||
                ERC721TokenReceiver(to).onERC721Received(msg.sender, address(0), id, "") ==
                ERC721TokenReceiver.onERC721Received.selector,
            "UNSAFE_RECIPIENT"
        );
    }

    function _safeMint(
        address to,
        uint256 id,
        bytes memory data
    ) internal virtual {
        _mint(to, id);

        require(
            to.code.length == 0 ||
                ERC721TokenReceiver(to).onERC721Received(msg.sender, address(0), id, data) ==
                ERC721TokenReceiver.onERC721Received.selector,
            "UNSAFE_RECIPIENT"
        );
    }
}

/// @notice A generic interface for a contract which properly accepts ERC721 tokens.
/// @author Solmate (https://github.com/Rari-Capital/solmate/blob/main/src/tokens/ERC721.sol)
abstract contract ERC721TokenReceiver {
    function onERC721Received(
        address,
        address,
        uint256,
        bytes calldata
    ) external virtual returns (bytes4) {
        return ERC721TokenReceiver.onERC721Received.selector;
    }
}

File 10 of 17 : PuttyV2Nft.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.16;

import "solmate/tokens/ERC721.sol";

// removes balanceOf modifications
// questionable tradeoff but given our use-case it's reasonable
abstract contract PuttyV2Nft is ERC721("Putty", "OPUT") {
    // remove balanceOf modifications
    function _mint(address to, uint256 id) internal override {
        require(to != address(0), "INVALID_RECIPIENT");
        require(_ownerOf[id] == address(0), "ALREADY_MINTED");

        _ownerOf[id] = to;

        emit Transfer(address(0), to, id);
    }

    // remove balanceOf modifications
    function transferFrom(address from, address to, uint256 id) public override {
        require(from == _ownerOf[id], "WRONG_FROM");
        require(to != address(0), "INVALID_RECIPIENT");
        require(
            msg.sender == from || isApprovedForAll[from][msg.sender] || msg.sender == getApproved[id], "NOT_AUTHORIZED"
        );

        _ownerOf[id] = to;
        delete getApproved[id];

        emit Transfer(from, to, id);
    }

    // set balanceOf to max for all users
    function balanceOf(address owner) public pure override returns (uint256) {
        require(owner != address(0), "ZERO_ADDRESS");
        return type(uint256).max;
    }
}

File 11 of 17 : PuttyV2Handler.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.16;

import "./PuttyV2.sol";

interface IPuttyV2Handler {
    function onFillOrder(PuttyV2.Order memory order, address taker, uint256[] memory floorAssetTokenIds) external;

    function onExercise(PuttyV2.Order memory order, address exerciser, uint256[] memory floorAssetTokenIds) external;
}

contract PuttyV2Handler {
    function onFillOrder(PuttyV2.Order memory order, address taker, uint256[] memory floorAssetTokenIds)
        public
        virtual
    {}

    function onExercise(PuttyV2.Order memory order, address exerciser, uint256[] memory floorAssetTokenIds)
        public
        virtual
    {}
}

File 12 of 17 : ECDSA.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.0) (utils/cryptography/ECDSA.sol)

pragma solidity ^0.8.0;

import "../Strings.sol";

/**
 * @dev Elliptic Curve Digital Signature Algorithm (ECDSA) operations.
 *
 * These functions can be used to verify that a message was signed by the holder
 * of the private keys of a given address.
 */
library ECDSA {
    enum RecoverError {
        NoError,
        InvalidSignature,
        InvalidSignatureLength,
        InvalidSignatureS,
        InvalidSignatureV
    }

    function _throwError(RecoverError error) private pure {
        if (error == RecoverError.NoError) {
            return; // no error: do nothing
        } else if (error == RecoverError.InvalidSignature) {
            revert("ECDSA: invalid signature");
        } else if (error == RecoverError.InvalidSignatureLength) {
            revert("ECDSA: invalid signature length");
        } else if (error == RecoverError.InvalidSignatureS) {
            revert("ECDSA: invalid signature 's' value");
        } else if (error == RecoverError.InvalidSignatureV) {
            revert("ECDSA: invalid signature 'v' value");
        }
    }

    /**
     * @dev Returns the address that signed a hashed message (`hash`) with
     * `signature` or error string. This address can then be used for verification purposes.
     *
     * The `ecrecover` EVM opcode allows for malleable (non-unique) signatures:
     * this function rejects them by requiring the `s` value to be in the lower
     * half order, and the `v` value to be either 27 or 28.
     *
     * IMPORTANT: `hash` _must_ be the result of a hash operation for the
     * verification to be secure: it is possible to craft signatures that
     * recover to arbitrary addresses for non-hashed data. A safe way to ensure
     * this is by receiving a hash of the original message (which may otherwise
     * be too long), and then calling {toEthSignedMessageHash} on it.
     *
     * Documentation for signature generation:
     * - with https://web3js.readthedocs.io/en/v1.3.4/web3-eth-accounts.html#sign[Web3.js]
     * - with https://docs.ethers.io/v5/api/signer/#Signer-signMessage[ethers]
     *
     * _Available since v4.3._
     */
    function tryRecover(bytes32 hash, bytes memory signature) internal pure returns (address, RecoverError) {
        if (signature.length == 65) {
            bytes32 r;
            bytes32 s;
            uint8 v;
            // ecrecover takes the signature parameters, and the only way to get them
            // currently is to use assembly.
            /// @solidity memory-safe-assembly
            assembly {
                r := mload(add(signature, 0x20))
                s := mload(add(signature, 0x40))
                v := byte(0, mload(add(signature, 0x60)))
            }
            return tryRecover(hash, v, r, s);
        } else {
            return (address(0), RecoverError.InvalidSignatureLength);
        }
    }

    /**
     * @dev Returns the address that signed a hashed message (`hash`) with
     * `signature`. This address can then be used for verification purposes.
     *
     * The `ecrecover` EVM opcode allows for malleable (non-unique) signatures:
     * this function rejects them by requiring the `s` value to be in the lower
     * half order, and the `v` value to be either 27 or 28.
     *
     * IMPORTANT: `hash` _must_ be the result of a hash operation for the
     * verification to be secure: it is possible to craft signatures that
     * recover to arbitrary addresses for non-hashed data. A safe way to ensure
     * this is by receiving a hash of the original message (which may otherwise
     * be too long), and then calling {toEthSignedMessageHash} on it.
     */
    function recover(bytes32 hash, bytes memory signature) internal pure returns (address) {
        (address recovered, RecoverError error) = tryRecover(hash, signature);
        _throwError(error);
        return recovered;
    }

    /**
     * @dev Overload of {ECDSA-tryRecover} that receives the `r` and `vs` short-signature fields separately.
     *
     * See https://eips.ethereum.org/EIPS/eip-2098[EIP-2098 short signatures]
     *
     * _Available since v4.3._
     */
    function tryRecover(
        bytes32 hash,
        bytes32 r,
        bytes32 vs
    ) internal pure returns (address, RecoverError) {
        bytes32 s = vs & bytes32(0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff);
        uint8 v = uint8((uint256(vs) >> 255) + 27);
        return tryRecover(hash, v, r, s);
    }

    /**
     * @dev Overload of {ECDSA-recover} that receives the `r and `vs` short-signature fields separately.
     *
     * _Available since v4.2._
     */
    function recover(
        bytes32 hash,
        bytes32 r,
        bytes32 vs
    ) internal pure returns (address) {
        (address recovered, RecoverError error) = tryRecover(hash, r, vs);
        _throwError(error);
        return recovered;
    }

    /**
     * @dev Overload of {ECDSA-tryRecover} that receives the `v`,
     * `r` and `s` signature fields separately.
     *
     * _Available since v4.3._
     */
    function tryRecover(
        bytes32 hash,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) internal pure returns (address, RecoverError) {
        // EIP-2 still allows signature malleability for ecrecover(). Remove this possibility and make the signature
        // unique. Appendix F in the Ethereum Yellow paper (https://ethereum.github.io/yellowpaper/paper.pdf), defines
        // the valid range for s in (301): 0 < s < secp256k1n ÷ 2 + 1, and for v in (302): v ∈ {27, 28}. Most
        // signatures from current libraries generate a unique signature with an s-value in the lower half order.
        //
        // If your library generates malleable signatures, such as s-values in the upper range, calculate a new s-value
        // with 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141 - s1 and flip v from 27 to 28 or
        // vice versa. If your library also generates signatures with 0/1 for v instead 27/28, add 27 to v to accept
        // these malleable signatures as well.
        if (uint256(s) > 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0) {
            return (address(0), RecoverError.InvalidSignatureS);
        }
        if (v != 27 && v != 28) {
            return (address(0), RecoverError.InvalidSignatureV);
        }

        // If the signature is valid (and not malleable), return the signer address
        address signer = ecrecover(hash, v, r, s);
        if (signer == address(0)) {
            return (address(0), RecoverError.InvalidSignature);
        }

        return (signer, RecoverError.NoError);
    }

    /**
     * @dev Overload of {ECDSA-recover} that receives the `v`,
     * `r` and `s` signature fields separately.
     */
    function recover(
        bytes32 hash,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) internal pure returns (address) {
        (address recovered, RecoverError error) = tryRecover(hash, v, r, s);
        _throwError(error);
        return recovered;
    }

    /**
     * @dev Returns an Ethereum Signed Message, created from a `hash`. This
     * produces hash corresponding to the one signed with the
     * https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`]
     * JSON-RPC method as part of EIP-191.
     *
     * See {recover}.
     */
    function toEthSignedMessageHash(bytes32 hash) internal pure returns (bytes32) {
        // 32 is the length in bytes of hash,
        // enforced by the type signature above
        return keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n32", hash));
    }

    /**
     * @dev Returns an Ethereum Signed Message, created from `s`. This
     * produces hash corresponding to the one signed with the
     * https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`]
     * JSON-RPC method as part of EIP-191.
     *
     * See {recover}.
     */
    function toEthSignedMessageHash(bytes memory s) internal pure returns (bytes32) {
        return keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n", Strings.toString(s.length), s));
    }

    /**
     * @dev Returns an Ethereum Signed Typed Data, created from a
     * `domainSeparator` and a `structHash`. This produces hash corresponding
     * to the one signed with the
     * https://eips.ethereum.org/EIPS/eip-712[`eth_signTypedData`]
     * JSON-RPC method as part of EIP-712.
     *
     * See {recover}.
     */
    function toTypedDataHash(bytes32 domainSeparator, bytes32 structHash) internal pure returns (bytes32) {
        return keccak256(abi.encodePacked("\x19\x01", domainSeparator, structHash));
    }
}

File 13 of 17 : Address.sol
// SPDX-License-Identifier: MIT
// 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 functionCallWithValue(target, data, 0, "Address: low-level call failed");
    }

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

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

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

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

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

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

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

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

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

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

File 14 of 17 : IERC1271.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (interfaces/IERC1271.sol)

pragma solidity ^0.8.0;

/**
 * @dev Interface of the ERC1271 standard signature validation method for
 * contracts as defined in https://eips.ethereum.org/EIPS/eip-1271[ERC-1271].
 *
 * _Available since v4.1._
 */
interface IERC1271 {
    /**
     * @dev Should return whether the signature provided is valid for the provided data
     * @param hash      Hash of the data to be signed
     * @param signature Signature byte array associated with _data
     */
    function isValidSignature(bytes32 hash, bytes memory signature) external view returns (bytes4 magicValue);
}

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

pragma solidity ^0.8.0;

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

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

pragma solidity ^0.8.0;

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

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

File 17 of 17 : ERC20.sol
// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity >=0.8.0;

/// @notice Modern and gas efficient ERC20 + EIP-2612 implementation.
/// @author Solmate (https://github.com/Rari-Capital/solmate/blob/main/src/tokens/ERC20.sol)
/// @author Modified from Uniswap (https://github.com/Uniswap/uniswap-v2-core/blob/master/contracts/UniswapV2ERC20.sol)
/// @dev Do not manually set balances without updating totalSupply, as the sum of all user balances must not exceed it.
abstract contract ERC20 {
    /*//////////////////////////////////////////////////////////////
                                 EVENTS
    //////////////////////////////////////////////////////////////*/

    event Transfer(address indexed from, address indexed to, uint256 amount);

    event Approval(address indexed owner, address indexed spender, uint256 amount);

    /*//////////////////////////////////////////////////////////////
                            METADATA STORAGE
    //////////////////////////////////////////////////////////////*/

    string public name;

    string public symbol;

    uint8 public immutable decimals;

    /*//////////////////////////////////////////////////////////////
                              ERC20 STORAGE
    //////////////////////////////////////////////////////////////*/

    uint256 public totalSupply;

    mapping(address => uint256) public balanceOf;

    mapping(address => mapping(address => uint256)) public allowance;

    /*//////////////////////////////////////////////////////////////
                            EIP-2612 STORAGE
    //////////////////////////////////////////////////////////////*/

    uint256 internal immutable INITIAL_CHAIN_ID;

    bytes32 internal immutable INITIAL_DOMAIN_SEPARATOR;

    mapping(address => uint256) public nonces;

    /*//////////////////////////////////////////////////////////////
                               CONSTRUCTOR
    //////////////////////////////////////////////////////////////*/

    constructor(
        string memory _name,
        string memory _symbol,
        uint8 _decimals
    ) {
        name = _name;
        symbol = _symbol;
        decimals = _decimals;

        INITIAL_CHAIN_ID = block.chainid;
        INITIAL_DOMAIN_SEPARATOR = computeDomainSeparator();
    }

    /*//////////////////////////////////////////////////////////////
                               ERC20 LOGIC
    //////////////////////////////////////////////////////////////*/

    function approve(address spender, uint256 amount) public virtual returns (bool) {
        allowance[msg.sender][spender] = amount;

        emit Approval(msg.sender, spender, amount);

        return true;
    }

    function transfer(address to, uint256 amount) public virtual returns (bool) {
        balanceOf[msg.sender] -= amount;

        // Cannot overflow because the sum of all user
        // balances can't exceed the max uint256 value.
        unchecked {
            balanceOf[to] += amount;
        }

        emit Transfer(msg.sender, to, amount);

        return true;
    }

    function transferFrom(
        address from,
        address to,
        uint256 amount
    ) public virtual returns (bool) {
        uint256 allowed = allowance[from][msg.sender]; // Saves gas for limited approvals.

        if (allowed != type(uint256).max) allowance[from][msg.sender] = allowed - amount;

        balanceOf[from] -= amount;

        // Cannot overflow because the sum of all user
        // balances can't exceed the max uint256 value.
        unchecked {
            balanceOf[to] += amount;
        }

        emit Transfer(from, to, amount);

        return true;
    }

    /*//////////////////////////////////////////////////////////////
                             EIP-2612 LOGIC
    //////////////////////////////////////////////////////////////*/

    function permit(
        address owner,
        address spender,
        uint256 value,
        uint256 deadline,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) public virtual {
        require(deadline >= block.timestamp, "PERMIT_DEADLINE_EXPIRED");

        // Unchecked because the only math done is incrementing
        // the owner's nonce which cannot realistically overflow.
        unchecked {
            address recoveredAddress = ecrecover(
                keccak256(
                    abi.encodePacked(
                        "\x19\x01",
                        DOMAIN_SEPARATOR(),
                        keccak256(
                            abi.encode(
                                keccak256(
                                    "Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)"
                                ),
                                owner,
                                spender,
                                value,
                                nonces[owner]++,
                                deadline
                            )
                        )
                    )
                ),
                v,
                r,
                s
            );

            require(recoveredAddress != address(0) && recoveredAddress == owner, "INVALID_SIGNER");

            allowance[recoveredAddress][spender] = value;
        }

        emit Approval(owner, spender, value);
    }

    function DOMAIN_SEPARATOR() public view virtual returns (bytes32) {
        return block.chainid == INITIAL_CHAIN_ID ? INITIAL_DOMAIN_SEPARATOR : computeDomainSeparator();
    }

    function computeDomainSeparator() internal view virtual returns (bytes32) {
        return
            keccak256(
                abi.encode(
                    keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"),
                    keccak256(bytes(name)),
                    keccak256("1"),
                    block.chainid,
                    address(this)
                )
            );
    }

    /*//////////////////////////////////////////////////////////////
                        INTERNAL MINT/BURN LOGIC
    //////////////////////////////////////////////////////////////*/

    function _mint(address to, uint256 amount) internal virtual {
        totalSupply += amount;

        // Cannot overflow because the sum of all user
        // balances can't exceed the max uint256 value.
        unchecked {
            balanceOf[to] += amount;
        }

        emit Transfer(address(0), to, amount);
    }

    function _burn(address from, uint256 amount) internal virtual {
        balanceOf[from] -= amount;

        // Cannot underflow because a user's balance
        // will never be larger than the total supply.
        unchecked {
            totalSupply -= amount;
        }

        emit Transfer(from, address(0), amount);
    }
}

Settings
{
  "remappings": [
    "ds-test/=lib/solmate/lib/ds-test/src/",
    "forge-std/=lib/forge-std/src/",
    "openzeppelin-contracts/=lib/openzeppelin-contracts/",
    "openzeppelin/=lib/openzeppelin-contracts/contracts/",
    "solmate/=lib/solmate/src/"
  ],
  "optimizer": {
    "enabled": true,
    "runs": 10000
  },
  "metadata": {
    "bytecodeHash": "ipfs"
  },
  "outputSelection": {
    "*": {
      "*": [
        "evm.bytecode",
        "evm.deployedBytecode",
        "devdoc",
        "userdoc",
        "metadata",
        "abi"
      ]
    }
  },
  "evmVersion": "london",
  "libraries": {}
}

Contract Security Audit

Contract ABI

[{"inputs":[{"internalType":"string","name":"_baseURI","type":"string"},{"internalType":"uint256","name":"_fee","type":"uint256"},{"internalType":"address","name":"_weth","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"spender","type":"address"},{"indexed":true,"internalType":"uint256","name":"id","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"operator","type":"address"},{"indexed":false,"internalType":"bool","name":"approved","type":"bool"}],"name":"ApprovalForAll","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"orderHash","type":"bytes32"},{"components":[{"internalType":"address","name":"maker","type":"address"},{"internalType":"bool","name":"isCall","type":"bool"},{"internalType":"bool","name":"isLong","type":"bool"},{"internalType":"address","name":"baseAsset","type":"address"},{"internalType":"uint256","name":"strike","type":"uint256"},{"internalType":"uint256","name":"premium","type":"uint256"},{"internalType":"uint256","name":"duration","type":"uint256"},{"internalType":"uint256","name":"expiration","type":"uint256"},{"internalType":"uint256","name":"nonce","type":"uint256"},{"internalType":"address[]","name":"whitelist","type":"address[]"},{"internalType":"address[]","name":"floorTokens","type":"address[]"},{"components":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"tokenAmount","type":"uint256"}],"internalType":"struct PuttyV2.ERC20Asset[]","name":"erc20Assets","type":"tuple[]"},{"components":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"internalType":"struct PuttyV2.ERC721Asset[]","name":"erc721Assets","type":"tuple[]"}],"indexed":false,"internalType":"struct PuttyV2.Order","name":"order","type":"tuple"}],"name":"CancelledOrder","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"orderHash","type":"bytes32"},{"indexed":false,"internalType":"uint256[]","name":"floorAssetTokenIds","type":"uint256[]"},{"components":[{"internalType":"address","name":"maker","type":"address"},{"internalType":"bool","name":"isCall","type":"bool"},{"internalType":"bool","name":"isLong","type":"bool"},{"internalType":"address","name":"baseAsset","type":"address"},{"internalType":"uint256","name":"strike","type":"uint256"},{"internalType":"uint256","name":"premium","type":"uint256"},{"internalType":"uint256","name":"duration","type":"uint256"},{"internalType":"uint256","name":"expiration","type":"uint256"},{"internalType":"uint256","name":"nonce","type":"uint256"},{"internalType":"address[]","name":"whitelist","type":"address[]"},{"internalType":"address[]","name":"floorTokens","type":"address[]"},{"components":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"tokenAmount","type":"uint256"}],"internalType":"struct PuttyV2.ERC20Asset[]","name":"erc20Assets","type":"tuple[]"},{"components":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"internalType":"struct PuttyV2.ERC721Asset[]","name":"erc721Assets","type":"tuple[]"}],"indexed":false,"internalType":"struct PuttyV2.Order","name":"order","type":"tuple"}],"name":"ExercisedOrder","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"orderHash","type":"bytes32"},{"indexed":true,"internalType":"bytes32","name":"oppositeOrderHash","type":"bytes32"},{"indexed":false,"internalType":"uint256[]","name":"floorAssetTokenIds","type":"uint256[]"},{"components":[{"internalType":"address","name":"maker","type":"address"},{"internalType":"bool","name":"isCall","type":"bool"},{"internalType":"bool","name":"isLong","type":"bool"},{"internalType":"address","name":"baseAsset","type":"address"},{"internalType":"uint256","name":"strike","type":"uint256"},{"internalType":"uint256","name":"premium","type":"uint256"},{"internalType":"uint256","name":"duration","type":"uint256"},{"internalType":"uint256","name":"expiration","type":"uint256"},{"internalType":"uint256","name":"nonce","type":"uint256"},{"internalType":"address[]","name":"whitelist","type":"address[]"},{"internalType":"address[]","name":"floorTokens","type":"address[]"},{"components":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"tokenAmount","type":"uint256"}],"internalType":"struct PuttyV2.ERC20Asset[]","name":"erc20Assets","type":"tuple[]"},{"components":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"internalType":"struct PuttyV2.ERC721Asset[]","name":"erc721Assets","type":"tuple[]"}],"indexed":false,"internalType":"struct PuttyV2.Order","name":"order","type":"tuple"}],"name":"FilledOrder","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"string","name":"baseURI","type":"string"}],"name":"NewBaseURI","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"fee","type":"uint256"}],"name":"NewFee","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":"uint256","name":"minimumValidNonce","type":"uint256"}],"name":"SetMinimumValidNonce","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":true,"internalType":"uint256","name":"id","type":"uint256"}],"name":"Transfer","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"orderHash","type":"bytes32"},{"components":[{"internalType":"address","name":"maker","type":"address"},{"internalType":"bool","name":"isCall","type":"bool"},{"internalType":"bool","name":"isLong","type":"bool"},{"internalType":"address","name":"baseAsset","type":"address"},{"internalType":"uint256","name":"strike","type":"uint256"},{"internalType":"uint256","name":"premium","type":"uint256"},{"internalType":"uint256","name":"duration","type":"uint256"},{"internalType":"uint256","name":"expiration","type":"uint256"},{"internalType":"uint256","name":"nonce","type":"uint256"},{"internalType":"address[]","name":"whitelist","type":"address[]"},{"internalType":"address[]","name":"floorTokens","type":"address[]"},{"components":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"tokenAmount","type":"uint256"}],"internalType":"struct PuttyV2.ERC20Asset[]","name":"erc20Assets","type":"tuple[]"},{"components":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"internalType":"struct PuttyV2.ERC721Asset[]","name":"erc721Assets","type":"tuple[]"}],"indexed":false,"internalType":"struct PuttyV2.Order","name":"order","type":"tuple"}],"name":"WithdrawOrder","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"asset","type":"address"},{"indexed":false,"internalType":"uint256","name":"fees","type":"uint256"},{"indexed":false,"internalType":"address","name":"recipient","type":"address"}],"name":"WithdrewFees","type":"event"},{"inputs":[],"name":"ERC20ASSET_TYPE_HASH","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"ERC721ASSET_TYPE_HASH","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"ORDER_TYPE_HASH","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"components":[{"internalType":"address","name":"maker","type":"address"},{"internalType":"bool","name":"isCall","type":"bool"},{"internalType":"bool","name":"isLong","type":"bool"},{"internalType":"address","name":"baseAsset","type":"address"},{"internalType":"uint256","name":"strike","type":"uint256"},{"internalType":"uint256","name":"premium","type":"uint256"},{"internalType":"uint256","name":"duration","type":"uint256"},{"internalType":"uint256","name":"expiration","type":"uint256"},{"internalType":"uint256","name":"nonce","type":"uint256"},{"internalType":"address[]","name":"whitelist","type":"address[]"},{"internalType":"address[]","name":"floorTokens","type":"address[]"},{"components":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"tokenAmount","type":"uint256"}],"internalType":"struct PuttyV2.ERC20Asset[]","name":"erc20Assets","type":"tuple[]"},{"components":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"internalType":"struct PuttyV2.ERC721Asset[]","name":"erc721Assets","type":"tuple[]"}],"internalType":"struct PuttyV2.Order","name":"order","type":"tuple"},{"internalType":"bytes","name":"signature","type":"bytes"},{"components":[{"internalType":"address","name":"maker","type":"address"},{"internalType":"bool","name":"isCall","type":"bool"},{"internalType":"bool","name":"isLong","type":"bool"},{"internalType":"address","name":"baseAsset","type":"address"},{"internalType":"uint256","name":"strike","type":"uint256"},{"internalType":"uint256","name":"premium","type":"uint256"},{"internalType":"uint256","name":"duration","type":"uint256"},{"internalType":"uint256","name":"expiration","type":"uint256"},{"internalType":"uint256","name":"nonce","type":"uint256"},{"internalType":"address[]","name":"whitelist","type":"address[]"},{"internalType":"address[]","name":"floorTokens","type":"address[]"},{"components":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"tokenAmount","type":"uint256"}],"internalType":"struct PuttyV2.ERC20Asset[]","name":"erc20Assets","type":"tuple[]"},{"components":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"internalType":"struct PuttyV2.ERC721Asset[]","name":"erc721Assets","type":"tuple[]"}],"internalType":"struct PuttyV2.Order","name":"originalOrder","type":"tuple"}],"name":"acceptCounterOffer","outputs":[{"internalType":"uint256","name":"positionId","type":"uint256"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"id","type":"uint256"}],"name":"approve","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"baseURI","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"components":[{"internalType":"address","name":"maker","type":"address"},{"internalType":"bool","name":"isCall","type":"bool"},{"internalType":"bool","name":"isLong","type":"bool"},{"internalType":"address","name":"baseAsset","type":"address"},{"internalType":"uint256","name":"strike","type":"uint256"},{"internalType":"uint256","name":"premium","type":"uint256"},{"internalType":"uint256","name":"duration","type":"uint256"},{"internalType":"uint256","name":"expiration","type":"uint256"},{"internalType":"uint256","name":"nonce","type":"uint256"},{"internalType":"address[]","name":"whitelist","type":"address[]"},{"internalType":"address[]","name":"floorTokens","type":"address[]"},{"components":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"tokenAmount","type":"uint256"}],"internalType":"struct PuttyV2.ERC20Asset[]","name":"erc20Assets","type":"tuple[]"},{"components":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"internalType":"struct PuttyV2.ERC721Asset[]","name":"erc721Assets","type":"tuple[]"}],"internalType":"struct PuttyV2.Order[]","name":"orders","type":"tuple[]"},{"internalType":"bytes[]","name":"signatures","type":"bytes[]"},{"internalType":"uint256[][]","name":"floorAssetTokenIds","type":"uint256[][]"}],"name":"batchFillOrder","outputs":[{"internalType":"uint256[]","name":"positionIds","type":"uint256[]"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"address","name":"maker","type":"address"},{"internalType":"bool","name":"isCall","type":"bool"},{"internalType":"bool","name":"isLong","type":"bool"},{"internalType":"address","name":"baseAsset","type":"address"},{"internalType":"uint256","name":"strike","type":"uint256"},{"internalType":"uint256","name":"premium","type":"uint256"},{"internalType":"uint256","name":"duration","type":"uint256"},{"internalType":"uint256","name":"expiration","type":"uint256"},{"internalType":"uint256","name":"nonce","type":"uint256"},{"internalType":"address[]","name":"whitelist","type":"address[]"},{"internalType":"address[]","name":"floorTokens","type":"address[]"},{"components":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"tokenAmount","type":"uint256"}],"internalType":"struct PuttyV2.ERC20Asset[]","name":"erc20Assets","type":"tuple[]"},{"components":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"internalType":"struct PuttyV2.ERC721Asset[]","name":"erc721Assets","type":"tuple[]"}],"internalType":"struct PuttyV2.Order","name":"order","type":"tuple"}],"name":"cancel","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"name":"cancelledOrders","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"domainSeparatorV4","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"components":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"tokenAmount","type":"uint256"}],"internalType":"struct PuttyV2.ERC20Asset[]","name":"arr","type":"tuple[]"}],"name":"encodeERC20Assets","outputs":[{"internalType":"bytes","name":"encoded","type":"bytes"}],"stateMutability":"pure","type":"function"},{"inputs":[{"components":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"internalType":"struct PuttyV2.ERC721Asset[]","name":"arr","type":"tuple[]"}],"name":"encodeERC721Assets","outputs":[{"internalType":"bytes","name":"encoded","type":"bytes"}],"stateMutability":"pure","type":"function"},{"inputs":[{"components":[{"internalType":"address","name":"maker","type":"address"},{"internalType":"bool","name":"isCall","type":"bool"},{"internalType":"bool","name":"isLong","type":"bool"},{"internalType":"address","name":"baseAsset","type":"address"},{"internalType":"uint256","name":"strike","type":"uint256"},{"internalType":"uint256","name":"premium","type":"uint256"},{"internalType":"uint256","name":"duration","type":"uint256"},{"internalType":"uint256","name":"expiration","type":"uint256"},{"internalType":"uint256","name":"nonce","type":"uint256"},{"internalType":"address[]","name":"whitelist","type":"address[]"},{"internalType":"address[]","name":"floorTokens","type":"address[]"},{"components":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"tokenAmount","type":"uint256"}],"internalType":"struct PuttyV2.ERC20Asset[]","name":"erc20Assets","type":"tuple[]"},{"components":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"internalType":"struct PuttyV2.ERC721Asset[]","name":"erc721Assets","type":"tuple[]"}],"internalType":"struct PuttyV2.Order","name":"order","type":"tuple"},{"internalType":"uint256[]","name":"floorAssetTokenIds","type":"uint256[]"}],"name":"exercise","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"exercisedPositions","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"fee","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"components":[{"internalType":"address","name":"maker","type":"address"},{"internalType":"bool","name":"isCall","type":"bool"},{"internalType":"bool","name":"isLong","type":"bool"},{"internalType":"address","name":"baseAsset","type":"address"},{"internalType":"uint256","name":"strike","type":"uint256"},{"internalType":"uint256","name":"premium","type":"uint256"},{"internalType":"uint256","name":"duration","type":"uint256"},{"internalType":"uint256","name":"expiration","type":"uint256"},{"internalType":"uint256","name":"nonce","type":"uint256"},{"internalType":"address[]","name":"whitelist","type":"address[]"},{"internalType":"address[]","name":"floorTokens","type":"address[]"},{"components":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"tokenAmount","type":"uint256"}],"internalType":"struct PuttyV2.ERC20Asset[]","name":"erc20Assets","type":"tuple[]"},{"components":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"internalType":"struct PuttyV2.ERC721Asset[]","name":"erc721Assets","type":"tuple[]"}],"internalType":"struct PuttyV2.Order","name":"order","type":"tuple"},{"internalType":"bytes","name":"signature","type":"bytes"},{"internalType":"uint256[]","name":"floorAssetTokenIds","type":"uint256[]"}],"name":"fillOrder","outputs":[{"internalType":"uint256","name":"positionId","type":"uint256"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"getApproved","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"components":[{"internalType":"address","name":"maker","type":"address"},{"internalType":"bool","name":"isCall","type":"bool"},{"internalType":"bool","name":"isLong","type":"bool"},{"internalType":"address","name":"baseAsset","type":"address"},{"internalType":"uint256","name":"strike","type":"uint256"},{"internalType":"uint256","name":"premium","type":"uint256"},{"internalType":"uint256","name":"duration","type":"uint256"},{"internalType":"uint256","name":"expiration","type":"uint256"},{"internalType":"uint256","name":"nonce","type":"uint256"},{"internalType":"address[]","name":"whitelist","type":"address[]"},{"internalType":"address[]","name":"floorTokens","type":"address[]"},{"components":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"tokenAmount","type":"uint256"}],"internalType":"struct PuttyV2.ERC20Asset[]","name":"erc20Assets","type":"tuple[]"},{"components":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"internalType":"struct PuttyV2.ERC721Asset[]","name":"erc721Assets","type":"tuple[]"}],"internalType":"struct PuttyV2.Order","name":"order","type":"tuple"}],"name":"hashOppositeOrder","outputs":[{"internalType":"bytes32","name":"orderHash","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"components":[{"internalType":"address","name":"maker","type":"address"},{"internalType":"bool","name":"isCall","type":"bool"},{"internalType":"bool","name":"isLong","type":"bool"},{"internalType":"address","name":"baseAsset","type":"address"},{"internalType":"uint256","name":"strike","type":"uint256"},{"internalType":"uint256","name":"premium","type":"uint256"},{"internalType":"uint256","name":"duration","type":"uint256"},{"internalType":"uint256","name":"expiration","type":"uint256"},{"internalType":"uint256","name":"nonce","type":"uint256"},{"internalType":"address[]","name":"whitelist","type":"address[]"},{"internalType":"address[]","name":"floorTokens","type":"address[]"},{"components":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"tokenAmount","type":"uint256"}],"internalType":"struct PuttyV2.ERC20Asset[]","name":"erc20Assets","type":"tuple[]"},{"components":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"internalType":"struct PuttyV2.ERC721Asset[]","name":"erc721Assets","type":"tuple[]"}],"internalType":"struct PuttyV2.Order","name":"order","type":"tuple"}],"name":"hashOrder","outputs":[{"internalType":"bytes32","name":"orderHash","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"isApprovedForAll","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address[]","name":"whitelist","type":"address[]"},{"internalType":"address","name":"target","type":"address"}],"name":"isWhitelisted","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"minimumValidNonce","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bytes","name":"","type":"bytes"}],"name":"onERC721Received","outputs":[{"internalType":"bytes4","name":"","type":"bytes4"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"id","type":"uint256"}],"name":"ownerOf","outputs":[{"internalType":"address","name":"owner","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"positionExpirations","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"positionFloorAssetTokenIds","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"id","type":"uint256"}],"name":"safeTransferFrom","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"id","type":"uint256"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"safeTransferFrom","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"operator","type":"address"},{"internalType":"bool","name":"approved","type":"bool"}],"name":"setApprovalForAll","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string","name":"_baseURI","type":"string"}],"name":"setBaseURI","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_fee","type":"uint256"}],"name":"setFee","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_minimumValidNonce","type":"uint256"}],"name":"setMinimumValidNonce","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes4","name":"interfaceId","type":"bytes4"}],"name":"supportsInterface","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"id","type":"uint256"}],"name":"tokenURI","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"id","type":"uint256"}],"name":"transferFrom","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"unclaimedFees","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"weth","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"components":[{"internalType":"address","name":"maker","type":"address"},{"internalType":"bool","name":"isCall","type":"bool"},{"internalType":"bool","name":"isLong","type":"bool"},{"internalType":"address","name":"baseAsset","type":"address"},{"internalType":"uint256","name":"strike","type":"uint256"},{"internalType":"uint256","name":"premium","type":"uint256"},{"internalType":"uint256","name":"duration","type":"uint256"},{"internalType":"uint256","name":"expiration","type":"uint256"},{"internalType":"uint256","name":"nonce","type":"uint256"},{"internalType":"address[]","name":"whitelist","type":"address[]"},{"internalType":"address[]","name":"floorTokens","type":"address[]"},{"components":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"tokenAmount","type":"uint256"}],"internalType":"struct PuttyV2.ERC20Asset[]","name":"erc20Assets","type":"tuple[]"},{"components":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"internalType":"struct PuttyV2.ERC721Asset[]","name":"erc721Assets","type":"tuple[]"}],"internalType":"struct PuttyV2.Order","name":"order","type":"tuple"}],"name":"withdraw","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"asset","type":"address"},{"internalType":"address","name":"recipient","type":"address"}],"name":"withdrawFees","outputs":[],"stateMutability":"payable","type":"function"}]

6101606040523480156200001257600080fd5b5060405162005d0438038062005d048339810160408190526200003591620003c7565b604080518082018252600580825264507574747960d81b60208084018290528451808601865260038152620322e360ec1b8183015285518087018752938452838201929092528451808601909552600485526313d4155560e21b9085015291926000620000a383826200052c565b506001620000b282826200052c565b5050825160209384012082519284019290922060e08390526101008190524660a0818152604080517f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f818901819052818301979097526060810194909452608080850193909352308483018190528151808603909301835260c09485019091528151919096012090529290925261012052506200014f33620001d7565b6001600160a01b038116620001ab5760405162461bcd60e51b815260206004820152601560248201527f4d7573742073657420776574682061646472657373000000000000000000000060448201526064015b60405180910390fd5b620001b68362000229565b620001c1826200027e565b6001600160a01b031661014052506200062d9050565b600680546001600160a01b038381166001600160a01b0319831681179093556040519116919082907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a35050565b6200023362000310565b60076200024182826200052c565b507f325d37e8fb549c86966f09bc3e6f62eb3afa93b255d6e3234338001f3d80bd8681604051620002739190620005f8565b60405180910390a150565b6200028862000310565b601e8110620002da5760405162461bcd60e51b815260206004820152601860248201527f666565206d757374206265206c657373207468616e20332500000000000000006044820152606401620001a2565b60088190556040518181527f63fe946ed58429ac3c5e64d4356ff92c26d7fa1e73586515df8ba9f059ab54a59060200162000273565b6006546001600160a01b031633146200036c5760405162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e65726044820152606401620001a2565b565b634e487b7160e01b600052604160045260246000fd5b60005b83811015620003a157818101518382015260200162000387565b50506000910152565b80516001600160a01b0381168114620003c257600080fd5b919050565b600080600060608486031215620003dd57600080fd5b83516001600160401b0380821115620003f557600080fd5b818601915086601f8301126200040a57600080fd5b8151818111156200041f576200041f6200036e565b604051601f8201601f19908116603f011681019083821181831017156200044a576200044a6200036e565b816040528281528960208487010111156200046457600080fd5b6200047783602083016020880162000384565b8097505050505050602084015191506200049460408501620003aa565b90509250925092565b600181811c90821680620004b257607f821691505b602082108103620004d357634e487b7160e01b600052602260045260246000fd5b50919050565b601f8211156200052757600081815260208120601f850160051c81016020861015620005025750805b601f850160051c820191505b8181101562000523578281556001016200050e565b5050505b505050565b81516001600160401b038111156200054857620005486200036e565b62000560816200055984546200049d565b84620004d9565b602080601f8311600181146200059857600084156200057f5750858301515b600019600386901b1c1916600185901b17855562000523565b600085815260208120601f198616915b82811015620005c957888601518255948401946001909101908401620005a8565b5085821015620005e85787850151600019600388901b60f8161c191681555b5050505050600190811b01905550565b60208152600082518060208401526200061981604085016020870162000384565b601f01601f19169190910160400192915050565b60805160a05160c05160e051610100516101205161014051615652620006b260003960008181610528015281816114590152818161168e015281816120e60152818161242101528181612499015261265501526000613c1101526000613c6001526000613c3b01526000613b9401526000613bbe01526000613be801526156526000f3fe6080604052600436106102f25760003560e01c80636c0360eb1161018f578063a639417c116100e1578063dead8d391161008a578063eb6398ba11610064578063eb6398ba1461091b578063f255527814610948578063f2fde38b1461095b57600080fd5b8063dead8d3914610893578063e8401335146108c0578063e985e9c5146108e057600080fd5b8063b91611f4116100bb578063b91611f414610829578063c87b56dd1461085d578063ddca3f431461087d57600080fd5b8063a639417c146107c6578063abaf6c4f146107d9578063b88d4fde1461080957600080fd5b80638639990a116101435780638f13f48e1161011d5780638f13f48e1461076157806395d89b4114610791578063a22cb465146107a657600080fd5b80638639990a146106f65780638da5cb5b146107235780638eb2c7c61461074157600080fd5b8063715018a611610174578063715018a614610698578063787afced146106ad57806378e890ba146106e157600080fd5b80636c0360eb1461066357806370a082311461067857600080fd5b80633fc8cef31161024857806361c9436a116101fc57806368840dd4116101d657806368840dd41461061d57806369fe0e2d146106305780636b3b94821461064357600080fd5b806361c9436a146105b05780636352211e146105d057806367426597146105f057600080fd5b806349ce218e1161022d57806349ce218e1461056a57806353373a561461057d57806355f804b31461059d57600080fd5b80633fc8cef31461051657806342842e0e1461054a57600080fd5b806318fc0b18116102aa5780632b2aa2be116102845780632b2aa2be146104a25780632ce33441146104c25780633a7a8d28146104f657600080fd5b806318fc0b18146104345780631913cfd71461046257806323b872dd1461048257600080fd5b8063081812fc116102db578063081812fc1461034e578063095ea7b31461039c578063150b7a02146103be57600080fd5b806301ffc9a7146102f757806306fdde031461032c575b600080fd5b34801561030357600080fd5b50610317610312366004614119565b61097b565b60405190151581526020015b60405180910390f35b34801561033857600080fd5b50610341610a60565b6040516103239190614186565b34801561035a57600080fd5b50610384610369366004614199565b6004602052600090815260409020546001600160a01b031681565b6040516001600160a01b039091168152602001610323565b3480156103a857600080fd5b506103bc6103b73660046141d2565b610aee565b005b3480156103ca57600080fd5b506104036103d9366004614240565b7f150b7a020000000000000000000000000000000000000000000000000000000095945050505050565b6040517fffffffff000000000000000000000000000000000000000000000000000000009091168152602001610323565b34801561044057600080fd5b5061045461044f3660046145d6565b610bfc565b604051908152602001610323565b34801561046e57600080fd5b506103bc61047d3660046145d6565b610c4f565b34801561048e57600080fd5b506103bc61049d36600461460b565b610ef7565b3480156104ae57600080fd5b506103416104bd36600461464c565b6110e4565b3480156104ce57600080fd5b506104547fa55d25ac87b1c0e125b844871149c64ce5636cae2bb0ed2f2eb4990df479cd9181565b34801561050257600080fd5b50610454610511366004614681565b6111cd565b34801561052257600080fd5b506103847f000000000000000000000000000000000000000000000000000000000000000081565b34801561055657600080fd5b506103bc61056536600461460b565b6111fe565b6103bc6105783660046146e8565b611334565b34801561058957600080fd5b5061034161059836600461464c565b6119f2565b6103bc6105ab366004614751565b611ad5565b3480156105bc57600080fd5b506103176105cb3660046147e6565b611b24565b3480156105dc57600080fd5b506103846105eb366004614199565b611b89565b3480156105fc57600080fd5b5061061061060b366004614913565b611bf3565b6040516103239190614a4c565b61045461062b366004614a5f565b611d3c565b6103bc61063e366004614199565b612903565b34801561064f57600080fd5b5061045461065e3660046145d6565b612990565b34801561066f57600080fd5b50610341612b02565b34801561068457600080fd5b50610454610693366004614ae1565b612b0f565b3480156106a457600080fd5b506103bc612b8e565b3480156106b957600080fd5b506104547f768c1d2c3157c9ca752098be2da2da3e1ddae5a69ca4394b1f83e1179407e8f081565b3480156106ed57600080fd5b50610454612ba2565b34801561070257600080fd5b50610454610711366004614ae1565b600e6020526000908152604090205481565b34801561072f57600080fd5b506006546001600160a01b0316610384565b34801561074d57600080fd5b506103bc61075c3660046145d6565b612bb1565b34801561076d57600080fd5b5061031761077c366004614199565b60096020526000908152604090205460ff1681565b34801561079d57600080fd5b50610341612cf2565b3480156107b257600080fd5b506103bc6107c1366004614afe565b612cff565b6104546107d4366004614b2c565b612d89565b3480156107e557600080fd5b506103176107f4366004614199565b600b6020526000908152604090205460ff1681565b34801561081557600080fd5b506103bc610824366004614240565b612db9565b34801561083557600080fd5b506104547f331cf33dce9314036c50f72ada91444e078be32f06bf1d891362de01ac1a8d6681565b34801561086957600080fd5b50610341610878366004614199565b612ed8565b34801561088957600080fd5b5061045460085481565b34801561089f57600080fd5b506104546108ae366004614ae1565b600d6020526000908152604090205481565b3480156108cc57600080fd5b506103bc6108db366004614199565b612f71565b3480156108ec57600080fd5b506103176108fb366004614bae565b600560209081526000928352604080842090915290825290205460ff1681565b34801561092757600080fd5b50610454610936366004614199565b600a6020526000908152604090205481565b6103bc610956366004614bae565b613013565b34801561096757600080fd5b506103bc610976366004614ae1565b61309a565b60007f01ffc9a7000000000000000000000000000000000000000000000000000000007fffffffff0000000000000000000000000000000000000000000000000000000083161480610a0e57507f80ac58cd000000000000000000000000000000000000000000000000000000007fffffffff000000000000000000000000000000000000000000000000000000008316145b80610a5a57507f5b5e139f000000000000000000000000000000000000000000000000000000007fffffffff000000000000000000000000000000000000000000000000000000008316145b92915050565b60008054610a6d90614bdc565b80601f0160208091040260200160405190810160405280929190818152602001828054610a9990614bdc565b8015610ae65780601f10610abb57610100808354040283529160200191610ae6565b820191906000526020600020905b815481529060010190602001808311610ac957829003601f168201915b505050505081565b6000818152600260205260409020546001600160a01b031633811480610b3757506001600160a01b038116600090815260056020908152604080832033845290915290205460ff165b610b885760405162461bcd60e51b815260206004820152600e60248201527f4e4f545f415554484f52495a454400000000000000000000000000000000000060448201526064015b60405180910390fd5b60008281526004602052604080822080547fffffffffffffffffffffffff0000000000000000000000000000000000000000166001600160a01b0387811691821790925591518593918516917f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92591a4505050565b60008082604051602001610c109190614d9b565b604051602081830303815290604052806020019051810190610c329190614eb0565b60408085015115908201529050610c4881612990565b9392505050565b806040015115610ca15760405162461bcd60e51b815260206004820152601660248201527f4d7573742062652073686f727420706f736974696f6e000000000000000000006044820152606401610b7f565b6000610cac82612990565b905033610cb882611b89565b6001600160a01b031614610d0e5760405162461bcd60e51b815260206004820152600960248201527f4e6f74206f776e657200000000000000000000000000000000000000000000006044820152606401610b7f565b6000610d1983610bfc565b6000818152600b602052604090205490915060ff168080610d4757506000828152600a602052604090205442115b610d935760405162461bcd60e51b815260206004820152601c60248201527f4d75737420626520657865726369736564206f722065787069726564000000006044820152606401610b7f565b610da03361dead85610ef7565b827ff68858cec1e3d5803133ca29f13a5cbfe5dd96d89a90beb216e3af6b5cd3019385604051610dd09190614d9b565b60405180910390a283602001518015610de65750805b80610dfc57508360200151158015610dfc575080155b15610e2d57610e2733856080015186606001516001600160a01b031661312a9092919063ffffffff16565b50505050565b83602001518015610e3c575080155b80610e5157508360200151158015610e515750805b15610e2757610e648461016001516131c9565b610e72846101800151613269565b60008460200151610e835783610e85565b825b6101408601516000828152600c60209081526040918290208054835181840281018401909452808452949550610ef0949091830182828015610ee657602002820191906000526020600020905b815481526020019060010190808311610ed2575b5050505050613355565b5050505050565b6000818152600260205260409020546001600160a01b03848116911614610f605760405162461bcd60e51b815260206004820152600a60248201527f57524f4e475f46524f4d000000000000000000000000000000000000000000006044820152606401610b7f565b6001600160a01b038216610fb65760405162461bcd60e51b815260206004820152601160248201527f494e56414c49445f524543495049454e540000000000000000000000000000006044820152606401610b7f565b336001600160a01b0384161480610ff057506001600160a01b038316600090815260056020908152604080832033845290915290205460ff165b8061101157506000818152600460205260409020546001600160a01b031633145b61105d5760405162461bcd60e51b815260206004820152600e60248201527f4e4f545f415554484f52495a45440000000000000000000000000000000000006044820152606401610b7f565b600081815260026020908152604080832080546001600160a01b038088167fffffffffffffffffffffffff000000000000000000000000000000000000000092831681179093556004909452828520805490911690559051849391928716917fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef91a4505050565b606060005b82518110156111c757817fa55d25ac87b1c0e125b844871149c64ce5636cae2bb0ed2f2eb4990df479cd918483815181106111265761112661500c565b6020026020010151600001518584815181106111445761114461500c565b60200260200101516020015160405160200161117c939291909283526001600160a01b03919091166020830152604082015260600190565b604051602081830303815290604052805190602001206040516020016111a392919061503b565b604051602081830303815290604052915080806111bf9061508c565b9150506110e9565b50919050565b600c60205281600052604060002081815481106111e957600080fd5b90600052602060002001600091509150505481565b611209838383610ef7565b6001600160a01b0382163b15806112e357506040517f150b7a02000000000000000000000000000000000000000000000000000000008082523360048301526001600160a01b03858116602484015260448301849052608060648401526000608484015290919084169063150b7a029060a4016020604051808303816000875af115801561129b573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906112bf91906150c4565b7fffffffff0000000000000000000000000000000000000000000000000000000016145b61132f5760405162461bcd60e51b815260206004820152601060248201527f554e534146455f524543495049454e54000000000000000000000000000000006044820152606401610b7f565b505050565b600061133f84612990565b90503361134b82611b89565b6001600160a01b0316146113a15760405162461bcd60e51b815260206004820152600960248201527f4e6f74206f776e657200000000000000000000000000000000000000000000006044820152606401610b7f565b83604001516113f25760405162461bcd60e51b815260206004820181905260248201527f43616e206f6e6c79206578657263697365206c6f6e6720706f736974696f6e736044820152606401610b7f565b6000818152600a6020526040902054421061144f5760405162461bcd60e51b815260206004820152601460248201527f506f736974696f6e2068617320657870697265640000000000000000000000006044820152606401610b7f565b34158061149157507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031684606001516001600160a01b0316145b6114dd5760405162461bcd60e51b815260206004820152601560248201527f43616e6e6f7420757365206e61746976652045544800000000000000000000006044820152606401610b7f565b83602001511561153a5781156115355760405162461bcd60e51b815260206004820152601d60248201527f496e76616c696420666c6f6f7220746f6b656e496473206c656e6774680000006044820152606401610b7f565b61158f565b61014084015151821461158f5760405162461bcd60e51b815260206004820152601e60248201527f57726f6e6720616d6f756e74206f6620666c6f6f7220746f6b656e49647300006044820152606401610b7f565b61159c3361dead83610ef7565b6000818152600b60205260409081902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001660011790555181907f5a6595ae5a8bfc7c3e5fa154d614f8cd831d1a1ab6424f84b7e00be52a2d64be906116099086908690899061512c565b60405180910390a2600061161c85610bfc565b6020860151909150156117b45760808501511561172d573415611705578460800151341461168c5760405162461bcd60e51b815260206004820152601960248201527f496e636f72726563742045544820616d6f756e742073656e74000000000000006044820152606401610b7f565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663d0e30db0346040518263ffffffff1660e01b81526004016000604051808303818588803b1580156116e757600080fd5b505af11580156116fb573d6000803e3d6000fd5b505050505061172d565b61172d3330876080015188606001516001600160a01b031661343a909392919063ffffffff16565b61173b8561016001516131c9565b611749856101800151613269565b6101408501516000838152600c602090815260409182902080548351818402810184019094528084526117af949392830182828015610ee65760200282019190600052602060002090815481526020019060010190808311610ed2575050505050613355565b6118a4565b34156118025760405162461bcd60e51b815260206004820152601960248201527f507574732063616e277420757365206e617469766520455448000000000000006044820152606401610b7f565b6000818152600c6020526040902061181b908585614050565b5061184233866080015187606001516001600160a01b031661312a9092919063ffffffff16565b611851856101600151336134df565b611860856101800151336135b9565b6118a48561014001518585808060200260200160405190810160405280939291908181526020018383602002808284376000920191909152503392506136a5915050565b60006118af82611b89565b6000604088015290506118e2817f117b68e20000000000000000000000000000000000000000000000000000000061378a565b156119ea57806001600160a01b0316613a985a6118ff9190615152565b6040517f53e0af970000000000000000000000000000000000000000000000000000000090611938908a9033908b908b90602401615165565b60408051601f198184030181529181526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff000000000000000000000000000000000000000000000000000000009094169390931790925290516119a3919061519a565b60006040518083038160008787f1925050503d80600081146119e1576040519150601f19603f3d011682016040523d82523d6000602084013e6119e6565b606091505b5050505b505050505050565b606060005b82518110156111c757817f768c1d2c3157c9ca752098be2da2da3e1ddae5a69ca4394b1f83e1179407e8f0848381518110611a3457611a3461500c565b602002602001015160000151858481518110611a5257611a5261500c565b602002602001015160200151604051602001611a8a939291909283526001600160a01b03919091166020830152604082015260600190565b60405160208183030381529060405280519060200120604051602001611ab192919061503b565b60405160208183030381529060405291508080611acd9061508c565b9150506119f7565b611add6137a6565b6007611ae982826151fc565b507f325d37e8fb549c86966f09bc3e6f62eb3afa93b255d6e3234338001f3d80bd8681604051611b199190614186565b60405180910390a150565b6000805b8351811015611b7f57838181518110611b4357611b4361500c565b60200260200101516001600160a01b0316836001600160a01b031603611b6d576001915050610a5a565b80611b778161508c565b915050611b28565b5060009392505050565b6000818152600260205260409020546001600160a01b031680611bee5760405162461bcd60e51b815260206004820152600a60248201527f4e4f545f4d494e544544000000000000000000000000000000000000000000006044820152606401610b7f565b919050565b835160609083148015611c065750815183145b611c525760405162461bcd60e51b815260206004820152601860248201527f4c656e677468206d69736d6174636820696e20696e70757400000000000000006044820152606401610b7f565b845167ffffffffffffffff811115611c6c57611c6c6142b3565b604051908082528060200260200182016040528015611c95578160200160208202803683370190505b50905060005b8551811015611d3357611d04868281518110611cb957611cb961500c565b6020026020010151868684818110611cd357611cd361500c565b9050602002810190611ce591906152f8565b868581518110611cf757611cf761500c565b6020026020010151611d3c565b828281518110611d1657611d1661500c565b602090810291909101015280611d2b8161508c565b915050611c9b565b50949350505050565b600080611d4886612990565b9050611d8f86600001518287878080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525061380092505050565b611ddb5760405162461bcd60e51b815260206004820152601160248201527f496e76616c6964207369676e61747572650000000000000000000000000000006044820152606401610b7f565b60008181526009602052604090205460ff1615611e3a5760405162461bcd60e51b815260206004820152601860248201527f4f7264657220686173206265656e2063616e63656c6c656400000000000000006044820152606401610b7f565b85516001600160a01b03166000908152600e60205260409020546101008701511015611ea85760405162461bcd60e51b815260206004820152601960248201527f4e6f6e636520697320736d616c6c6572207468616e206d696e000000000000006044820152606401610b7f565b610120860151511580611ec55750611ec586610120015133611b24565b611f115760405162461bcd60e51b815260206004820152600f60248201527f4e6f742077686974656c697374656400000000000000000000000000000000006044820152606401610b7f565b63337f98008660c001511115611f695760405162461bcd60e51b815260206004820152601160248201527f4475726174696f6e20746f6f206c6f6e670000000000000000000000000000006044820152606401610b7f565b6103848660c001511015611fbf5760405162461bcd60e51b815260206004820152601260248201527f4475726174696f6e20746f6f2073686f727400000000000000000000000000006044820152606401610b7f565b8560e0015142106120125760405162461bcd60e51b815260206004820152601160248201527f4f726465722068617320657870697265640000000000000000000000000000006044820152606401610b7f565b600086606001516001600160a01b03163b116120705760405162461bcd60e51b815260206004820152601960248201527f626173654173736574206973206e6f7420636f6e7472616374000000000000006044820152606401610b7f565b8560200151801561208357508560400151155b156120dc5761014086015151156120dc5760405162461bcd60e51b815260206004820181905260248201527f53686f72742063616c6c2063616e74206861766520666c6f6f72546f6b656e736044820152606401610b7f565b34158061211e57507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031686606001516001600160a01b0316145b61216a5760405162461bcd60e51b815260206004820152601560248201527f43616e6e6f7420757365206e61746976652045544800000000000000000000006044820152606401610b7f565b8560200151801561217c575085604001515b6121d4578251156121cf5760405162461bcd60e51b815260206004820152601b60248201527f496e76616c696420666c6f6f7220746f6b656e73206c656e67746800000000006044820152606401610b7f565b61222a565b8561014001515183511461222a5760405162461bcd60e51b815260206004820152601e60248201527f57726f6e6720616d6f756e74206f6620666c6f6f7220746f6b656e49647300006044820152606401610b7f565b85516122369082613988565b600061224187610bfc565b92508290506122503382613988565b86604001518015612262575086602001515b15612288576000828152600c6020908152604090912085516122869287019061409b565b505b60c0870151612297904261535d565b600a600089604001516122aa57856122ac565b845b81526020019081526020016000208190555080827f18cce19e7f1994d20463b7d7811a7deda47fbaf72748ad25b13cf6c2aaa2ee21868a6040516122f1929190615370565b60405180910390a36008546000901561235a576103e86008548960a00151612319919061539e565b612323919061540a565b60608901516001600160a01b03166000908152600d602052604081208054929350839290919061235490849061535d565b90915550505b60a0880151156125a1578760400151156123c65761239d886000015133838b60a001516123879190615152565b60608c01516001600160a01b031692919061343a565b80156123c157875160608901516123c1916001600160a01b0390911690308461343a565b6125a1565b3415612568578760a00151341461241f5760405162461bcd60e51b815260206004820152601960248201527f496e636f72726563742045544820616d6f756e742073656e74000000000000006044820152606401610b7f565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663d0e30db08960a001516040518263ffffffff1660e01b81526004016000604051808303818588803b15801561247e57600080fd5b505af1158015612492573d6000803e3d6000fd5b50505050507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663a9059cbb8960000151838b60a001516124db9190615152565b6040517fffffffff0000000000000000000000000000000000000000000000000000000060e085901b1681526001600160a01b03909216600483015260248201526044016020604051808303816000875af115801561253e573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612562919061541e565b506125a1565b612581338960000151838b60a001516123879190615152565b80156125a15760608801516125a1906001600160a01b031633308461343a565b87604001511580156125b557508760200151155b156125e2578751608089015160608a01516125dd926001600160a01b0390911691309061343a565b6127c5565b876040015180156125f557508760200151155b156126f45734156126cc57876080015134146126535760405162461bcd60e51b815260206004820152601960248201527f496e636f72726563742045544820616d6f756e742073656e74000000000000006044820152606401610b7f565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663d0e30db0346040518263ffffffff1660e01b81526004016000604051808303818588803b1580156126ae57600080fd5b505af11580156126c2573d6000803e3d6000fd5b50505050506127c5565b6125dd33308a608001518b606001516001600160a01b031661343a909392919063ffffffff16565b8760400151158015612707575087602001515b156127325761271f88610160015189600001516134df565b6125dd88610180015189600001516135b9565b87604001518015612744575087602001515b156127c55734156127975760405162461bcd60e51b815260206004820152601e60248201527f4c6f6e672063616c6c2063616e277420757365206e61746976652045544800006044820152606401610b7f565b6127a6886101600151336134df565b6127b5886101800151336135b9565b6127c588610140015186336136a5565b87516127f1907f117b68e20000000000000000000000000000000000000000000000000000000061378a565b156128f85787516001600160a01b0316613a985a61280f9190615152565b6040517f429bc7750000000000000000000000000000000000000000000000000000000090612846908c9033908b9060240161543b565b60408051601f198184030181529181526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff000000000000000000000000000000000000000000000000000000009094169390931790925290516128b1919061519a565b60006040518083038160008787f1925050503d80600081146128ef576040519150601f19603f3d011682016040523d82523d6000602084013e6128f4565b606091505b5050505b505050949350505050565b61290b6137a6565b601e811061295b5760405162461bcd60e51b815260206004820152601860248201527f666565206d757374206265206c657373207468616e20332500000000000000006044820152606401610b7f565b60088190556040518181527f63fe946ed58429ac3c5e64d4356ff92c26d7fa1e73586515df8ba9f059ab54a590602001611b19565b60007f331cf33dce9314036c50f72ada91444e078be32f06bf1d891362de01ac1a8d66826000015183602001518460400151856060015186608001518760a001518860c001518960e001518a61010001518b61012001516040516020016129f7919061546f565b604051602081830303815290604052805190602001208c6101400151604051602001612a23919061546f565b60405160208183030381529060405280519060200120612a478e61016001516110e4565b80519060200120612a5c8f61018001516119f2565b8051602091820120604080519283019f909f526001600160a01b039d8e169e82019e909e529a151560608c015298151560808b01529990961660a089015260c088019490945260e08701929092526101008601526101208501526101408401526101608301526101808201929092526101a08101919091526101c08101919091526101e001604051602081830303815290604052805190602001209050610a5a81613ab4565b60078054610a6d90614bdc565b60006001600160a01b038216612b675760405162461bcd60e51b815260206004820152600c60248201527f5a45524f5f4144445245535300000000000000000000000000000000000000006044820152606401610b7f565b507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff919050565b612b966137a6565b612ba06000613b1d565b565b6000612bac613b87565b905090565b80516001600160a01b03163314612c0a5760405162461bcd60e51b815260206004820152600e60248201527f4e6f7420796f7572206f726465720000000000000000000000000000000000006044820152606401610b7f565b6000612c1582612990565b6000818152600260205260409020549091506001600160a01b031615612c7d5760405162461bcd60e51b815260206004820152601460248201527f4f7264657220616c72656164792066696c6c65640000000000000000000000006044820152606401610b7f565b6000818152600960205260409081902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001660011790555181907f87bd0244ffd0d26804a2c0c77304351c38d53bc54c39f8b34739b051972698d990612ce6908590614d9b565b60405180910390a25050565b60018054610a6d90614bdc565b3360008181526005602090815260408083206001600160a01b0387168085529083529281902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001686151590811790915590519081529192917f17307eab39ab6107e8899845ad3d59bd9653f200f220920489ca2b5937696c31910160405180910390a35050565b6000612d9482612bb1565b604080516000815260208101909152612daf86868684611d3c565b9695505050505050565b612dc4858585610ef7565b6001600160a01b0384163b1580612e8c57506040517f150b7a0200000000000000000000000000000000000000000000000000000000808252906001600160a01b0386169063150b7a0290612e259033908a908990899089906004016154ae565b6020604051808303816000875af1158015612e44573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612e6891906150c4565b7fffffffff0000000000000000000000000000000000000000000000000000000016145b610ef05760405162461bcd60e51b815260206004820152601060248201527f554e534146455f524543495049454e54000000000000000000000000000000006044820152606401610b7f565b6000818152600260205260409020546060906001600160a01b0316612f3f5760405162461bcd60e51b815260206004820152601e60248201527f55524920717565727920666f72204e4f545f4d494e54454420746f6b656e00006044820152606401610b7f565b6007612f4a83613cae565b604051602001612f5b929190615502565b6040516020818303038152906040529050919050565b336000908152600e60205260409020548111612fcf5760405162461bcd60e51b815260206004820152601560248201527f4e6f6e63652073686f756c6420696e63726561736500000000000000000000006044820152606401610b7f565b336000908152600e602052604090819020829055517f7063087ceb22bbd43bac735fc64c753e025549bf590de2caef3e70b675ed783190611b199083815260200190565b61301b6137a6565b6001600160a01b0382166000818152600d60205260408082208054929055519091907f83ac76d99ab0e09d53da4de73a04e9b73e323b87cd1e723b66b178b5c064c0359061307e90849086909182526001600160a01b0316602082015260400190565b60405180910390a261132f6001600160a01b038416838361312a565b6130a26137a6565b6001600160a01b03811661311e5760405162461bcd60e51b815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201527f64647265737300000000000000000000000000000000000000000000000000006064820152608401610b7f565b61312781613b1d565b50565b60006040517fa9059cbb000000000000000000000000000000000000000000000000000000008152836004820152826024820152602060006044836000895af13d15601f3d1160016000511416171691505080610e275760405162461bcd60e51b815260206004820152600f60248201527f5452414e534645525f4641494c454400000000000000000000000000000000006044820152606401610b7f565b60005b81518110156132655760008282815181106131e9576131e961500c565b602002602001015160200151111561325357613253338383815181106132115761321161500c565b60200260200101516020015184848151811061322f5761322f61500c565b6020026020010151600001516001600160a01b031661312a9092919063ffffffff16565b8061325d8161508c565b9150506131cc565b5050565b60005b8151811015613265578181815181106132875761328761500c565b6020026020010151600001516001600160a01b03166342842e0e30338585815181106132b5576132b561500c565b60209081029190910181015101516040517fffffffff0000000000000000000000000000000000000000000000000000000060e086901b1681526001600160a01b0393841660048201529290911660248301526044820152606401600060405180830381600087803b15801561332a57600080fd5b505af115801561333e573d6000803e3d6000fd5b50505050808061334d9061508c565b91505061326c565b60005b825181101561132f578281815181106133735761337361500c565b60200260200101516001600160a01b03166342842e0e303385858151811061339d5761339d61500c565b60209081029190910101516040517fffffffff0000000000000000000000000000000000000000000000000000000060e086901b1681526001600160a01b0393841660048201529290911660248301526044820152606401600060405180830381600087803b15801561340f57600080fd5b505af1158015613423573d6000803e3d6000fd5b5050505080806134329061508c565b915050613358565b60006040517f23b872dd0000000000000000000000000000000000000000000000000000000081528460048201528360248201528260448201526020600060648360008a5af13d15601f3d1160016000511416171691505080610ef05760405162461bcd60e51b815260206004820152601460248201527f5452414e534645525f46524f4d5f4641494c45440000000000000000000000006044820152606401610b7f565b60005b825181101561132f5760008382815181106134ff576134ff61500c565b602002602001015160000151905060008483815181106135215761352161500c565b60200260200101516020015190506000826001600160a01b03163b116135895760405162461bcd60e51b815260206004820152601c60248201527f45524332303a20546f6b656e206973206e6f7420636f6e7472616374000000006044820152606401610b7f565b80156135a4576135a46001600160a01b03831685308461343a565b505080806135b19061508c565b9150506134e2565b60005b825181101561132f578281815181106135d7576135d761500c565b6020026020010151600001516001600160a01b03166342842e0e83308685815181106136055761360561500c565b60209081029190910181015101516040517fffffffff0000000000000000000000000000000000000000000000000000000060e086901b1681526001600160a01b0393841660048201529290911660248301526044820152606401600060405180830381600087803b15801561367a57600080fd5b505af115801561368e573d6000803e3d6000fd5b50505050808061369d9061508c565b9150506135bc565b60005b8351811015610e27578381815181106136c3576136c361500c565b60200260200101516001600160a01b03166342842e0e83308685815181106136ed576136ed61500c565b60209081029190910101516040517fffffffff0000000000000000000000000000000000000000000000000000000060e086901b1681526001600160a01b0393841660048201529290911660248301526044820152606401600060405180830381600087803b15801561375f57600080fd5b505af1158015613773573d6000803e3d6000fd5b5050505080806137829061508c565b9150506136a8565b600061379583613deb565b8015610c485750610c488383613e4f565b6006546001600160a01b03163314612ba05760405162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e65726044820152606401610b7f565b600080600061380f8585613f1e565b90925090506000816004811115613828576138286155a7565b1480156138465750856001600160a01b0316826001600160a01b0316145b1561385657600192505050610c48565b600080876001600160a01b0316631626ba7e60e01b888860405160240161387e9291906155d6565b60408051601f198184030181529181526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff000000000000000000000000000000000000000000000000000000009094169390931790925290516138e9919061519a565b600060405180830381855afa9150503d8060008114613924576040519150601f19603f3d011682016040523d82523d6000602084013e613929565b606091505b509150915081801561393c575080516020145b801561397c575080517f1626ba7e000000000000000000000000000000000000000000000000000000009061397a90830160209081019084016155ef565b145b98975050505050505050565b6001600160a01b0382166139de5760405162461bcd60e51b815260206004820152601160248201527f494e56414c49445f524543495049454e540000000000000000000000000000006044820152606401610b7f565b6000818152600260205260409020546001600160a01b031615613a435760405162461bcd60e51b815260206004820152600e60248201527f414c52454144595f4d494e5445440000000000000000000000000000000000006044820152606401610b7f565b60008181526002602052604080822080547fffffffffffffffffffffffff0000000000000000000000000000000000000000166001600160a01b03861690811790915590518392907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef908290a45050565b6000610a5a613ac1613b87565b836040517f19010000000000000000000000000000000000000000000000000000000000006020820152602281018390526042810182905260009060620160405160208183030381529060405280519060200120905092915050565b600680546001600160a01b038381167fffffffffffffffffffffffff0000000000000000000000000000000000000000831681179093556040519116919082907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a35050565b6000306001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016148015613be057507f000000000000000000000000000000000000000000000000000000000000000046145b15613c0a57507f000000000000000000000000000000000000000000000000000000000000000090565b50604080517f00000000000000000000000000000000000000000000000000000000000000006020808301919091527f0000000000000000000000000000000000000000000000000000000000000000828401527f000000000000000000000000000000000000000000000000000000000000000060608301524660808301523060a0808401919091528351808403909101815260c0909201909252805191012090565b606081600003613cf157505060408051808201909152600181527f3000000000000000000000000000000000000000000000000000000000000000602082015290565b8160005b8115613d1b5780613d058161508c565b9150613d149050600a8361540a565b9150613cf5565b60008167ffffffffffffffff811115613d3657613d366142b3565b6040519080825280601f01601f191660200182016040528015613d60576020820181803683370190505b5090505b8415613de357613d75600183615152565b9150613d82600a86615608565b613d8d90603061535d565b60f81b818381518110613da257613da261500c565b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a905350613ddc600a8661540a565b9450613d64565b949350505050565b6000613e17827f01ffc9a700000000000000000000000000000000000000000000000000000000613e4f565b8015610a5a5750613e48827fffffffff00000000000000000000000000000000000000000000000000000000613e4f565b1592915050565b604080517fffffffff000000000000000000000000000000000000000000000000000000008316602480830191909152825180830390910181526044909101909152602080820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167f01ffc9a700000000000000000000000000000000000000000000000000000000178152825160009392849283928392918391908a617530fa92503d91506000519050828015613f07575060208210155b8015613f135750600081115b979650505050505050565b6000808251604103613f545760208301516040840151606085015160001a613f4887828585613f63565b94509450505050613f5c565b506000905060025b9250929050565b6000807f7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a0831115613f9a5750600090506003614047565b8460ff16601b14158015613fb257508460ff16601c14155b15613fc35750600090506004614047565b6040805160008082526020820180845289905260ff881692820192909252606081018690526080810185905260019060a0016020604051602081039080840390855afa158015614017573d6000803e3d6000fd5b5050604051601f1901519150506001600160a01b03811661404057600060019250925050614047565b9150600090505b94509492505050565b82805482825590600052602060002090810192821561408b579160200282015b8281111561408b578235825591602001919060010190614070565b506140979291506140d6565b5090565b82805482825590600052602060002090810192821561408b579160200282015b8281111561408b5782518255916020019190600101906140bb565b5b8082111561409757600081556001016140d7565b7fffffffff000000000000000000000000000000000000000000000000000000008116811461312757600080fd5b60006020828403121561412b57600080fd5b8135610c48816140eb565b60005b83811015614151578181015183820152602001614139565b50506000910152565b60008151808452614172816020860160208601614136565b601f01601f19169290920160200192915050565b602081526000610c48602083018461415a565b6000602082840312156141ab57600080fd5b5035919050565b6001600160a01b038116811461312757600080fd5b8035611bee816141b2565b600080604083850312156141e557600080fd5b82356141f0816141b2565b946020939093013593505050565b60008083601f84011261421057600080fd5b50813567ffffffffffffffff81111561422857600080fd5b602083019150836020828501011115613f5c57600080fd5b60008060008060006080868803121561425857600080fd5b8535614263816141b2565b94506020860135614273816141b2565b935060408601359250606086013567ffffffffffffffff81111561429657600080fd5b6142a2888289016141fe565b969995985093965092949392505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b6040805190810167ffffffffffffffff81118282101715614305576143056142b3565b60405290565b6040516101a0810167ffffffffffffffff81118282101715614305576143056142b3565b604051601f8201601f1916810167ffffffffffffffff81118282101715614358576143586142b3565b604052919050565b801515811461312757600080fd5b8035611bee81614360565b600067ffffffffffffffff821115614393576143936142b3565b5060051b60200190565b600082601f8301126143ae57600080fd5b813560206143c36143be83614379565b61432f565b82815260059290921b840181019181810190868411156143e257600080fd5b8286015b848110156144065780356143f9816141b2565b83529183019183016143e6565b509695505050505050565b600082601f83011261442257600080fd5b813560206144326143be83614379565b82815260069290921b8401810191818101908684111561445157600080fd5b8286015b84811015614406576040818903121561446e5760008081fd5b6144766142e2565b8135614481816141b2565b81528185013585820152835291830191604001614455565b60006101a082840312156144ac57600080fd5b6144b461430b565b90506144bf826141c7565b81526144cd6020830161436e565b60208201526144de6040830161436e565b60408201526144ef606083016141c7565b60608201526080820135608082015260a082013560a082015260c082013560c082015260e082013560e08201526101008083013581830152506101208083013567ffffffffffffffff8082111561454557600080fd5b6145518683870161439d565b8385015261014092508285013591508082111561456d57600080fd5b6145798683870161439d565b8385015261016092508285013591508082111561459557600080fd5b6145a186838701614411565b838501526101809250828501359150808211156145bd57600080fd5b506145ca85828601614411565b82840152505092915050565b6000602082840312156145e857600080fd5b813567ffffffffffffffff8111156145ff57600080fd5b613de384828501614499565b60008060006060848603121561462057600080fd5b833561462b816141b2565b9250602084013561463b816141b2565b929592945050506040919091013590565b60006020828403121561465e57600080fd5b813567ffffffffffffffff81111561467557600080fd5b613de384828501614411565b6000806040838503121561469457600080fd5b50508035926020909101359150565b60008083601f8401126146b557600080fd5b50813567ffffffffffffffff8111156146cd57600080fd5b6020830191508360208260051b8501011115613f5c57600080fd5b6000806000604084860312156146fd57600080fd5b833567ffffffffffffffff8082111561471557600080fd5b61472187838801614499565b9450602086013591508082111561473757600080fd5b50614744868287016146a3565b9497909650939450505050565b6000602080838503121561476457600080fd5b823567ffffffffffffffff8082111561477c57600080fd5b818501915085601f83011261479057600080fd5b8135818111156147a2576147a26142b3565b6147b484601f19601f8401160161432f565b915080825286848285010111156147ca57600080fd5b8084840185840137600090820190930192909252509392505050565b600080604083850312156147f957600080fd5b823567ffffffffffffffff81111561481057600080fd5b61481c8582860161439d565b925050602083013561482d816141b2565b809150509250929050565b600082601f83011261484957600080fd5b813560206148596143be83614379565b82815260059290921b8401810191818101908684111561487857600080fd5b8286015b84811015614406578035835291830191830161487c565b600082601f8301126148a457600080fd5b813560206148b46143be83614379565b82815260059290921b840181019181810190868411156148d357600080fd5b8286015b8481101561440657803567ffffffffffffffff8111156148f75760008081fd5b6149058986838b0101614838565b8452509183019183016148d7565b6000806000806060858703121561492957600080fd5b843567ffffffffffffffff8082111561494157600080fd5b818701915087601f83011261495557600080fd5b813560206149656143be83614379565b82815260059290921b8401810191818101908b84111561498457600080fd5b8286015b848110156149bc578035868111156149a05760008081fd5b6149ae8e86838b0101614499565b845250918301918301614988565b50985050880135925050808211156149d357600080fd5b6149df888389016146a3565b909550935060408701359150808211156149f857600080fd5b50614a0587828801614893565b91505092959194509250565b600081518084526020808501945080840160005b83811015614a4157815187529582019590820190600101614a25565b509495945050505050565b602081526000610c486020830184614a11565b60008060008060608587031215614a7557600080fd5b843567ffffffffffffffff80821115614a8d57600080fd5b614a9988838901614499565b95506020870135915080821115614aaf57600080fd5b614abb888389016141fe565b90955093506040870135915080821115614ad457600080fd5b50614a0587828801614838565b600060208284031215614af357600080fd5b8135610c48816141b2565b60008060408385031215614b1157600080fd5b8235614b1c816141b2565b9150602083013561482d81614360565b60008060008060608587031215614b4257600080fd5b843567ffffffffffffffff80821115614b5a57600080fd5b614b6688838901614499565b95506020870135915080821115614b7c57600080fd5b614b88888389016141fe565b90955093506040870135915080821115614ba157600080fd5b50614a0587828801614499565b60008060408385031215614bc157600080fd5b8235614bcc816141b2565b9150602083013561482d816141b2565b600181811c90821680614bf057607f821691505b6020821081036111c7577f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b600081518084526020808501945080840160005b83811015614a415781516001600160a01b031687529582019590820190600101614c3d565b600081518084526020808501945080840160005b83811015614a4157815180516001600160a01b031688528301518388015260409096019590820190600101614c76565b80516001600160a01b0316825260006101a06020830151614ccb602086018215159052565b506040830151614cdf604086018215159052565b506060830151614cfa60608601826001600160a01b03169052565b506080830151608085015260a083015160a085015260c083015160c085015260e083015160e0850152610100808401518186015250610120808401518282870152614d4783870182614c29565b925050506101408084015185830382870152614d638382614c29565b925050506101608084015185830382870152614d7f8382614c62565b925050506101808084015185830382870152612daf8382614c62565b602081526000610c486020830184614ca6565b8051611bee816141b2565b8051611bee81614360565b600082601f830112614dd557600080fd5b81516020614de56143be83614379565b82815260059290921b84018101918181019086841115614e0457600080fd5b8286015b84811015614406578051614e1b816141b2565b8352918301918301614e08565b600082601f830112614e3957600080fd5b81516020614e496143be83614379565b82815260069290921b84018101918181019086841115614e6857600080fd5b8286015b848110156144065760408189031215614e855760008081fd5b614e8d6142e2565b8151614e98816141b2565b81528185015185820152835291830191604001614e6c565b600060208284031215614ec257600080fd5b815167ffffffffffffffff80821115614eda57600080fd5b908301906101a08286031215614eef57600080fd5b614ef761430b565b614f0083614dae565b8152614f0e60208401614db9565b6020820152614f1f60408401614db9565b6040820152614f3060608401614dae565b60608201526080830151608082015260a083015160a082015260c083015160c082015260e083015160e08201526101008084015181830152506101208084015183811115614f7d57600080fd5b614f8988828701614dc4565b8284015250506101408084015183811115614fa357600080fd5b614faf88828701614dc4565b8284015250506101608084015183811115614fc957600080fd5b614fd588828701614e28565b8284015250506101808084015183811115614fef57600080fd5b614ffb88828701614e28565b918301919091525095945050505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b6000835161504d818460208801614136565b9190910191825250602001919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff82036150bd576150bd61505d565b5060010190565b6000602082840312156150d657600080fd5b8151610c48816140eb565b81835260007f07ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff83111561511357600080fd5b8260051b80836020870137939093016020019392505050565b6040815260006151406040830185876150e1565b8281036020840152612daf8185614ca6565b81810381811115610a5a57610a5a61505d565b6060815260006151786060830187614ca6565b6001600160a01b03861660208401528281036040840152613f138185876150e1565b600082516151ac818460208701614136565b9190910192915050565b601f82111561132f57600081815260208120601f850160051c810160208610156151dd5750805b601f850160051c820191505b818110156119ea578281556001016151e9565b815167ffffffffffffffff811115615216576152166142b3565b61522a816152248454614bdc565b846151b6565b602080601f83116001811461527d57600084156152475750858301515b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600386901b1c1916600185901b1785556119ea565b600085815260208120601f198616915b828110156152ac5788860151825594840194600190910190840161528d565b50858210156152e857878501517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600388901b60f8161c191681555b5050505050600190811b01905550565b60008083357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe184360301811261532d57600080fd5b83018035915067ffffffffffffffff82111561534857600080fd5b602001915036819003821315613f5c57600080fd5b80820180821115610a5a57610a5a61505d565b6040815260006153836040830185614a11565b82810360208401526153958185614ca6565b95945050505050565b6000817fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff04831182151516156153d6576153d661505d565b500290565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b600082615419576154196153db565b500490565b60006020828403121561543057600080fd5b8151610c4881614360565b60608152600061544e6060830186614ca6565b6001600160a01b03851660208401528281036040840152612daf8185614a11565b815160009082906020808601845b838110156154a25781516001600160a01b03168552938201939082019060010161547d565b50929695505050505050565b60006001600160a01b03808816835280871660208401525084604083015260806060830152826080830152828460a0840137600060a0848401015260a0601f19601f85011683010190509695505050505050565b600080845461551081614bdc565b60018281168015615528576001811461555b5761558a565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff008416875282151583028701945061558a565b8860005260208060002060005b858110156155815781548a820152908401908201615568565b50505082870194505b50505050835161559e818360208801614136565b01949350505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fd5b828152604060208201526000613de3604083018461415a565b60006020828403121561560157600080fd5b5051919050565b600082615617576156176153db565b50069056fea26469706673582212204a7c0a0073e530ef976dee1e73912eb3b8a58221ff1b9c392ddf1a424855312b64736f6c6343000810003300000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2000000000000000000000000000000000000000000000000000000000000002368747470733a2f2f6170692e70757474792e66696e616e63652f6d657461646174612f0000000000000000000000000000000000000000000000000000000000

Deployed Bytecode

0x6080604052600436106102f25760003560e01c80636c0360eb1161018f578063a639417c116100e1578063dead8d391161008a578063eb6398ba11610064578063eb6398ba1461091b578063f255527814610948578063f2fde38b1461095b57600080fd5b8063dead8d3914610893578063e8401335146108c0578063e985e9c5146108e057600080fd5b8063b91611f4116100bb578063b91611f414610829578063c87b56dd1461085d578063ddca3f431461087d57600080fd5b8063a639417c146107c6578063abaf6c4f146107d9578063b88d4fde1461080957600080fd5b80638639990a116101435780638f13f48e1161011d5780638f13f48e1461076157806395d89b4114610791578063a22cb465146107a657600080fd5b80638639990a146106f65780638da5cb5b146107235780638eb2c7c61461074157600080fd5b8063715018a611610174578063715018a614610698578063787afced146106ad57806378e890ba146106e157600080fd5b80636c0360eb1461066357806370a082311461067857600080fd5b80633fc8cef31161024857806361c9436a116101fc57806368840dd4116101d657806368840dd41461061d57806369fe0e2d146106305780636b3b94821461064357600080fd5b806361c9436a146105b05780636352211e146105d057806367426597146105f057600080fd5b806349ce218e1161022d57806349ce218e1461056a57806353373a561461057d57806355f804b31461059d57600080fd5b80633fc8cef31461051657806342842e0e1461054a57600080fd5b806318fc0b18116102aa5780632b2aa2be116102845780632b2aa2be146104a25780632ce33441146104c25780633a7a8d28146104f657600080fd5b806318fc0b18146104345780631913cfd71461046257806323b872dd1461048257600080fd5b8063081812fc116102db578063081812fc1461034e578063095ea7b31461039c578063150b7a02146103be57600080fd5b806301ffc9a7146102f757806306fdde031461032c575b600080fd5b34801561030357600080fd5b50610317610312366004614119565b61097b565b60405190151581526020015b60405180910390f35b34801561033857600080fd5b50610341610a60565b6040516103239190614186565b34801561035a57600080fd5b50610384610369366004614199565b6004602052600090815260409020546001600160a01b031681565b6040516001600160a01b039091168152602001610323565b3480156103a857600080fd5b506103bc6103b73660046141d2565b610aee565b005b3480156103ca57600080fd5b506104036103d9366004614240565b7f150b7a020000000000000000000000000000000000000000000000000000000095945050505050565b6040517fffffffff000000000000000000000000000000000000000000000000000000009091168152602001610323565b34801561044057600080fd5b5061045461044f3660046145d6565b610bfc565b604051908152602001610323565b34801561046e57600080fd5b506103bc61047d3660046145d6565b610c4f565b34801561048e57600080fd5b506103bc61049d36600461460b565b610ef7565b3480156104ae57600080fd5b506103416104bd36600461464c565b6110e4565b3480156104ce57600080fd5b506104547fa55d25ac87b1c0e125b844871149c64ce5636cae2bb0ed2f2eb4990df479cd9181565b34801561050257600080fd5b50610454610511366004614681565b6111cd565b34801561052257600080fd5b506103847f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc281565b34801561055657600080fd5b506103bc61056536600461460b565b6111fe565b6103bc6105783660046146e8565b611334565b34801561058957600080fd5b5061034161059836600461464c565b6119f2565b6103bc6105ab366004614751565b611ad5565b3480156105bc57600080fd5b506103176105cb3660046147e6565b611b24565b3480156105dc57600080fd5b506103846105eb366004614199565b611b89565b3480156105fc57600080fd5b5061061061060b366004614913565b611bf3565b6040516103239190614a4c565b61045461062b366004614a5f565b611d3c565b6103bc61063e366004614199565b612903565b34801561064f57600080fd5b5061045461065e3660046145d6565b612990565b34801561066f57600080fd5b50610341612b02565b34801561068457600080fd5b50610454610693366004614ae1565b612b0f565b3480156106a457600080fd5b506103bc612b8e565b3480156106b957600080fd5b506104547f768c1d2c3157c9ca752098be2da2da3e1ddae5a69ca4394b1f83e1179407e8f081565b3480156106ed57600080fd5b50610454612ba2565b34801561070257600080fd5b50610454610711366004614ae1565b600e6020526000908152604090205481565b34801561072f57600080fd5b506006546001600160a01b0316610384565b34801561074d57600080fd5b506103bc61075c3660046145d6565b612bb1565b34801561076d57600080fd5b5061031761077c366004614199565b60096020526000908152604090205460ff1681565b34801561079d57600080fd5b50610341612cf2565b3480156107b257600080fd5b506103bc6107c1366004614afe565b612cff565b6104546107d4366004614b2c565b612d89565b3480156107e557600080fd5b506103176107f4366004614199565b600b6020526000908152604090205460ff1681565b34801561081557600080fd5b506103bc610824366004614240565b612db9565b34801561083557600080fd5b506104547f331cf33dce9314036c50f72ada91444e078be32f06bf1d891362de01ac1a8d6681565b34801561086957600080fd5b50610341610878366004614199565b612ed8565b34801561088957600080fd5b5061045460085481565b34801561089f57600080fd5b506104546108ae366004614ae1565b600d6020526000908152604090205481565b3480156108cc57600080fd5b506103bc6108db366004614199565b612f71565b3480156108ec57600080fd5b506103176108fb366004614bae565b600560209081526000928352604080842090915290825290205460ff1681565b34801561092757600080fd5b50610454610936366004614199565b600a6020526000908152604090205481565b6103bc610956366004614bae565b613013565b34801561096757600080fd5b506103bc610976366004614ae1565b61309a565b60007f01ffc9a7000000000000000000000000000000000000000000000000000000007fffffffff0000000000000000000000000000000000000000000000000000000083161480610a0e57507f80ac58cd000000000000000000000000000000000000000000000000000000007fffffffff000000000000000000000000000000000000000000000000000000008316145b80610a5a57507f5b5e139f000000000000000000000000000000000000000000000000000000007fffffffff000000000000000000000000000000000000000000000000000000008316145b92915050565b60008054610a6d90614bdc565b80601f0160208091040260200160405190810160405280929190818152602001828054610a9990614bdc565b8015610ae65780601f10610abb57610100808354040283529160200191610ae6565b820191906000526020600020905b815481529060010190602001808311610ac957829003601f168201915b505050505081565b6000818152600260205260409020546001600160a01b031633811480610b3757506001600160a01b038116600090815260056020908152604080832033845290915290205460ff165b610b885760405162461bcd60e51b815260206004820152600e60248201527f4e4f545f415554484f52495a454400000000000000000000000000000000000060448201526064015b60405180910390fd5b60008281526004602052604080822080547fffffffffffffffffffffffff0000000000000000000000000000000000000000166001600160a01b0387811691821790925591518593918516917f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92591a4505050565b60008082604051602001610c109190614d9b565b604051602081830303815290604052806020019051810190610c329190614eb0565b60408085015115908201529050610c4881612990565b9392505050565b806040015115610ca15760405162461bcd60e51b815260206004820152601660248201527f4d7573742062652073686f727420706f736974696f6e000000000000000000006044820152606401610b7f565b6000610cac82612990565b905033610cb882611b89565b6001600160a01b031614610d0e5760405162461bcd60e51b815260206004820152600960248201527f4e6f74206f776e657200000000000000000000000000000000000000000000006044820152606401610b7f565b6000610d1983610bfc565b6000818152600b602052604090205490915060ff168080610d4757506000828152600a602052604090205442115b610d935760405162461bcd60e51b815260206004820152601c60248201527f4d75737420626520657865726369736564206f722065787069726564000000006044820152606401610b7f565b610da03361dead85610ef7565b827ff68858cec1e3d5803133ca29f13a5cbfe5dd96d89a90beb216e3af6b5cd3019385604051610dd09190614d9b565b60405180910390a283602001518015610de65750805b80610dfc57508360200151158015610dfc575080155b15610e2d57610e2733856080015186606001516001600160a01b031661312a9092919063ffffffff16565b50505050565b83602001518015610e3c575080155b80610e5157508360200151158015610e515750805b15610e2757610e648461016001516131c9565b610e72846101800151613269565b60008460200151610e835783610e85565b825b6101408601516000828152600c60209081526040918290208054835181840281018401909452808452949550610ef0949091830182828015610ee657602002820191906000526020600020905b815481526020019060010190808311610ed2575b5050505050613355565b5050505050565b6000818152600260205260409020546001600160a01b03848116911614610f605760405162461bcd60e51b815260206004820152600a60248201527f57524f4e475f46524f4d000000000000000000000000000000000000000000006044820152606401610b7f565b6001600160a01b038216610fb65760405162461bcd60e51b815260206004820152601160248201527f494e56414c49445f524543495049454e540000000000000000000000000000006044820152606401610b7f565b336001600160a01b0384161480610ff057506001600160a01b038316600090815260056020908152604080832033845290915290205460ff165b8061101157506000818152600460205260409020546001600160a01b031633145b61105d5760405162461bcd60e51b815260206004820152600e60248201527f4e4f545f415554484f52495a45440000000000000000000000000000000000006044820152606401610b7f565b600081815260026020908152604080832080546001600160a01b038088167fffffffffffffffffffffffff000000000000000000000000000000000000000092831681179093556004909452828520805490911690559051849391928716917fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef91a4505050565b606060005b82518110156111c757817fa55d25ac87b1c0e125b844871149c64ce5636cae2bb0ed2f2eb4990df479cd918483815181106111265761112661500c565b6020026020010151600001518584815181106111445761114461500c565b60200260200101516020015160405160200161117c939291909283526001600160a01b03919091166020830152604082015260600190565b604051602081830303815290604052805190602001206040516020016111a392919061503b565b604051602081830303815290604052915080806111bf9061508c565b9150506110e9565b50919050565b600c60205281600052604060002081815481106111e957600080fd5b90600052602060002001600091509150505481565b611209838383610ef7565b6001600160a01b0382163b15806112e357506040517f150b7a02000000000000000000000000000000000000000000000000000000008082523360048301526001600160a01b03858116602484015260448301849052608060648401526000608484015290919084169063150b7a029060a4016020604051808303816000875af115801561129b573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906112bf91906150c4565b7fffffffff0000000000000000000000000000000000000000000000000000000016145b61132f5760405162461bcd60e51b815260206004820152601060248201527f554e534146455f524543495049454e54000000000000000000000000000000006044820152606401610b7f565b505050565b600061133f84612990565b90503361134b82611b89565b6001600160a01b0316146113a15760405162461bcd60e51b815260206004820152600960248201527f4e6f74206f776e657200000000000000000000000000000000000000000000006044820152606401610b7f565b83604001516113f25760405162461bcd60e51b815260206004820181905260248201527f43616e206f6e6c79206578657263697365206c6f6e6720706f736974696f6e736044820152606401610b7f565b6000818152600a6020526040902054421061144f5760405162461bcd60e51b815260206004820152601460248201527f506f736974696f6e2068617320657870697265640000000000000000000000006044820152606401610b7f565b34158061149157507f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc26001600160a01b031684606001516001600160a01b0316145b6114dd5760405162461bcd60e51b815260206004820152601560248201527f43616e6e6f7420757365206e61746976652045544800000000000000000000006044820152606401610b7f565b83602001511561153a5781156115355760405162461bcd60e51b815260206004820152601d60248201527f496e76616c696420666c6f6f7220746f6b656e496473206c656e6774680000006044820152606401610b7f565b61158f565b61014084015151821461158f5760405162461bcd60e51b815260206004820152601e60248201527f57726f6e6720616d6f756e74206f6620666c6f6f7220746f6b656e49647300006044820152606401610b7f565b61159c3361dead83610ef7565b6000818152600b60205260409081902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001660011790555181907f5a6595ae5a8bfc7c3e5fa154d614f8cd831d1a1ab6424f84b7e00be52a2d64be906116099086908690899061512c565b60405180910390a2600061161c85610bfc565b6020860151909150156117b45760808501511561172d573415611705578460800151341461168c5760405162461bcd60e51b815260206004820152601960248201527f496e636f72726563742045544820616d6f756e742073656e74000000000000006044820152606401610b7f565b7f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc26001600160a01b031663d0e30db0346040518263ffffffff1660e01b81526004016000604051808303818588803b1580156116e757600080fd5b505af11580156116fb573d6000803e3d6000fd5b505050505061172d565b61172d3330876080015188606001516001600160a01b031661343a909392919063ffffffff16565b61173b8561016001516131c9565b611749856101800151613269565b6101408501516000838152600c602090815260409182902080548351818402810184019094528084526117af949392830182828015610ee65760200282019190600052602060002090815481526020019060010190808311610ed2575050505050613355565b6118a4565b34156118025760405162461bcd60e51b815260206004820152601960248201527f507574732063616e277420757365206e617469766520455448000000000000006044820152606401610b7f565b6000818152600c6020526040902061181b908585614050565b5061184233866080015187606001516001600160a01b031661312a9092919063ffffffff16565b611851856101600151336134df565b611860856101800151336135b9565b6118a48561014001518585808060200260200160405190810160405280939291908181526020018383602002808284376000920191909152503392506136a5915050565b60006118af82611b89565b6000604088015290506118e2817f117b68e20000000000000000000000000000000000000000000000000000000061378a565b156119ea57806001600160a01b0316613a985a6118ff9190615152565b6040517f53e0af970000000000000000000000000000000000000000000000000000000090611938908a9033908b908b90602401615165565b60408051601f198184030181529181526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff000000000000000000000000000000000000000000000000000000009094169390931790925290516119a3919061519a565b60006040518083038160008787f1925050503d80600081146119e1576040519150601f19603f3d011682016040523d82523d6000602084013e6119e6565b606091505b5050505b505050505050565b606060005b82518110156111c757817f768c1d2c3157c9ca752098be2da2da3e1ddae5a69ca4394b1f83e1179407e8f0848381518110611a3457611a3461500c565b602002602001015160000151858481518110611a5257611a5261500c565b602002602001015160200151604051602001611a8a939291909283526001600160a01b03919091166020830152604082015260600190565b60405160208183030381529060405280519060200120604051602001611ab192919061503b565b60405160208183030381529060405291508080611acd9061508c565b9150506119f7565b611add6137a6565b6007611ae982826151fc565b507f325d37e8fb549c86966f09bc3e6f62eb3afa93b255d6e3234338001f3d80bd8681604051611b199190614186565b60405180910390a150565b6000805b8351811015611b7f57838181518110611b4357611b4361500c565b60200260200101516001600160a01b0316836001600160a01b031603611b6d576001915050610a5a565b80611b778161508c565b915050611b28565b5060009392505050565b6000818152600260205260409020546001600160a01b031680611bee5760405162461bcd60e51b815260206004820152600a60248201527f4e4f545f4d494e544544000000000000000000000000000000000000000000006044820152606401610b7f565b919050565b835160609083148015611c065750815183145b611c525760405162461bcd60e51b815260206004820152601860248201527f4c656e677468206d69736d6174636820696e20696e70757400000000000000006044820152606401610b7f565b845167ffffffffffffffff811115611c6c57611c6c6142b3565b604051908082528060200260200182016040528015611c95578160200160208202803683370190505b50905060005b8551811015611d3357611d04868281518110611cb957611cb961500c565b6020026020010151868684818110611cd357611cd361500c565b9050602002810190611ce591906152f8565b868581518110611cf757611cf761500c565b6020026020010151611d3c565b828281518110611d1657611d1661500c565b602090810291909101015280611d2b8161508c565b915050611c9b565b50949350505050565b600080611d4886612990565b9050611d8f86600001518287878080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525061380092505050565b611ddb5760405162461bcd60e51b815260206004820152601160248201527f496e76616c6964207369676e61747572650000000000000000000000000000006044820152606401610b7f565b60008181526009602052604090205460ff1615611e3a5760405162461bcd60e51b815260206004820152601860248201527f4f7264657220686173206265656e2063616e63656c6c656400000000000000006044820152606401610b7f565b85516001600160a01b03166000908152600e60205260409020546101008701511015611ea85760405162461bcd60e51b815260206004820152601960248201527f4e6f6e636520697320736d616c6c6572207468616e206d696e000000000000006044820152606401610b7f565b610120860151511580611ec55750611ec586610120015133611b24565b611f115760405162461bcd60e51b815260206004820152600f60248201527f4e6f742077686974656c697374656400000000000000000000000000000000006044820152606401610b7f565b63337f98008660c001511115611f695760405162461bcd60e51b815260206004820152601160248201527f4475726174696f6e20746f6f206c6f6e670000000000000000000000000000006044820152606401610b7f565b6103848660c001511015611fbf5760405162461bcd60e51b815260206004820152601260248201527f4475726174696f6e20746f6f2073686f727400000000000000000000000000006044820152606401610b7f565b8560e0015142106120125760405162461bcd60e51b815260206004820152601160248201527f4f726465722068617320657870697265640000000000000000000000000000006044820152606401610b7f565b600086606001516001600160a01b03163b116120705760405162461bcd60e51b815260206004820152601960248201527f626173654173736574206973206e6f7420636f6e7472616374000000000000006044820152606401610b7f565b8560200151801561208357508560400151155b156120dc5761014086015151156120dc5760405162461bcd60e51b815260206004820181905260248201527f53686f72742063616c6c2063616e74206861766520666c6f6f72546f6b656e736044820152606401610b7f565b34158061211e57507f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc26001600160a01b031686606001516001600160a01b0316145b61216a5760405162461bcd60e51b815260206004820152601560248201527f43616e6e6f7420757365206e61746976652045544800000000000000000000006044820152606401610b7f565b8560200151801561217c575085604001515b6121d4578251156121cf5760405162461bcd60e51b815260206004820152601b60248201527f496e76616c696420666c6f6f7220746f6b656e73206c656e67746800000000006044820152606401610b7f565b61222a565b8561014001515183511461222a5760405162461bcd60e51b815260206004820152601e60248201527f57726f6e6720616d6f756e74206f6620666c6f6f7220746f6b656e49647300006044820152606401610b7f565b85516122369082613988565b600061224187610bfc565b92508290506122503382613988565b86604001518015612262575086602001515b15612288576000828152600c6020908152604090912085516122869287019061409b565b505b60c0870151612297904261535d565b600a600089604001516122aa57856122ac565b845b81526020019081526020016000208190555080827f18cce19e7f1994d20463b7d7811a7deda47fbaf72748ad25b13cf6c2aaa2ee21868a6040516122f1929190615370565b60405180910390a36008546000901561235a576103e86008548960a00151612319919061539e565b612323919061540a565b60608901516001600160a01b03166000908152600d602052604081208054929350839290919061235490849061535d565b90915550505b60a0880151156125a1578760400151156123c65761239d886000015133838b60a001516123879190615152565b60608c01516001600160a01b031692919061343a565b80156123c157875160608901516123c1916001600160a01b0390911690308461343a565b6125a1565b3415612568578760a00151341461241f5760405162461bcd60e51b815260206004820152601960248201527f496e636f72726563742045544820616d6f756e742073656e74000000000000006044820152606401610b7f565b7f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc26001600160a01b031663d0e30db08960a001516040518263ffffffff1660e01b81526004016000604051808303818588803b15801561247e57600080fd5b505af1158015612492573d6000803e3d6000fd5b50505050507f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc26001600160a01b031663a9059cbb8960000151838b60a001516124db9190615152565b6040517fffffffff0000000000000000000000000000000000000000000000000000000060e085901b1681526001600160a01b03909216600483015260248201526044016020604051808303816000875af115801561253e573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612562919061541e565b506125a1565b612581338960000151838b60a001516123879190615152565b80156125a15760608801516125a1906001600160a01b031633308461343a565b87604001511580156125b557508760200151155b156125e2578751608089015160608a01516125dd926001600160a01b0390911691309061343a565b6127c5565b876040015180156125f557508760200151155b156126f45734156126cc57876080015134146126535760405162461bcd60e51b815260206004820152601960248201527f496e636f72726563742045544820616d6f756e742073656e74000000000000006044820152606401610b7f565b7f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc26001600160a01b031663d0e30db0346040518263ffffffff1660e01b81526004016000604051808303818588803b1580156126ae57600080fd5b505af11580156126c2573d6000803e3d6000fd5b50505050506127c5565b6125dd33308a608001518b606001516001600160a01b031661343a909392919063ffffffff16565b8760400151158015612707575087602001515b156127325761271f88610160015189600001516134df565b6125dd88610180015189600001516135b9565b87604001518015612744575087602001515b156127c55734156127975760405162461bcd60e51b815260206004820152601e60248201527f4c6f6e672063616c6c2063616e277420757365206e61746976652045544800006044820152606401610b7f565b6127a6886101600151336134df565b6127b5886101800151336135b9565b6127c588610140015186336136a5565b87516127f1907f117b68e20000000000000000000000000000000000000000000000000000000061378a565b156128f85787516001600160a01b0316613a985a61280f9190615152565b6040517f429bc7750000000000000000000000000000000000000000000000000000000090612846908c9033908b9060240161543b565b60408051601f198184030181529181526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff000000000000000000000000000000000000000000000000000000009094169390931790925290516128b1919061519a565b60006040518083038160008787f1925050503d80600081146128ef576040519150601f19603f3d011682016040523d82523d6000602084013e6128f4565b606091505b5050505b505050949350505050565b61290b6137a6565b601e811061295b5760405162461bcd60e51b815260206004820152601860248201527f666565206d757374206265206c657373207468616e20332500000000000000006044820152606401610b7f565b60088190556040518181527f63fe946ed58429ac3c5e64d4356ff92c26d7fa1e73586515df8ba9f059ab54a590602001611b19565b60007f331cf33dce9314036c50f72ada91444e078be32f06bf1d891362de01ac1a8d66826000015183602001518460400151856060015186608001518760a001518860c001518960e001518a61010001518b61012001516040516020016129f7919061546f565b604051602081830303815290604052805190602001208c6101400151604051602001612a23919061546f565b60405160208183030381529060405280519060200120612a478e61016001516110e4565b80519060200120612a5c8f61018001516119f2565b8051602091820120604080519283019f909f526001600160a01b039d8e169e82019e909e529a151560608c015298151560808b01529990961660a089015260c088019490945260e08701929092526101008601526101208501526101408401526101608301526101808201929092526101a08101919091526101c08101919091526101e001604051602081830303815290604052805190602001209050610a5a81613ab4565b60078054610a6d90614bdc565b60006001600160a01b038216612b675760405162461bcd60e51b815260206004820152600c60248201527f5a45524f5f4144445245535300000000000000000000000000000000000000006044820152606401610b7f565b507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff919050565b612b966137a6565b612ba06000613b1d565b565b6000612bac613b87565b905090565b80516001600160a01b03163314612c0a5760405162461bcd60e51b815260206004820152600e60248201527f4e6f7420796f7572206f726465720000000000000000000000000000000000006044820152606401610b7f565b6000612c1582612990565b6000818152600260205260409020549091506001600160a01b031615612c7d5760405162461bcd60e51b815260206004820152601460248201527f4f7264657220616c72656164792066696c6c65640000000000000000000000006044820152606401610b7f565b6000818152600960205260409081902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001660011790555181907f87bd0244ffd0d26804a2c0c77304351c38d53bc54c39f8b34739b051972698d990612ce6908590614d9b565b60405180910390a25050565b60018054610a6d90614bdc565b3360008181526005602090815260408083206001600160a01b0387168085529083529281902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001686151590811790915590519081529192917f17307eab39ab6107e8899845ad3d59bd9653f200f220920489ca2b5937696c31910160405180910390a35050565b6000612d9482612bb1565b604080516000815260208101909152612daf86868684611d3c565b9695505050505050565b612dc4858585610ef7565b6001600160a01b0384163b1580612e8c57506040517f150b7a0200000000000000000000000000000000000000000000000000000000808252906001600160a01b0386169063150b7a0290612e259033908a908990899089906004016154ae565b6020604051808303816000875af1158015612e44573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612e6891906150c4565b7fffffffff0000000000000000000000000000000000000000000000000000000016145b610ef05760405162461bcd60e51b815260206004820152601060248201527f554e534146455f524543495049454e54000000000000000000000000000000006044820152606401610b7f565b6000818152600260205260409020546060906001600160a01b0316612f3f5760405162461bcd60e51b815260206004820152601e60248201527f55524920717565727920666f72204e4f545f4d494e54454420746f6b656e00006044820152606401610b7f565b6007612f4a83613cae565b604051602001612f5b929190615502565b6040516020818303038152906040529050919050565b336000908152600e60205260409020548111612fcf5760405162461bcd60e51b815260206004820152601560248201527f4e6f6e63652073686f756c6420696e63726561736500000000000000000000006044820152606401610b7f565b336000908152600e602052604090819020829055517f7063087ceb22bbd43bac735fc64c753e025549bf590de2caef3e70b675ed783190611b199083815260200190565b61301b6137a6565b6001600160a01b0382166000818152600d60205260408082208054929055519091907f83ac76d99ab0e09d53da4de73a04e9b73e323b87cd1e723b66b178b5c064c0359061307e90849086909182526001600160a01b0316602082015260400190565b60405180910390a261132f6001600160a01b038416838361312a565b6130a26137a6565b6001600160a01b03811661311e5760405162461bcd60e51b815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201527f64647265737300000000000000000000000000000000000000000000000000006064820152608401610b7f565b61312781613b1d565b50565b60006040517fa9059cbb000000000000000000000000000000000000000000000000000000008152836004820152826024820152602060006044836000895af13d15601f3d1160016000511416171691505080610e275760405162461bcd60e51b815260206004820152600f60248201527f5452414e534645525f4641494c454400000000000000000000000000000000006044820152606401610b7f565b60005b81518110156132655760008282815181106131e9576131e961500c565b602002602001015160200151111561325357613253338383815181106132115761321161500c565b60200260200101516020015184848151811061322f5761322f61500c565b6020026020010151600001516001600160a01b031661312a9092919063ffffffff16565b8061325d8161508c565b9150506131cc565b5050565b60005b8151811015613265578181815181106132875761328761500c565b6020026020010151600001516001600160a01b03166342842e0e30338585815181106132b5576132b561500c565b60209081029190910181015101516040517fffffffff0000000000000000000000000000000000000000000000000000000060e086901b1681526001600160a01b0393841660048201529290911660248301526044820152606401600060405180830381600087803b15801561332a57600080fd5b505af115801561333e573d6000803e3d6000fd5b50505050808061334d9061508c565b91505061326c565b60005b825181101561132f578281815181106133735761337361500c565b60200260200101516001600160a01b03166342842e0e303385858151811061339d5761339d61500c565b60209081029190910101516040517fffffffff0000000000000000000000000000000000000000000000000000000060e086901b1681526001600160a01b0393841660048201529290911660248301526044820152606401600060405180830381600087803b15801561340f57600080fd5b505af1158015613423573d6000803e3d6000fd5b5050505080806134329061508c565b915050613358565b60006040517f23b872dd0000000000000000000000000000000000000000000000000000000081528460048201528360248201528260448201526020600060648360008a5af13d15601f3d1160016000511416171691505080610ef05760405162461bcd60e51b815260206004820152601460248201527f5452414e534645525f46524f4d5f4641494c45440000000000000000000000006044820152606401610b7f565b60005b825181101561132f5760008382815181106134ff576134ff61500c565b602002602001015160000151905060008483815181106135215761352161500c565b60200260200101516020015190506000826001600160a01b03163b116135895760405162461bcd60e51b815260206004820152601c60248201527f45524332303a20546f6b656e206973206e6f7420636f6e7472616374000000006044820152606401610b7f565b80156135a4576135a46001600160a01b03831685308461343a565b505080806135b19061508c565b9150506134e2565b60005b825181101561132f578281815181106135d7576135d761500c565b6020026020010151600001516001600160a01b03166342842e0e83308685815181106136055761360561500c565b60209081029190910181015101516040517fffffffff0000000000000000000000000000000000000000000000000000000060e086901b1681526001600160a01b0393841660048201529290911660248301526044820152606401600060405180830381600087803b15801561367a57600080fd5b505af115801561368e573d6000803e3d6000fd5b50505050808061369d9061508c565b9150506135bc565b60005b8351811015610e27578381815181106136c3576136c361500c565b60200260200101516001600160a01b03166342842e0e83308685815181106136ed576136ed61500c565b60209081029190910101516040517fffffffff0000000000000000000000000000000000000000000000000000000060e086901b1681526001600160a01b0393841660048201529290911660248301526044820152606401600060405180830381600087803b15801561375f57600080fd5b505af1158015613773573d6000803e3d6000fd5b5050505080806137829061508c565b9150506136a8565b600061379583613deb565b8015610c485750610c488383613e4f565b6006546001600160a01b03163314612ba05760405162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e65726044820152606401610b7f565b600080600061380f8585613f1e565b90925090506000816004811115613828576138286155a7565b1480156138465750856001600160a01b0316826001600160a01b0316145b1561385657600192505050610c48565b600080876001600160a01b0316631626ba7e60e01b888860405160240161387e9291906155d6565b60408051601f198184030181529181526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff000000000000000000000000000000000000000000000000000000009094169390931790925290516138e9919061519a565b600060405180830381855afa9150503d8060008114613924576040519150601f19603f3d011682016040523d82523d6000602084013e613929565b606091505b509150915081801561393c575080516020145b801561397c575080517f1626ba7e000000000000000000000000000000000000000000000000000000009061397a90830160209081019084016155ef565b145b98975050505050505050565b6001600160a01b0382166139de5760405162461bcd60e51b815260206004820152601160248201527f494e56414c49445f524543495049454e540000000000000000000000000000006044820152606401610b7f565b6000818152600260205260409020546001600160a01b031615613a435760405162461bcd60e51b815260206004820152600e60248201527f414c52454144595f4d494e5445440000000000000000000000000000000000006044820152606401610b7f565b60008181526002602052604080822080547fffffffffffffffffffffffff0000000000000000000000000000000000000000166001600160a01b03861690811790915590518392907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef908290a45050565b6000610a5a613ac1613b87565b836040517f19010000000000000000000000000000000000000000000000000000000000006020820152602281018390526042810182905260009060620160405160208183030381529060405280519060200120905092915050565b600680546001600160a01b038381167fffffffffffffffffffffffff0000000000000000000000000000000000000000831681179093556040519116919082907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a35050565b6000306001600160a01b037f0000000000000000000000008ad3668b088bf7abee6488da8f6570e8c3586c2216148015613be057507f000000000000000000000000000000000000000000000000000000000000000146145b15613c0a57507f15d618f7c8044689a973e5c11a3384bda4528f5324ad688b3f005852a24e733890565b50604080517f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f6020808301919091527fe48f317abed0a1f28166cccfb2dd91ae40762bd907cae90a85d622e9bc6476f7828401527f88f72b566ae0c96f6fffac4bc8ac74909f61512ac0c06a8124d5ed420d306f9060608301524660808301523060a0808401919091528351808403909101815260c0909201909252805191012090565b606081600003613cf157505060408051808201909152600181527f3000000000000000000000000000000000000000000000000000000000000000602082015290565b8160005b8115613d1b5780613d058161508c565b9150613d149050600a8361540a565b9150613cf5565b60008167ffffffffffffffff811115613d3657613d366142b3565b6040519080825280601f01601f191660200182016040528015613d60576020820181803683370190505b5090505b8415613de357613d75600183615152565b9150613d82600a86615608565b613d8d90603061535d565b60f81b818381518110613da257613da261500c565b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a905350613ddc600a8661540a565b9450613d64565b949350505050565b6000613e17827f01ffc9a700000000000000000000000000000000000000000000000000000000613e4f565b8015610a5a5750613e48827fffffffff00000000000000000000000000000000000000000000000000000000613e4f565b1592915050565b604080517fffffffff000000000000000000000000000000000000000000000000000000008316602480830191909152825180830390910181526044909101909152602080820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167f01ffc9a700000000000000000000000000000000000000000000000000000000178152825160009392849283928392918391908a617530fa92503d91506000519050828015613f07575060208210155b8015613f135750600081115b979650505050505050565b6000808251604103613f545760208301516040840151606085015160001a613f4887828585613f63565b94509450505050613f5c565b506000905060025b9250929050565b6000807f7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a0831115613f9a5750600090506003614047565b8460ff16601b14158015613fb257508460ff16601c14155b15613fc35750600090506004614047565b6040805160008082526020820180845289905260ff881692820192909252606081018690526080810185905260019060a0016020604051602081039080840390855afa158015614017573d6000803e3d6000fd5b5050604051601f1901519150506001600160a01b03811661404057600060019250925050614047565b9150600090505b94509492505050565b82805482825590600052602060002090810192821561408b579160200282015b8281111561408b578235825591602001919060010190614070565b506140979291506140d6565b5090565b82805482825590600052602060002090810192821561408b579160200282015b8281111561408b5782518255916020019190600101906140bb565b5b8082111561409757600081556001016140d7565b7fffffffff000000000000000000000000000000000000000000000000000000008116811461312757600080fd5b60006020828403121561412b57600080fd5b8135610c48816140eb565b60005b83811015614151578181015183820152602001614139565b50506000910152565b60008151808452614172816020860160208601614136565b601f01601f19169290920160200192915050565b602081526000610c48602083018461415a565b6000602082840312156141ab57600080fd5b5035919050565b6001600160a01b038116811461312757600080fd5b8035611bee816141b2565b600080604083850312156141e557600080fd5b82356141f0816141b2565b946020939093013593505050565b60008083601f84011261421057600080fd5b50813567ffffffffffffffff81111561422857600080fd5b602083019150836020828501011115613f5c57600080fd5b60008060008060006080868803121561425857600080fd5b8535614263816141b2565b94506020860135614273816141b2565b935060408601359250606086013567ffffffffffffffff81111561429657600080fd5b6142a2888289016141fe565b969995985093965092949392505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b6040805190810167ffffffffffffffff81118282101715614305576143056142b3565b60405290565b6040516101a0810167ffffffffffffffff81118282101715614305576143056142b3565b604051601f8201601f1916810167ffffffffffffffff81118282101715614358576143586142b3565b604052919050565b801515811461312757600080fd5b8035611bee81614360565b600067ffffffffffffffff821115614393576143936142b3565b5060051b60200190565b600082601f8301126143ae57600080fd5b813560206143c36143be83614379565b61432f565b82815260059290921b840181019181810190868411156143e257600080fd5b8286015b848110156144065780356143f9816141b2565b83529183019183016143e6565b509695505050505050565b600082601f83011261442257600080fd5b813560206144326143be83614379565b82815260069290921b8401810191818101908684111561445157600080fd5b8286015b84811015614406576040818903121561446e5760008081fd5b6144766142e2565b8135614481816141b2565b81528185013585820152835291830191604001614455565b60006101a082840312156144ac57600080fd5b6144b461430b565b90506144bf826141c7565b81526144cd6020830161436e565b60208201526144de6040830161436e565b60408201526144ef606083016141c7565b60608201526080820135608082015260a082013560a082015260c082013560c082015260e082013560e08201526101008083013581830152506101208083013567ffffffffffffffff8082111561454557600080fd5b6145518683870161439d565b8385015261014092508285013591508082111561456d57600080fd5b6145798683870161439d565b8385015261016092508285013591508082111561459557600080fd5b6145a186838701614411565b838501526101809250828501359150808211156145bd57600080fd5b506145ca85828601614411565b82840152505092915050565b6000602082840312156145e857600080fd5b813567ffffffffffffffff8111156145ff57600080fd5b613de384828501614499565b60008060006060848603121561462057600080fd5b833561462b816141b2565b9250602084013561463b816141b2565b929592945050506040919091013590565b60006020828403121561465e57600080fd5b813567ffffffffffffffff81111561467557600080fd5b613de384828501614411565b6000806040838503121561469457600080fd5b50508035926020909101359150565b60008083601f8401126146b557600080fd5b50813567ffffffffffffffff8111156146cd57600080fd5b6020830191508360208260051b8501011115613f5c57600080fd5b6000806000604084860312156146fd57600080fd5b833567ffffffffffffffff8082111561471557600080fd5b61472187838801614499565b9450602086013591508082111561473757600080fd5b50614744868287016146a3565b9497909650939450505050565b6000602080838503121561476457600080fd5b823567ffffffffffffffff8082111561477c57600080fd5b818501915085601f83011261479057600080fd5b8135818111156147a2576147a26142b3565b6147b484601f19601f8401160161432f565b915080825286848285010111156147ca57600080fd5b8084840185840137600090820190930192909252509392505050565b600080604083850312156147f957600080fd5b823567ffffffffffffffff81111561481057600080fd5b61481c8582860161439d565b925050602083013561482d816141b2565b809150509250929050565b600082601f83011261484957600080fd5b813560206148596143be83614379565b82815260059290921b8401810191818101908684111561487857600080fd5b8286015b84811015614406578035835291830191830161487c565b600082601f8301126148a457600080fd5b813560206148b46143be83614379565b82815260059290921b840181019181810190868411156148d357600080fd5b8286015b8481101561440657803567ffffffffffffffff8111156148f75760008081fd5b6149058986838b0101614838565b8452509183019183016148d7565b6000806000806060858703121561492957600080fd5b843567ffffffffffffffff8082111561494157600080fd5b818701915087601f83011261495557600080fd5b813560206149656143be83614379565b82815260059290921b8401810191818101908b84111561498457600080fd5b8286015b848110156149bc578035868111156149a05760008081fd5b6149ae8e86838b0101614499565b845250918301918301614988565b50985050880135925050808211156149d357600080fd5b6149df888389016146a3565b909550935060408701359150808211156149f857600080fd5b50614a0587828801614893565b91505092959194509250565b600081518084526020808501945080840160005b83811015614a4157815187529582019590820190600101614a25565b509495945050505050565b602081526000610c486020830184614a11565b60008060008060608587031215614a7557600080fd5b843567ffffffffffffffff80821115614a8d57600080fd5b614a9988838901614499565b95506020870135915080821115614aaf57600080fd5b614abb888389016141fe565b90955093506040870135915080821115614ad457600080fd5b50614a0587828801614838565b600060208284031215614af357600080fd5b8135610c48816141b2565b60008060408385031215614b1157600080fd5b8235614b1c816141b2565b9150602083013561482d81614360565b60008060008060608587031215614b4257600080fd5b843567ffffffffffffffff80821115614b5a57600080fd5b614b6688838901614499565b95506020870135915080821115614b7c57600080fd5b614b88888389016141fe565b90955093506040870135915080821115614ba157600080fd5b50614a0587828801614499565b60008060408385031215614bc157600080fd5b8235614bcc816141b2565b9150602083013561482d816141b2565b600181811c90821680614bf057607f821691505b6020821081036111c7577f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b600081518084526020808501945080840160005b83811015614a415781516001600160a01b031687529582019590820190600101614c3d565b600081518084526020808501945080840160005b83811015614a4157815180516001600160a01b031688528301518388015260409096019590820190600101614c76565b80516001600160a01b0316825260006101a06020830151614ccb602086018215159052565b506040830151614cdf604086018215159052565b506060830151614cfa60608601826001600160a01b03169052565b506080830151608085015260a083015160a085015260c083015160c085015260e083015160e0850152610100808401518186015250610120808401518282870152614d4783870182614c29565b925050506101408084015185830382870152614d638382614c29565b925050506101608084015185830382870152614d7f8382614c62565b925050506101808084015185830382870152612daf8382614c62565b602081526000610c486020830184614ca6565b8051611bee816141b2565b8051611bee81614360565b600082601f830112614dd557600080fd5b81516020614de56143be83614379565b82815260059290921b84018101918181019086841115614e0457600080fd5b8286015b84811015614406578051614e1b816141b2565b8352918301918301614e08565b600082601f830112614e3957600080fd5b81516020614e496143be83614379565b82815260069290921b84018101918181019086841115614e6857600080fd5b8286015b848110156144065760408189031215614e855760008081fd5b614e8d6142e2565b8151614e98816141b2565b81528185015185820152835291830191604001614e6c565b600060208284031215614ec257600080fd5b815167ffffffffffffffff80821115614eda57600080fd5b908301906101a08286031215614eef57600080fd5b614ef761430b565b614f0083614dae565b8152614f0e60208401614db9565b6020820152614f1f60408401614db9565b6040820152614f3060608401614dae565b60608201526080830151608082015260a083015160a082015260c083015160c082015260e083015160e08201526101008084015181830152506101208084015183811115614f7d57600080fd5b614f8988828701614dc4565b8284015250506101408084015183811115614fa357600080fd5b614faf88828701614dc4565b8284015250506101608084015183811115614fc957600080fd5b614fd588828701614e28565b8284015250506101808084015183811115614fef57600080fd5b614ffb88828701614e28565b918301919091525095945050505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b6000835161504d818460208801614136565b9190910191825250602001919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff82036150bd576150bd61505d565b5060010190565b6000602082840312156150d657600080fd5b8151610c48816140eb565b81835260007f07ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff83111561511357600080fd5b8260051b80836020870137939093016020019392505050565b6040815260006151406040830185876150e1565b8281036020840152612daf8185614ca6565b81810381811115610a5a57610a5a61505d565b6060815260006151786060830187614ca6565b6001600160a01b03861660208401528281036040840152613f138185876150e1565b600082516151ac818460208701614136565b9190910192915050565b601f82111561132f57600081815260208120601f850160051c810160208610156151dd5750805b601f850160051c820191505b818110156119ea578281556001016151e9565b815167ffffffffffffffff811115615216576152166142b3565b61522a816152248454614bdc565b846151b6565b602080601f83116001811461527d57600084156152475750858301515b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600386901b1c1916600185901b1785556119ea565b600085815260208120601f198616915b828110156152ac5788860151825594840194600190910190840161528d565b50858210156152e857878501517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600388901b60f8161c191681555b5050505050600190811b01905550565b60008083357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe184360301811261532d57600080fd5b83018035915067ffffffffffffffff82111561534857600080fd5b602001915036819003821315613f5c57600080fd5b80820180821115610a5a57610a5a61505d565b6040815260006153836040830185614a11565b82810360208401526153958185614ca6565b95945050505050565b6000817fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff04831182151516156153d6576153d661505d565b500290565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b600082615419576154196153db565b500490565b60006020828403121561543057600080fd5b8151610c4881614360565b60608152600061544e6060830186614ca6565b6001600160a01b03851660208401528281036040840152612daf8185614a11565b815160009082906020808601845b838110156154a25781516001600160a01b03168552938201939082019060010161547d565b50929695505050505050565b60006001600160a01b03808816835280871660208401525084604083015260806060830152826080830152828460a0840137600060a0848401015260a0601f19601f85011683010190509695505050505050565b600080845461551081614bdc565b60018281168015615528576001811461555b5761558a565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff008416875282151583028701945061558a565b8860005260208060002060005b858110156155815781548a820152908401908201615568565b50505082870194505b50505050835161559e818360208801614136565b01949350505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fd5b828152604060208201526000613de3604083018461415a565b60006020828403121561560157600080fd5b5051919050565b600082615617576156176153db565b50069056fea26469706673582212204a7c0a0073e530ef976dee1e73912eb3b8a58221ff1b9c392ddf1a424855312b64736f6c63430008100033

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

00000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2000000000000000000000000000000000000000000000000000000000000002368747470733a2f2f6170692e70757474792e66696e616e63652f6d657461646174612f0000000000000000000000000000000000000000000000000000000000

-----Decoded View---------------
Arg [0] : _baseURI (string): https://api.putty.finance/metadata/
Arg [1] : _fee (uint256): 0
Arg [2] : _weth (address): 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2

-----Encoded View---------------
6 Constructor Arguments found :
Arg [0] : 0000000000000000000000000000000000000000000000000000000000000060
Arg [1] : 0000000000000000000000000000000000000000000000000000000000000000
Arg [2] : 000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2
Arg [3] : 0000000000000000000000000000000000000000000000000000000000000023
Arg [4] : 68747470733a2f2f6170692e70757474792e66696e616e63652f6d6574616461
Arg [5] : 74612f0000000000000000000000000000000000000000000000000000000000


Block Transaction Difficulty Gas Used Reward
View All Blocks Produced

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

Validator Index Block Amount
View All Withdrawals

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

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