ETH Price: $2,355.46 (+0.65%)

Token

Tentacular (TNTCL)
 

Overview

Max Total Supply

166 TNTCL

Holders

75

Market

Volume (24H)

N/A

Min Price (24H)

N/A

Max Price (24H)

N/A
Balance
3 TNTCL
0x42d6a5072ea2d1be8ef1f6a4c3a63f7ea2a1d751
Loading...
Loading
Loading...
Loading
Loading...
Loading

Click here to update the token information / general information
# Exchange Pair Price  24H Volume % Volume

Contract Source Code Verified (Exact Match)

Contract Name:
Tentacular

Compiler Version
v0.8.11+commit.d7f03943

Optimization Enabled:
No with 200 runs

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

pragma solidity ^0.8.11;

// simple ownership check, ownership transfer + noContracts modifier 
import {SecuredBase} from "../src/base/SecuredBase.sol";

// https://github.com/chiru-labs/ERC721A
import {ERC721AQueryable, ERC721A} from "../src/erc721a/extensions/ERC721AQueryable.sol";

// https://github.com/fx-portal
import {FxBaseRootTunnel} from "../src/fx-portal/tunnel/FxBaseRootTunnel.sol";

contract Tentacular is ERC721AQueryable, SecuredBase, FxBaseRootTunnel {
    event RequestSend(string cmd, address wallet, uint[] tokenIds);

    bytes32 public constant BOUND = keccak256("BOUND");
    bytes32 public constant UNBOUND = keccak256("UNBOUND");

    uint constant MAX_SUPPLY = 5556;

    address salesContractAddress;

    string baseURI;

    bool public bondsEnabled;

    error MaxSupplyReached();
    error NotSalesContract();
    error BondsDisabled();
    error AlreadyBound(uint tokenId);
    error NotBound(uint tokenId);
    error NotAnOwner();

    mapping(uint => bool) public tokenIdToBound;

    constructor(
        address _checkpointManager,
        address _fxRoot,
        string memory name,
        string memory symbol
    ) FxBaseRootTunnel(_checkpointManager, _fxRoot) ERC721A(name, symbol) {}

    ////////////////////////////////////////////////////////////////////////////////
    //// USER ACTIONS
    ////////////////////////////////////////////////////////////////////////////////

    /** 
    @dev mint amount of tokens to wallet
    @notice can be called from the sales contract only
    @param wallet wallet to receive tokens
    @param amount of tokens to be minted
    */
    function mint(address wallet, uint amount) external salesContractOnly {
        if (_totalMinted() + amount > MAX_SUPPLY) revert MaxSupplyReached();

        _mint(wallet, amount);
    } 

    /** 
    @dev bound the specified tokens to BerryJuicer
    @notice can be called when bondsEnabled only, sends the request via bridge to Polygon network 
    @param tokenIds tokens to be bound
    */
    function bound(uint[] calldata tokenIds) external onlyBondsEnabled noContracts {
        for (uint i;i<tokenIds.length;i++) {
            if (ownerOf(tokenIds[i])!=msg.sender) revert NotAnOwner();
            if (tokenIdToBound[tokenIds[i]]) revert AlreadyBound(tokenIds[i]);

            tokenIdToBound[tokenIds[i]]=true;
        }

        bytes memory message = abi.encode(BOUND, abi.encode(msg.sender, tokenIds));
        emit RequestSend("BOUND", msg.sender, tokenIds);
        _sendMessageToChild(message);
    }

    /** 
    @dev unbound the specified tokens from BerryJuicer
    @notice sends the request via bridge to Polygon network 
    @param tokenIds tokens to be unbound
    */
    function unbound(uint[] calldata tokenIds) external noContracts {
        for (uint i;i<tokenIds.length;i++) {
            if (ownerOf(tokenIds[i])!=msg.sender) revert NotAnOwner();
            if (tokenIdToBound[tokenIds[i]]==false) revert NotBound(tokenIds[i]);

            tokenIdToBound[tokenIds[i]]=false;
        }

        bytes memory message = abi.encode(UNBOUND, abi.encode(msg.sender, tokenIds));
        emit RequestSend("UNBOUND", msg.sender, tokenIds);
        _sendMessageToChild(message);
    }

    ////////////////////////////////////////////////////////////////////////////////
    //// OWNER ONLY
    ////////////////////////////////////////////////////////////////////////////////

    /** 
    @dev set the base address for token URI
    @param URI base URI to use
    */
    function setBaseTokenURI(string calldata URI) external onlyOwner {
        baseURI = URI;
    }

    /** 
    @dev set the sales contract address
    @notice used for salesContractOnly modifier checks
    @param _salesContractAddress sales contract address
    */
    function setSalesContract(address _salesContractAddress) external onlyOwner {
        salesContractAddress=_salesContractAddress;
    }

    /** 
    @dev set the bound function status
    @param status true to turn it on, false to turn it off
    */
    function setBondsEnabled(bool status) external onlyOwner {
        bondsEnabled=status;
    }

    ////////////////////////////////////////////////////////////////////////////////
    //// OVERRIDES
    ////////////////////////////////////////////////////////////////////////////////


    /** 
    @dev handle message from the bridge
    @notice not used, the polygon contract connected send no messages
    @param message message to be handled
    */
    function _processMessageFromChild(bytes memory message) internal override {
        // Not used
    }

    /** 
    @dev set child tunnel address
    @notice override to keep the possibility to change it anytime
    @param _fxChildTunnel child tunnel address
    */
    function setFxChildTunnel(address _fxChildTunnel) public virtual override onlyOwner {
        fxChildTunnel = _fxChildTunnel;
    }

    /** 
    @dev returns the baseURI
    @notice override to return variable we can change in setBaseTokenURI
    */
    function _baseURI() internal view override returns (string memory) {
        return baseURI;
    }

    /** 
    @dev called before token is transfered, minted or burned
    @notice override to unbound token automatically during the transfer
    */
    function _beforeTokenTransfers(
        address from,
        address, // to,
        uint256 startTokenId,
        uint256  // quantity
    ) internal virtual override {
        if (tokenIdToBound[startTokenId]) {
            tokenIdToBound[startTokenId] = false;
            uint[] memory tokens = new uint[](1);
            tokens[0]=startTokenId;

            bytes memory message = abi.encode(UNBOUND, abi.encode(from, tokens));
            emit RequestSend("UNBOUND", from, tokens);
            _sendMessageToChild(message);
        }
    }

    ////////////////////////////////////////////////////////////////////////////////
    //// MODIFIERS
    ////////////////////////////////////////////////////////////////////////////////

    /** 
    @dev allow method to be executed only if bondsEnabled
    */
    modifier onlyBondsEnabled() {
        if (!bondsEnabled) revert BondsDisabled();
        _;
    }

    /** 
    @dev allow method to be executed only if caller is salesContract
    */
    modifier salesContractOnly() {
        if (msg.sender!=salesContractAddress) revert NotSalesContract();
        _;
    }
}

File 2 of 11 : SecuredBase.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.11;

contract SecuredBase {
    address public owner;

    error NoContractsAllowed();
    error NotContractOwner();
    
    constructor() { 
        owner=msg.sender;
    }

    function transferOwnership(address newOwner) external onlyOwner {
        owner=newOwner;
    }

    modifier onlyOwner() {
        if (msg.sender!=owner) revert NotContractOwner();
        _;
    }

    modifier noContracts() {
        uint256 size = 0;
        address acc = msg.sender;
        assembly { size := extcodesize(acc)}

        if ((msg.sender != tx.origin) || (size != 0)) revert NoContractsAllowed();
        _;
    }
}

File 3 of 11 : ERC721AQueryable.sol
// SPDX-License-Identifier: MIT
// ERC721A Contracts v4.2.2
// Creator: Chiru Labs

pragma solidity ^0.8.4;

import './IERC721AQueryable.sol';
import '../ERC721A.sol';

/**
 * @title ERC721AQueryable.
 *
 * @dev ERC721A subclass with convenience query functions.
 */
abstract contract ERC721AQueryable is ERC721A, IERC721AQueryable {
    /**
     * @dev Returns the `TokenOwnership` struct at `tokenId` without reverting.
     *
     * If the `tokenId` is out of bounds:
     *
     * - `addr = address(0)`
     * - `startTimestamp = 0`
     * - `burned = false`
     * - `extraData = 0`
     *
     * If the `tokenId` is burned:
     *
     * - `addr = <Address of owner before token was burned>`
     * - `startTimestamp = <Timestamp when token was burned>`
     * - `burned = true`
     * - `extraData = <Extra data when token was burned>`
     *
     * Otherwise:
     *
     * - `addr = <Address of owner>`
     * - `startTimestamp = <Timestamp of start of ownership>`
     * - `burned = false`
     * - `extraData = <Extra data at start of ownership>`
     */
    function explicitOwnershipOf(uint256 tokenId) public view virtual override returns (TokenOwnership memory) {
        TokenOwnership memory ownership;
        if (tokenId < _startTokenId() || tokenId >= _nextTokenId()) {
            return ownership;
        }
        ownership = _ownershipAt(tokenId);
        if (ownership.burned) {
            return ownership;
        }
        return _ownershipOf(tokenId);
    }

    /**
     * @dev Returns an array of `TokenOwnership` structs at `tokenIds` in order.
     * See {ERC721AQueryable-explicitOwnershipOf}
     */
    function explicitOwnershipsOf(uint256[] calldata tokenIds)
        external
        view
        virtual
        override
        returns (TokenOwnership[] memory)
    {
        unchecked {
            uint256 tokenIdsLength = tokenIds.length;
            TokenOwnership[] memory ownerships = new TokenOwnership[](tokenIdsLength);
            for (uint256 i; i != tokenIdsLength; ++i) {
                ownerships[i] = explicitOwnershipOf(tokenIds[i]);
            }
            return ownerships;
        }
    }

    /**
     * @dev Returns an array of token IDs owned by `owner`,
     * in the range [`start`, `stop`)
     * (i.e. `start <= tokenId < stop`).
     *
     * This function allows for tokens to be queried if the collection
     * grows too big for a single call of {ERC721AQueryable-tokensOfOwner}.
     *
     * Requirements:
     *
     * - `start < stop`
     */
    function tokensOfOwnerIn(
        address owner,
        uint256 start,
        uint256 stop
    ) external view virtual override returns (uint256[] memory) {
        unchecked {
            if (start >= stop) revert InvalidQueryRange();
            uint256 tokenIdsIdx;
            uint256 stopLimit = _nextTokenId();
            // Set `start = max(start, _startTokenId())`.
            if (start < _startTokenId()) {
                start = _startTokenId();
            }
            // Set `stop = min(stop, stopLimit)`.
            if (stop > stopLimit) {
                stop = stopLimit;
            }
            uint256 tokenIdsMaxLength = balanceOf(owner);
            // Set `tokenIdsMaxLength = min(balanceOf(owner), stop - start)`,
            // to cater for cases where `balanceOf(owner)` is too big.
            if (start < stop) {
                uint256 rangeLength = stop - start;
                if (rangeLength < tokenIdsMaxLength) {
                    tokenIdsMaxLength = rangeLength;
                }
            } else {
                tokenIdsMaxLength = 0;
            }
            uint256[] memory tokenIds = new uint256[](tokenIdsMaxLength);
            if (tokenIdsMaxLength == 0) {
                return tokenIds;
            }
            // We need to call `explicitOwnershipOf(start)`,
            // because the slot at `start` may not be initialized.
            TokenOwnership memory ownership = explicitOwnershipOf(start);
            address currOwnershipAddr;
            // If the starting slot exists (i.e. not burned), initialize `currOwnershipAddr`.
            // `ownership.address` will not be zero, as `start` is clamped to the valid token ID range.
            if (!ownership.burned) {
                currOwnershipAddr = ownership.addr;
            }
            for (uint256 i = start; i != stop && tokenIdsIdx != tokenIdsMaxLength; ++i) {
                ownership = _ownershipAt(i);
                if (ownership.burned) {
                    continue;
                }
                if (ownership.addr != address(0)) {
                    currOwnershipAddr = ownership.addr;
                }
                if (currOwnershipAddr == owner) {
                    tokenIds[tokenIdsIdx++] = i;
                }
            }
            // Downsize the array to fit.
            assembly {
                mstore(tokenIds, tokenIdsIdx)
            }
            return tokenIds;
        }
    }

    /**
     * @dev Returns an array of token IDs owned by `owner`.
     *
     * This function scans the ownership mapping and is O(`totalSupply`) in complexity.
     * It is meant to be called off-chain.
     *
     * See {ERC721AQueryable-tokensOfOwnerIn} for splitting the scan into
     * multiple smaller scans if the collection is large enough to cause
     * an out-of-gas error (10K collections should be fine).
     */
    function tokensOfOwner(address owner) external view virtual override returns (uint256[] memory) {
        unchecked {
            uint256 tokenIdsIdx;
            address currOwnershipAddr;
            uint256 tokenIdsLength = balanceOf(owner);
            uint256[] memory tokenIds = new uint256[](tokenIdsLength);
            TokenOwnership memory ownership;
            for (uint256 i = _startTokenId(); tokenIdsIdx != tokenIdsLength; ++i) {
                ownership = _ownershipAt(i);
                if (ownership.burned) {
                    continue;
                }
                if (ownership.addr != address(0)) {
                    currOwnershipAddr = ownership.addr;
                }
                if (currOwnershipAddr == owner) {
                    tokenIds[tokenIdsIdx++] = i;
                }
            }
            return tokenIds;
        }
    }
}

File 4 of 11 : FxBaseRootTunnel.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import {RLPReader} from "../lib/RLPReader.sol";
import {MerklePatriciaProof} from "../lib/MerklePatriciaProof.sol";
import {Merkle} from "../lib/Merkle.sol";
import "../lib/ExitPayloadReader.sol";

interface IFxStateSender {
    function sendMessageToChild(address _receiver, bytes calldata _data) external;
}

contract ICheckpointManager {
    struct HeaderBlock {
        bytes32 root;
        uint256 start;
        uint256 end;
        uint256 createdAt;
        address proposer;
    }

    /**
     * @notice mapping of checkpoint header numbers to block details
     * @dev These checkpoints are submited by plasma contracts
     */
    mapping(uint256 => HeaderBlock) public headerBlocks;
}

abstract contract FxBaseRootTunnel {
    using RLPReader for RLPReader.RLPItem;
    using Merkle for bytes32;
    using ExitPayloadReader for bytes;
    using ExitPayloadReader for ExitPayloadReader.ExitPayload;
    using ExitPayloadReader for ExitPayloadReader.Log;
    using ExitPayloadReader for ExitPayloadReader.LogTopics;
    using ExitPayloadReader for ExitPayloadReader.Receipt;

    // keccak256(MessageSent(bytes))
    bytes32 public constant SEND_MESSAGE_EVENT_SIG = 0x8c5261668696ce22758910d05bab8f186d6eb247ceac2af2e82c7dc17669b036;

    // state sender contract
    IFxStateSender public fxRoot;
    // root chain manager
    ICheckpointManager public checkpointManager;
    // child tunnel contract which receives and sends messages
    address public fxChildTunnel;

    // storage to avoid duplicate exits
    mapping(bytes32 => bool) public processedExits;

    constructor(address _checkpointManager, address _fxRoot) {
        checkpointManager = ICheckpointManager(_checkpointManager);
        fxRoot = IFxStateSender(_fxRoot);
    }

    // set fxChildTunnel if not set already
    function setFxChildTunnel(address _fxChildTunnel) public virtual {
        require(fxChildTunnel == address(0x0), "FxBaseRootTunnel: CHILD_TUNNEL_ALREADY_SET");
        fxChildTunnel = _fxChildTunnel;
    }

    /**
     * @notice Send bytes message to Child Tunnel
     * @param message bytes message that will be sent to Child Tunnel
     * some message examples -
     *   abi.encode(tokenId);
     *   abi.encode(tokenId, tokenMetadata);
     *   abi.encode(messageType, messageData);
     */
    function _sendMessageToChild(bytes memory message) internal {
        fxRoot.sendMessageToChild(fxChildTunnel, message);
    }

    function _validateAndExtractMessage(bytes memory inputData) internal returns (bytes memory) {
        ExitPayloadReader.ExitPayload memory payload = inputData.toExitPayload();

        bytes memory branchMaskBytes = payload.getBranchMaskAsBytes();
        uint256 blockNumber = payload.getBlockNumber();
        // checking if exit has already been processed
        // unique exit is identified using hash of (blockNumber, branchMask, receiptLogIndex)
        bytes32 exitHash = keccak256(
            abi.encodePacked(
                blockNumber,
                // first 2 nibbles are dropped while generating nibble array
                // this allows branch masks that are valid but bypass exitHash check (changing first 2 nibbles only)
                // so converting to nibble array and then hashing it
                MerklePatriciaProof._getNibbleArray(branchMaskBytes),
                payload.getReceiptLogIndex()
            )
        );
        require(processedExits[exitHash] == false, "FxRootTunnel: EXIT_ALREADY_PROCESSED");
        processedExits[exitHash] = true;

        ExitPayloadReader.Receipt memory receipt = payload.getReceipt();
        ExitPayloadReader.Log memory log = receipt.getLog();

        // check child tunnel
        require(fxChildTunnel == log.getEmitter(), "FxRootTunnel: INVALID_FX_CHILD_TUNNEL");

        bytes32 receiptRoot = payload.getReceiptRoot();
        // verify receipt inclusion
        require(
            MerklePatriciaProof.verify(receipt.toBytes(), branchMaskBytes, payload.getReceiptProof(), receiptRoot),
            "FxRootTunnel: INVALID_RECEIPT_PROOF"
        );

        // verify checkpoint inclusion
        _checkBlockMembershipInCheckpoint(
            blockNumber,
            payload.getBlockTime(),
            payload.getTxRoot(),
            receiptRoot,
            payload.getHeaderNumber(),
            payload.getBlockProof()
        );

        ExitPayloadReader.LogTopics memory topics = log.getTopics();

        require(
            bytes32(topics.getField(0).toUint()) == SEND_MESSAGE_EVENT_SIG, // topic0 is event sig
            "FxRootTunnel: INVALID_SIGNATURE"
        );

        // received message data
        bytes memory message = abi.decode(log.getData(), (bytes)); // event decodes params again, so decoding bytes to get message
        return message;
    }

    function _checkBlockMembershipInCheckpoint(
        uint256 blockNumber,
        uint256 blockTime,
        bytes32 txRoot,
        bytes32 receiptRoot,
        uint256 headerNumber,
        bytes memory blockProof
    ) private view returns (uint256) {
        (bytes32 headerRoot, uint256 startBlock, , uint256 createdAt, ) = checkpointManager.headerBlocks(headerNumber);

        require(
            keccak256(abi.encodePacked(blockNumber, blockTime, txRoot, receiptRoot)).checkMembership(
                blockNumber - startBlock,
                headerRoot,
                blockProof
            ),
            "FxRootTunnel: INVALID_HEADER"
        );
        return createdAt;
    }

    /**
     * @notice receive message from  L2 to L1, validated by proof
     * @dev This function verifies if the transaction actually happened on child chain
     *
     * @param inputData RLP encoded data of the reference tx containing following list of fields
     *  0 - headerNumber - Checkpoint header block number containing the reference tx
     *  1 - blockProof - Proof that the block header (in the child chain) is a leaf in the submitted merkle root
     *  2 - blockNumber - Block number containing the reference tx on child chain
     *  3 - blockTime - Reference tx block time
     *  4 - txRoot - Transactions root of block
     *  5 - receiptRoot - Receipts root of block
     *  6 - receipt - Receipt of the reference transaction
     *  7 - receiptProof - Merkle proof of the reference receipt
     *  8 - branchMask - 32 bits denoting the path of receipt in merkle tree
     *  9 - receiptLogIndex - Log Index to read from the receipt
     */
    function receiveMessage(bytes memory inputData) public virtual {
        bytes memory message = _validateAndExtractMessage(inputData);
        _processMessageFromChild(message);
    }

    /**
     * @notice Process message received from Child Tunnel
     * @dev function needs to be implemented to handle message as per requirement
     * This is called by onStateReceive function.
     * Since it is called via a system call, any event will not be emitted during its execution.
     * @param message bytes message that was sent from Child Tunnel
     */
    function _processMessageFromChild(bytes memory message) internal virtual;
}

File 5 of 11 : IERC721AQueryable.sol
// SPDX-License-Identifier: MIT
// ERC721A Contracts v4.2.2
// Creator: Chiru Labs

pragma solidity ^0.8.4;

import '../IERC721A.sol';

/**
 * @dev Interface of ERC721AQueryable.
 */
interface IERC721AQueryable is IERC721A {
    /**
     * Invalid query range (`start` >= `stop`).
     */
    error InvalidQueryRange();

    /**
     * @dev Returns the `TokenOwnership` struct at `tokenId` without reverting.
     *
     * If the `tokenId` is out of bounds:
     *
     * - `addr = address(0)`
     * - `startTimestamp = 0`
     * - `burned = false`
     * - `extraData = 0`
     *
     * If the `tokenId` is burned:
     *
     * - `addr = <Address of owner before token was burned>`
     * - `startTimestamp = <Timestamp when token was burned>`
     * - `burned = true`
     * - `extraData = <Extra data when token was burned>`
     *
     * Otherwise:
     *
     * - `addr = <Address of owner>`
     * - `startTimestamp = <Timestamp of start of ownership>`
     * - `burned = false`
     * - `extraData = <Extra data at start of ownership>`
     */
    function explicitOwnershipOf(uint256 tokenId) external view returns (TokenOwnership memory);

    /**
     * @dev Returns an array of `TokenOwnership` structs at `tokenIds` in order.
     * See {ERC721AQueryable-explicitOwnershipOf}
     */
    function explicitOwnershipsOf(uint256[] memory tokenIds) external view returns (TokenOwnership[] memory);

    /**
     * @dev Returns an array of token IDs owned by `owner`,
     * in the range [`start`, `stop`)
     * (i.e. `start <= tokenId < stop`).
     *
     * This function allows for tokens to be queried if the collection
     * grows too big for a single call of {ERC721AQueryable-tokensOfOwner}.
     *
     * Requirements:
     *
     * - `start < stop`
     */
    function tokensOfOwnerIn(
        address owner,
        uint256 start,
        uint256 stop
    ) external view returns (uint256[] memory);

    /**
     * @dev Returns an array of token IDs owned by `owner`.
     *
     * This function scans the ownership mapping and is O(`totalSupply`) in complexity.
     * It is meant to be called off-chain.
     *
     * See {ERC721AQueryable-tokensOfOwnerIn} for splitting the scan into
     * multiple smaller scans if the collection is large enough to cause
     * an out-of-gas error (10K collections should be fine).
     */
    function tokensOfOwner(address owner) external view returns (uint256[] memory);
}

File 6 of 11 : ERC721A.sol
// SPDX-License-Identifier: MIT
// ERC721A Contracts v4.2.2
// Creator: Chiru Labs

pragma solidity ^0.8.4;

import './IERC721A.sol';

/**
 * @dev Interface of ERC721 token receiver.
 */
interface ERC721A__IERC721Receiver {
    function onERC721Received(
        address operator,
        address from,
        uint256 tokenId,
        bytes calldata data
    ) external returns (bytes4);
}

/**
 * @title ERC721A
 *
 * @dev Implementation of the [ERC721](https://eips.ethereum.org/EIPS/eip-721)
 * Non-Fungible Token Standard, including the Metadata extension.
 * Optimized for lower gas during batch mints.
 *
 * Token IDs are minted in sequential order (e.g. 0, 1, 2, 3, ...)
 * starting from `_startTokenId()`.
 *
 * Assumptions:
 *
 * - An owner cannot have more than 2**64 - 1 (max value of uint64) of supply.
 * - The maximum token ID cannot exceed 2**256 - 1 (max value of uint256).
 */
contract ERC721A is IERC721A {
    // Reference type for token approval.
    struct TokenApprovalRef {
        address value;
    }

    // =============================================================
    //                           CONSTANTS
    // =============================================================

    // Mask of an entry in packed address data.
    uint256 private constant _BITMASK_ADDRESS_DATA_ENTRY = (1 << 64) - 1;

    // The bit position of `numberMinted` in packed address data.
    uint256 private constant _BITPOS_NUMBER_MINTED = 64;

    // The bit position of `numberBurned` in packed address data.
    uint256 private constant _BITPOS_NUMBER_BURNED = 128;

    // The bit position of `aux` in packed address data.
    uint256 private constant _BITPOS_AUX = 192;

    // Mask of all 256 bits in packed address data except the 64 bits for `aux`.
    uint256 private constant _BITMASK_AUX_COMPLEMENT = (1 << 192) - 1;

    // The bit position of `startTimestamp` in packed ownership.
    uint256 private constant _BITPOS_START_TIMESTAMP = 160;

    // The bit mask of the `burned` bit in packed ownership.
    uint256 private constant _BITMASK_BURNED = 1 << 224;

    // The bit position of the `nextInitialized` bit in packed ownership.
    uint256 private constant _BITPOS_NEXT_INITIALIZED = 225;

    // The bit mask of the `nextInitialized` bit in packed ownership.
    uint256 private constant _BITMASK_NEXT_INITIALIZED = 1 << 225;

    // The bit position of `extraData` in packed ownership.
    uint256 private constant _BITPOS_EXTRA_DATA = 232;

    // Mask of all 256 bits in a packed ownership except the 24 bits for `extraData`.
    uint256 private constant _BITMASK_EXTRA_DATA_COMPLEMENT = (1 << 232) - 1;

    // The mask of the lower 160 bits for addresses.
    uint256 private constant _BITMASK_ADDRESS = (1 << 160) - 1;

    // The maximum `quantity` that can be minted with {_mintERC2309}.
    // This limit is to prevent overflows on the address data entries.
    // For a limit of 5000, a total of 3.689e15 calls to {_mintERC2309}
    // is required to cause an overflow, which is unrealistic.
    uint256 private constant _MAX_MINT_ERC2309_QUANTITY_LIMIT = 5000;

    // The `Transfer` event signature is given by:
    // `keccak256(bytes("Transfer(address,address,uint256)"))`.
    bytes32 private constant _TRANSFER_EVENT_SIGNATURE =
        0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef;

    // =============================================================
    //                            STORAGE
    // =============================================================

    // The next token ID to be minted.
    uint256 private _currentIndex;

    // The number of tokens burned.
    uint256 private _burnCounter;

    // Token name
    string private _name;

    // Token symbol
    string private _symbol;

    // Mapping from token ID to ownership details
    // An empty struct value does not necessarily mean the token is unowned.
    // See {_packedOwnershipOf} implementation for details.
    //
    // Bits Layout:
    // - [0..159]   `addr`
    // - [160..223] `startTimestamp`
    // - [224]      `burned`
    // - [225]      `nextInitialized`
    // - [232..255] `extraData`
    mapping(uint256 => uint256) private _packedOwnerships;

    // Mapping owner address to address data.
    //
    // Bits Layout:
    // - [0..63]    `balance`
    // - [64..127]  `numberMinted`
    // - [128..191] `numberBurned`
    // - [192..255] `aux`
    mapping(address => uint256) private _packedAddressData;

    // Mapping from token ID to approved address.
    mapping(uint256 => TokenApprovalRef) private _tokenApprovals;

    // Mapping from owner to operator approvals
    mapping(address => mapping(address => bool)) private _operatorApprovals;

    // =============================================================
    //                          CONSTRUCTOR
    // =============================================================

    constructor(string memory name_, string memory symbol_) {
        _name = name_;
        _symbol = symbol_;
        _currentIndex = _startTokenId();
    }

    // =============================================================
    //                   TOKEN COUNTING OPERATIONS
    // =============================================================

    /**
     * @dev Returns the starting token ID.
     * To change the starting token ID, please override this function.
     */
    function _startTokenId() internal view virtual returns (uint256) {
        return 0;
    }

    /**
     * @dev Returns the next token ID to be minted.
     */
    function _nextTokenId() internal view virtual returns (uint256) {
        return _currentIndex;
    }

    /**
     * @dev Returns the total number of tokens in existence.
     * Burned tokens will reduce the count.
     * To get the total number of tokens minted, please see {_totalMinted}.
     */
    function totalSupply() public view virtual override returns (uint256) {
        // Counter underflow is impossible as _burnCounter cannot be incremented
        // more than `_currentIndex - _startTokenId()` times.
        unchecked {
            return _currentIndex - _burnCounter - _startTokenId();
        }
    }

    /**
     * @dev Returns the total amount of tokens minted in the contract.
     */
    function _totalMinted() internal view virtual returns (uint256) {
        // Counter underflow is impossible as `_currentIndex` does not decrement,
        // and it is initialized to `_startTokenId()`.
        unchecked {
            return _currentIndex - _startTokenId();
        }
    }

    /**
     * @dev Returns the total number of tokens burned.
     */
    function _totalBurned() internal view virtual returns (uint256) {
        return _burnCounter;
    }

    // =============================================================
    //                    ADDRESS DATA OPERATIONS
    // =============================================================

    /**
     * @dev Returns the number of tokens in `owner`'s account.
     */
    function balanceOf(address owner) public view virtual override returns (uint256) {
        if (owner == address(0)) revert BalanceQueryForZeroAddress();
        return _packedAddressData[owner] & _BITMASK_ADDRESS_DATA_ENTRY;
    }

    /**
     * Returns the number of tokens minted by `owner`.
     */
    function _numberMinted(address owner) internal view returns (uint256) {
        return (_packedAddressData[owner] >> _BITPOS_NUMBER_MINTED) & _BITMASK_ADDRESS_DATA_ENTRY;
    }

    /**
     * Returns the number of tokens burned by or on behalf of `owner`.
     */
    function _numberBurned(address owner) internal view returns (uint256) {
        return (_packedAddressData[owner] >> _BITPOS_NUMBER_BURNED) & _BITMASK_ADDRESS_DATA_ENTRY;
    }

    /**
     * Returns the auxiliary data for `owner`. (e.g. number of whitelist mint slots used).
     */
    function _getAux(address owner) internal view returns (uint64) {
        return uint64(_packedAddressData[owner] >> _BITPOS_AUX);
    }

    /**
     * Sets the auxiliary data for `owner`. (e.g. number of whitelist mint slots used).
     * If there are multiple variables, please pack them into a uint64.
     */
    function _setAux(address owner, uint64 aux) internal virtual {
        uint256 packed = _packedAddressData[owner];
        uint256 auxCasted;
        // Cast `aux` with assembly to avoid redundant masking.
        assembly {
            auxCasted := aux
        }
        packed = (packed & _BITMASK_AUX_COMPLEMENT) | (auxCasted << _BITPOS_AUX);
        _packedAddressData[owner] = packed;
    }

    // =============================================================
    //                            IERC165
    // =============================================================

    /**
     * @dev Returns true if this contract implements the interface defined by
     * `interfaceId`. See the corresponding
     * [EIP section](https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified)
     * to learn more about how these ids are created.
     *
     * This function call must use less than 30000 gas.
     */
    function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
        // The interface IDs are constants representing the first 4 bytes
        // of the XOR of all function selectors in the interface.
        // See: [ERC165](https://eips.ethereum.org/EIPS/eip-165)
        // (e.g. `bytes4(i.functionA.selector ^ i.functionB.selector ^ ...)`)
        return
            interfaceId == 0x01ffc9a7 || // ERC165 interface ID for ERC165.
            interfaceId == 0x80ac58cd || // ERC165 interface ID for ERC721.
            interfaceId == 0x5b5e139f; // ERC165 interface ID for ERC721Metadata.
    }

    // =============================================================
    //                        IERC721Metadata
    // =============================================================

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

    /**
     * @dev Returns the token collection symbol.
     */
    function symbol() public view virtual override returns (string memory) {
        return _symbol;
    }

    /**
     * @dev Returns the Uniform Resource Identifier (URI) for `tokenId` token.
     */
    function tokenURI(uint256 tokenId) public view virtual override returns (string memory) {
        if (!_exists(tokenId)) revert URIQueryForNonexistentToken();

        string memory baseURI = _baseURI();
        return bytes(baseURI).length != 0 ? string(abi.encodePacked(baseURI, _toString(tokenId))) : '';
    }

    /**
     * @dev Base URI for computing {tokenURI}. If set, the resulting URI for each
     * token will be the concatenation of the `baseURI` and the `tokenId`. Empty
     * by default, it can be overridden in child contracts.
     */
    function _baseURI() internal view virtual returns (string memory) {
        return '';
    }

    // =============================================================
    //                     OWNERSHIPS OPERATIONS
    // =============================================================

    /**
     * @dev Returns the owner of the `tokenId` token.
     *
     * Requirements:
     *
     * - `tokenId` must exist.
     */
    function ownerOf(uint256 tokenId) public view virtual override returns (address) {
        return address(uint160(_packedOwnershipOf(tokenId)));
    }

    /**
     * @dev Gas spent here starts off proportional to the maximum mint batch size.
     * It gradually moves to O(1) as tokens get transferred around over time.
     */
    function _ownershipOf(uint256 tokenId) internal view virtual returns (TokenOwnership memory) {
        return _unpackedOwnership(_packedOwnershipOf(tokenId));
    }

    /**
     * @dev Returns the unpacked `TokenOwnership` struct at `index`.
     */
    function _ownershipAt(uint256 index) internal view virtual returns (TokenOwnership memory) {
        return _unpackedOwnership(_packedOwnerships[index]);
    }

    /**
     * @dev Initializes the ownership slot minted at `index` for efficiency purposes.
     */
    function _initializeOwnershipAt(uint256 index) internal virtual {
        if (_packedOwnerships[index] == 0) {
            _packedOwnerships[index] = _packedOwnershipOf(index);
        }
    }

    /**
     * Returns the packed ownership data of `tokenId`.
     */
    function _packedOwnershipOf(uint256 tokenId) private view returns (uint256) {
        uint256 curr = tokenId;

        unchecked {
            if (_startTokenId() <= curr)
                if (curr < _currentIndex) {
                    uint256 packed = _packedOwnerships[curr];
                    // If not burned.
                    if (packed & _BITMASK_BURNED == 0) {
                        // Invariant:
                        // There will always be an initialized ownership slot
                        // (i.e. `ownership.addr != address(0) && ownership.burned == false`)
                        // before an unintialized ownership slot
                        // (i.e. `ownership.addr == address(0) && ownership.burned == false`)
                        // Hence, `curr` will not underflow.
                        //
                        // We can directly compare the packed value.
                        // If the address is zero, packed will be zero.
                        while (packed == 0) {
                            packed = _packedOwnerships[--curr];
                        }
                        return packed;
                    }
                }
        }
        revert OwnerQueryForNonexistentToken();
    }

    /**
     * @dev Returns the unpacked `TokenOwnership` struct from `packed`.
     */
    function _unpackedOwnership(uint256 packed) private pure returns (TokenOwnership memory ownership) {
        ownership.addr = address(uint160(packed));
        ownership.startTimestamp = uint64(packed >> _BITPOS_START_TIMESTAMP);
        ownership.burned = packed & _BITMASK_BURNED != 0;
        ownership.extraData = uint24(packed >> _BITPOS_EXTRA_DATA);
    }

    /**
     * @dev Packs ownership data into a single uint256.
     */
    function _packOwnershipData(address owner, uint256 flags) private view returns (uint256 result) {
        assembly {
            // Mask `owner` to the lower 160 bits, in case the upper bits somehow aren't clean.
            owner := and(owner, _BITMASK_ADDRESS)
            // `owner | (block.timestamp << _BITPOS_START_TIMESTAMP) | flags`.
            result := or(owner, or(shl(_BITPOS_START_TIMESTAMP, timestamp()), flags))
        }
    }

    /**
     * @dev Returns the `nextInitialized` flag set if `quantity` equals 1.
     */
    function _nextInitializedFlag(uint256 quantity) private pure returns (uint256 result) {
        // For branchless setting of the `nextInitialized` flag.
        assembly {
            // `(quantity == 1) << _BITPOS_NEXT_INITIALIZED`.
            result := shl(_BITPOS_NEXT_INITIALIZED, eq(quantity, 1))
        }
    }

    // =============================================================
    //                      APPROVAL OPERATIONS
    // =============================================================

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

        if (_msgSenderERC721A() != owner)
            if (!isApprovedForAll(owner, _msgSenderERC721A())) {
                revert ApprovalCallerNotOwnerNorApproved();
            }

        _tokenApprovals[tokenId].value = to;
        emit Approval(owner, to, tokenId);
    }

    /**
     * @dev Returns the account approved for `tokenId` token.
     *
     * Requirements:
     *
     * - `tokenId` must exist.
     */
    function getApproved(uint256 tokenId) public view virtual override returns (address) {
        if (!_exists(tokenId)) revert ApprovalQueryForNonexistentToken();

        return _tokenApprovals[tokenId].value;
    }

    /**
     * @dev Approve or remove `operator` as an operator for the caller.
     * Operators can call {transferFrom} or {safeTransferFrom}
     * for any token owned by the caller.
     *
     * Requirements:
     *
     * - The `operator` cannot be the caller.
     *
     * Emits an {ApprovalForAll} event.
     */
    function setApprovalForAll(address operator, bool approved) public virtual override {
        _operatorApprovals[_msgSenderERC721A()][operator] = approved;
        emit ApprovalForAll(_msgSenderERC721A(), operator, approved);
    }

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

    /**
     * @dev Returns whether `tokenId` exists.
     *
     * Tokens can be managed by their owner or approved accounts via {approve} or {setApprovalForAll}.
     *
     * Tokens start existing when they are minted. See {_mint}.
     */
    function _exists(uint256 tokenId) internal view virtual returns (bool) {
        return
            _startTokenId() <= tokenId &&
            tokenId < _currentIndex && // If within bounds,
            _packedOwnerships[tokenId] & _BITMASK_BURNED == 0; // and not burned.
    }

    /**
     * @dev Returns whether `msgSender` is equal to `approvedAddress` or `owner`.
     */
    function _isSenderApprovedOrOwner(
        address approvedAddress,
        address owner,
        address msgSender
    ) private pure returns (bool result) {
        assembly {
            // Mask `owner` to the lower 160 bits, in case the upper bits somehow aren't clean.
            owner := and(owner, _BITMASK_ADDRESS)
            // Mask `msgSender` to the lower 160 bits, in case the upper bits somehow aren't clean.
            msgSender := and(msgSender, _BITMASK_ADDRESS)
            // `msgSender == owner || msgSender == approvedAddress`.
            result := or(eq(msgSender, owner), eq(msgSender, approvedAddress))
        }
    }

    /**
     * @dev Returns the storage slot and value for the approved address of `tokenId`.
     */
    function _getApprovedSlotAndAddress(uint256 tokenId)
        private
        view
        returns (uint256 approvedAddressSlot, address approvedAddress)
    {
        TokenApprovalRef storage tokenApproval = _tokenApprovals[tokenId];
        // The following is equivalent to `approvedAddress = _tokenApprovals[tokenId].value`.
        assembly {
            approvedAddressSlot := tokenApproval.slot
            approvedAddress := sload(approvedAddressSlot)
        }
    }

    // =============================================================
    //                      TRANSFER OPERATIONS
    // =============================================================

    /**
     * @dev Transfers `tokenId` from `from` to `to`.
     *
     * Requirements:
     *
     * - `from` cannot be the zero address.
     * - `to` cannot be the zero address.
     * - `tokenId` token must be owned by `from`.
     * - If the caller is not `from`, it must be approved to move this token
     * by either {approve} or {setApprovalForAll}.
     *
     * Emits a {Transfer} event.
     */
    function transferFrom(
        address from,
        address to,
        uint256 tokenId
    ) public virtual override {
        uint256 prevOwnershipPacked = _packedOwnershipOf(tokenId);

        if (address(uint160(prevOwnershipPacked)) != from) revert TransferFromIncorrectOwner();

        (uint256 approvedAddressSlot, address approvedAddress) = _getApprovedSlotAndAddress(tokenId);

        // The nested ifs save around 20+ gas over a compound boolean condition.
        if (!_isSenderApprovedOrOwner(approvedAddress, from, _msgSenderERC721A()))
            if (!isApprovedForAll(from, _msgSenderERC721A())) revert TransferCallerNotOwnerNorApproved();

        if (to == address(0)) revert TransferToZeroAddress();

        _beforeTokenTransfers(from, to, tokenId, 1);

        // Clear approvals from the previous owner.
        assembly {
            if approvedAddress {
                // This is equivalent to `delete _tokenApprovals[tokenId]`.
                sstore(approvedAddressSlot, 0)
            }
        }

        // Underflow of the sender's balance is impossible because we check for
        // ownership above and the recipient's balance can't realistically overflow.
        // Counter overflow is incredibly unrealistic as `tokenId` would have to be 2**256.
        unchecked {
            // We can directly increment and decrement the balances.
            --_packedAddressData[from]; // Updates: `balance -= 1`.
            ++_packedAddressData[to]; // Updates: `balance += 1`.

            // Updates:
            // - `address` to the next owner.
            // - `startTimestamp` to the timestamp of transfering.
            // - `burned` to `false`.
            // - `nextInitialized` to `true`.
            _packedOwnerships[tokenId] = _packOwnershipData(
                to,
                _BITMASK_NEXT_INITIALIZED | _nextExtraData(from, to, prevOwnershipPacked)
            );

            // If the next slot may not have been initialized (i.e. `nextInitialized == false`) .
            if (prevOwnershipPacked & _BITMASK_NEXT_INITIALIZED == 0) {
                uint256 nextTokenId = tokenId + 1;
                // If the next slot's address is zero and not burned (i.e. packed value is zero).
                if (_packedOwnerships[nextTokenId] == 0) {
                    // If the next slot is within bounds.
                    if (nextTokenId != _currentIndex) {
                        // Initialize the next slot to maintain correctness for `ownerOf(tokenId + 1)`.
                        _packedOwnerships[nextTokenId] = prevOwnershipPacked;
                    }
                }
            }
        }

        emit Transfer(from, to, tokenId);
        _afterTokenTransfers(from, to, tokenId, 1);
    }

    /**
     * @dev Equivalent to `safeTransferFrom(from, to, tokenId, '')`.
     */
    function safeTransferFrom(
        address from,
        address to,
        uint256 tokenId
    ) public virtual override {
        safeTransferFrom(from, to, tokenId, '');
    }

    /**
     * @dev Safely transfers `tokenId` token from `from` to `to`.
     *
     * Requirements:
     *
     * - `from` cannot be the zero address.
     * - `to` cannot be the zero address.
     * - `tokenId` token must exist and be owned by `from`.
     * - If the caller is not `from`, it must be approved to move this token
     * by either {approve} or {setApprovalForAll}.
     * - If `to` refers to a smart contract, it must implement
     * {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
     *
     * Emits a {Transfer} event.
     */
    function safeTransferFrom(
        address from,
        address to,
        uint256 tokenId,
        bytes memory _data
    ) public virtual override {
        transferFrom(from, to, tokenId);
        if (to.code.length != 0)
            if (!_checkContractOnERC721Received(from, to, tokenId, _data)) {
                revert TransferToNonERC721ReceiverImplementer();
            }
    }

    /**
     * @dev Hook that is called before a set of serially-ordered token IDs
     * are about to be transferred. This includes minting.
     * And also called before burning one token.
     *
     * `startTokenId` - the first token ID to be transferred.
     * `quantity` - the amount to be transferred.
     *
     * Calling conditions:
     *
     * - When `from` and `to` are both non-zero, `from`'s `tokenId` will be
     * transferred to `to`.
     * - When `from` is zero, `tokenId` will be minted for `to`.
     * - When `to` is zero, `tokenId` will be burned by `from`.
     * - `from` and `to` are never both zero.
     */
    function _beforeTokenTransfers(
        address from,
        address to,
        uint256 startTokenId,
        uint256 quantity
    ) internal virtual {}

    /**
     * @dev Hook that is called after a set of serially-ordered token IDs
     * have been transferred. This includes minting.
     * And also called after one token has been burned.
     *
     * `startTokenId` - the first token ID to be transferred.
     * `quantity` - the amount to be transferred.
     *
     * Calling conditions:
     *
     * - When `from` and `to` are both non-zero, `from`'s `tokenId` has been
     * transferred to `to`.
     * - When `from` is zero, `tokenId` has been minted for `to`.
     * - When `to` is zero, `tokenId` has been burned by `from`.
     * - `from` and `to` are never both zero.
     */
    function _afterTokenTransfers(
        address from,
        address to,
        uint256 startTokenId,
        uint256 quantity
    ) internal virtual {}

    /**
     * @dev Private function to invoke {IERC721Receiver-onERC721Received} on a target contract.
     *
     * `from` - Previous owner of the given token ID.
     * `to` - Target address that will receive the token.
     * `tokenId` - Token ID to be transferred.
     * `_data` - Optional data to send along with the call.
     *
     * Returns whether the call correctly returned the expected magic value.
     */
    function _checkContractOnERC721Received(
        address from,
        address to,
        uint256 tokenId,
        bytes memory _data
    ) private returns (bool) {
        try ERC721A__IERC721Receiver(to).onERC721Received(_msgSenderERC721A(), from, tokenId, _data) returns (
            bytes4 retval
        ) {
            return retval == ERC721A__IERC721Receiver(to).onERC721Received.selector;
        } catch (bytes memory reason) {
            if (reason.length == 0) {
                revert TransferToNonERC721ReceiverImplementer();
            } else {
                assembly {
                    revert(add(32, reason), mload(reason))
                }
            }
        }
    }

    // =============================================================
    //                        MINT OPERATIONS
    // =============================================================

    /**
     * @dev Mints `quantity` tokens and transfers them to `to`.
     *
     * Requirements:
     *
     * - `to` cannot be the zero address.
     * - `quantity` must be greater than 0.
     *
     * Emits a {Transfer} event for each mint.
     */
    function _mint(address to, uint256 quantity) internal virtual {
        uint256 startTokenId = _currentIndex;
        if (quantity == 0) revert MintZeroQuantity();

        _beforeTokenTransfers(address(0), to, startTokenId, quantity);

        // Overflows are incredibly unrealistic.
        // `balance` and `numberMinted` have a maximum limit of 2**64.
        // `tokenId` has a maximum limit of 2**256.
        unchecked {
            // Updates:
            // - `balance += quantity`.
            // - `numberMinted += quantity`.
            //
            // We can directly add to the `balance` and `numberMinted`.
            _packedAddressData[to] += quantity * ((1 << _BITPOS_NUMBER_MINTED) | 1);

            // Updates:
            // - `address` to the owner.
            // - `startTimestamp` to the timestamp of minting.
            // - `burned` to `false`.
            // - `nextInitialized` to `quantity == 1`.
            _packedOwnerships[startTokenId] = _packOwnershipData(
                to,
                _nextInitializedFlag(quantity) | _nextExtraData(address(0), to, 0)
            );

            uint256 toMasked;
            uint256 end = startTokenId + quantity;

            // Use assembly to loop and emit the `Transfer` event for gas savings.
            // The duplicated `log4` removes an extra check and reduces stack juggling.
            // The assembly, together with the surrounding Solidity code, have been
            // delicately arranged to nudge the compiler into producing optimized opcodes.
            assembly {
                // Mask `to` to the lower 160 bits, in case the upper bits somehow aren't clean.
                toMasked := and(to, _BITMASK_ADDRESS)
                // Emit the `Transfer` event.
                log4(
                    0, // Start of data (0, since no data).
                    0, // End of data (0, since no data).
                    _TRANSFER_EVENT_SIGNATURE, // Signature.
                    0, // `address(0)`.
                    toMasked, // `to`.
                    startTokenId // `tokenId`.
                )

                for {
                    let tokenId := add(startTokenId, 1)
                } iszero(eq(tokenId, end)) {
                    tokenId := add(tokenId, 1)
                } {
                    // Emit the `Transfer` event. Similar to above.
                    log4(0, 0, _TRANSFER_EVENT_SIGNATURE, 0, toMasked, tokenId)
                }
            }
            if (toMasked == 0) revert MintToZeroAddress();

            _currentIndex = end;
        }
        _afterTokenTransfers(address(0), to, startTokenId, quantity);
    }

    /**
     * @dev Mints `quantity` tokens and transfers them to `to`.
     *
     * This function is intended for efficient minting only during contract creation.
     *
     * It emits only one {ConsecutiveTransfer} as defined in
     * [ERC2309](https://eips.ethereum.org/EIPS/eip-2309),
     * instead of a sequence of {Transfer} event(s).
     *
     * Calling this function outside of contract creation WILL make your contract
     * non-compliant with the ERC721 standard.
     * For full ERC721 compliance, substituting ERC721 {Transfer} event(s) with the ERC2309
     * {ConsecutiveTransfer} event is only permissible during contract creation.
     *
     * Requirements:
     *
     * - `to` cannot be the zero address.
     * - `quantity` must be greater than 0.
     *
     * Emits a {ConsecutiveTransfer} event.
     */
    function _mintERC2309(address to, uint256 quantity) internal virtual {
        uint256 startTokenId = _currentIndex;
        if (to == address(0)) revert MintToZeroAddress();
        if (quantity == 0) revert MintZeroQuantity();
        if (quantity > _MAX_MINT_ERC2309_QUANTITY_LIMIT) revert MintERC2309QuantityExceedsLimit();

        _beforeTokenTransfers(address(0), to, startTokenId, quantity);

        // Overflows are unrealistic due to the above check for `quantity` to be below the limit.
        unchecked {
            // Updates:
            // - `balance += quantity`.
            // - `numberMinted += quantity`.
            //
            // We can directly add to the `balance` and `numberMinted`.
            _packedAddressData[to] += quantity * ((1 << _BITPOS_NUMBER_MINTED) | 1);

            // Updates:
            // - `address` to the owner.
            // - `startTimestamp` to the timestamp of minting.
            // - `burned` to `false`.
            // - `nextInitialized` to `quantity == 1`.
            _packedOwnerships[startTokenId] = _packOwnershipData(
                to,
                _nextInitializedFlag(quantity) | _nextExtraData(address(0), to, 0)
            );

            emit ConsecutiveTransfer(startTokenId, startTokenId + quantity - 1, address(0), to);

            _currentIndex = startTokenId + quantity;
        }
        _afterTokenTransfers(address(0), to, startTokenId, quantity);
    }

    /**
     * @dev Safely mints `quantity` tokens and transfers them to `to`.
     *
     * Requirements:
     *
     * - If `to` refers to a smart contract, it must implement
     * {IERC721Receiver-onERC721Received}, which is called for each safe transfer.
     * - `quantity` must be greater than 0.
     *
     * See {_mint}.
     *
     * Emits a {Transfer} event for each mint.
     */
    function _safeMint(
        address to,
        uint256 quantity,
        bytes memory _data
    ) internal virtual {
        _mint(to, quantity);

        unchecked {
            if (to.code.length != 0) {
                uint256 end = _currentIndex;
                uint256 index = end - quantity;
                do {
                    if (!_checkContractOnERC721Received(address(0), to, index++, _data)) {
                        revert TransferToNonERC721ReceiverImplementer();
                    }
                } while (index < end);
                // Reentrancy protection.
                if (_currentIndex != end) revert();
            }
        }
    }

    /**
     * @dev Equivalent to `_safeMint(to, quantity, '')`.
     */
    function _safeMint(address to, uint256 quantity) internal virtual {
        _safeMint(to, quantity, '');
    }

    // =============================================================
    //                        BURN OPERATIONS
    // =============================================================

    /**
     * @dev Equivalent to `_burn(tokenId, false)`.
     */
    function _burn(uint256 tokenId) internal virtual {
        _burn(tokenId, false);
    }

    /**
     * @dev Destroys `tokenId`.
     * The approval is cleared when the token is burned.
     *
     * Requirements:
     *
     * - `tokenId` must exist.
     *
     * Emits a {Transfer} event.
     */
    function _burn(uint256 tokenId, bool approvalCheck) internal virtual {
        uint256 prevOwnershipPacked = _packedOwnershipOf(tokenId);

        address from = address(uint160(prevOwnershipPacked));

        (uint256 approvedAddressSlot, address approvedAddress) = _getApprovedSlotAndAddress(tokenId);

        if (approvalCheck) {
            // The nested ifs save around 20+ gas over a compound boolean condition.
            if (!_isSenderApprovedOrOwner(approvedAddress, from, _msgSenderERC721A()))
                if (!isApprovedForAll(from, _msgSenderERC721A())) revert TransferCallerNotOwnerNorApproved();
        }

        _beforeTokenTransfers(from, address(0), tokenId, 1);

        // Clear approvals from the previous owner.
        assembly {
            if approvedAddress {
                // This is equivalent to `delete _tokenApprovals[tokenId]`.
                sstore(approvedAddressSlot, 0)
            }
        }

        // Underflow of the sender's balance is impossible because we check for
        // ownership above and the recipient's balance can't realistically overflow.
        // Counter overflow is incredibly unrealistic as `tokenId` would have to be 2**256.
        unchecked {
            // Updates:
            // - `balance -= 1`.
            // - `numberBurned += 1`.
            //
            // We can directly decrement the balance, and increment the number burned.
            // This is equivalent to `packed -= 1; packed += 1 << _BITPOS_NUMBER_BURNED;`.
            _packedAddressData[from] += (1 << _BITPOS_NUMBER_BURNED) - 1;

            // Updates:
            // - `address` to the last owner.
            // - `startTimestamp` to the timestamp of burning.
            // - `burned` to `true`.
            // - `nextInitialized` to `true`.
            _packedOwnerships[tokenId] = _packOwnershipData(
                from,
                (_BITMASK_BURNED | _BITMASK_NEXT_INITIALIZED) | _nextExtraData(from, address(0), prevOwnershipPacked)
            );

            // If the next slot may not have been initialized (i.e. `nextInitialized == false`) .
            if (prevOwnershipPacked & _BITMASK_NEXT_INITIALIZED == 0) {
                uint256 nextTokenId = tokenId + 1;
                // If the next slot's address is zero and not burned (i.e. packed value is zero).
                if (_packedOwnerships[nextTokenId] == 0) {
                    // If the next slot is within bounds.
                    if (nextTokenId != _currentIndex) {
                        // Initialize the next slot to maintain correctness for `ownerOf(tokenId + 1)`.
                        _packedOwnerships[nextTokenId] = prevOwnershipPacked;
                    }
                }
            }
        }

        emit Transfer(from, address(0), tokenId);
        _afterTokenTransfers(from, address(0), tokenId, 1);

        // Overflow not possible, as _burnCounter cannot be exceed _currentIndex times.
        unchecked {
            _burnCounter++;
        }
    }

    // =============================================================
    //                     EXTRA DATA OPERATIONS
    // =============================================================

    /**
     * @dev Directly sets the extra data for the ownership data `index`.
     */
    function _setExtraDataAt(uint256 index, uint24 extraData) internal virtual {
        uint256 packed = _packedOwnerships[index];
        if (packed == 0) revert OwnershipNotInitializedForExtraData();
        uint256 extraDataCasted;
        // Cast `extraData` with assembly to avoid redundant masking.
        assembly {
            extraDataCasted := extraData
        }
        packed = (packed & _BITMASK_EXTRA_DATA_COMPLEMENT) | (extraDataCasted << _BITPOS_EXTRA_DATA);
        _packedOwnerships[index] = packed;
    }

    /**
     * @dev Called during each token transfer to set the 24bit `extraData` field.
     * Intended to be overridden by the cosumer contract.
     *
     * `previousExtraData` - the value of `extraData` before transfer.
     *
     * Calling conditions:
     *
     * - When `from` and `to` are both non-zero, `from`'s `tokenId` will be
     * transferred to `to`.
     * - When `from` is zero, `tokenId` will be minted for `to`.
     * - When `to` is zero, `tokenId` will be burned by `from`.
     * - `from` and `to` are never both zero.
     */
    function _extraData(
        address from,
        address to,
        uint24 previousExtraData
    ) internal view virtual returns (uint24) {}

    /**
     * @dev Returns the next extra data for the packed ownership data.
     * The returned result is shifted into position.
     */
    function _nextExtraData(
        address from,
        address to,
        uint256 prevOwnershipPacked
    ) private view returns (uint256) {
        uint24 extraData = uint24(prevOwnershipPacked >> _BITPOS_EXTRA_DATA);
        return uint256(_extraData(from, to, extraData)) << _BITPOS_EXTRA_DATA;
    }

    // =============================================================
    //                       OTHER OPERATIONS
    // =============================================================

    /**
     * @dev Returns the message sender (defaults to `msg.sender`).
     *
     * If you are writing GSN compatible contracts, you need to override this function.
     */
    function _msgSenderERC721A() internal view virtual returns (address) {
        return msg.sender;
    }

    /**
     * @dev Converts a uint256 to its ASCII string decimal representation.
     */
    function _toString(uint256 value) internal pure virtual returns (string memory str) {
        assembly {
            // The maximum value of a uint256 contains 78 digits (1 byte per digit),
            // but we allocate 0x80 bytes to keep the free memory pointer 32-byte word aligned.
            // We will need 1 32-byte word to store the length,
            // and 3 32-byte words to store a maximum of 78 digits. Total: 0x20 + 3 * 0x20 = 0x80.
            str := add(mload(0x40), 0x80)
            // Update the free memory pointer to allocate.
            mstore(0x40, str)

            // Cache the end of the memory to calculate the length later.
            let end := str

            // We write the string from rightmost digit to leftmost digit.
            // The following is essentially a do-while loop that also handles the zero case.
            // prettier-ignore
            for { let temp := value } 1 {} {
                str := sub(str, 1)
                // Write the character to the pointer.
                // The ASCII index of the '0' character is 48.
                mstore8(str, add(48, mod(temp, 10)))
                // Keep dividing `temp` until zero.
                temp := div(temp, 10)
                // prettier-ignore
                if iszero(temp) { break }
            }

            let length := sub(end, str)
            // Move the pointer 32 bytes leftwards to make room for the length.
            str := sub(str, 0x20)
            // Store the length.
            mstore(str, length)
        }
    }
}

File 7 of 11 : IERC721A.sol
// SPDX-License-Identifier: MIT
// ERC721A Contracts v4.2.2
// Creator: Chiru Labs

pragma solidity ^0.8.4;

/**
 * @dev Interface of ERC721A.
 */
interface IERC721A {
    /**
     * The caller must own the token or be an approved operator.
     */
    error ApprovalCallerNotOwnerNorApproved();

    /**
     * The token does not exist.
     */
    error ApprovalQueryForNonexistentToken();

    /**
     * Cannot query the balance for the zero address.
     */
    error BalanceQueryForZeroAddress();

    /**
     * Cannot mint to the zero address.
     */
    error MintToZeroAddress();

    /**
     * The quantity of tokens minted must be more than zero.
     */
    error MintZeroQuantity();

    /**
     * The token does not exist.
     */
    error OwnerQueryForNonexistentToken();

    /**
     * The caller must own the token or be an approved operator.
     */
    error TransferCallerNotOwnerNorApproved();

    /**
     * The token must be owned by `from`.
     */
    error TransferFromIncorrectOwner();

    /**
     * Cannot safely transfer to a contract that does not implement the
     * ERC721Receiver interface.
     */
    error TransferToNonERC721ReceiverImplementer();

    /**
     * Cannot transfer to the zero address.
     */
    error TransferToZeroAddress();

    /**
     * The token does not exist.
     */
    error URIQueryForNonexistentToken();

    /**
     * The `quantity` minted with ERC2309 exceeds the safety limit.
     */
    error MintERC2309QuantityExceedsLimit();

    /**
     * The `extraData` cannot be set on an unintialized ownership slot.
     */
    error OwnershipNotInitializedForExtraData();

    // =============================================================
    //                            STRUCTS
    // =============================================================

    struct TokenOwnership {
        // The address of the owner.
        address addr;
        // Stores the start time of ownership with minimal overhead for tokenomics.
        uint64 startTimestamp;
        // Whether the token has been burned.
        bool burned;
        // Arbitrary data similar to `startTimestamp` that can be set via {_extraData}.
        uint24 extraData;
    }

    // =============================================================
    //                         TOKEN COUNTERS
    // =============================================================

    /**
     * @dev Returns the total number of tokens in existence.
     * Burned tokens will reduce the count.
     * To get the total number of tokens minted, please see {_totalMinted}.
     */
    function totalSupply() external view returns (uint256);

    // =============================================================
    //                            IERC165
    // =============================================================

    /**
     * @dev Returns true if this contract implements the interface defined by
     * `interfaceId`. See the corresponding
     * [EIP section](https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified)
     * to learn more about how these ids are created.
     *
     * This function call must use less than 30000 gas.
     */
    function supportsInterface(bytes4 interfaceId) external view returns (bool);

    // =============================================================
    //                            IERC721
    // =============================================================

    /**
     * @dev Emitted when `tokenId` token is transferred from `from` to `to`.
     */
    event Transfer(address indexed from, address indexed to, uint256 indexed tokenId);

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

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

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

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

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

    /**
     * @dev Equivalent to `safeTransferFrom(from, to, tokenId, '')`.
     */
    function safeTransferFrom(
        address from,
        address to,
        uint256 tokenId
    ) external;

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

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

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

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

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

    // =============================================================
    //                        IERC721Metadata
    // =============================================================

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

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

    /**
     * @dev Returns the Uniform Resource Identifier (URI) for `tokenId` token.
     */
    function tokenURI(uint256 tokenId) external view returns (string memory);

    // =============================================================
    //                           IERC2309
    // =============================================================

    /**
     * @dev Emitted when tokens in `fromTokenId` to `toTokenId`
     * (inclusive) is transferred from `from` to `to`, as defined in the
     * [ERC2309](https://eips.ethereum.org/EIPS/eip-2309) standard.
     *
     * See {_mintERC2309} for more details.
     */
    event ConsecutiveTransfer(uint256 indexed fromTokenId, uint256 toTokenId, address indexed from, address indexed to);
}

File 8 of 11 : RLPReader.sol
/*
 * @author Hamdi Allam [email protected]
 * Please reach out with any questions or concerns
 */
pragma solidity ^0.8.0;

library RLPReader {
    uint8 constant STRING_SHORT_START = 0x80;
    uint8 constant STRING_LONG_START = 0xb8;
    uint8 constant LIST_SHORT_START = 0xc0;
    uint8 constant LIST_LONG_START = 0xf8;
    uint8 constant WORD_SIZE = 32;

    struct RLPItem {
        uint256 len;
        uint256 memPtr;
    }

    struct Iterator {
        RLPItem item; // Item that's being iterated over.
        uint256 nextPtr; // Position of the next item in the list.
    }

    /*
     * @dev Returns the next element in the iteration. Reverts if it has not next element.
     * @param self The iterator.
     * @return The next element in the iteration.
     */
    function next(Iterator memory self) internal pure returns (RLPItem memory) {
        require(hasNext(self));

        uint256 ptr = self.nextPtr;
        uint256 itemLength = _itemLength(ptr);
        self.nextPtr = ptr + itemLength;

        return RLPItem(itemLength, ptr);
    }

    /*
     * @dev Returns true if the iteration has more elements.
     * @param self The iterator.
     * @return true if the iteration has more elements.
     */
    function hasNext(Iterator memory self) internal pure returns (bool) {
        RLPItem memory item = self.item;
        return self.nextPtr < item.memPtr + item.len;
    }

    /*
     * @param item RLP encoded bytes
     */
    function toRlpItem(bytes memory item) internal pure returns (RLPItem memory) {
        uint256 memPtr;
        assembly {
            memPtr := add(item, 0x20)
        }

        return RLPItem(item.length, memPtr);
    }

    /*
     * @dev Create an iterator. Reverts if item is not a list.
     * @param self The RLP item.
     * @return An 'Iterator' over the item.
     */
    function iterator(RLPItem memory self) internal pure returns (Iterator memory) {
        require(isList(self));

        uint256 ptr = self.memPtr + _payloadOffset(self.memPtr);
        return Iterator(self, ptr);
    }

    /*
     * @param item RLP encoded bytes
     */
    function rlpLen(RLPItem memory item) internal pure returns (uint256) {
        return item.len;
    }

    /*
     * @param item RLP encoded bytes
     */
    function payloadLen(RLPItem memory item) internal pure returns (uint256) {
        return item.len - _payloadOffset(item.memPtr);
    }

    /*
     * @param item RLP encoded list in bytes
     */
    function toList(RLPItem memory item) internal pure returns (RLPItem[] memory) {
        require(isList(item));

        uint256 items = numItems(item);
        RLPItem[] memory result = new RLPItem[](items);

        uint256 memPtr = item.memPtr + _payloadOffset(item.memPtr);
        uint256 dataLen;
        for (uint256 i = 0; i < items; i++) {
            dataLen = _itemLength(memPtr);
            result[i] = RLPItem(dataLen, memPtr);
            memPtr = memPtr + dataLen;
        }

        return result;
    }

    // @return indicator whether encoded payload is a list. negate this function call for isData.
    function isList(RLPItem memory item) internal pure returns (bool) {
        if (item.len == 0) return false;

        uint8 byte0;
        uint256 memPtr = item.memPtr;
        assembly {
            byte0 := byte(0, mload(memPtr))
        }

        if (byte0 < LIST_SHORT_START) return false;
        return true;
    }

    /*
     * @dev A cheaper version of keccak256(toRlpBytes(item)) that avoids copying memory.
     * @return keccak256 hash of RLP encoded bytes.
     */
    function rlpBytesKeccak256(RLPItem memory item) internal pure returns (bytes32) {
        uint256 ptr = item.memPtr;
        uint256 len = item.len;
        bytes32 result;
        assembly {
            result := keccak256(ptr, len)
        }
        return result;
    }

    function payloadLocation(RLPItem memory item) internal pure returns (uint256, uint256) {
        uint256 offset = _payloadOffset(item.memPtr);
        uint256 memPtr = item.memPtr + offset;
        uint256 len = item.len - offset; // data length
        return (memPtr, len);
    }

    /*
     * @dev A cheaper version of keccak256(toBytes(item)) that avoids copying memory.
     * @return keccak256 hash of the item payload.
     */
    function payloadKeccak256(RLPItem memory item) internal pure returns (bytes32) {
        (uint256 memPtr, uint256 len) = payloadLocation(item);
        bytes32 result;
        assembly {
            result := keccak256(memPtr, len)
        }
        return result;
    }

    /** RLPItem conversions into data types **/

    // @returns raw rlp encoding in bytes
    function toRlpBytes(RLPItem memory item) internal pure returns (bytes memory) {
        bytes memory result = new bytes(item.len);
        if (result.length == 0) return result;

        uint256 ptr;
        assembly {
            ptr := add(0x20, result)
        }

        copy(item.memPtr, ptr, item.len);
        return result;
    }

    // any non-zero byte is considered true
    function toBoolean(RLPItem memory item) internal pure returns (bool) {
        require(item.len == 1);
        uint256 result;
        uint256 memPtr = item.memPtr;
        assembly {
            result := byte(0, mload(memPtr))
        }

        return result == 0 ? false : true;
    }

    function toAddress(RLPItem memory item) internal pure returns (address) {
        // 1 byte for the length prefix
        require(item.len == 21);

        return address(uint160(toUint(item)));
    }

    function toUint(RLPItem memory item) internal pure returns (uint256) {
        require(item.len > 0 && item.len <= 33);

        uint256 offset = _payloadOffset(item.memPtr);
        uint256 len = item.len - offset;

        uint256 result;
        uint256 memPtr = item.memPtr + offset;
        assembly {
            result := mload(memPtr)

            // shfit to the correct location if neccesary
            if lt(len, 32) {
                result := div(result, exp(256, sub(32, len)))
            }
        }

        return result;
    }

    // enforces 32 byte length
    function toUintStrict(RLPItem memory item) internal pure returns (uint256) {
        // one byte prefix
        require(item.len == 33);

        uint256 result;
        uint256 memPtr = item.memPtr + 1;
        assembly {
            result := mload(memPtr)
        }

        return result;
    }

    function toBytes(RLPItem memory item) internal pure returns (bytes memory) {
        require(item.len > 0);

        uint256 offset = _payloadOffset(item.memPtr);
        uint256 len = item.len - offset; // data length
        bytes memory result = new bytes(len);

        uint256 destPtr;
        assembly {
            destPtr := add(0x20, result)
        }

        copy(item.memPtr + offset, destPtr, len);
        return result;
    }

    /*
     * Private Helpers
     */

    // @return number of payload items inside an encoded list.
    function numItems(RLPItem memory item) private pure returns (uint256) {
        if (item.len == 0) return 0;

        uint256 count = 0;
        uint256 currPtr = item.memPtr + _payloadOffset(item.memPtr);
        uint256 endPtr = item.memPtr + item.len;
        while (currPtr < endPtr) {
            currPtr = currPtr + _itemLength(currPtr); // skip over an item
            count++;
        }

        return count;
    }

    // @return entire rlp item byte length
    function _itemLength(uint256 memPtr) private pure returns (uint256) {
        uint256 itemLen;
        uint256 byte0;
        assembly {
            byte0 := byte(0, mload(memPtr))
        }

        if (byte0 < STRING_SHORT_START) itemLen = 1;
        else if (byte0 < STRING_LONG_START) itemLen = byte0 - STRING_SHORT_START + 1;
        else if (byte0 < LIST_SHORT_START) {
            assembly {
                let byteLen := sub(byte0, 0xb7) // # of bytes the actual length is
                memPtr := add(memPtr, 1) // skip over the first byte
                /* 32 byte word size */
                let dataLen := div(mload(memPtr), exp(256, sub(32, byteLen))) // right shifting to get the len
                itemLen := add(dataLen, add(byteLen, 1))
            }
        } else if (byte0 < LIST_LONG_START) {
            itemLen = byte0 - LIST_SHORT_START + 1;
        } else {
            assembly {
                let byteLen := sub(byte0, 0xf7)
                memPtr := add(memPtr, 1)

                let dataLen := div(mload(memPtr), exp(256, sub(32, byteLen))) // right shifting to the correct length
                itemLen := add(dataLen, add(byteLen, 1))
            }
        }

        return itemLen;
    }

    // @return number of bytes until the data
    function _payloadOffset(uint256 memPtr) private pure returns (uint256) {
        uint256 byte0;
        assembly {
            byte0 := byte(0, mload(memPtr))
        }

        if (byte0 < STRING_SHORT_START) return 0;
        else if (byte0 < STRING_LONG_START || (byte0 >= LIST_SHORT_START && byte0 < LIST_LONG_START)) return 1;
        else if (byte0 < LIST_SHORT_START)
            // being explicit
            return byte0 - (STRING_LONG_START - 1) + 1;
        else return byte0 - (LIST_LONG_START - 1) + 1;
    }

    /*
     * @param src Pointer to source
     * @param dest Pointer to destination
     * @param len Amount of memory to copy from the source
     */
    function copy(
        uint256 src,
        uint256 dest,
        uint256 len
    ) private pure {
        if (len == 0) return;

        // copy as many word sizes as possible
        for (; len >= WORD_SIZE; len -= WORD_SIZE) {
            assembly {
                mstore(dest, mload(src))
            }

            src += WORD_SIZE;
            dest += WORD_SIZE;
        }

        if (len == 0) return;

        // left over bytes. Mask is used to remove unwanted bytes from the word
        uint256 mask = 256**(WORD_SIZE - len) - 1;

        assembly {
            let srcpart := and(mload(src), not(mask)) // zero out src
            let destpart := and(mload(dest), mask) // retrieve the bytes
            mstore(dest, or(destpart, srcpart))
        }
    }
}

File 9 of 11 : MerklePatriciaProof.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

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

library MerklePatriciaProof {
    /*
     * @dev Verifies a merkle patricia proof.
     * @param value The terminating value in the trie.
     * @param encodedPath The path in the trie leading to value.
     * @param rlpParentNodes The rlp encoded stack of nodes.
     * @param root The root hash of the trie.
     * @return The boolean validity of the proof.
     */
    function verify(
        bytes memory value,
        bytes memory encodedPath,
        bytes memory rlpParentNodes,
        bytes32 root
    ) internal pure returns (bool) {
        RLPReader.RLPItem memory item = RLPReader.toRlpItem(rlpParentNodes);
        RLPReader.RLPItem[] memory parentNodes = RLPReader.toList(item);

        bytes memory currentNode;
        RLPReader.RLPItem[] memory currentNodeList;

        bytes32 nodeKey = root;
        uint256 pathPtr = 0;

        bytes memory path = _getNibbleArray(encodedPath);
        if (path.length == 0) {
            return false;
        }

        for (uint256 i = 0; i < parentNodes.length; i++) {
            if (pathPtr > path.length) {
                return false;
            }

            currentNode = RLPReader.toRlpBytes(parentNodes[i]);
            if (nodeKey != keccak256(currentNode)) {
                return false;
            }
            currentNodeList = RLPReader.toList(parentNodes[i]);

            if (currentNodeList.length == 17) {
                if (pathPtr == path.length) {
                    if (keccak256(RLPReader.toBytes(currentNodeList[16])) == keccak256(value)) {
                        return true;
                    } else {
                        return false;
                    }
                }

                uint8 nextPathNibble = uint8(path[pathPtr]);
                if (nextPathNibble > 16) {
                    return false;
                }
                nodeKey = bytes32(RLPReader.toUintStrict(currentNodeList[nextPathNibble]));
                pathPtr += 1;
            } else if (currentNodeList.length == 2) {
                uint256 traversed = _nibblesToTraverse(RLPReader.toBytes(currentNodeList[0]), path, pathPtr);
                if (pathPtr + traversed == path.length) {
                    //leaf node
                    if (keccak256(RLPReader.toBytes(currentNodeList[1])) == keccak256(value)) {
                        return true;
                    } else {
                        return false;
                    }
                }

                //extension node
                if (traversed == 0) {
                    return false;
                }

                pathPtr += traversed;
                nodeKey = bytes32(RLPReader.toUintStrict(currentNodeList[1]));
            } else {
                return false;
            }
        }
    }

    function _nibblesToTraverse(
        bytes memory encodedPartialPath,
        bytes memory path,
        uint256 pathPtr
    ) private pure returns (uint256) {
        uint256 len = 0;
        // encodedPartialPath has elements that are each two hex characters (1 byte), but partialPath
        // and slicedPath have elements that are each one hex character (1 nibble)
        bytes memory partialPath = _getNibbleArray(encodedPartialPath);
        bytes memory slicedPath = new bytes(partialPath.length);

        // pathPtr counts nibbles in path
        // partialPath.length is a number of nibbles
        for (uint256 i = pathPtr; i < pathPtr + partialPath.length; i++) {
            bytes1 pathNibble = path[i];
            slicedPath[i - pathPtr] = pathNibble;
        }

        if (keccak256(partialPath) == keccak256(slicedPath)) {
            len = partialPath.length;
        } else {
            len = 0;
        }
        return len;
    }

    // bytes b must be hp encoded
    function _getNibbleArray(bytes memory b) internal pure returns (bytes memory) {
        bytes memory nibbles = "";
        if (b.length > 0) {
            uint8 offset;
            uint8 hpNibble = uint8(_getNthNibbleOfBytes(0, b));
            if (hpNibble == 1 || hpNibble == 3) {
                nibbles = new bytes(b.length * 2 - 1);
                bytes1 oddNibble = _getNthNibbleOfBytes(1, b);
                nibbles[0] = oddNibble;
                offset = 1;
            } else {
                nibbles = new bytes(b.length * 2 - 2);
                offset = 0;
            }

            for (uint256 i = offset; i < nibbles.length; i++) {
                nibbles[i] = _getNthNibbleOfBytes(i - offset + 2, b);
            }
        }
        return nibbles;
    }

    function _getNthNibbleOfBytes(uint256 n, bytes memory str) private pure returns (bytes1) {
        return bytes1(n % 2 == 0 ? uint8(str[n / 2]) / 0x10 : uint8(str[n / 2]) % 0x10);
    }
}

File 10 of 11 : Merkle.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

library Merkle {
    function checkMembership(
        bytes32 leaf,
        uint256 index,
        bytes32 rootHash,
        bytes memory proof
    ) internal pure returns (bool) {
        require(proof.length % 32 == 0, "Invalid proof length");
        uint256 proofHeight = proof.length / 32;
        // Proof of size n means, height of the tree is n+1.
        // In a tree of height n+1, max #leafs possible is 2 ^ n
        require(index < 2**proofHeight, "Leaf index is too big");

        bytes32 proofElement;
        bytes32 computedHash = leaf;
        for (uint256 i = 32; i <= proof.length; i += 32) {
            assembly {
                proofElement := mload(add(proof, i))
            }

            if (index % 2 == 0) {
                computedHash = keccak256(abi.encodePacked(computedHash, proofElement));
            } else {
                computedHash = keccak256(abi.encodePacked(proofElement, computedHash));
            }

            index = index / 2;
        }
        return computedHash == rootHash;
    }
}

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

pragma solidity ^0.8.0;

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

library ExitPayloadReader {
    using RLPReader for bytes;
    using RLPReader for RLPReader.RLPItem;

    uint8 constant WORD_SIZE = 32;

    struct ExitPayload {
        RLPReader.RLPItem[] data;
    }

    struct Receipt {
        RLPReader.RLPItem[] data;
        bytes raw;
        uint256 logIndex;
    }

    struct Log {
        RLPReader.RLPItem data;
        RLPReader.RLPItem[] list;
    }

    struct LogTopics {
        RLPReader.RLPItem[] data;
    }

    // copy paste of private copy() from RLPReader to avoid changing of existing contracts
    function copy(
        uint256 src,
        uint256 dest,
        uint256 len
    ) private pure {
        if (len == 0) return;

        // copy as many word sizes as possible
        for (; len >= WORD_SIZE; len -= WORD_SIZE) {
            assembly {
                mstore(dest, mload(src))
            }

            src += WORD_SIZE;
            dest += WORD_SIZE;
        }
        
        if (len == 0) return;

        // left over bytes. Mask is used to remove unwanted bytes from the word
        uint256 mask = 256**(WORD_SIZE - len) - 1;
        assembly {
            let srcpart := and(mload(src), not(mask)) // zero out src
            let destpart := and(mload(dest), mask) // retrieve the bytes
            mstore(dest, or(destpart, srcpart))
        }
    }

    function toExitPayload(bytes memory data) internal pure returns (ExitPayload memory) {
        RLPReader.RLPItem[] memory payloadData = data.toRlpItem().toList();

        return ExitPayload(payloadData);
    }

    function getHeaderNumber(ExitPayload memory payload) internal pure returns (uint256) {
        return payload.data[0].toUint();
    }

    function getBlockProof(ExitPayload memory payload) internal pure returns (bytes memory) {
        return payload.data[1].toBytes();
    }

    function getBlockNumber(ExitPayload memory payload) internal pure returns (uint256) {
        return payload.data[2].toUint();
    }

    function getBlockTime(ExitPayload memory payload) internal pure returns (uint256) {
        return payload.data[3].toUint();
    }

    function getTxRoot(ExitPayload memory payload) internal pure returns (bytes32) {
        return bytes32(payload.data[4].toUint());
    }

    function getReceiptRoot(ExitPayload memory payload) internal pure returns (bytes32) {
        return bytes32(payload.data[5].toUint());
    }

    function getReceipt(ExitPayload memory payload) internal pure returns (Receipt memory receipt) {
        receipt.raw = payload.data[6].toBytes();
        RLPReader.RLPItem memory receiptItem = receipt.raw.toRlpItem();

        if (receiptItem.isList()) {
            // legacy tx
            receipt.data = receiptItem.toList();
        } else {
            // pop first byte before parsting receipt
            bytes memory typedBytes = receipt.raw;
            bytes memory result = new bytes(typedBytes.length - 1);
            uint256 srcPtr;
            uint256 destPtr;
            assembly {
                srcPtr := add(33, typedBytes)
                destPtr := add(0x20, result)
            }

            copy(srcPtr, destPtr, result.length);
            receipt.data = result.toRlpItem().toList();
        }

        receipt.logIndex = getReceiptLogIndex(payload);
        return receipt;
    }

    function getReceiptProof(ExitPayload memory payload) internal pure returns (bytes memory) {
        return payload.data[7].toBytes();
    }

    function getBranchMaskAsBytes(ExitPayload memory payload) internal pure returns (bytes memory) {
        return payload.data[8].toBytes();
    }

    function getBranchMaskAsUint(ExitPayload memory payload) internal pure returns (uint256) {
        return payload.data[8].toUint();
    }

    function getReceiptLogIndex(ExitPayload memory payload) internal pure returns (uint256) {
        return payload.data[9].toUint();
    }

    // Receipt methods
    function toBytes(Receipt memory receipt) internal pure returns (bytes memory) {
        return receipt.raw;
    }

    function getLog(Receipt memory receipt) internal pure returns (Log memory) {
        RLPReader.RLPItem memory logData = receipt.data[3].toList()[receipt.logIndex];
        return Log(logData, logData.toList());
    }

    // Log methods
    function getEmitter(Log memory log) internal pure returns (address) {
        return RLPReader.toAddress(log.list[0]);
    }

    function getTopics(Log memory log) internal pure returns (LogTopics memory) {
        return LogTopics(log.list[1].toList());
    }

    function getData(Log memory log) internal pure returns (bytes memory) {
        return log.list[2].toBytes();
    }

    function toRlpBytes(Log memory log) internal pure returns (bytes memory) {
        return log.data.toRlpBytes();
    }

    // LogTopics methods
    function getField(LogTopics memory topics, uint256 index) internal pure returns (RLPReader.RLPItem memory) {
        return topics.data[index];
    }
}

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

Contract Security Audit

Contract ABI

[{"inputs":[{"internalType":"address","name":"_checkpointManager","type":"address"},{"internalType":"address","name":"_fxRoot","type":"address"},{"internalType":"string","name":"name","type":"string"},{"internalType":"string","name":"symbol","type":"string"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"AlreadyBound","type":"error"},{"inputs":[],"name":"ApprovalCallerNotOwnerNorApproved","type":"error"},{"inputs":[],"name":"ApprovalQueryForNonexistentToken","type":"error"},{"inputs":[],"name":"BalanceQueryForZeroAddress","type":"error"},{"inputs":[],"name":"BondsDisabled","type":"error"},{"inputs":[],"name":"InvalidQueryRange","type":"error"},{"inputs":[],"name":"MaxSupplyReached","type":"error"},{"inputs":[],"name":"MintERC2309QuantityExceedsLimit","type":"error"},{"inputs":[],"name":"MintToZeroAddress","type":"error"},{"inputs":[],"name":"MintZeroQuantity","type":"error"},{"inputs":[],"name":"NoContractsAllowed","type":"error"},{"inputs":[],"name":"NotAnOwner","type":"error"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"NotBound","type":"error"},{"inputs":[],"name":"NotContractOwner","type":"error"},{"inputs":[],"name":"NotSalesContract","type":"error"},{"inputs":[],"name":"OwnerQueryForNonexistentToken","type":"error"},{"inputs":[],"name":"OwnershipNotInitializedForExtraData","type":"error"},{"inputs":[],"name":"TransferCallerNotOwnerNorApproved","type":"error"},{"inputs":[],"name":"TransferFromIncorrectOwner","type":"error"},{"inputs":[],"name":"TransferToNonERC721ReceiverImplementer","type":"error"},{"inputs":[],"name":"TransferToZeroAddress","type":"error"},{"inputs":[],"name":"URIQueryForNonexistentToken","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"approved","type":"address"},{"indexed":true,"internalType":"uint256","name":"tokenId","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":"uint256","name":"fromTokenId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"toTokenId","type":"uint256"},{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"}],"name":"ConsecutiveTransfer","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"string","name":"cmd","type":"string"},{"indexed":false,"internalType":"address","name":"wallet","type":"address"},{"indexed":false,"internalType":"uint256[]","name":"tokenIds","type":"uint256[]"}],"name":"RequestSend","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":"tokenId","type":"uint256"}],"name":"Transfer","type":"event"},{"inputs":[],"name":"BOUND","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"SEND_MESSAGE_EVENT_SIG","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"UNBOUND","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","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":"view","type":"function"},{"inputs":[],"name":"bondsEnabled","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256[]","name":"tokenIds","type":"uint256[]"}],"name":"bound","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"checkpointManager","outputs":[{"internalType":"contract ICheckpointManager","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"explicitOwnershipOf","outputs":[{"components":[{"internalType":"address","name":"addr","type":"address"},{"internalType":"uint64","name":"startTimestamp","type":"uint64"},{"internalType":"bool","name":"burned","type":"bool"},{"internalType":"uint24","name":"extraData","type":"uint24"}],"internalType":"struct IERC721A.TokenOwnership","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256[]","name":"tokenIds","type":"uint256[]"}],"name":"explicitOwnershipsOf","outputs":[{"components":[{"internalType":"address","name":"addr","type":"address"},{"internalType":"uint64","name":"startTimestamp","type":"uint64"},{"internalType":"bool","name":"burned","type":"bool"},{"internalType":"uint24","name":"extraData","type":"uint24"}],"internalType":"struct IERC721A.TokenOwnership[]","name":"","type":"tuple[]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"fxChildTunnel","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"fxRoot","outputs":[{"internalType":"contract IFxStateSender","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"getApproved","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"operator","type":"address"}],"name":"isApprovedForAll","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"wallet","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"mint","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"ownerOf","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"name":"processedExits","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes","name":"inputData","type":"bytes"}],"name":"receiveMessage","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","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":"tokenId","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":"URI","type":"string"}],"name":"setBaseTokenURI","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bool","name":"status","type":"bool"}],"name":"setBondsEnabled","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_fxChildTunnel","type":"address"}],"name":"setFxChildTunnel","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_salesContractAddress","type":"address"}],"name":"setSalesContract","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":"","type":"uint256"}],"name":"tokenIdToBound","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"tokenURI","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"tokensOfOwner","outputs":[{"internalType":"uint256[]","name":"","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"uint256","name":"start","type":"uint256"},{"internalType":"uint256","name":"stop","type":"uint256"}],"name":"tokensOfOwnerIn","outputs":[{"internalType":"uint256[]","name":"","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","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":"uint256[]","name":"tokenIds","type":"uint256[]"}],"name":"unbound","outputs":[],"stateMutability":"nonpayable","type":"function"}]

60806040523480156200001157600080fd5b50604051620064a2380380620064a283398181016040528101906200003791906200040b565b8383838381600290805190602001906200005392919062000159565b5080600390805190602001906200006c92919062000159565b506200007d6200015460201b60201c565b600081905550505033600860006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555081600a60006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555080600960006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050505050505062000520565b600090565b8280546200016790620004ea565b90600052602060002090601f0160209004810192826200018b5760008555620001d7565b82601f10620001a657805160ff1916838001178555620001d7565b82800160010185558215620001d7579182015b82811115620001d6578251825591602001919060010190620001b9565b5b509050620001e69190620001ea565b5090565b5b8082111562000205576000816000905550600101620001eb565b5090565b6000604051905090565b600080fd5b600080fd5b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b60006200024a826200021d565b9050919050565b6200025c816200023d565b81146200026857600080fd5b50565b6000815190506200027c8162000251565b92915050565b600080fd5b600080fd5b6000601f19601f8301169050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b620002d7826200028c565b810181811067ffffffffffffffff82111715620002f957620002f86200029d565b5b80604052505050565b60006200030e62000209565b90506200031c8282620002cc565b919050565b600067ffffffffffffffff8211156200033f576200033e6200029d565b5b6200034a826200028c565b9050602081019050919050565b60005b83811015620003775780820151818401526020810190506200035a565b8381111562000387576000848401525b50505050565b6000620003a46200039e8462000321565b62000302565b905082815260208101848484011115620003c357620003c262000287565b5b620003d084828562000357565b509392505050565b600082601f830112620003f057620003ef62000282565b5b8151620004028482602086016200038d565b91505092915050565b6000806000806080858703121562000428576200042762000213565b5b600062000438878288016200026b565b94505060206200044b878288016200026b565b935050604085015167ffffffffffffffff8111156200046f576200046e62000218565b5b6200047d87828801620003d8565b925050606085015167ffffffffffffffff811115620004a157620004a062000218565b5b620004af87828801620003d8565b91505092959194509250565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b600060028204905060018216806200050357607f821691505b602082108114156200051a5762000519620004bb565b5b50919050565b615f7280620005306000396000f3fe608060405234801561001057600080fd5b50600436106102275760003560e01c80638da5cb5b11610130578063b88d4fde116100b8578063ccea458f1161007c578063ccea458f1461069a578063de9b771f146106b8578063e985e9c5146106d6578063f2fde38b14610706578063f953cec71461072257610227565b8063b88d4fde146105e2578063c0857ba0146105fe578063c23dc68f1461061c578063c52141911461064c578063c87b56dd1461066a57610227565b806399a2557a116100ff57806399a2557a146105425780639b5719a614610572578063a22cb4651461058e578063aa22b45c146105aa578063aea4e49e146105c657610227565b80638da5cb5b146104b85780639094adb3146104d657806395d89b4114610506578063972c49281461052457610227565b806340c10f19116101b35780636352211e116101825780636352211e146103f057806370734dcf1461042057806370a082311461043c5780637bef587c1461046c5780638462151c1461048857610227565b806340c10f191461035857806342842e0e146103745780635bbb217714610390578063607f2d42146103c057610227565b80630e387de6116101fa5780630e387de6146102c657806318160ddd146102e45780631edc2e721461030257806323b872dd1461032057806330176e131461033c57610227565b806301ffc9a71461022c57806306fdde031461025c578063081812fc1461027a578063095ea7b3146102aa575b600080fd5b61024660048036038101906102419190614541565b61073e565b6040516102539190614589565b60405180910390f35b6102646107d0565b604051610271919061463d565b60405180910390f35b610294600480360381019061028f9190614695565b610862565b6040516102a19190614703565b60405180910390f35b6102c460048036038101906102bf919061474a565b6108e1565b005b6102ce610a25565b6040516102db91906147a3565b60405180910390f35b6102ec610a4c565b6040516102f991906147cd565b60405180910390f35b61030a610a63565b6040516103179190614589565b60405180910390f35b61033a600480360381019061033591906147e8565b610a76565b005b610356600480360381019061035191906148a0565b610d9b565b005b610372600480360381019061036d919061474a565b610e38565b005b61038e600480360381019061038991906147e8565b610f1b565b005b6103aa60048036038101906103a59190614943565b610f3b565b6040516103b79190614af3565b60405180910390f35b6103da60048036038101906103d59190614b41565b610ffe565b6040516103e79190614589565b60405180910390f35b61040a60048036038101906104059190614695565b61101e565b6040516104179190614703565b60405180910390f35b61043a60048036038101906104359190614943565b611030565b005b61045660048036038101906104519190614b6e565b6112ea565b60405161046391906147cd565b60405180910390f35b61048660048036038101906104819190614943565b6113a3565b005b6104a2600480360381019061049d9190614b6e565b61169c565b6040516104af9190614c59565b60405180910390f35b6104c06117e6565b6040516104cd9190614703565b60405180910390f35b6104f060048036038101906104eb9190614695565b61180c565b6040516104fd9190614589565b60405180910390f35b61050e61182c565b60405161051b919061463d565b60405180910390f35b61052c6118be565b6040516105399190614703565b60405180910390f35b61055c60048036038101906105579190614c7b565b6118e4565b6040516105699190614c59565b60405180910390f35b61058c60048036038101906105879190614b6e565b611af8565b005b6105a860048036038101906105a39190614cfa565b611bc3565b005b6105c460048036038101906105bf9190614d3a565b611cce565b005b6105e060048036038101906105db9190614b6e565b611d72565b005b6105fc60048036038101906105f79190614e97565b611e3d565b005b610606611eb0565b6040516106139190614f79565b60405180910390f35b61063660048036038101906106319190614695565b611ed6565b6040516106439190614fe9565b60405180910390f35b610654611f40565b60405161066191906147a3565b60405180910390f35b610684600480360381019061067f9190614695565b611f64565b604051610691919061463d565b60405180910390f35b6106a2612003565b6040516106af91906147a3565b60405180910390f35b6106c0612027565b6040516106cd9190615025565b60405180910390f35b6106f060048036038101906106eb9190615040565b61204d565b6040516106fd9190614589565b60405180910390f35b610720600480360381019061071b9190614b6e565b6120e1565b005b61073c60048036038101906107379190615080565b6121ac565b005b60006301ffc9a760e01b827bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916148061079957506380ac58cd60e01b827bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916145b806107c95750635b5e139f60e01b827bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916145b9050919050565b6060600280546107df906150f8565b80601f016020809104026020016040519081016040528092919081815260200182805461080b906150f8565b80156108585780601f1061082d57610100808354040283529160200191610858565b820191906000526020600020905b81548152906001019060200180831161083b57829003601f168201915b5050505050905090565b600061086d826121c6565b6108a3576040517fcf4700e400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6006600083815260200190815260200160002060000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff169050919050565b60006108ec8261101e565b90508073ffffffffffffffffffffffffffffffffffffffff1661090d612225565b73ffffffffffffffffffffffffffffffffffffffff16146109705761093981610934612225565b61204d565b61096f576040517fcfb3b94200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5b826006600084815260200190815260200160002060000160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550818373ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92560405160405180910390a4505050565b7f8c5261668696ce22758910d05bab8f186d6eb247ceac2af2e82c7dc17669b03660001b81565b6000610a5661222d565b6001546000540303905090565b600f60009054906101000a900460ff1681565b6000610a8182612232565b90508373ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff1614610ae8576040517fa114810000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600080610af484612300565b91509150610b0a8187610b05612225565b612327565b610b5657610b1f86610b1a612225565b61204d565b610b55576040517f59c896be00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5b600073ffffffffffffffffffffffffffffffffffffffff168573ffffffffffffffffffffffffffffffffffffffff161415610bbd576040517fea553b3400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b610bca868686600161236b565b8015610bd557600082555b600560008773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600081546001900391905081905550600560008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000815460010191905081905550610ca385610c7f8888876124de565b7c020000000000000000000000000000000000000000000000000000000017612506565b600460008681526020019081526020016000208190555060007c020000000000000000000000000000000000000000000000000000000084161415610d2b576000600185019050600060046000838152602001908152602001600020541415610d29576000548114610d28578360046000838152602001908152602001600020819055505b5b505b838573ffffffffffffffffffffffffffffffffffffffff168773ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef60405160405180910390a4610d938686866001612531565b505050505050565b600860009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614610e22576040517fbfcafd3700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8181600e9190610e33929190614362565b505050565b600d60009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614610ebf576040517ff90715e100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6115b481610ecb612537565b610ed59190615159565b1115610f0d576040517fd05cb60900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b610f17828261254a565b5050565b610f3683838360405180602001604052806000815250611e3d565b505050565b6060600083839050905060008167ffffffffffffffff811115610f6157610f60614d6c565b5b604051908082528060200260200182016040528015610f9a57816020015b610f876143e8565b815260200190600190039081610f7f5790505b50905060005b828114610ff257610fc9868683818110610fbd57610fbc6151af565b5b90506020020135611ed6565b828281518110610fdc57610fdb6151af565b5b6020026020010181905250806001019050610fa0565b50809250505092915050565b600c6020528060005260406000206000915054906101000a900460ff1681565b600061102982612232565b9050919050565b600080339050803b91503273ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16141580611076575060008214155b156110ad576040517f5a156d6000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60005b84849050811015611235573373ffffffffffffffffffffffffffffffffffffffff166110f48686848181106110e8576110e76151af565b5b9050602002013561101e565b73ffffffffffffffffffffffffffffffffffffffff1614611141576040517feea91ff800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600015156010600087878581811061115c5761115b6151af565b5b90506020020135815260200190815260200160002060009054906101000a900460ff16151514156111dd5784848281811061119a576111996151af565b5b905060200201356040517fd0078a0a0000000000000000000000000000000000000000000000000000000081526004016111d491906147cd565b60405180910390fd5b6000601060008787858181106111f6576111f56151af565b5b90506020020135815260200190815260200160002060006101000a81548160ff021916908315150217905550808061122d906151de565b9150506110b0565b5060007f46ea84878955907861000722f4dd80a932935b7b3669b3a736e54d8ed9dca5c633868660405160200161126e93929190615288565b60405160208183030381529060405260405160200161128e92919061530f565b60405160208183030381529060405290507f137b854e7e99f3421d5319954d65a2fb8cd25a72297f898f1e662f0843b727d53386866040516112d29392919061538b565b60405180910390a16112e381612707565b5050505050565b60008073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff161415611352576040517f8f4eb60400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b67ffffffffffffffff600560008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054169050919050565b600f60009054906101000a900460ff166113e9576040517f78d9002800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600080339050803b91503273ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614158061142f575060008214155b15611466576040517f5a156d6000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60005b848490508110156115e7573373ffffffffffffffffffffffffffffffffffffffff166114ad8686848181106114a1576114a06151af565b5b9050602002013561101e565b73ffffffffffffffffffffffffffffffffffffffff16146114fa576040517feea91ff800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60106000868684818110611511576115106151af565b5b90506020020135815260200190815260200160002060009054906101000a900460ff161561158f5784848281811061154c5761154b6151af565b5b905060200201356040517f3de74d5b00000000000000000000000000000000000000000000000000000000815260040161158691906147cd565b60405180910390fd5b6001601060008787858181106115a8576115a76151af565b5b90506020020135815260200190815260200160002060006101000a81548160ff02191690831515021790555080806115df906151de565b915050611469565b5060007f49a368f7e595a507e0eaa27f07cfde74ff475d8dafcdf3829d5d9b4a75f28a8433868660405160200161162093929190615288565b60405160208183030381529060405260405160200161164092919061530f565b60405160208183030381529060405290507f137b854e7e99f3421d5319954d65a2fb8cd25a72297f898f1e662f0843b727d53386866040516116849392919061541c565b60405180910390a161169581612707565b5050505050565b606060008060006116ac856112ea565b905060008167ffffffffffffffff8111156116ca576116c9614d6c565b5b6040519080825280602002602001820160405280156116f85781602001602082028036833780820191505090505b5090506117036143e8565b600061170d61222d565b90505b8386146117d857611720816127bb565b9150816040015115611731576117cd565b600073ffffffffffffffffffffffffffffffffffffffff16826000015173ffffffffffffffffffffffffffffffffffffffff161461177157816000015194505b8773ffffffffffffffffffffffffffffffffffffffff168573ffffffffffffffffffffffffffffffffffffffff1614156117cc57808387806001019850815181106117bf576117be6151af565b5b6020026020010181815250505b5b806001019050611710565b508195505050505050919050565b600860009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b60106020528060005260406000206000915054906101000a900460ff1681565b60606003805461183b906150f8565b80601f0160208091040260200160405190810160405280929190818152602001828054611867906150f8565b80156118b45780601f10611889576101008083540402835291602001916118b4565b820191906000526020600020905b81548152906001019060200180831161189757829003601f168201915b5050505050905090565b600b60009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b606081831061191f576040517f32c1995a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60008061192a6127e6565b905061193461222d565b8510156119465761194361222d565b94505b80841115611952578093505b600061195d876112ea565b90508486101561198057600086860390508181101561197a578091505b50611985565b600090505b60008167ffffffffffffffff8111156119a1576119a0614d6c565b5b6040519080825280602002602001820160405280156119cf5781602001602082028036833780820191505090505b50905060008214156119e75780945050505050611af1565b60006119f288611ed6565b905060008160400151611a0757816000015190505b60008990505b888114158015611a1d5750848714155b15611ae357611a2b816127bb565b9250826040015115611a3c57611ad8565b600073ffffffffffffffffffffffffffffffffffffffff16836000015173ffffffffffffffffffffffffffffffffffffffff1614611a7c57826000015191505b8a73ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff161415611ad75780848880600101995081518110611aca57611ac96151af565b5b6020026020010181815250505b5b806001019050611a0d565b508583528296505050505050505b9392505050565b600860009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614611b7f576040517fbfcafd3700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b80600d60006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050565b8060076000611bd0612225565b73ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548160ff0219169083151502179055508173ffffffffffffffffffffffffffffffffffffffff16611c7d612225565b73ffffffffffffffffffffffffffffffffffffffff167f17307eab39ab6107e8899845ad3d59bd9653f200f220920489ca2b5937696c3183604051611cc29190614589565b60405180910390a35050565b600860009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614611d55576040517fbfcafd3700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b80600f60006101000a81548160ff02191690831515021790555050565b600860009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614611df9576040517fbfcafd3700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b80600b60006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050565b611e48848484610a76565b60008373ffffffffffffffffffffffffffffffffffffffff163b14611eaa57611e73848484846127ef565b611ea9576040517fd1a57ed600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5b50505050565b600a60009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b611ede6143e8565b611ee66143e8565b611eee61222d565b831080611f025750611efe6127e6565b8310155b15611f105780915050611f3b565b611f19836127bb565b9050806040015115611f2e5780915050611f3b565b611f3783612940565b9150505b919050565b7f49a368f7e595a507e0eaa27f07cfde74ff475d8dafcdf3829d5d9b4a75f28a8481565b6060611f6f826121c6565b611fa5576040517fa14c4b5000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000611faf612960565b9050600081511415611fd05760405180602001604052806000815250611ffb565b80611fda846129f2565b604051602001611feb92919061549d565b6040516020818303038152906040525b915050919050565b7f46ea84878955907861000722f4dd80a932935b7b3669b3a736e54d8ed9dca5c681565b600960009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b6000600760008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff16905092915050565b600860009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614612168576040517fbfcafd3700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b80600860006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050565b60006121b782612a42565b90506121c281612d47565b5050565b6000816121d161222d565b111580156121e0575060005482105b801561221e575060007c0100000000000000000000000000000000000000000000000000000000600460008581526020019081526020016000205416145b9050919050565b600033905090565b600090565b6000808290508061224161222d565b116122c9576000548110156122c85760006004600083815260200190815260200160002054905060007c0100000000000000000000000000000000000000000000000000000000821614156122c6575b60008114156122bc576004600083600190039350838152602001908152602001600020549050612291565b80925050506122fb565b505b5b6040517fdf2d9b4200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b919050565b60008060006006600085815260200190815260200160002090508092508254915050915091565b600073ffffffffffffffffffffffffffffffffffffffff8316925073ffffffffffffffffffffffffffffffffffffffff821691508382148383141790509392505050565b6010600083815260200190815260200160002060009054906101000a900460ff16156124d85760006010600084815260200190815260200160002060006101000a81548160ff0219169083151502179055506000600167ffffffffffffffff8111156123da576123d9614d6c565b5b6040519080825280602002602001820160405280156124085781602001602082028036833780820191505090505b50905082816000815181106124205761241f6151af565b5b60200260200101818152505060007f46ea84878955907861000722f4dd80a932935b7b3669b3a736e54d8ed9dca5c686836040516020016124629291906154c1565b60405160208183030381529060405260405160200161248292919061530f565b60405160208183030381529060405290507f137b854e7e99f3421d5319954d65a2fb8cd25a72297f898f1e662f0843b727d586836040516124c49291906154f1565b60405180910390a16124d581612707565b50505b50505050565b60008060e883901c905060e86124f5868684612d4a565b62ffffff16901b9150509392505050565b600073ffffffffffffffffffffffffffffffffffffffff83169250814260a01b178317905092915050565b50505050565b600061254161222d565b60005403905090565b600080549050600082141561258b576040517fb562e8dd00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b612598600084838561236b565b600160406001901b178202600560008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000828254019250508190555061260f8361260060008660006124de565b61260985612d53565b17612506565b6004600083815260200190815260200160002081905550600080838301905073ffffffffffffffffffffffffffffffffffffffff85169150828260007fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef600080a4600183015b8181146126b057808360007fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef600080a4600181019050612675565b5060008214156126ec576040517f2e07630000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8060008190555050506127026000848385612531565b505050565b600960009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663b4720477600b60009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16836040518363ffffffff1660e01b8152600401612786929190615534565b600060405180830381600087803b1580156127a057600080fd5b505af11580156127b4573d6000803e3d6000fd5b5050505050565b6127c36143e8565b6127df6004600084815260200190815260200160002054612d63565b9050919050565b60008054905090565b60008373ffffffffffffffffffffffffffffffffffffffff1663150b7a02612815612225565b8786866040518563ffffffff1660e01b81526004016128379493929190615564565b6020604051808303816000875af192505050801561287357506040513d601f19601f8201168201806040525081019061287091906155c5565b60015b6128ed573d80600081146128a3576040519150601f19603f3d011682016040523d82523d6000602084013e6128a8565b606091505b506000815114156128e5576040517fd1a57ed600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b805181602001fd5b63150b7a0260e01b7bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916817bffffffffffffffffffffffffffffffffffffffffffffffffffffffff191614915050949350505050565b6129486143e8565b61295961295483612232565b612d63565b9050919050565b6060600e805461296f906150f8565b80601f016020809104026020016040519081016040528092919081815260200182805461299b906150f8565b80156129e85780601f106129bd576101008083540402835291602001916129e8565b820191906000526020600020905b8154815290600101906020018083116129cb57829003601f168201915b5050505050905090565b606060806040510190508060405280825b600115612a2e57600183039250600a81066030018353600a8104905080612a2957612a2e565b612a03565b508181036020830392508083525050919050565b60606000612a4f83612e19565b90506000612a5c82612e4d565b90506000612a6983612e7e565b9050600081612a7784612eaf565b612a80866130c5565b604051602001612a929392919061564f565b60405160208183030381529060405280519060200120905060001515600c600083815260200190815260200160002060009054906101000a900460ff16151514612b11576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401612b08906156fa565b60405180910390fd5b6001600c600083815260200190815260200160002060006101000a81548160ff0219169083151502179055506000612b48856130f6565b90506000612b558261321b565b9050612b6081613292565b73ffffffffffffffffffffffffffffffffffffffff16600b60009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1614612bef576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401612be69061578c565b60405180910390fd5b6000612bfa876132c3565b9050612c18612c08846132f7565b87612c128a613305565b84613336565b612c57576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401612c4e9061581e565b60405180910390fd5b612c8585612c648961363c565b612c6d8a61366d565b84612c778c6136a1565b612c808d6136d2565b613703565b506000612c918361384a565b90507f8c5261668696ce22758910d05bab8f186d6eb247ceac2af2e82c7dc17669b03660001b612cd3612cce60008461388f90919063ffffffff16565b6138be565b60001b14612d16576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401612d0d9061588a565b60405180910390fd5b6000612d2184613941565b806020019051810190612d34919061591a565b9050809950505050505050505050919050565b50565b60009392505050565b60006001821460e11b9050919050565b612d6b6143e8565b81816000019073ffffffffffffffffffffffffffffffffffffffff16908173ffffffffffffffffffffffffffffffffffffffff168152505060a082901c816020019067ffffffffffffffff16908167ffffffffffffffff168152505060007c01000000000000000000000000000000000000000000000000000000008316141581604001901515908115158152505060e882901c816060019062ffffff16908162ffffff1681525050919050565b612e21614437565b6000612e34612e2f84613972565b6139a0565b9050604051806020016040528082815250915050919050565b6060612e778260000151600881518110612e6a57612e696151af565b5b6020026020010151613ab5565b9050919050565b6000612ea88260000151600281518110612e9b57612e9a6151af565b5b60200260200101516138be565b9050919050565b606060006040518060200160405280600081525090506000835111156130bc57600080612edd600086613b6c565b60f81c905060018160ff161480612ef7575060038160ff16145b15612fc157600160028651612f0c9190615963565b612f1691906159bd565b67ffffffffffffffff811115612f2f57612f2e614d6c565b5b6040519080825280601f01601f191660200182016040528015612f615781602001600182028036833780820191505090505b5092506000612f71600187613b6c565b90508084600081518110612f8857612f876151af565b5b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a905350600192505061302d565b6002808651612fd09190615963565b612fda91906159bd565b67ffffffffffffffff811115612ff357612ff2614d6c565b5b6040519080825280601f01601f1916602001820160405280156130255781602001600182028036833780820191505090505b509250600091505b60008260ff1690505b83518110156130b85761306360028460ff168361305391906159bd565b61305d9190615159565b87613b6c565b848281518110613076576130756151af565b5b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a90535080806130b0906151de565b915050613036565b5050505b80915050919050565b60006130ef82600001516009815181106130e2576130e16151af565b5b60200260200101516138be565b9050919050565b6130fe61444a565b6131268260000151600681518110613119576131186151af565b5b6020026020010151613ab5565b8160200181905250600061313d8260200151613972565b905061314881613c04565b1561316357613156816139a0565b8260000181905250613203565b60008260200151905060006001825161317c91906159bd565b67ffffffffffffffff81111561319557613194614d6c565b5b6040519080825280601f01601f1916602001820160405280156131c75781602001600182028036833780820191505090505b5090506000808360210191508260200190506131e582828551613c52565b6131f66131f184613972565b6139a0565b8660000181905250505050505b61320c836130c5565b82604001818152505050919050565b61322361446b565b600061324d83600001516003815181106132405761323f6151af565b5b60200260200101516139a0565b836040015181518110613263576132626151af565b5b602002602001015190506040518060400160405280828152602001613287836139a0565b815250915050919050565b60006132bc82602001516000815181106132af576132ae6151af565b5b6020026020010151613cfd565b9050919050565b60006132ed82600001516005815181106132e0576132df6151af565b5b60200260200101516138be565b60001b9050919050565b606081602001519050919050565b606061332f8260000151600781518110613322576133216151af565b5b6020026020010151613ab5565b9050919050565b60008061334284613972565b9050600061334f826139a0565b905060608060008690506000806133658b612eaf565b9050600081511415613381576000975050505050505050613634565b60005b865181101561362b5781518311156133a757600098505050505050505050613634565b6133ca8782815181106133bd576133bc6151af565b5b6020026020010151613d20565b9550858051906020012084146133eb57600098505050505050505050613634565b61340e878281518110613401576134006151af565b5b60200260200101516139a0565b945060118551141561350157815183141561347e578c8051906020012061344f86601081518110613442576134416151af565b5b6020026020010151613ab5565b80519060200120141561346d57600198505050505050505050613634565b600098505050505050505050613634565b6000828481518110613493576134926151af565b5b602001015160f81c60f81b60f81c905060108160ff1611156134c15760009950505050505050505050613634565b6134e7868260ff16815181106134da576134d96151af565b5b6020026020010151613db0565b60001b94506001846134f99190615159565b935050613618565b60028551141561360657600061353b61353487600081518110613527576135266151af565b5b6020026020010151613ab5565b8486613de7565b90508251818561354b9190615159565b14156135ae578d8051906020012061357d876001815181106135705761356f6151af565b5b6020026020010151613ab5565b80519060200120141561359c5760019950505050505050505050613634565b60009950505050505050505050613634565b60008114156135c95760009950505050505050505050613634565b80846135d59190615159565b93506135fb866001815181106135ee576135ed6151af565b5b6020026020010151613db0565b60001b945050613617565b600098505050505050505050613634565b5b8080613623906151de565b915050613384565b50505050505050505b949350505050565b60006136668260000151600381518110613659576136586151af565b5b60200260200101516138be565b9050919050565b6000613697826000015160048151811061368a576136896151af565b5b60200260200101516138be565b60001b9050919050565b60006136cb82600001516000815181106136be576136bd6151af565b5b60200260200101516138be565b9050919050565b60606136fc82600001516001815181106136ef576136ee6151af565b5b6020026020010151613ab5565b9050919050565b600080600080600a60009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166341539d4a876040518263ffffffff1660e01b815260040161376491906147cd565b60a060405180830381865afa158015613781573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906137a59190615a30565b50935050925092506137fb828b6137bc91906159bd565b84878d8d8d8d6040516020016137d59493929190615acc565b60405160208183030381529060405280519060200120613f1d909392919063ffffffff16565b61383a576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161383190615b66565b60405180910390fd5b8093505050509695505050505050565b61385261448b565b60405180602001604052806138858460200151600181518110613878576138776151af565b5b60200260200101516139a0565b8152509050919050565b61389761449e565b826000015182815181106138ae576138ad6151af565b5b6020026020010151905092915050565b60008082600001511180156138d857506021826000015111155b6138e157600080fd5b60006138f0836020015161408e565b9050600081846000015161390491906159bd565b90506000808386602001516139199190615159565b905080519150602083101561393557826020036101000a820491505b81945050505050919050565b606061396b826020015160028151811061395e5761395d6151af565b5b6020026020010151613ab5565b9050919050565b61397a61449e565b600060208301905060405180604001604052808451815260200182815250915050919050565b60606139ab82613c04565b6139b457600080fd5b60006139bf8361414d565b905060008167ffffffffffffffff8111156139dd576139dc614d6c565b5b604051908082528060200260200182016040528015613a1657816020015b613a0361449e565b8152602001906001900390816139fb5790505b5090506000613a28856020015161408e565b8560200151613a379190615159565b9050600080600090505b84811015613aa857613a52836141db565b9150604051806040016040528083815260200184815250848281518110613a7c57613a7b6151af565b5b60200260200101819052508183613a939190615159565b92508080613aa0906151de565b915050613a41565b5082945050505050919050565b60606000826000015111613ac857600080fd5b6000613ad7836020015161408e565b90506000818460000151613aeb91906159bd565b905060008167ffffffffffffffff811115613b0957613b08614d6c565b5b6040519080825280601f01601f191660200182016040528015613b3b5781602001600182028036833780820191505090505b5090506000816020019050613b60848760200151613b599190615159565b82856142b7565b81945050505050919050565b600080600284613b7c9190615bb5565b14613bbf57601082600285613b919190615be6565b81518110613ba257613ba16151af565b5b602001015160f81c60f81b60f81c613bba9190615c24565b613bf9565b601082600285613bcf9190615be6565b81518110613be057613bdf6151af565b5b602001015160f81c60f81b60f81c613bf89190615c55565b5b60f81b905092915050565b60008082600001511415613c1b5760009050613c4d565b60008083602001519050805160001a915060c060ff168260ff161015613c4657600092505050613c4d565b6001925050505b919050565b6000811415613c6057613cf8565b5b602060ff168110613cab5782518252602060ff1683613c809190615159565b9250602060ff1682613c929190615159565b9150602060ff1681613ca491906159bd565b9050613c61565b6000811415613cb957613cf8565b6000600182602060ff16613ccd91906159bd565b610100613cda9190615db9565b613ce491906159bd565b905080198451168184511681811785525050505b505050565b60006015826000015114613d1057600080fd5b613d19826138be565b9050919050565b60606000826000015167ffffffffffffffff811115613d4257613d41614d6c565b5b6040519080825280601f01601f191660200182016040528015613d745781602001600182028036833780820191505090505b509050600081511415613d8a5780915050613dab565b6000816020019050613da584602001518286600001516142b7565b81925050505b919050565b60006021826000015114613dc357600080fd5b60008060018460200151613dd79190615159565b9050805191508192505050919050565b600080600090506000613df986612eaf565b90506000815167ffffffffffffffff811115613e1857613e17614d6c565b5b6040519080825280601f01601f191660200182016040528015613e4a5781602001600182028036833780820191505090505b50905060008590505b825186613e609190615159565b811015613eeb576000878281518110613e7c57613e7b6151af565b5b602001015160f81c60f81b905080838884613e9791906159bd565b81518110613ea857613ea76151af565b5b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a905350508080613ee3906151de565b915050613e53565b50808051906020012082805190602001201415613f0b5781519250613f10565b600092505b8293505050509392505050565b60008060208351613f2e9190615bb5565b14613f6e576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401613f6590615e50565b60405180910390fd5b600060208351613f7e9190615be6565b9050806002613f8d9190615db9565b8510613fce576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401613fc590615ebc565b60405180910390fd5b6000808790506000602090505b8551811161407d578086015192506000600289613ff89190615bb5565b141561402e578183604051602001614011929190615edc565b60405160208183030381529060405280519060200120915061405a565b8282604051602001614041929190615edc565b6040516020818303038152906040528051906020012091505b6002886140679190615be6565b97506020816140769190615159565b9050613fdb565b508581149350505050949350505050565b600080825160001a9050608060ff168110156140ae576000915050614148565b60b860ff168110806140d3575060c060ff1681101580156140d2575060f860ff1681105b5b156140e2576001915050614148565b60c060ff1681101561411d5760018060b86140fd9190615f08565b60ff168261410b91906159bd565b6141159190615159565b915050614148565b60018060f861412c9190615f08565b60ff168261413a91906159bd565b6141449190615159565b9150505b919050565b6000808260000151141561416457600090506141d6565b600080614174846020015161408e565b84602001516141839190615159565b905060008460000151856020015161419b9190615159565b90505b808210156141cf576141af826141db565b826141ba9190615159565b915082806141c7906151de565b93505061419e565b8293505050505b919050565b6000806000835160001a9050608060ff168110156141fc57600191506142ad565b60b860ff1681101561422b576001608060ff168261421a91906159bd565b6142249190615159565b91506142ac565b60c060ff1681101561425b5760b78103600185019450806020036101000a855104600182018101935050506142ab565b60f860ff1681101561428a57600160c060ff168261427991906159bd565b6142839190615159565b91506142aa565b60f78103600185019450806020036101000a855104600182018101935050505b5b5b5b8192505050919050565b60008114156142c55761435d565b5b602060ff1681106143105782518252602060ff16836142e59190615159565b9250602060ff16826142f79190615159565b9150602060ff168161430991906159bd565b90506142c6565b600081141561431e5761435d565b6000600182602060ff1661433291906159bd565b61010061433f9190615db9565b61434991906159bd565b905080198451168184511681811785525050505b505050565b82805461436e906150f8565b90600052602060002090601f01602090048101928261439057600085556143d7565b82601f106143a957803560ff19168380011785556143d7565b828001600101855582156143d7579182015b828111156143d65782358255916020019190600101906143bb565b5b5090506143e491906144b8565b5090565b6040518060800160405280600073ffffffffffffffffffffffffffffffffffffffff168152602001600067ffffffffffffffff168152602001600015158152602001600062ffffff1681525090565b6040518060200160405280606081525090565b60405180606001604052806060815260200160608152602001600081525090565b604051806040016040528061447e61449e565b8152602001606081525090565b6040518060200160405280606081525090565b604051806040016040528060008152602001600081525090565b5b808211156144d15760008160009055506001016144b9565b5090565b6000604051905090565b600080fd5b600080fd5b60007fffffffff0000000000000000000000000000000000000000000000000000000082169050919050565b61451e816144e9565b811461452957600080fd5b50565b60008135905061453b81614515565b92915050565b600060208284031215614557576145566144df565b5b60006145658482850161452c565b91505092915050565b60008115159050919050565b6145838161456e565b82525050565b600060208201905061459e600083018461457a565b92915050565b600081519050919050565b600082825260208201905092915050565b60005b838110156145de5780820151818401526020810190506145c3565b838111156145ed576000848401525b50505050565b6000601f19601f8301169050919050565b600061460f826145a4565b61461981856145af565b93506146298185602086016145c0565b614632816145f3565b840191505092915050565b600060208201905081810360008301526146578184614604565b905092915050565b6000819050919050565b6146728161465f565b811461467d57600080fd5b50565b60008135905061468f81614669565b92915050565b6000602082840312156146ab576146aa6144df565b5b60006146b984828501614680565b91505092915050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b60006146ed826146c2565b9050919050565b6146fd816146e2565b82525050565b600060208201905061471860008301846146f4565b92915050565b614727816146e2565b811461473257600080fd5b50565b6000813590506147448161471e565b92915050565b60008060408385031215614761576147606144df565b5b600061476f85828601614735565b925050602061478085828601614680565b9150509250929050565b6000819050919050565b61479d8161478a565b82525050565b60006020820190506147b86000830184614794565b92915050565b6147c78161465f565b82525050565b60006020820190506147e260008301846147be565b92915050565b600080600060608486031215614801576148006144df565b5b600061480f86828701614735565b935050602061482086828701614735565b925050604061483186828701614680565b9150509250925092565b600080fd5b600080fd5b600080fd5b60008083601f8401126148605761485f61483b565b5b8235905067ffffffffffffffff81111561487d5761487c614840565b5b60208301915083600182028301111561489957614898614845565b5b9250929050565b600080602083850312156148b7576148b66144df565b5b600083013567ffffffffffffffff8111156148d5576148d46144e4565b5b6148e18582860161484a565b92509250509250929050565b60008083601f8401126149035761490261483b565b5b8235905067ffffffffffffffff8111156149205761491f614840565b5b60208301915083602082028301111561493c5761493b614845565b5b9250929050565b6000806020838503121561495a576149596144df565b5b600083013567ffffffffffffffff811115614978576149776144e4565b5b614984858286016148ed565b92509250509250929050565b600081519050919050565b600082825260208201905092915050565b6000819050602082019050919050565b6149c5816146e2565b82525050565b600067ffffffffffffffff82169050919050565b6149e8816149cb565b82525050565b6149f78161456e565b82525050565b600062ffffff82169050919050565b614a15816149fd565b82525050565b608082016000820151614a3160008501826149bc565b506020820151614a4460208501826149df565b506040820151614a5760408501826149ee565b506060820151614a6a6060850182614a0c565b50505050565b6000614a7c8383614a1b565b60808301905092915050565b6000602082019050919050565b6000614aa082614990565b614aaa818561499b565b9350614ab5836149ac565b8060005b83811015614ae6578151614acd8882614a70565b9750614ad883614a88565b925050600181019050614ab9565b5085935050505092915050565b60006020820190508181036000830152614b0d8184614a95565b905092915050565b614b1e8161478a565b8114614b2957600080fd5b50565b600081359050614b3b81614b15565b92915050565b600060208284031215614b5757614b566144df565b5b6000614b6584828501614b2c565b91505092915050565b600060208284031215614b8457614b836144df565b5b6000614b9284828501614735565b91505092915050565b600081519050919050565b600082825260208201905092915050565b6000819050602082019050919050565b614bd08161465f565b82525050565b6000614be28383614bc7565b60208301905092915050565b6000602082019050919050565b6000614c0682614b9b565b614c108185614ba6565b9350614c1b83614bb7565b8060005b83811015614c4c578151614c338882614bd6565b9750614c3e83614bee565b925050600181019050614c1f565b5085935050505092915050565b60006020820190508181036000830152614c738184614bfb565b905092915050565b600080600060608486031215614c9457614c936144df565b5b6000614ca286828701614735565b9350506020614cb386828701614680565b9250506040614cc486828701614680565b9150509250925092565b614cd78161456e565b8114614ce257600080fd5b50565b600081359050614cf481614cce565b92915050565b60008060408385031215614d1157614d106144df565b5b6000614d1f85828601614735565b9250506020614d3085828601614ce5565b9150509250929050565b600060208284031215614d5057614d4f6144df565b5b6000614d5e84828501614ce5565b91505092915050565b600080fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b614da4826145f3565b810181811067ffffffffffffffff82111715614dc357614dc2614d6c565b5b80604052505050565b6000614dd66144d5565b9050614de28282614d9b565b919050565b600067ffffffffffffffff821115614e0257614e01614d6c565b5b614e0b826145f3565b9050602081019050919050565b82818337600083830152505050565b6000614e3a614e3584614de7565b614dcc565b905082815260208101848484011115614e5657614e55614d67565b5b614e61848285614e18565b509392505050565b600082601f830112614e7e57614e7d61483b565b5b8135614e8e848260208601614e27565b91505092915050565b60008060008060808587031215614eb157614eb06144df565b5b6000614ebf87828801614735565b9450506020614ed087828801614735565b9350506040614ee187828801614680565b925050606085013567ffffffffffffffff811115614f0257614f016144e4565b5b614f0e87828801614e69565b91505092959194509250565b6000819050919050565b6000614f3f614f3a614f35846146c2565b614f1a565b6146c2565b9050919050565b6000614f5182614f24565b9050919050565b6000614f6382614f46565b9050919050565b614f7381614f58565b82525050565b6000602082019050614f8e6000830184614f6a565b92915050565b608082016000820151614faa60008501826149bc565b506020820151614fbd60208501826149df565b506040820151614fd060408501826149ee565b506060820151614fe36060850182614a0c565b50505050565b6000608082019050614ffe6000830184614f94565b92915050565b600061500f82614f46565b9050919050565b61501f81615004565b82525050565b600060208201905061503a6000830184615016565b92915050565b60008060408385031215615057576150566144df565b5b600061506585828601614735565b925050602061507685828601614735565b9150509250929050565b600060208284031215615096576150956144df565b5b600082013567ffffffffffffffff8111156150b4576150b36144e4565b5b6150c084828501614e69565b91505092915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b6000600282049050600182168061511057607f821691505b60208210811415615124576151236150c9565b5b50919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b60006151648261465f565b915061516f8361465f565b9250827fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff038211156151a4576151a361512a565b5b828201905092915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b60006151e98261465f565b91507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff82141561521c5761521b61512a565b5b600182019050919050565b600080fd5b60006152388385614ba6565b93507f07ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff83111561526b5761526a615227565b5b60208302925061527c838584614e18565b82840190509392505050565b600060408201905061529d60008301866146f4565b81810360208301526152b081848661522c565b9050949350505050565b600081519050919050565b600082825260208201905092915050565b60006152e1826152ba565b6152eb81856152c5565b93506152fb8185602086016145c0565b615304816145f3565b840191505092915050565b60006040820190506153246000830185614794565b818103602083015261533681846152d6565b90509392505050565b7f554e424f554e4400000000000000000000000000000000000000000000000000600082015250565b60006153756007836145af565b91506153808261533f565b602082019050919050565b600060608201905081810360008301526153a481615368565b90506153b360208301866146f4565b81810360408301526153c681848661522c565b9050949350505050565b7f424f554e44000000000000000000000000000000000000000000000000000000600082015250565b60006154066005836145af565b9150615411826153d0565b602082019050919050565b60006060820190508181036000830152615435816153f9565b905061544460208301866146f4565b818103604083015261545781848661522c565b9050949350505050565b600081905092915050565b6000615477826145a4565b6154818185615461565b93506154918185602086016145c0565b80840191505092915050565b60006154a9828561546c565b91506154b5828461546c565b91508190509392505050565b60006040820190506154d660008301856146f4565b81810360208301526154e88184614bfb565b90509392505050565b6000606082019050818103600083015261550a81615368565b905061551960208301856146f4565b818103604083015261552b8184614bfb565b90509392505050565b600060408201905061554960008301856146f4565b818103602083015261555b81846152d6565b90509392505050565b600060808201905061557960008301876146f4565b61558660208301866146f4565b61559360408301856147be565b81810360608301526155a581846152d6565b905095945050505050565b6000815190506155bf81614515565b92915050565b6000602082840312156155db576155da6144df565b5b60006155e9848285016155b0565b91505092915050565b6000819050919050565b61560d6156088261465f565b6155f2565b82525050565b600081905092915050565b6000615629826152ba565b6156338185615613565b93506156438185602086016145c0565b80840191505092915050565b600061565b82866155fc565b60208201915061566b828561561e565b915061567782846155fc565b602082019150819050949350505050565b7f4678526f6f7454756e6e656c3a20455849545f414c52454144595f50524f434560008201527f5353454400000000000000000000000000000000000000000000000000000000602082015250565b60006156e46024836145af565b91506156ef82615688565b604082019050919050565b60006020820190508181036000830152615713816156d7565b9050919050565b7f4678526f6f7454756e6e656c3a20494e56414c49445f46585f4348494c445f5460008201527f554e4e454c000000000000000000000000000000000000000000000000000000602082015250565b60006157766025836145af565b91506157818261571a565b604082019050919050565b600060208201905081810360008301526157a581615769565b9050919050565b7f4678526f6f7454756e6e656c3a20494e56414c49445f524543454950545f505260008201527f4f4f460000000000000000000000000000000000000000000000000000000000602082015250565b60006158086023836145af565b9150615813826157ac565b604082019050919050565b60006020820190508181036000830152615837816157fb565b9050919050565b7f4678526f6f7454756e6e656c3a20494e56414c49445f5349474e415455524500600082015250565b6000615874601f836145af565b915061587f8261583e565b602082019050919050565b600060208201905081810360008301526158a381615867565b9050919050565b60006158bd6158b884614de7565b614dcc565b9050828152602081018484840111156158d9576158d8614d67565b5b6158e48482856145c0565b509392505050565b600082601f8301126159015761590061483b565b5b81516159118482602086016158aa565b91505092915050565b6000602082840312156159305761592f6144df565b5b600082015167ffffffffffffffff81111561594e5761594d6144e4565b5b61595a848285016158ec565b91505092915050565b600061596e8261465f565b91506159798361465f565b9250817fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff04831182151516156159b2576159b161512a565b5b828202905092915050565b60006159c88261465f565b91506159d38361465f565b9250828210156159e6576159e561512a565b5b828203905092915050565b600081519050615a0081614b15565b92915050565b600081519050615a1581614669565b92915050565b600081519050615a2a8161471e565b92915050565b600080600080600060a08688031215615a4c57615a4b6144df565b5b6000615a5a888289016159f1565b9550506020615a6b88828901615a06565b9450506040615a7c88828901615a06565b9350506060615a8d88828901615a06565b9250506080615a9e88828901615a1b565b9150509295509295909350565b6000819050919050565b615ac6615ac18261478a565b615aab565b82525050565b6000615ad882876155fc565b602082019150615ae882866155fc565b602082019150615af88285615ab5565b602082019150615b088284615ab5565b60208201915081905095945050505050565b7f4678526f6f7454756e6e656c3a20494e56414c49445f48454144455200000000600082015250565b6000615b50601c836145af565b9150615b5b82615b1a565b602082019050919050565b60006020820190508181036000830152615b7f81615b43565b9050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b6000615bc08261465f565b9150615bcb8361465f565b925082615bdb57615bda615b86565b5b828206905092915050565b6000615bf18261465f565b9150615bfc8361465f565b925082615c0c57615c0b615b86565b5b828204905092915050565b600060ff82169050919050565b6000615c2f82615c17565b9150615c3a83615c17565b925082615c4a57615c49615b86565b5b828206905092915050565b6000615c6082615c17565b9150615c6b83615c17565b925082615c7b57615c7a615b86565b5b828204905092915050565b60008160011c9050919050565b6000808291508390505b6001851115615cdd57808604811115615cb957615cb861512a565b5b6001851615615cc85780820291505b8081029050615cd685615c86565b9450615c9d565b94509492505050565b600082615cf65760019050615db2565b81615d045760009050615db2565b8160018114615d1a5760028114615d2457615d53565b6001915050615db2565b60ff841115615d3657615d3561512a565b5b8360020a915084821115615d4d57615d4c61512a565b5b50615db2565b5060208310610133831016604e8410600b8410161715615d885782820a905083811115615d8357615d8261512a565b5b615db2565b615d958484846001615c93565b92509050818404811115615dac57615dab61512a565b5b81810290505b9392505050565b6000615dc48261465f565b9150615dcf8361465f565b9250615dfc7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8484615ce6565b905092915050565b7f496e76616c69642070726f6f66206c656e677468000000000000000000000000600082015250565b6000615e3a6014836145af565b9150615e4582615e04565b602082019050919050565b60006020820190508181036000830152615e6981615e2d565b9050919050565b7f4c65616620696e64657820697320746f6f206269670000000000000000000000600082015250565b6000615ea66015836145af565b9150615eb182615e70565b602082019050919050565b60006020820190508181036000830152615ed581615e99565b9050919050565b6000615ee88285615ab5565b602082019150615ef88284615ab5565b6020820191508190509392505050565b6000615f1382615c17565b9150615f1e83615c17565b925082821015615f3157615f3061512a565b5b82820390509291505056fea264697066735822122095f2c7aa8b702b7d9ee463edbb4ea1564d2f604ceea72920974767f0fefaf41b64736f6c634300080b0033000000000000000000000000fe5e5d361b2ad62c541bab87c45a0b9b018389a200000000000000000000000086e4dc95c7fbdbf52e33d563bbdb00823894c287000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000000a54656e746163756c6172000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005544e54434c000000000000000000000000000000000000000000000000000000

Deployed Bytecode

0x608060405234801561001057600080fd5b50600436106102275760003560e01c80638da5cb5b11610130578063b88d4fde116100b8578063ccea458f1161007c578063ccea458f1461069a578063de9b771f146106b8578063e985e9c5146106d6578063f2fde38b14610706578063f953cec71461072257610227565b8063b88d4fde146105e2578063c0857ba0146105fe578063c23dc68f1461061c578063c52141911461064c578063c87b56dd1461066a57610227565b806399a2557a116100ff57806399a2557a146105425780639b5719a614610572578063a22cb4651461058e578063aa22b45c146105aa578063aea4e49e146105c657610227565b80638da5cb5b146104b85780639094adb3146104d657806395d89b4114610506578063972c49281461052457610227565b806340c10f19116101b35780636352211e116101825780636352211e146103f057806370734dcf1461042057806370a082311461043c5780637bef587c1461046c5780638462151c1461048857610227565b806340c10f191461035857806342842e0e146103745780635bbb217714610390578063607f2d42146103c057610227565b80630e387de6116101fa5780630e387de6146102c657806318160ddd146102e45780631edc2e721461030257806323b872dd1461032057806330176e131461033c57610227565b806301ffc9a71461022c57806306fdde031461025c578063081812fc1461027a578063095ea7b3146102aa575b600080fd5b61024660048036038101906102419190614541565b61073e565b6040516102539190614589565b60405180910390f35b6102646107d0565b604051610271919061463d565b60405180910390f35b610294600480360381019061028f9190614695565b610862565b6040516102a19190614703565b60405180910390f35b6102c460048036038101906102bf919061474a565b6108e1565b005b6102ce610a25565b6040516102db91906147a3565b60405180910390f35b6102ec610a4c565b6040516102f991906147cd565b60405180910390f35b61030a610a63565b6040516103179190614589565b60405180910390f35b61033a600480360381019061033591906147e8565b610a76565b005b610356600480360381019061035191906148a0565b610d9b565b005b610372600480360381019061036d919061474a565b610e38565b005b61038e600480360381019061038991906147e8565b610f1b565b005b6103aa60048036038101906103a59190614943565b610f3b565b6040516103b79190614af3565b60405180910390f35b6103da60048036038101906103d59190614b41565b610ffe565b6040516103e79190614589565b60405180910390f35b61040a60048036038101906104059190614695565b61101e565b6040516104179190614703565b60405180910390f35b61043a60048036038101906104359190614943565b611030565b005b61045660048036038101906104519190614b6e565b6112ea565b60405161046391906147cd565b60405180910390f35b61048660048036038101906104819190614943565b6113a3565b005b6104a2600480360381019061049d9190614b6e565b61169c565b6040516104af9190614c59565b60405180910390f35b6104c06117e6565b6040516104cd9190614703565b60405180910390f35b6104f060048036038101906104eb9190614695565b61180c565b6040516104fd9190614589565b60405180910390f35b61050e61182c565b60405161051b919061463d565b60405180910390f35b61052c6118be565b6040516105399190614703565b60405180910390f35b61055c60048036038101906105579190614c7b565b6118e4565b6040516105699190614c59565b60405180910390f35b61058c60048036038101906105879190614b6e565b611af8565b005b6105a860048036038101906105a39190614cfa565b611bc3565b005b6105c460048036038101906105bf9190614d3a565b611cce565b005b6105e060048036038101906105db9190614b6e565b611d72565b005b6105fc60048036038101906105f79190614e97565b611e3d565b005b610606611eb0565b6040516106139190614f79565b60405180910390f35b61063660048036038101906106319190614695565b611ed6565b6040516106439190614fe9565b60405180910390f35b610654611f40565b60405161066191906147a3565b60405180910390f35b610684600480360381019061067f9190614695565b611f64565b604051610691919061463d565b60405180910390f35b6106a2612003565b6040516106af91906147a3565b60405180910390f35b6106c0612027565b6040516106cd9190615025565b60405180910390f35b6106f060048036038101906106eb9190615040565b61204d565b6040516106fd9190614589565b60405180910390f35b610720600480360381019061071b9190614b6e565b6120e1565b005b61073c60048036038101906107379190615080565b6121ac565b005b60006301ffc9a760e01b827bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916148061079957506380ac58cd60e01b827bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916145b806107c95750635b5e139f60e01b827bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916145b9050919050565b6060600280546107df906150f8565b80601f016020809104026020016040519081016040528092919081815260200182805461080b906150f8565b80156108585780601f1061082d57610100808354040283529160200191610858565b820191906000526020600020905b81548152906001019060200180831161083b57829003601f168201915b5050505050905090565b600061086d826121c6565b6108a3576040517fcf4700e400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6006600083815260200190815260200160002060000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff169050919050565b60006108ec8261101e565b90508073ffffffffffffffffffffffffffffffffffffffff1661090d612225565b73ffffffffffffffffffffffffffffffffffffffff16146109705761093981610934612225565b61204d565b61096f576040517fcfb3b94200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5b826006600084815260200190815260200160002060000160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550818373ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92560405160405180910390a4505050565b7f8c5261668696ce22758910d05bab8f186d6eb247ceac2af2e82c7dc17669b03660001b81565b6000610a5661222d565b6001546000540303905090565b600f60009054906101000a900460ff1681565b6000610a8182612232565b90508373ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff1614610ae8576040517fa114810000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600080610af484612300565b91509150610b0a8187610b05612225565b612327565b610b5657610b1f86610b1a612225565b61204d565b610b55576040517f59c896be00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5b600073ffffffffffffffffffffffffffffffffffffffff168573ffffffffffffffffffffffffffffffffffffffff161415610bbd576040517fea553b3400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b610bca868686600161236b565b8015610bd557600082555b600560008773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600081546001900391905081905550600560008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000815460010191905081905550610ca385610c7f8888876124de565b7c020000000000000000000000000000000000000000000000000000000017612506565b600460008681526020019081526020016000208190555060007c020000000000000000000000000000000000000000000000000000000084161415610d2b576000600185019050600060046000838152602001908152602001600020541415610d29576000548114610d28578360046000838152602001908152602001600020819055505b5b505b838573ffffffffffffffffffffffffffffffffffffffff168773ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef60405160405180910390a4610d938686866001612531565b505050505050565b600860009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614610e22576040517fbfcafd3700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8181600e9190610e33929190614362565b505050565b600d60009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614610ebf576040517ff90715e100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6115b481610ecb612537565b610ed59190615159565b1115610f0d576040517fd05cb60900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b610f17828261254a565b5050565b610f3683838360405180602001604052806000815250611e3d565b505050565b6060600083839050905060008167ffffffffffffffff811115610f6157610f60614d6c565b5b604051908082528060200260200182016040528015610f9a57816020015b610f876143e8565b815260200190600190039081610f7f5790505b50905060005b828114610ff257610fc9868683818110610fbd57610fbc6151af565b5b90506020020135611ed6565b828281518110610fdc57610fdb6151af565b5b6020026020010181905250806001019050610fa0565b50809250505092915050565b600c6020528060005260406000206000915054906101000a900460ff1681565b600061102982612232565b9050919050565b600080339050803b91503273ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16141580611076575060008214155b156110ad576040517f5a156d6000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60005b84849050811015611235573373ffffffffffffffffffffffffffffffffffffffff166110f48686848181106110e8576110e76151af565b5b9050602002013561101e565b73ffffffffffffffffffffffffffffffffffffffff1614611141576040517feea91ff800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600015156010600087878581811061115c5761115b6151af565b5b90506020020135815260200190815260200160002060009054906101000a900460ff16151514156111dd5784848281811061119a576111996151af565b5b905060200201356040517fd0078a0a0000000000000000000000000000000000000000000000000000000081526004016111d491906147cd565b60405180910390fd5b6000601060008787858181106111f6576111f56151af565b5b90506020020135815260200190815260200160002060006101000a81548160ff021916908315150217905550808061122d906151de565b9150506110b0565b5060007f46ea84878955907861000722f4dd80a932935b7b3669b3a736e54d8ed9dca5c633868660405160200161126e93929190615288565b60405160208183030381529060405260405160200161128e92919061530f565b60405160208183030381529060405290507f137b854e7e99f3421d5319954d65a2fb8cd25a72297f898f1e662f0843b727d53386866040516112d29392919061538b565b60405180910390a16112e381612707565b5050505050565b60008073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff161415611352576040517f8f4eb60400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b67ffffffffffffffff600560008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054169050919050565b600f60009054906101000a900460ff166113e9576040517f78d9002800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600080339050803b91503273ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614158061142f575060008214155b15611466576040517f5a156d6000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60005b848490508110156115e7573373ffffffffffffffffffffffffffffffffffffffff166114ad8686848181106114a1576114a06151af565b5b9050602002013561101e565b73ffffffffffffffffffffffffffffffffffffffff16146114fa576040517feea91ff800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60106000868684818110611511576115106151af565b5b90506020020135815260200190815260200160002060009054906101000a900460ff161561158f5784848281811061154c5761154b6151af565b5b905060200201356040517f3de74d5b00000000000000000000000000000000000000000000000000000000815260040161158691906147cd565b60405180910390fd5b6001601060008787858181106115a8576115a76151af565b5b90506020020135815260200190815260200160002060006101000a81548160ff02191690831515021790555080806115df906151de565b915050611469565b5060007f49a368f7e595a507e0eaa27f07cfde74ff475d8dafcdf3829d5d9b4a75f28a8433868660405160200161162093929190615288565b60405160208183030381529060405260405160200161164092919061530f565b60405160208183030381529060405290507f137b854e7e99f3421d5319954d65a2fb8cd25a72297f898f1e662f0843b727d53386866040516116849392919061541c565b60405180910390a161169581612707565b5050505050565b606060008060006116ac856112ea565b905060008167ffffffffffffffff8111156116ca576116c9614d6c565b5b6040519080825280602002602001820160405280156116f85781602001602082028036833780820191505090505b5090506117036143e8565b600061170d61222d565b90505b8386146117d857611720816127bb565b9150816040015115611731576117cd565b600073ffffffffffffffffffffffffffffffffffffffff16826000015173ffffffffffffffffffffffffffffffffffffffff161461177157816000015194505b8773ffffffffffffffffffffffffffffffffffffffff168573ffffffffffffffffffffffffffffffffffffffff1614156117cc57808387806001019850815181106117bf576117be6151af565b5b6020026020010181815250505b5b806001019050611710565b508195505050505050919050565b600860009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b60106020528060005260406000206000915054906101000a900460ff1681565b60606003805461183b906150f8565b80601f0160208091040260200160405190810160405280929190818152602001828054611867906150f8565b80156118b45780601f10611889576101008083540402835291602001916118b4565b820191906000526020600020905b81548152906001019060200180831161189757829003601f168201915b5050505050905090565b600b60009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b606081831061191f576040517f32c1995a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60008061192a6127e6565b905061193461222d565b8510156119465761194361222d565b94505b80841115611952578093505b600061195d876112ea565b90508486101561198057600086860390508181101561197a578091505b50611985565b600090505b60008167ffffffffffffffff8111156119a1576119a0614d6c565b5b6040519080825280602002602001820160405280156119cf5781602001602082028036833780820191505090505b50905060008214156119e75780945050505050611af1565b60006119f288611ed6565b905060008160400151611a0757816000015190505b60008990505b888114158015611a1d5750848714155b15611ae357611a2b816127bb565b9250826040015115611a3c57611ad8565b600073ffffffffffffffffffffffffffffffffffffffff16836000015173ffffffffffffffffffffffffffffffffffffffff1614611a7c57826000015191505b8a73ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff161415611ad75780848880600101995081518110611aca57611ac96151af565b5b6020026020010181815250505b5b806001019050611a0d565b508583528296505050505050505b9392505050565b600860009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614611b7f576040517fbfcafd3700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b80600d60006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050565b8060076000611bd0612225565b73ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548160ff0219169083151502179055508173ffffffffffffffffffffffffffffffffffffffff16611c7d612225565b73ffffffffffffffffffffffffffffffffffffffff167f17307eab39ab6107e8899845ad3d59bd9653f200f220920489ca2b5937696c3183604051611cc29190614589565b60405180910390a35050565b600860009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614611d55576040517fbfcafd3700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b80600f60006101000a81548160ff02191690831515021790555050565b600860009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614611df9576040517fbfcafd3700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b80600b60006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050565b611e48848484610a76565b60008373ffffffffffffffffffffffffffffffffffffffff163b14611eaa57611e73848484846127ef565b611ea9576040517fd1a57ed600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5b50505050565b600a60009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b611ede6143e8565b611ee66143e8565b611eee61222d565b831080611f025750611efe6127e6565b8310155b15611f105780915050611f3b565b611f19836127bb565b9050806040015115611f2e5780915050611f3b565b611f3783612940565b9150505b919050565b7f49a368f7e595a507e0eaa27f07cfde74ff475d8dafcdf3829d5d9b4a75f28a8481565b6060611f6f826121c6565b611fa5576040517fa14c4b5000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000611faf612960565b9050600081511415611fd05760405180602001604052806000815250611ffb565b80611fda846129f2565b604051602001611feb92919061549d565b6040516020818303038152906040525b915050919050565b7f46ea84878955907861000722f4dd80a932935b7b3669b3a736e54d8ed9dca5c681565b600960009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b6000600760008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff16905092915050565b600860009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614612168576040517fbfcafd3700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b80600860006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050565b60006121b782612a42565b90506121c281612d47565b5050565b6000816121d161222d565b111580156121e0575060005482105b801561221e575060007c0100000000000000000000000000000000000000000000000000000000600460008581526020019081526020016000205416145b9050919050565b600033905090565b600090565b6000808290508061224161222d565b116122c9576000548110156122c85760006004600083815260200190815260200160002054905060007c0100000000000000000000000000000000000000000000000000000000821614156122c6575b60008114156122bc576004600083600190039350838152602001908152602001600020549050612291565b80925050506122fb565b505b5b6040517fdf2d9b4200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b919050565b60008060006006600085815260200190815260200160002090508092508254915050915091565b600073ffffffffffffffffffffffffffffffffffffffff8316925073ffffffffffffffffffffffffffffffffffffffff821691508382148383141790509392505050565b6010600083815260200190815260200160002060009054906101000a900460ff16156124d85760006010600084815260200190815260200160002060006101000a81548160ff0219169083151502179055506000600167ffffffffffffffff8111156123da576123d9614d6c565b5b6040519080825280602002602001820160405280156124085781602001602082028036833780820191505090505b50905082816000815181106124205761241f6151af565b5b60200260200101818152505060007f46ea84878955907861000722f4dd80a932935b7b3669b3a736e54d8ed9dca5c686836040516020016124629291906154c1565b60405160208183030381529060405260405160200161248292919061530f565b60405160208183030381529060405290507f137b854e7e99f3421d5319954d65a2fb8cd25a72297f898f1e662f0843b727d586836040516124c49291906154f1565b60405180910390a16124d581612707565b50505b50505050565b60008060e883901c905060e86124f5868684612d4a565b62ffffff16901b9150509392505050565b600073ffffffffffffffffffffffffffffffffffffffff83169250814260a01b178317905092915050565b50505050565b600061254161222d565b60005403905090565b600080549050600082141561258b576040517fb562e8dd00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b612598600084838561236b565b600160406001901b178202600560008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000828254019250508190555061260f8361260060008660006124de565b61260985612d53565b17612506565b6004600083815260200190815260200160002081905550600080838301905073ffffffffffffffffffffffffffffffffffffffff85169150828260007fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef600080a4600183015b8181146126b057808360007fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef600080a4600181019050612675565b5060008214156126ec576040517f2e07630000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8060008190555050506127026000848385612531565b505050565b600960009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663b4720477600b60009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16836040518363ffffffff1660e01b8152600401612786929190615534565b600060405180830381600087803b1580156127a057600080fd5b505af11580156127b4573d6000803e3d6000fd5b5050505050565b6127c36143e8565b6127df6004600084815260200190815260200160002054612d63565b9050919050565b60008054905090565b60008373ffffffffffffffffffffffffffffffffffffffff1663150b7a02612815612225565b8786866040518563ffffffff1660e01b81526004016128379493929190615564565b6020604051808303816000875af192505050801561287357506040513d601f19601f8201168201806040525081019061287091906155c5565b60015b6128ed573d80600081146128a3576040519150601f19603f3d011682016040523d82523d6000602084013e6128a8565b606091505b506000815114156128e5576040517fd1a57ed600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b805181602001fd5b63150b7a0260e01b7bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916817bffffffffffffffffffffffffffffffffffffffffffffffffffffffff191614915050949350505050565b6129486143e8565b61295961295483612232565b612d63565b9050919050565b6060600e805461296f906150f8565b80601f016020809104026020016040519081016040528092919081815260200182805461299b906150f8565b80156129e85780601f106129bd576101008083540402835291602001916129e8565b820191906000526020600020905b8154815290600101906020018083116129cb57829003601f168201915b5050505050905090565b606060806040510190508060405280825b600115612a2e57600183039250600a81066030018353600a8104905080612a2957612a2e565b612a03565b508181036020830392508083525050919050565b60606000612a4f83612e19565b90506000612a5c82612e4d565b90506000612a6983612e7e565b9050600081612a7784612eaf565b612a80866130c5565b604051602001612a929392919061564f565b60405160208183030381529060405280519060200120905060001515600c600083815260200190815260200160002060009054906101000a900460ff16151514612b11576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401612b08906156fa565b60405180910390fd5b6001600c600083815260200190815260200160002060006101000a81548160ff0219169083151502179055506000612b48856130f6565b90506000612b558261321b565b9050612b6081613292565b73ffffffffffffffffffffffffffffffffffffffff16600b60009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1614612bef576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401612be69061578c565b60405180910390fd5b6000612bfa876132c3565b9050612c18612c08846132f7565b87612c128a613305565b84613336565b612c57576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401612c4e9061581e565b60405180910390fd5b612c8585612c648961363c565b612c6d8a61366d565b84612c778c6136a1565b612c808d6136d2565b613703565b506000612c918361384a565b90507f8c5261668696ce22758910d05bab8f186d6eb247ceac2af2e82c7dc17669b03660001b612cd3612cce60008461388f90919063ffffffff16565b6138be565b60001b14612d16576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401612d0d9061588a565b60405180910390fd5b6000612d2184613941565b806020019051810190612d34919061591a565b9050809950505050505050505050919050565b50565b60009392505050565b60006001821460e11b9050919050565b612d6b6143e8565b81816000019073ffffffffffffffffffffffffffffffffffffffff16908173ffffffffffffffffffffffffffffffffffffffff168152505060a082901c816020019067ffffffffffffffff16908167ffffffffffffffff168152505060007c01000000000000000000000000000000000000000000000000000000008316141581604001901515908115158152505060e882901c816060019062ffffff16908162ffffff1681525050919050565b612e21614437565b6000612e34612e2f84613972565b6139a0565b9050604051806020016040528082815250915050919050565b6060612e778260000151600881518110612e6a57612e696151af565b5b6020026020010151613ab5565b9050919050565b6000612ea88260000151600281518110612e9b57612e9a6151af565b5b60200260200101516138be565b9050919050565b606060006040518060200160405280600081525090506000835111156130bc57600080612edd600086613b6c565b60f81c905060018160ff161480612ef7575060038160ff16145b15612fc157600160028651612f0c9190615963565b612f1691906159bd565b67ffffffffffffffff811115612f2f57612f2e614d6c565b5b6040519080825280601f01601f191660200182016040528015612f615781602001600182028036833780820191505090505b5092506000612f71600187613b6c565b90508084600081518110612f8857612f876151af565b5b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a905350600192505061302d565b6002808651612fd09190615963565b612fda91906159bd565b67ffffffffffffffff811115612ff357612ff2614d6c565b5b6040519080825280601f01601f1916602001820160405280156130255781602001600182028036833780820191505090505b509250600091505b60008260ff1690505b83518110156130b85761306360028460ff168361305391906159bd565b61305d9190615159565b87613b6c565b848281518110613076576130756151af565b5b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a90535080806130b0906151de565b915050613036565b5050505b80915050919050565b60006130ef82600001516009815181106130e2576130e16151af565b5b60200260200101516138be565b9050919050565b6130fe61444a565b6131268260000151600681518110613119576131186151af565b5b6020026020010151613ab5565b8160200181905250600061313d8260200151613972565b905061314881613c04565b1561316357613156816139a0565b8260000181905250613203565b60008260200151905060006001825161317c91906159bd565b67ffffffffffffffff81111561319557613194614d6c565b5b6040519080825280601f01601f1916602001820160405280156131c75781602001600182028036833780820191505090505b5090506000808360210191508260200190506131e582828551613c52565b6131f66131f184613972565b6139a0565b8660000181905250505050505b61320c836130c5565b82604001818152505050919050565b61322361446b565b600061324d83600001516003815181106132405761323f6151af565b5b60200260200101516139a0565b836040015181518110613263576132626151af565b5b602002602001015190506040518060400160405280828152602001613287836139a0565b815250915050919050565b60006132bc82602001516000815181106132af576132ae6151af565b5b6020026020010151613cfd565b9050919050565b60006132ed82600001516005815181106132e0576132df6151af565b5b60200260200101516138be565b60001b9050919050565b606081602001519050919050565b606061332f8260000151600781518110613322576133216151af565b5b6020026020010151613ab5565b9050919050565b60008061334284613972565b9050600061334f826139a0565b905060608060008690506000806133658b612eaf565b9050600081511415613381576000975050505050505050613634565b60005b865181101561362b5781518311156133a757600098505050505050505050613634565b6133ca8782815181106133bd576133bc6151af565b5b6020026020010151613d20565b9550858051906020012084146133eb57600098505050505050505050613634565b61340e878281518110613401576134006151af565b5b60200260200101516139a0565b945060118551141561350157815183141561347e578c8051906020012061344f86601081518110613442576134416151af565b5b6020026020010151613ab5565b80519060200120141561346d57600198505050505050505050613634565b600098505050505050505050613634565b6000828481518110613493576134926151af565b5b602001015160f81c60f81b60f81c905060108160ff1611156134c15760009950505050505050505050613634565b6134e7868260ff16815181106134da576134d96151af565b5b6020026020010151613db0565b60001b94506001846134f99190615159565b935050613618565b60028551141561360657600061353b61353487600081518110613527576135266151af565b5b6020026020010151613ab5565b8486613de7565b90508251818561354b9190615159565b14156135ae578d8051906020012061357d876001815181106135705761356f6151af565b5b6020026020010151613ab5565b80519060200120141561359c5760019950505050505050505050613634565b60009950505050505050505050613634565b60008114156135c95760009950505050505050505050613634565b80846135d59190615159565b93506135fb866001815181106135ee576135ed6151af565b5b6020026020010151613db0565b60001b945050613617565b600098505050505050505050613634565b5b8080613623906151de565b915050613384565b50505050505050505b949350505050565b60006136668260000151600381518110613659576136586151af565b5b60200260200101516138be565b9050919050565b6000613697826000015160048151811061368a576136896151af565b5b60200260200101516138be565b60001b9050919050565b60006136cb82600001516000815181106136be576136bd6151af565b5b60200260200101516138be565b9050919050565b60606136fc82600001516001815181106136ef576136ee6151af565b5b6020026020010151613ab5565b9050919050565b600080600080600a60009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166341539d4a876040518263ffffffff1660e01b815260040161376491906147cd565b60a060405180830381865afa158015613781573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906137a59190615a30565b50935050925092506137fb828b6137bc91906159bd565b84878d8d8d8d6040516020016137d59493929190615acc565b60405160208183030381529060405280519060200120613f1d909392919063ffffffff16565b61383a576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161383190615b66565b60405180910390fd5b8093505050509695505050505050565b61385261448b565b60405180602001604052806138858460200151600181518110613878576138776151af565b5b60200260200101516139a0565b8152509050919050565b61389761449e565b826000015182815181106138ae576138ad6151af565b5b6020026020010151905092915050565b60008082600001511180156138d857506021826000015111155b6138e157600080fd5b60006138f0836020015161408e565b9050600081846000015161390491906159bd565b90506000808386602001516139199190615159565b905080519150602083101561393557826020036101000a820491505b81945050505050919050565b606061396b826020015160028151811061395e5761395d6151af565b5b6020026020010151613ab5565b9050919050565b61397a61449e565b600060208301905060405180604001604052808451815260200182815250915050919050565b60606139ab82613c04565b6139b457600080fd5b60006139bf8361414d565b905060008167ffffffffffffffff8111156139dd576139dc614d6c565b5b604051908082528060200260200182016040528015613a1657816020015b613a0361449e565b8152602001906001900390816139fb5790505b5090506000613a28856020015161408e565b8560200151613a379190615159565b9050600080600090505b84811015613aa857613a52836141db565b9150604051806040016040528083815260200184815250848281518110613a7c57613a7b6151af565b5b60200260200101819052508183613a939190615159565b92508080613aa0906151de565b915050613a41565b5082945050505050919050565b60606000826000015111613ac857600080fd5b6000613ad7836020015161408e565b90506000818460000151613aeb91906159bd565b905060008167ffffffffffffffff811115613b0957613b08614d6c565b5b6040519080825280601f01601f191660200182016040528015613b3b5781602001600182028036833780820191505090505b5090506000816020019050613b60848760200151613b599190615159565b82856142b7565b81945050505050919050565b600080600284613b7c9190615bb5565b14613bbf57601082600285613b919190615be6565b81518110613ba257613ba16151af565b5b602001015160f81c60f81b60f81c613bba9190615c24565b613bf9565b601082600285613bcf9190615be6565b81518110613be057613bdf6151af565b5b602001015160f81c60f81b60f81c613bf89190615c55565b5b60f81b905092915050565b60008082600001511415613c1b5760009050613c4d565b60008083602001519050805160001a915060c060ff168260ff161015613c4657600092505050613c4d565b6001925050505b919050565b6000811415613c6057613cf8565b5b602060ff168110613cab5782518252602060ff1683613c809190615159565b9250602060ff1682613c929190615159565b9150602060ff1681613ca491906159bd565b9050613c61565b6000811415613cb957613cf8565b6000600182602060ff16613ccd91906159bd565b610100613cda9190615db9565b613ce491906159bd565b905080198451168184511681811785525050505b505050565b60006015826000015114613d1057600080fd5b613d19826138be565b9050919050565b60606000826000015167ffffffffffffffff811115613d4257613d41614d6c565b5b6040519080825280601f01601f191660200182016040528015613d745781602001600182028036833780820191505090505b509050600081511415613d8a5780915050613dab565b6000816020019050613da584602001518286600001516142b7565b81925050505b919050565b60006021826000015114613dc357600080fd5b60008060018460200151613dd79190615159565b9050805191508192505050919050565b600080600090506000613df986612eaf565b90506000815167ffffffffffffffff811115613e1857613e17614d6c565b5b6040519080825280601f01601f191660200182016040528015613e4a5781602001600182028036833780820191505090505b50905060008590505b825186613e609190615159565b811015613eeb576000878281518110613e7c57613e7b6151af565b5b602001015160f81c60f81b905080838884613e9791906159bd565b81518110613ea857613ea76151af565b5b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a905350508080613ee3906151de565b915050613e53565b50808051906020012082805190602001201415613f0b5781519250613f10565b600092505b8293505050509392505050565b60008060208351613f2e9190615bb5565b14613f6e576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401613f6590615e50565b60405180910390fd5b600060208351613f7e9190615be6565b9050806002613f8d9190615db9565b8510613fce576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401613fc590615ebc565b60405180910390fd5b6000808790506000602090505b8551811161407d578086015192506000600289613ff89190615bb5565b141561402e578183604051602001614011929190615edc565b60405160208183030381529060405280519060200120915061405a565b8282604051602001614041929190615edc565b6040516020818303038152906040528051906020012091505b6002886140679190615be6565b97506020816140769190615159565b9050613fdb565b508581149350505050949350505050565b600080825160001a9050608060ff168110156140ae576000915050614148565b60b860ff168110806140d3575060c060ff1681101580156140d2575060f860ff1681105b5b156140e2576001915050614148565b60c060ff1681101561411d5760018060b86140fd9190615f08565b60ff168261410b91906159bd565b6141159190615159565b915050614148565b60018060f861412c9190615f08565b60ff168261413a91906159bd565b6141449190615159565b9150505b919050565b6000808260000151141561416457600090506141d6565b600080614174846020015161408e565b84602001516141839190615159565b905060008460000151856020015161419b9190615159565b90505b808210156141cf576141af826141db565b826141ba9190615159565b915082806141c7906151de565b93505061419e565b8293505050505b919050565b6000806000835160001a9050608060ff168110156141fc57600191506142ad565b60b860ff1681101561422b576001608060ff168261421a91906159bd565b6142249190615159565b91506142ac565b60c060ff1681101561425b5760b78103600185019450806020036101000a855104600182018101935050506142ab565b60f860ff1681101561428a57600160c060ff168261427991906159bd565b6142839190615159565b91506142aa565b60f78103600185019450806020036101000a855104600182018101935050505b5b5b5b8192505050919050565b60008114156142c55761435d565b5b602060ff1681106143105782518252602060ff16836142e59190615159565b9250602060ff16826142f79190615159565b9150602060ff168161430991906159bd565b90506142c6565b600081141561431e5761435d565b6000600182602060ff1661433291906159bd565b61010061433f9190615db9565b61434991906159bd565b905080198451168184511681811785525050505b505050565b82805461436e906150f8565b90600052602060002090601f01602090048101928261439057600085556143d7565b82601f106143a957803560ff19168380011785556143d7565b828001600101855582156143d7579182015b828111156143d65782358255916020019190600101906143bb565b5b5090506143e491906144b8565b5090565b6040518060800160405280600073ffffffffffffffffffffffffffffffffffffffff168152602001600067ffffffffffffffff168152602001600015158152602001600062ffffff1681525090565b6040518060200160405280606081525090565b60405180606001604052806060815260200160608152602001600081525090565b604051806040016040528061447e61449e565b8152602001606081525090565b6040518060200160405280606081525090565b604051806040016040528060008152602001600081525090565b5b808211156144d15760008160009055506001016144b9565b5090565b6000604051905090565b600080fd5b600080fd5b60007fffffffff0000000000000000000000000000000000000000000000000000000082169050919050565b61451e816144e9565b811461452957600080fd5b50565b60008135905061453b81614515565b92915050565b600060208284031215614557576145566144df565b5b60006145658482850161452c565b91505092915050565b60008115159050919050565b6145838161456e565b82525050565b600060208201905061459e600083018461457a565b92915050565b600081519050919050565b600082825260208201905092915050565b60005b838110156145de5780820151818401526020810190506145c3565b838111156145ed576000848401525b50505050565b6000601f19601f8301169050919050565b600061460f826145a4565b61461981856145af565b93506146298185602086016145c0565b614632816145f3565b840191505092915050565b600060208201905081810360008301526146578184614604565b905092915050565b6000819050919050565b6146728161465f565b811461467d57600080fd5b50565b60008135905061468f81614669565b92915050565b6000602082840312156146ab576146aa6144df565b5b60006146b984828501614680565b91505092915050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b60006146ed826146c2565b9050919050565b6146fd816146e2565b82525050565b600060208201905061471860008301846146f4565b92915050565b614727816146e2565b811461473257600080fd5b50565b6000813590506147448161471e565b92915050565b60008060408385031215614761576147606144df565b5b600061476f85828601614735565b925050602061478085828601614680565b9150509250929050565b6000819050919050565b61479d8161478a565b82525050565b60006020820190506147b86000830184614794565b92915050565b6147c78161465f565b82525050565b60006020820190506147e260008301846147be565b92915050565b600080600060608486031215614801576148006144df565b5b600061480f86828701614735565b935050602061482086828701614735565b925050604061483186828701614680565b9150509250925092565b600080fd5b600080fd5b600080fd5b60008083601f8401126148605761485f61483b565b5b8235905067ffffffffffffffff81111561487d5761487c614840565b5b60208301915083600182028301111561489957614898614845565b5b9250929050565b600080602083850312156148b7576148b66144df565b5b600083013567ffffffffffffffff8111156148d5576148d46144e4565b5b6148e18582860161484a565b92509250509250929050565b60008083601f8401126149035761490261483b565b5b8235905067ffffffffffffffff8111156149205761491f614840565b5b60208301915083602082028301111561493c5761493b614845565b5b9250929050565b6000806020838503121561495a576149596144df565b5b600083013567ffffffffffffffff811115614978576149776144e4565b5b614984858286016148ed565b92509250509250929050565b600081519050919050565b600082825260208201905092915050565b6000819050602082019050919050565b6149c5816146e2565b82525050565b600067ffffffffffffffff82169050919050565b6149e8816149cb565b82525050565b6149f78161456e565b82525050565b600062ffffff82169050919050565b614a15816149fd565b82525050565b608082016000820151614a3160008501826149bc565b506020820151614a4460208501826149df565b506040820151614a5760408501826149ee565b506060820151614a6a6060850182614a0c565b50505050565b6000614a7c8383614a1b565b60808301905092915050565b6000602082019050919050565b6000614aa082614990565b614aaa818561499b565b9350614ab5836149ac565b8060005b83811015614ae6578151614acd8882614a70565b9750614ad883614a88565b925050600181019050614ab9565b5085935050505092915050565b60006020820190508181036000830152614b0d8184614a95565b905092915050565b614b1e8161478a565b8114614b2957600080fd5b50565b600081359050614b3b81614b15565b92915050565b600060208284031215614b5757614b566144df565b5b6000614b6584828501614b2c565b91505092915050565b600060208284031215614b8457614b836144df565b5b6000614b9284828501614735565b91505092915050565b600081519050919050565b600082825260208201905092915050565b6000819050602082019050919050565b614bd08161465f565b82525050565b6000614be28383614bc7565b60208301905092915050565b6000602082019050919050565b6000614c0682614b9b565b614c108185614ba6565b9350614c1b83614bb7565b8060005b83811015614c4c578151614c338882614bd6565b9750614c3e83614bee565b925050600181019050614c1f565b5085935050505092915050565b60006020820190508181036000830152614c738184614bfb565b905092915050565b600080600060608486031215614c9457614c936144df565b5b6000614ca286828701614735565b9350506020614cb386828701614680565b9250506040614cc486828701614680565b9150509250925092565b614cd78161456e565b8114614ce257600080fd5b50565b600081359050614cf481614cce565b92915050565b60008060408385031215614d1157614d106144df565b5b6000614d1f85828601614735565b9250506020614d3085828601614ce5565b9150509250929050565b600060208284031215614d5057614d4f6144df565b5b6000614d5e84828501614ce5565b91505092915050565b600080fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b614da4826145f3565b810181811067ffffffffffffffff82111715614dc357614dc2614d6c565b5b80604052505050565b6000614dd66144d5565b9050614de28282614d9b565b919050565b600067ffffffffffffffff821115614e0257614e01614d6c565b5b614e0b826145f3565b9050602081019050919050565b82818337600083830152505050565b6000614e3a614e3584614de7565b614dcc565b905082815260208101848484011115614e5657614e55614d67565b5b614e61848285614e18565b509392505050565b600082601f830112614e7e57614e7d61483b565b5b8135614e8e848260208601614e27565b91505092915050565b60008060008060808587031215614eb157614eb06144df565b5b6000614ebf87828801614735565b9450506020614ed087828801614735565b9350506040614ee187828801614680565b925050606085013567ffffffffffffffff811115614f0257614f016144e4565b5b614f0e87828801614e69565b91505092959194509250565b6000819050919050565b6000614f3f614f3a614f35846146c2565b614f1a565b6146c2565b9050919050565b6000614f5182614f24565b9050919050565b6000614f6382614f46565b9050919050565b614f7381614f58565b82525050565b6000602082019050614f8e6000830184614f6a565b92915050565b608082016000820151614faa60008501826149bc565b506020820151614fbd60208501826149df565b506040820151614fd060408501826149ee565b506060820151614fe36060850182614a0c565b50505050565b6000608082019050614ffe6000830184614f94565b92915050565b600061500f82614f46565b9050919050565b61501f81615004565b82525050565b600060208201905061503a6000830184615016565b92915050565b60008060408385031215615057576150566144df565b5b600061506585828601614735565b925050602061507685828601614735565b9150509250929050565b600060208284031215615096576150956144df565b5b600082013567ffffffffffffffff8111156150b4576150b36144e4565b5b6150c084828501614e69565b91505092915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b6000600282049050600182168061511057607f821691505b60208210811415615124576151236150c9565b5b50919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b60006151648261465f565b915061516f8361465f565b9250827fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff038211156151a4576151a361512a565b5b828201905092915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b60006151e98261465f565b91507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff82141561521c5761521b61512a565b5b600182019050919050565b600080fd5b60006152388385614ba6565b93507f07ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff83111561526b5761526a615227565b5b60208302925061527c838584614e18565b82840190509392505050565b600060408201905061529d60008301866146f4565b81810360208301526152b081848661522c565b9050949350505050565b600081519050919050565b600082825260208201905092915050565b60006152e1826152ba565b6152eb81856152c5565b93506152fb8185602086016145c0565b615304816145f3565b840191505092915050565b60006040820190506153246000830185614794565b818103602083015261533681846152d6565b90509392505050565b7f554e424f554e4400000000000000000000000000000000000000000000000000600082015250565b60006153756007836145af565b91506153808261533f565b602082019050919050565b600060608201905081810360008301526153a481615368565b90506153b360208301866146f4565b81810360408301526153c681848661522c565b9050949350505050565b7f424f554e44000000000000000000000000000000000000000000000000000000600082015250565b60006154066005836145af565b9150615411826153d0565b602082019050919050565b60006060820190508181036000830152615435816153f9565b905061544460208301866146f4565b818103604083015261545781848661522c565b9050949350505050565b600081905092915050565b6000615477826145a4565b6154818185615461565b93506154918185602086016145c0565b80840191505092915050565b60006154a9828561546c565b91506154b5828461546c565b91508190509392505050565b60006040820190506154d660008301856146f4565b81810360208301526154e88184614bfb565b90509392505050565b6000606082019050818103600083015261550a81615368565b905061551960208301856146f4565b818103604083015261552b8184614bfb565b90509392505050565b600060408201905061554960008301856146f4565b818103602083015261555b81846152d6565b90509392505050565b600060808201905061557960008301876146f4565b61558660208301866146f4565b61559360408301856147be565b81810360608301526155a581846152d6565b905095945050505050565b6000815190506155bf81614515565b92915050565b6000602082840312156155db576155da6144df565b5b60006155e9848285016155b0565b91505092915050565b6000819050919050565b61560d6156088261465f565b6155f2565b82525050565b600081905092915050565b6000615629826152ba565b6156338185615613565b93506156438185602086016145c0565b80840191505092915050565b600061565b82866155fc565b60208201915061566b828561561e565b915061567782846155fc565b602082019150819050949350505050565b7f4678526f6f7454756e6e656c3a20455849545f414c52454144595f50524f434560008201527f5353454400000000000000000000000000000000000000000000000000000000602082015250565b60006156e46024836145af565b91506156ef82615688565b604082019050919050565b60006020820190508181036000830152615713816156d7565b9050919050565b7f4678526f6f7454756e6e656c3a20494e56414c49445f46585f4348494c445f5460008201527f554e4e454c000000000000000000000000000000000000000000000000000000602082015250565b60006157766025836145af565b91506157818261571a565b604082019050919050565b600060208201905081810360008301526157a581615769565b9050919050565b7f4678526f6f7454756e6e656c3a20494e56414c49445f524543454950545f505260008201527f4f4f460000000000000000000000000000000000000000000000000000000000602082015250565b60006158086023836145af565b9150615813826157ac565b604082019050919050565b60006020820190508181036000830152615837816157fb565b9050919050565b7f4678526f6f7454756e6e656c3a20494e56414c49445f5349474e415455524500600082015250565b6000615874601f836145af565b915061587f8261583e565b602082019050919050565b600060208201905081810360008301526158a381615867565b9050919050565b60006158bd6158b884614de7565b614dcc565b9050828152602081018484840111156158d9576158d8614d67565b5b6158e48482856145c0565b509392505050565b600082601f8301126159015761590061483b565b5b81516159118482602086016158aa565b91505092915050565b6000602082840312156159305761592f6144df565b5b600082015167ffffffffffffffff81111561594e5761594d6144e4565b5b61595a848285016158ec565b91505092915050565b600061596e8261465f565b91506159798361465f565b9250817fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff04831182151516156159b2576159b161512a565b5b828202905092915050565b60006159c88261465f565b91506159d38361465f565b9250828210156159e6576159e561512a565b5b828203905092915050565b600081519050615a0081614b15565b92915050565b600081519050615a1581614669565b92915050565b600081519050615a2a8161471e565b92915050565b600080600080600060a08688031215615a4c57615a4b6144df565b5b6000615a5a888289016159f1565b9550506020615a6b88828901615a06565b9450506040615a7c88828901615a06565b9350506060615a8d88828901615a06565b9250506080615a9e88828901615a1b565b9150509295509295909350565b6000819050919050565b615ac6615ac18261478a565b615aab565b82525050565b6000615ad882876155fc565b602082019150615ae882866155fc565b602082019150615af88285615ab5565b602082019150615b088284615ab5565b60208201915081905095945050505050565b7f4678526f6f7454756e6e656c3a20494e56414c49445f48454144455200000000600082015250565b6000615b50601c836145af565b9150615b5b82615b1a565b602082019050919050565b60006020820190508181036000830152615b7f81615b43565b9050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b6000615bc08261465f565b9150615bcb8361465f565b925082615bdb57615bda615b86565b5b828206905092915050565b6000615bf18261465f565b9150615bfc8361465f565b925082615c0c57615c0b615b86565b5b828204905092915050565b600060ff82169050919050565b6000615c2f82615c17565b9150615c3a83615c17565b925082615c4a57615c49615b86565b5b828206905092915050565b6000615c6082615c17565b9150615c6b83615c17565b925082615c7b57615c7a615b86565b5b828204905092915050565b60008160011c9050919050565b6000808291508390505b6001851115615cdd57808604811115615cb957615cb861512a565b5b6001851615615cc85780820291505b8081029050615cd685615c86565b9450615c9d565b94509492505050565b600082615cf65760019050615db2565b81615d045760009050615db2565b8160018114615d1a5760028114615d2457615d53565b6001915050615db2565b60ff841115615d3657615d3561512a565b5b8360020a915084821115615d4d57615d4c61512a565b5b50615db2565b5060208310610133831016604e8410600b8410161715615d885782820a905083811115615d8357615d8261512a565b5b615db2565b615d958484846001615c93565b92509050818404811115615dac57615dab61512a565b5b81810290505b9392505050565b6000615dc48261465f565b9150615dcf8361465f565b9250615dfc7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8484615ce6565b905092915050565b7f496e76616c69642070726f6f66206c656e677468000000000000000000000000600082015250565b6000615e3a6014836145af565b9150615e4582615e04565b602082019050919050565b60006020820190508181036000830152615e6981615e2d565b9050919050565b7f4c65616620696e64657820697320746f6f206269670000000000000000000000600082015250565b6000615ea66015836145af565b9150615eb182615e70565b602082019050919050565b60006020820190508181036000830152615ed581615e99565b9050919050565b6000615ee88285615ab5565b602082019150615ef88284615ab5565b6020820191508190509392505050565b6000615f1382615c17565b9150615f1e83615c17565b925082821015615f3157615f3061512a565b5b82820390509291505056fea264697066735822122095f2c7aa8b702b7d9ee463edbb4ea1564d2f604ceea72920974767f0fefaf41b64736f6c634300080b0033

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

000000000000000000000000fe5e5d361b2ad62c541bab87c45a0b9b018389a200000000000000000000000086e4dc95c7fbdbf52e33d563bbdb00823894c287000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000000a54656e746163756c6172000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005544e54434c000000000000000000000000000000000000000000000000000000

-----Decoded View---------------
Arg [0] : _checkpointManager (address): 0xfe5e5D361b2ad62c541bAb87C45a0B9B018389a2
Arg [1] : _fxRoot (address): 0x86E4Dc95c7FBdBf52e33D563BbDB00823894C287
Arg [2] : name (string): Tentacular
Arg [3] : symbol (string): TNTCL

-----Encoded View---------------
8 Constructor Arguments found :
Arg [0] : 000000000000000000000000fe5e5d361b2ad62c541bab87c45a0b9b018389a2
Arg [1] : 00000000000000000000000086e4dc95c7fbdbf52e33d563bbdb00823894c287
Arg [2] : 0000000000000000000000000000000000000000000000000000000000000080
Arg [3] : 00000000000000000000000000000000000000000000000000000000000000c0
Arg [4] : 000000000000000000000000000000000000000000000000000000000000000a
Arg [5] : 54656e746163756c617200000000000000000000000000000000000000000000
Arg [6] : 0000000000000000000000000000000000000000000000000000000000000005
Arg [7] : 544e54434c000000000000000000000000000000000000000000000000000000


Loading...
Loading
Loading...
Loading
[ Download: CSV Export  ]
[ Download: CSV Export  ]

A token is a representation of an on-chain or off-chain asset. The token page shows information such as price, total supply, holders, transfers and social links. Learn more about this page in our Knowledge Base.