ETH Price: $3,517.65 (+5.14%)

Token

 

Overview

Max Total Supply

1,126

Holders

647

Market

Volume (24H)

N/A

Min Price (24H)

N/A

Max Price (24H)

N/A
0xba7dDD1aEa2b2af6eea96a1Ed68Da42Fbe4b6c37
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:
ArtDubaiAlexisChristodoulou

Compiler Version
v0.8.17+commit.8df45f5f

Optimization Enabled:
No with 200 runs

Other Settings:
default evmVersion
File 1 of 18 : Alexis.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;

/*
	    :::     :::::::::      :::      ::::::::  
	  :+: :+:   :+:    :+:   :+: :+:   :+:    :+: 
	 +:+   +:+  +:+    +:+  +:+   +:+  +:+        
	+#++:++#++: +#+    +:+ +#++:++#++: +#+        
	+#+     +#+ +#+    +#+ +#+     +#+ +#+        
	#+#     #+# #+#    #+# #+#     #+# #+#    #+# 
	###     ### #########  ###     ###  ########  

    UAE NFT - Art Dubai | Alexis Christodoulou
    All rights reserved 2023
    Developed by DeployLabs.io ([email protected])
*/

import "./ArtDubai.sol";

error Alexis__EmptyVouchersArray();
error Alexis__VoucherAlreadyUsed();

error Alexis__HashComparisonFailed();
error Alexis__SignatureExpired();
error Alexis__UntrustedSigner();
error Alexis__SignatureAlreadyUsed();

/**
 * @title UAE NFT - Art Dubai | Alexis Christodoulou
 * @author DeployLabs.io
 *
 * @dev This contract is a collection for Art Dubai by Alexis Christodoulou.
 */
contract ArtDubaiAlexisChristodoulou is ArtDubai {
	/**
	 * @dev A signature package, that secures the minting of reward tokens.
	 *
	 * @param hash The hash of the minting operation.
	 * @param signature The signature for the hash.
	 * @param signatureValidUntil The timestamp until which the signature is valid.
	 * @param nonce The nonce, that is used to prevent replay attacks.
	 */
	struct RewardSignaturePackage {
		bytes32 hash;
		bytes signature;
		uint32 signatureValidUntil;
		uint64 nonce;
	}

	/**
	 * @dev A voucher, that can be used to mint reward tokens.
	 *
	 * @param collection The collection address.
	 * @param tokenId The token ID.
	 */
	struct RewardVoucher {
		address collection;
		uint64 tokenId;
	}

	event RewardVoucherUsed(address indexed collection, uint256 indexed tokenId);

	uint8 private s_daytimeRewardTokenId;
	uint8 private s_nighttimeRewardTokenId;

	// Collection address to token ID to used/unused
	mapping(address => mapping(uint256 => bool)) private s_tokenIdsUsedForRewards;

	constructor() ArtDubai(0xaca187d2a59b04b4, 0x89994365E8538F61a906c84cb15B6d415051fed0) {}

	/**
	 * @dev Mint reward tokens for the caller.
	 *
	 * @param signaturePackage The signature package.
	 * @param rewardVouchers The reward vouchers to use.
	 */
	function mintRewardTokens(
		RewardSignaturePackage calldata signaturePackage,
		RewardVoucher[] calldata rewardVouchers
	) external {
		if (rewardVouchers.length == 0) revert Alexis__EmptyVouchersArray();

		uint8 tokenIdToMint = _itIsDaytime() ? s_daytimeRewardTokenId : s_nighttimeRewardTokenId;

		for (uint16 i = 0; i < rewardVouchers.length; i++) {
			RewardVoucher memory rewardVoucher = rewardVouchers[i];
			if (s_tokenIdsUsedForRewards[rewardVoucher.collection][rewardVoucher.tokenId])
				revert Alexis__VoucherAlreadyUsed();

			s_tokenIdsUsedForRewards[rewardVoucher.collection][rewardVoucher.tokenId] = true;
			emit RewardVoucherUsed(rewardVoucher.collection, rewardVoucher.tokenId);
		}

		if (signaturePackage.signatureValidUntil < block.timestamp) revert Alexis__SignatureExpired();
		if (!_isCorrectRewardMintOperationHash(signaturePackage, rewardVouchers, msg.sender))
			revert Alexis__HashComparisonFailed();
		if (!_isTrustedSigner(signaturePackage.hash, signaturePackage.signature))
			revert Alexis__UntrustedSigner();
		if (s_usedNonces[signaturePackage.nonce]) revert Alexis__SignatureAlreadyUsed();

		s_numberMinted[tokenIdToMint][msg.sender] += uint16(rewardVouchers.length);
		s_usedNonces[signaturePackage.nonce] = true;

		_mint(msg.sender, tokenIdToMint, rewardVouchers.length, "");
	}

	/**
	 * @dev Specify which token IDs are going to be used as reward tokens.
	 *
	 * @param daytimeRewardTokenId The daytime reward token ID.
	 * @param nighttimeRewardTokenId The nighttime reward token ID.
	 */
	function setRewardTokenIds(
		uint8 daytimeRewardTokenId,
		uint8 nighttimeRewardTokenId
	) external onlyOwner {
		s_daytimeRewardTokenId = daytimeRewardTokenId;
		s_nighttimeRewardTokenId = nighttimeRewardTokenId;
	}

	/**
	 * @dev Get the reward token ID.
	 *
	 * @return daytimeRewardTokenId The daytime reward token ID.
	 * @return nighttimeRewardTokenId The nighttime reward token ID.
	 */
	function getRewardTokenIds()
		public
		view
		returns (uint8 daytimeRewardTokenId, uint8 nighttimeRewardTokenId)
	{
		daytimeRewardTokenId = s_daytimeRewardTokenId;
		nighttimeRewardTokenId = s_nighttimeRewardTokenId;
	}

	/// @inheritdoc ArtDubai
	function isTokenForSale(uint8 tokenId) public view override returns (bool isForSale) {
		isForSale =
			(tokenId != s_daytimeRewardTokenId) &&
			(tokenId != s_nighttimeRewardTokenId) &&
			super.isTokenForSale(tokenId);
	}

	/**
	 * @dev Check if it's daytime in Dubai (GMT+4).
	 *
	 * @return isDaytime True if it's daytime in Dubai.
	 */
	function _itIsDaytime() internal view returns (bool isDaytime) {
		// Dubai is GMT+4, thus we add 14400 seconds to the current timestamp
		uint256 hour = ((block.timestamp + 14400) % 86400) / 3600;
		isDaytime = (hour >= 6) && (hour < 18);
	}

	/**
	 * @dev Check whether a message hash is the one that has been signed.
	 *
	 * @param signaturePackage The signature package.
	 * @param mintTo The address to mint to.
	 *
	 * @return isCorrectRewardMintOperationHash Whether the message hash matches the one that has been signed.
	 */
	function _isCorrectRewardMintOperationHash(
		RewardSignaturePackage calldata signaturePackage,
		RewardVoucher[] calldata rewardVouchers,
		address mintTo
	) internal view returns (bool isCorrectRewardMintOperationHash) {
		bytes memory collectionBytes;
		bytes memory tokenIdBytes;

		for (uint16 i = 0; i < rewardVouchers.length; i++) {
			collectionBytes = abi.encodePacked(collectionBytes, rewardVouchers[i].collection);
			tokenIdBytes = abi.encodePacked(tokenIdBytes, rewardVouchers[i].tokenId);
		}

		bytes memory message = abi.encodePacked(
			i_hashSalt,
			mintTo,
			uint64(block.chainid),
			collectionBytes,
			tokenIdBytes,
			signaturePackage.signatureValidUntil,
			signaturePackage.nonce
		);
		bytes32 messageHash = keccak256(message);

		isCorrectRewardMintOperationHash = messageHash == signaturePackage.hash;
	}
}

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

pragma solidity ^0.8.0;

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

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

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

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

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

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

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

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

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

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

File 3 of 18 : ERC1155.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.0) (token/ERC1155/ERC1155.sol)

pragma solidity ^0.8.0;

import "./IERC1155.sol";
import "./IERC1155Receiver.sol";
import "./extensions/IERC1155MetadataURI.sol";
import "../../utils/Address.sol";
import "../../utils/Context.sol";
import "../../utils/introspection/ERC165.sol";

/**
 * @dev Implementation of the basic standard multi-token.
 * See https://eips.ethereum.org/EIPS/eip-1155
 * Originally based on code by Enjin: https://github.com/enjin/erc-1155
 *
 * _Available since v3.1._
 */
contract ERC1155 is Context, ERC165, IERC1155, IERC1155MetadataURI {
    using Address for address;

    // Mapping from token ID to account balances
    mapping(uint256 => mapping(address => uint256)) private _balances;

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

    // Used as the URI for all token types by relying on ID substitution, e.g. https://token-cdn-domain/{id}.json
    string private _uri;

    /**
     * @dev See {_setURI}.
     */
    constructor(string memory uri_) {
        _setURI(uri_);
    }

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

    /**
     * @dev See {IERC1155MetadataURI-uri}.
     *
     * This implementation returns the same URI for *all* token types. It relies
     * on the token type ID substitution mechanism
     * https://eips.ethereum.org/EIPS/eip-1155#metadata[defined in the EIP].
     *
     * Clients calling this function must replace the `\{id\}` substring with the
     * actual token type ID.
     */
    function uri(uint256) public view virtual override returns (string memory) {
        return _uri;
    }

    /**
     * @dev See {IERC1155-balanceOf}.
     *
     * Requirements:
     *
     * - `account` cannot be the zero address.
     */
    function balanceOf(address account, uint256 id) public view virtual override returns (uint256) {
        require(account != address(0), "ERC1155: address zero is not a valid owner");
        return _balances[id][account];
    }

    /**
     * @dev See {IERC1155-balanceOfBatch}.
     *
     * Requirements:
     *
     * - `accounts` and `ids` must have the same length.
     */
    function balanceOfBatch(address[] memory accounts, uint256[] memory ids)
        public
        view
        virtual
        override
        returns (uint256[] memory)
    {
        require(accounts.length == ids.length, "ERC1155: accounts and ids length mismatch");

        uint256[] memory batchBalances = new uint256[](accounts.length);

        for (uint256 i = 0; i < accounts.length; ++i) {
            batchBalances[i] = balanceOf(accounts[i], ids[i]);
        }

        return batchBalances;
    }

    /**
     * @dev See {IERC1155-setApprovalForAll}.
     */
    function setApprovalForAll(address operator, bool approved) public virtual override {
        _setApprovalForAll(_msgSender(), operator, approved);
    }

    /**
     * @dev See {IERC1155-isApprovedForAll}.
     */
    function isApprovedForAll(address account, address operator) public view virtual override returns (bool) {
        return _operatorApprovals[account][operator];
    }

    /**
     * @dev See {IERC1155-safeTransferFrom}.
     */
    function safeTransferFrom(
        address from,
        address to,
        uint256 id,
        uint256 amount,
        bytes memory data
    ) public virtual override {
        require(
            from == _msgSender() || isApprovedForAll(from, _msgSender()),
            "ERC1155: caller is not token owner or approved"
        );
        _safeTransferFrom(from, to, id, amount, data);
    }

    /**
     * @dev See {IERC1155-safeBatchTransferFrom}.
     */
    function safeBatchTransferFrom(
        address from,
        address to,
        uint256[] memory ids,
        uint256[] memory amounts,
        bytes memory data
    ) public virtual override {
        require(
            from == _msgSender() || isApprovedForAll(from, _msgSender()),
            "ERC1155: caller is not token owner or approved"
        );
        _safeBatchTransferFrom(from, to, ids, amounts, data);
    }

    /**
     * @dev Transfers `amount` tokens of token type `id` from `from` to `to`.
     *
     * Emits a {TransferSingle} event.
     *
     * Requirements:
     *
     * - `to` cannot be the zero address.
     * - `from` must have a balance of tokens of type `id` of at least `amount`.
     * - If `to` refers to a smart contract, it must implement {IERC1155Receiver-onERC1155Received} and return the
     * acceptance magic value.
     */
    function _safeTransferFrom(
        address from,
        address to,
        uint256 id,
        uint256 amount,
        bytes memory data
    ) internal virtual {
        require(to != address(0), "ERC1155: transfer to the zero address");

        address operator = _msgSender();
        uint256[] memory ids = _asSingletonArray(id);
        uint256[] memory amounts = _asSingletonArray(amount);

        _beforeTokenTransfer(operator, from, to, ids, amounts, data);

        uint256 fromBalance = _balances[id][from];
        require(fromBalance >= amount, "ERC1155: insufficient balance for transfer");
        unchecked {
            _balances[id][from] = fromBalance - amount;
        }
        _balances[id][to] += amount;

        emit TransferSingle(operator, from, to, id, amount);

        _afterTokenTransfer(operator, from, to, ids, amounts, data);

        _doSafeTransferAcceptanceCheck(operator, from, to, id, amount, data);
    }

    /**
     * @dev xref:ROOT:erc1155.adoc#batch-operations[Batched] version of {_safeTransferFrom}.
     *
     * Emits a {TransferBatch} event.
     *
     * Requirements:
     *
     * - If `to` refers to a smart contract, it must implement {IERC1155Receiver-onERC1155BatchReceived} and return the
     * acceptance magic value.
     */
    function _safeBatchTransferFrom(
        address from,
        address to,
        uint256[] memory ids,
        uint256[] memory amounts,
        bytes memory data
    ) internal virtual {
        require(ids.length == amounts.length, "ERC1155: ids and amounts length mismatch");
        require(to != address(0), "ERC1155: transfer to the zero address");

        address operator = _msgSender();

        _beforeTokenTransfer(operator, from, to, ids, amounts, data);

        for (uint256 i = 0; i < ids.length; ++i) {
            uint256 id = ids[i];
            uint256 amount = amounts[i];

            uint256 fromBalance = _balances[id][from];
            require(fromBalance >= amount, "ERC1155: insufficient balance for transfer");
            unchecked {
                _balances[id][from] = fromBalance - amount;
            }
            _balances[id][to] += amount;
        }

        emit TransferBatch(operator, from, to, ids, amounts);

        _afterTokenTransfer(operator, from, to, ids, amounts, data);

        _doSafeBatchTransferAcceptanceCheck(operator, from, to, ids, amounts, data);
    }

    /**
     * @dev Sets a new URI for all token types, by relying on the token type ID
     * substitution mechanism
     * https://eips.ethereum.org/EIPS/eip-1155#metadata[defined in the EIP].
     *
     * By this mechanism, any occurrence of the `\{id\}` substring in either the
     * URI or any of the amounts in the JSON file at said URI will be replaced by
     * clients with the token type ID.
     *
     * For example, the `https://token-cdn-domain/\{id\}.json` URI would be
     * interpreted by clients as
     * `https://token-cdn-domain/000000000000000000000000000000000000000000000000000000000004cce0.json`
     * for token type ID 0x4cce0.
     *
     * See {uri}.
     *
     * Because these URIs cannot be meaningfully represented by the {URI} event,
     * this function emits no events.
     */
    function _setURI(string memory newuri) internal virtual {
        _uri = newuri;
    }

    /**
     * @dev Creates `amount` tokens of token type `id`, and assigns them to `to`.
     *
     * Emits a {TransferSingle} event.
     *
     * Requirements:
     *
     * - `to` cannot be the zero address.
     * - If `to` refers to a smart contract, it must implement {IERC1155Receiver-onERC1155Received} and return the
     * acceptance magic value.
     */
    function _mint(
        address to,
        uint256 id,
        uint256 amount,
        bytes memory data
    ) internal virtual {
        require(to != address(0), "ERC1155: mint to the zero address");

        address operator = _msgSender();
        uint256[] memory ids = _asSingletonArray(id);
        uint256[] memory amounts = _asSingletonArray(amount);

        _beforeTokenTransfer(operator, address(0), to, ids, amounts, data);

        _balances[id][to] += amount;
        emit TransferSingle(operator, address(0), to, id, amount);

        _afterTokenTransfer(operator, address(0), to, ids, amounts, data);

        _doSafeTransferAcceptanceCheck(operator, address(0), to, id, amount, data);
    }

    /**
     * @dev xref:ROOT:erc1155.adoc#batch-operations[Batched] version of {_mint}.
     *
     * Emits a {TransferBatch} event.
     *
     * Requirements:
     *
     * - `ids` and `amounts` must have the same length.
     * - If `to` refers to a smart contract, it must implement {IERC1155Receiver-onERC1155BatchReceived} and return the
     * acceptance magic value.
     */
    function _mintBatch(
        address to,
        uint256[] memory ids,
        uint256[] memory amounts,
        bytes memory data
    ) internal virtual {
        require(to != address(0), "ERC1155: mint to the zero address");
        require(ids.length == amounts.length, "ERC1155: ids and amounts length mismatch");

        address operator = _msgSender();

        _beforeTokenTransfer(operator, address(0), to, ids, amounts, data);

        for (uint256 i = 0; i < ids.length; i++) {
            _balances[ids[i]][to] += amounts[i];
        }

        emit TransferBatch(operator, address(0), to, ids, amounts);

        _afterTokenTransfer(operator, address(0), to, ids, amounts, data);

        _doSafeBatchTransferAcceptanceCheck(operator, address(0), to, ids, amounts, data);
    }

    /**
     * @dev Destroys `amount` tokens of token type `id` from `from`
     *
     * Emits a {TransferSingle} event.
     *
     * Requirements:
     *
     * - `from` cannot be the zero address.
     * - `from` must have at least `amount` tokens of token type `id`.
     */
    function _burn(
        address from,
        uint256 id,
        uint256 amount
    ) internal virtual {
        require(from != address(0), "ERC1155: burn from the zero address");

        address operator = _msgSender();
        uint256[] memory ids = _asSingletonArray(id);
        uint256[] memory amounts = _asSingletonArray(amount);

        _beforeTokenTransfer(operator, from, address(0), ids, amounts, "");

        uint256 fromBalance = _balances[id][from];
        require(fromBalance >= amount, "ERC1155: burn amount exceeds balance");
        unchecked {
            _balances[id][from] = fromBalance - amount;
        }

        emit TransferSingle(operator, from, address(0), id, amount);

        _afterTokenTransfer(operator, from, address(0), ids, amounts, "");
    }

    /**
     * @dev xref:ROOT:erc1155.adoc#batch-operations[Batched] version of {_burn}.
     *
     * Emits a {TransferBatch} event.
     *
     * Requirements:
     *
     * - `ids` and `amounts` must have the same length.
     */
    function _burnBatch(
        address from,
        uint256[] memory ids,
        uint256[] memory amounts
    ) internal virtual {
        require(from != address(0), "ERC1155: burn from the zero address");
        require(ids.length == amounts.length, "ERC1155: ids and amounts length mismatch");

        address operator = _msgSender();

        _beforeTokenTransfer(operator, from, address(0), ids, amounts, "");

        for (uint256 i = 0; i < ids.length; i++) {
            uint256 id = ids[i];
            uint256 amount = amounts[i];

            uint256 fromBalance = _balances[id][from];
            require(fromBalance >= amount, "ERC1155: burn amount exceeds balance");
            unchecked {
                _balances[id][from] = fromBalance - amount;
            }
        }

        emit TransferBatch(operator, from, address(0), ids, amounts);

        _afterTokenTransfer(operator, from, address(0), ids, amounts, "");
    }

    /**
     * @dev Approve `operator` to operate on all of `owner` tokens
     *
     * Emits an {ApprovalForAll} event.
     */
    function _setApprovalForAll(
        address owner,
        address operator,
        bool approved
    ) internal virtual {
        require(owner != operator, "ERC1155: setting approval status for self");
        _operatorApprovals[owner][operator] = approved;
        emit ApprovalForAll(owner, operator, approved);
    }

    /**
     * @dev Hook that is called before any token transfer. This includes minting
     * and burning, as well as batched variants.
     *
     * The same hook is called on both single and batched variants. For single
     * transfers, the length of the `ids` and `amounts` arrays will be 1.
     *
     * Calling conditions (for each `id` and `amount` pair):
     *
     * - When `from` and `to` are both non-zero, `amount` of ``from``'s tokens
     * of token type `id` will be  transferred to `to`.
     * - When `from` is zero, `amount` tokens of token type `id` will be minted
     * for `to`.
     * - when `to` is zero, `amount` of ``from``'s tokens of token type `id`
     * will be burned.
     * - `from` and `to` are never both zero.
     * - `ids` and `amounts` have the same, non-zero length.
     *
     * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
     */
    function _beforeTokenTransfer(
        address operator,
        address from,
        address to,
        uint256[] memory ids,
        uint256[] memory amounts,
        bytes memory data
    ) internal virtual {}

    /**
     * @dev Hook that is called after any token transfer. This includes minting
     * and burning, as well as batched variants.
     *
     * The same hook is called on both single and batched variants. For single
     * transfers, the length of the `id` and `amount` arrays will be 1.
     *
     * Calling conditions (for each `id` and `amount` pair):
     *
     * - When `from` and `to` are both non-zero, `amount` of ``from``'s tokens
     * of token type `id` will be  transferred to `to`.
     * - When `from` is zero, `amount` tokens of token type `id` will be minted
     * for `to`.
     * - when `to` is zero, `amount` of ``from``'s tokens of token type `id`
     * will be burned.
     * - `from` and `to` are never both zero.
     * - `ids` and `amounts` have the same, non-zero length.
     *
     * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
     */
    function _afterTokenTransfer(
        address operator,
        address from,
        address to,
        uint256[] memory ids,
        uint256[] memory amounts,
        bytes memory data
    ) internal virtual {}

    function _doSafeTransferAcceptanceCheck(
        address operator,
        address from,
        address to,
        uint256 id,
        uint256 amount,
        bytes memory data
    ) private {
        if (to.isContract()) {
            try IERC1155Receiver(to).onERC1155Received(operator, from, id, amount, data) returns (bytes4 response) {
                if (response != IERC1155Receiver.onERC1155Received.selector) {
                    revert("ERC1155: ERC1155Receiver rejected tokens");
                }
            } catch Error(string memory reason) {
                revert(reason);
            } catch {
                revert("ERC1155: transfer to non-ERC1155Receiver implementer");
            }
        }
    }

    function _doSafeBatchTransferAcceptanceCheck(
        address operator,
        address from,
        address to,
        uint256[] memory ids,
        uint256[] memory amounts,
        bytes memory data
    ) private {
        if (to.isContract()) {
            try IERC1155Receiver(to).onERC1155BatchReceived(operator, from, ids, amounts, data) returns (
                bytes4 response
            ) {
                if (response != IERC1155Receiver.onERC1155BatchReceived.selector) {
                    revert("ERC1155: ERC1155Receiver rejected tokens");
                }
            } catch Error(string memory reason) {
                revert(reason);
            } catch {
                revert("ERC1155: transfer to non-ERC1155Receiver implementer");
            }
        }
    }

    function _asSingletonArray(uint256 element) private pure returns (uint256[] memory) {
        uint256[] memory array = new uint256[](1);
        array[0] = element;

        return array;
    }
}

File 4 of 18 : ERC1155Supply.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.6.0) (token/ERC1155/extensions/ERC1155Supply.sol)

pragma solidity ^0.8.0;

import "../ERC1155.sol";

/**
 * @dev Extension of ERC1155 that adds tracking of total supply per id.
 *
 * Useful for scenarios where Fungible and Non-fungible tokens have to be
 * clearly identified. Note: While a totalSupply of 1 might mean the
 * corresponding is an NFT, there is no guarantees that no other token with the
 * same id are not going to be minted.
 */
abstract contract ERC1155Supply is ERC1155 {
    mapping(uint256 => uint256) private _totalSupply;

    /**
     * @dev Total amount of tokens in with a given id.
     */
    function totalSupply(uint256 id) public view virtual returns (uint256) {
        return _totalSupply[id];
    }

    /**
     * @dev Indicates whether any token exist with a given id, or not.
     */
    function exists(uint256 id) public view virtual returns (bool) {
        return ERC1155Supply.totalSupply(id) > 0;
    }

    /**
     * @dev See {ERC1155-_beforeTokenTransfer}.
     */
    function _beforeTokenTransfer(
        address operator,
        address from,
        address to,
        uint256[] memory ids,
        uint256[] memory amounts,
        bytes memory data
    ) internal virtual override {
        super._beforeTokenTransfer(operator, from, to, ids, amounts, data);

        if (from == address(0)) {
            for (uint256 i = 0; i < ids.length; ++i) {
                _totalSupply[ids[i]] += amounts[i];
            }
        }

        if (to == address(0)) {
            for (uint256 i = 0; i < ids.length; ++i) {
                uint256 id = ids[i];
                uint256 amount = amounts[i];
                uint256 supply = _totalSupply[id];
                require(supply >= amount, "ERC1155: burn amount exceeds totalSupply");
                unchecked {
                    _totalSupply[id] = supply - amount;
                }
            }
        }
    }
}

File 5 of 18 : IERC1155MetadataURI.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (token/ERC1155/extensions/IERC1155MetadataURI.sol)

pragma solidity ^0.8.0;

import "../IERC1155.sol";

/**
 * @dev Interface of the optional ERC1155MetadataExtension interface, as defined
 * in the https://eips.ethereum.org/EIPS/eip-1155#metadata-extensions[EIP].
 *
 * _Available since v3.1._
 */
interface IERC1155MetadataURI is IERC1155 {
    /**
     * @dev Returns the URI for token type `id`.
     *
     * If the `\{id\}` substring is present in the URI, it must be replaced by
     * clients with the actual token type ID.
     */
    function uri(uint256 id) external view returns (string memory);
}

File 6 of 18 : IERC1155.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.0) (token/ERC1155/IERC1155.sol)

pragma solidity ^0.8.0;

import "../../utils/introspection/IERC165.sol";

/**
 * @dev Required interface of an ERC1155 compliant contract, as defined in the
 * https://eips.ethereum.org/EIPS/eip-1155[EIP].
 *
 * _Available since v3.1._
 */
interface IERC1155 is IERC165 {
    /**
     * @dev Emitted when `value` tokens of token type `id` are transferred from `from` to `to` by `operator`.
     */
    event TransferSingle(address indexed operator, address indexed from, address indexed to, uint256 id, uint256 value);

    /**
     * @dev Equivalent to multiple {TransferSingle} events, where `operator`, `from` and `to` are the same for all
     * transfers.
     */
    event TransferBatch(
        address indexed operator,
        address indexed from,
        address indexed to,
        uint256[] ids,
        uint256[] values
    );

    /**
     * @dev Emitted when `account` grants or revokes permission to `operator` to transfer their tokens, according to
     * `approved`.
     */
    event ApprovalForAll(address indexed account, address indexed operator, bool approved);

    /**
     * @dev Emitted when the URI for token type `id` changes to `value`, if it is a non-programmatic URI.
     *
     * If an {URI} event was emitted for `id`, the standard
     * https://eips.ethereum.org/EIPS/eip-1155#metadata-extensions[guarantees] that `value` will equal the value
     * returned by {IERC1155MetadataURI-uri}.
     */
    event URI(string value, uint256 indexed id);

    /**
     * @dev Returns the amount of tokens of token type `id` owned by `account`.
     *
     * Requirements:
     *
     * - `account` cannot be the zero address.
     */
    function balanceOf(address account, uint256 id) external view returns (uint256);

    /**
     * @dev xref:ROOT:erc1155.adoc#batch-operations[Batched] version of {balanceOf}.
     *
     * Requirements:
     *
     * - `accounts` and `ids` must have the same length.
     */
    function balanceOfBatch(address[] calldata accounts, uint256[] calldata ids)
        external
        view
        returns (uint256[] memory);

    /**
     * @dev Grants or revokes permission to `operator` to transfer the caller's tokens, according to `approved`,
     *
     * Emits an {ApprovalForAll} event.
     *
     * Requirements:
     *
     * - `operator` cannot be the caller.
     */
    function setApprovalForAll(address operator, bool approved) external;

    /**
     * @dev Returns true if `operator` is approved to transfer ``account``'s tokens.
     *
     * See {setApprovalForAll}.
     */
    function isApprovedForAll(address account, address operator) external view returns (bool);

    /**
     * @dev Transfers `amount` tokens of token type `id` from `from` to `to`.
     *
     * Emits a {TransferSingle} event.
     *
     * Requirements:
     *
     * - `to` cannot be the zero address.
     * - If the caller is not `from`, it must have been approved to spend ``from``'s tokens via {setApprovalForAll}.
     * - `from` must have a balance of tokens of type `id` of at least `amount`.
     * - If `to` refers to a smart contract, it must implement {IERC1155Receiver-onERC1155Received} and return the
     * acceptance magic value.
     */
    function safeTransferFrom(
        address from,
        address to,
        uint256 id,
        uint256 amount,
        bytes calldata data
    ) external;

    /**
     * @dev xref:ROOT:erc1155.adoc#batch-operations[Batched] version of {safeTransferFrom}.
     *
     * Emits a {TransferBatch} event.
     *
     * Requirements:
     *
     * - `ids` and `amounts` must have the same length.
     * - If `to` refers to a smart contract, it must implement {IERC1155Receiver-onERC1155BatchReceived} and return the
     * acceptance magic value.
     */
    function safeBatchTransferFrom(
        address from,
        address to,
        uint256[] calldata ids,
        uint256[] calldata amounts,
        bytes calldata data
    ) external;
}

File 7 of 18 : IERC1155Receiver.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.5.0) (token/ERC1155/IERC1155Receiver.sol)

pragma solidity ^0.8.0;

import "../../utils/introspection/IERC165.sol";

/**
 * @dev _Available since v3.1._
 */
interface IERC1155Receiver is IERC165 {
    /**
     * @dev Handles the receipt of a single ERC1155 token type. This function is
     * called at the end of a `safeTransferFrom` after the balance has been updated.
     *
     * NOTE: To accept the transfer, this must return
     * `bytes4(keccak256("onERC1155Received(address,address,uint256,uint256,bytes)"))`
     * (i.e. 0xf23a6e61, or its own function selector).
     *
     * @param operator The address which initiated the transfer (i.e. msg.sender)
     * @param from The address which previously owned the token
     * @param id The ID of the token being transferred
     * @param value The amount of tokens being transferred
     * @param data Additional data with no specified format
     * @return `bytes4(keccak256("onERC1155Received(address,address,uint256,uint256,bytes)"))` if transfer is allowed
     */
    function onERC1155Received(
        address operator,
        address from,
        uint256 id,
        uint256 value,
        bytes calldata data
    ) external returns (bytes4);

    /**
     * @dev Handles the receipt of a multiple ERC1155 token types. This function
     * is called at the end of a `safeBatchTransferFrom` after the balances have
     * been updated.
     *
     * NOTE: To accept the transfer(s), this must return
     * `bytes4(keccak256("onERC1155BatchReceived(address,address,uint256[],uint256[],bytes)"))`
     * (i.e. 0xbc197c81, or its own function selector).
     *
     * @param operator The address which initiated the batch transfer (i.e. msg.sender)
     * @param from The address which previously owned the token
     * @param ids An array containing ids of each token being transferred (order and length must match values array)
     * @param values An array containing amounts of each token being transferred (order and length must match ids array)
     * @param data Additional data with no specified format
     * @return `bytes4(keccak256("onERC1155BatchReceived(address,address,uint256[],uint256[],bytes)"))` if transfer is allowed
     */
    function onERC1155BatchReceived(
        address operator,
        address from,
        uint256[] calldata ids,
        uint256[] calldata values,
        bytes calldata data
    ) external returns (bytes4);
}

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

pragma solidity ^0.8.1;

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

        return account.code.length > 0;
    }

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

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

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

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

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

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

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

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

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

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

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

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

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

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

pragma solidity ^0.8.0;

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

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

File 10 of 18 : ECDSA.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.0) (utils/cryptography/ECDSA.sol)

pragma solidity ^0.8.0;

import "../Strings.sol";

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

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

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

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

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

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

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

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

        return (signer, RecoverError.NoError);
    }

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

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

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

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

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

pragma solidity ^0.8.0;

import "./IERC165.sol";

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

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

pragma solidity ^0.8.0;

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

File 13 of 18 : Math.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.0) (utils/math/Math.sol)

pragma solidity ^0.8.0;

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

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

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

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

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

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

            // Handle non-overflow cases, 256 by 256 division.
            if (prod1 == 0) {
                return prod0 / denominator;
            }

            // Make sure the result is less than 2^256. Also prevents denominator == 0.
            require(denominator > prod1);

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

File 14 of 18 : Strings.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.0) (utils/Strings.sol)

pragma solidity ^0.8.0;

import "./math/Math.sol";

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

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

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

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

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

File 15 of 18 : ArtDubai.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;

import "@openzeppelin/contracts/token/ERC1155/ERC1155.sol";
import "@openzeppelin/contracts/token/ERC1155/extensions/ERC1155Supply.sol";

import "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts/utils/cryptography/ECDSA.sol";
import "./library/operator_filterer/DefaultOperatorFilterer.sol";

error ArtDubai__TokenNotForSale();
error ArtDubai__WrongEtherAmmount();
error ArtDubai__ExceedingMaxSupply();
error ArtDubai__ExceedingTokensPerWalletLimit();

error ArtDubai__ExceedingSignatureMaxQuantity();
error ArtDubai__HashComparisonFailed();
error ArtDubai__SignatureExpired();
error ArtDubai__UntrustedSigner();
error ArtDubai__SignatureAlreadyUsed();

error ArtDubai__InvalidInput();
error ArtDubai__NothingToWithdraw();

/**
 * @dev A struct, that contains the sale conditions for a token.
 *
 * @param supplyLimit The maximum amount of tokens that can be minted.
 * @param maxTokensPerWallet The maximum amount of tokens that can be minted per wallet.
 * @param weiTokenPrice The price of a token in wei.
 */
struct SaleConditions {
	uint16 supplyLimit;
	uint16 maxTokensPerWallet;
	uint256 weiTokenPrice;
}

/**
 * @dev A signature package, that secures the minting of tokens.
 *
 * @param hash The hash of the minting operation.
 * @param signature The signature for the hash.
 * @param signatureValidUntil The timestamp until which the signature is valid.
 * @param nonce The nonce, that is used to prevent replay attacks.
 * @param maxQuantity The maximum quantity of tokens that can be minted with this signature.
 */
struct SignaturePackage {
	bytes32 hash;
	bytes signature;
	uint32 signatureValidUntil;
	uint64 nonce;
	uint16 maxQuantity;
}

/**
 * @title Art Dubai
 * @author DeployLabs.io
 *
 * @dev This contract is used as a base contract for all Art Dubai collections.
 */
abstract contract ArtDubai is ERC1155, ERC1155Supply, Ownable, DefaultOperatorFilterer {
	bytes8 internal immutable i_hashSalt;
	address internal immutable i_signerAddress;

	mapping(uint64 => bool) internal s_usedNonces;

	mapping(uint8 => SaleConditions) internal s_saleConditions;
	mapping(uint8 => mapping(address => uint16)) internal s_numberMinted;

	string internal s_baseTokenUri;

	constructor(bytes8 hashSalt, address signerAddress) ERC1155("") {
		i_hashSalt = hashSalt;
		i_signerAddress = signerAddress;
	}

	/**
	 * @dev Mint a token to a specified wallet.
	 *
	 * @param signaturePackage The signature package for security.
	 * @param mintTo The address to mint the token to.
	 * @param tokenId The id of the token to mint.
	 * @param quantity The quantity of tokens to mint.
	 */
	function mint(
		SignaturePackage calldata signaturePackage,
		address mintTo,
		uint8 tokenId,
		uint16 quantity
	) external payable {
		SaleConditions memory conditions = s_saleConditions[tokenId];

		if (!isTokenForSale(tokenId)) revert ArtDubai__TokenNotForSale();

		if (msg.value != conditions.weiTokenPrice * quantity) revert ArtDubai__WrongEtherAmmount();
		if (totalSupply(tokenId) + quantity > conditions.supplyLimit)
			revert ArtDubai__ExceedingMaxSupply();
		if (s_numberMinted[tokenId][mintTo] + quantity > conditions.maxTokensPerWallet)
			revert ArtDubai__ExceedingTokensPerWalletLimit();

		if (quantity > signaturePackage.maxQuantity) revert ArtDubai__ExceedingSignatureMaxQuantity();
		if (signaturePackage.signatureValidUntil < block.timestamp) revert ArtDubai__SignatureExpired();
		if (!_isCorrectMintOperationHash(signaturePackage, tokenId, mintTo))
			revert ArtDubai__HashComparisonFailed();
		if (!_isTrustedSigner(signaturePackage.hash, signaturePackage.signature))
			revert ArtDubai__UntrustedSigner();
		if (s_usedNonces[signaturePackage.nonce]) revert ArtDubai__SignatureAlreadyUsed();

		s_numberMinted[tokenId][mintTo] += quantity;
		s_usedNonces[signaturePackage.nonce] = true;

		_mint(mintTo, tokenId, quantity, "");
	}

	/**
	 * @dev Withdraw the balance of the contract.
	 *
	 * @param to The address to send the balance to.
	 */
	function withdraw(address payable to) external onlyOwner {
		uint256 balance = address(this).balance;
		if (balance == 0) revert ArtDubai__NothingToWithdraw();
		if (to == address(0)) revert ArtDubai__InvalidInput();

		to.transfer(balance);
	}

	/**
	 * @dev Set base URI for token metadata.
	 *
	 * @param baseUri The base URI for token metadata.
	 */
	function setBaseUri(string calldata baseUri) external onlyOwner {
		s_baseTokenUri = baseUri;
	}

	/**
	 * @dev Sets sale conditions for a set of tokens.
	 *
	 * @param tokenIds Token IDs
	 * @param saleConditions Sale conditions for each token
	 */
	function setSaleConditions(
		uint8[] calldata tokenIds,
		SaleConditions[] calldata saleConditions
	) external onlyOwner {
		if (tokenIds.length != saleConditions.length) revert ArtDubai__InvalidInput();

		for (uint8 i = 0; i < tokenIds.length; i++) {
			s_saleConditions[tokenIds[i]] = saleConditions[i];
		}
	}

	/**
	 * @dev Gets sale conditions for a token.
	 *
	 * @param tokenId Token ID
	 *
	 * @return conditions Sale conditions.
	 */
	function getSaleConditions(
		uint8 tokenId
	) external view returns (SaleConditions memory conditions) {
		conditions = s_saleConditions[tokenId];
	}

	/**
	 * @dev Get the number of tokens minted by a wallet.
	 *
	 * @param tokenId The id of the token.
	 * @param wallet The wallet to check.
	 *
	 * @return countMinted The number of tokens minted by the wallet.
	 */
	function getMintedCount(
		uint8 tokenId,
		address wallet
	) external view returns (uint16 countMinted) {
		countMinted = s_numberMinted[tokenId][wallet];
	}

	// Overrides for marketplace restrictions.
	function setApprovalForAll(
		address operator,
		bool approved
	) public override onlyAllowedOperatorApproval(operator) {
		super.setApprovalForAll(operator, approved);
	}

	function safeTransferFrom(
		address from,
		address to,
		uint256 tokenId,
		uint256 amount,
		bytes memory data
	) public override onlyAllowedOperator(from) {
		super.safeTransferFrom(from, to, tokenId, amount, data);
	}

	function safeBatchTransferFrom(
		address from,
		address to,
		uint256[] memory ids,
		uint256[] memory amounts,
		bytes memory data
	) public virtual override onlyAllowedOperator(from) {
		super.safeBatchTransferFrom(from, to, ids, amounts, data);
	}

	/**
	 * @dev Check, if a token is for sale.
	 *
	 * @param tokenId The id of the token.
	 *
	 * @return isForSale Whether the token is for sale.
	 */
	function isTokenForSale(uint8 tokenId) public view virtual returns (bool isForSale) {
		isForSale = s_saleConditions[tokenId].supplyLimit > 0;
	}

	/**
	 * @dev Get data used to generate the signature.
	 *
	 * @return hashSalt The hash salt.
	 * @return signerAddress The signer address.
	 */
	function getSignatureData() public view returns (bytes8 hashSalt, address signerAddress) {
		hashSalt = i_hashSalt;
		signerAddress = i_signerAddress;
	}

	/**
	 * @dev Get the URI for a token.
	 *
	 * @param tokenId The token ID.
	 *
	 * @return uri The URI for the token.
	 */
	function uri(uint256 tokenId) public view virtual override returns (string memory) {
		return string(abi.encodePacked(s_baseTokenUri, Strings.toString(tokenId)));
	}

	// Override for ERC1155Supply.
	function _beforeTokenTransfer(
		address operator,
		address from,
		address to,
		uint256[] memory ids,
		uint256[] memory amounts,
		bytes memory data
	) internal override(ERC1155, ERC1155Supply) {
		super._beforeTokenTransfer(operator, from, to, ids, amounts, data);
	}

	/**
	 * @dev Check whether a message hash is the one that has been signed.
	 *
	 * @param signaturePackage The signature package.
	 * @param tokenId The id of the token.
	 * @param mintTo The address to mint to.
	 *
	 * @return isCorrectMintOperationHash Whether the message hash matches the one that has been signed.
	 */
	function _isCorrectMintOperationHash(
		SignaturePackage calldata signaturePackage,
		uint8 tokenId,
		address mintTo
	) internal view returns (bool isCorrectMintOperationHash) {
		bytes memory message = abi.encodePacked(
			i_hashSalt,
			mintTo,
			uint64(block.chainid),
			tokenId,
			signaturePackage.maxQuantity,
			signaturePackage.signatureValidUntil,
			signaturePackage.nonce
		);
		bytes32 messageHash = keccak256(message);

		isCorrectMintOperationHash = messageHash == signaturePackage.hash;
	}

	/**
	 * @dev Check whether a message was signed by a trusted address.
	 *
	 * @param hash The hash of the message.
	 * @param signature The signature of the message.
	 *
	 * @return isTrustedSigner Whether the message was signed by a trusted address.
	 */
	function _isTrustedSigner(
		bytes32 hash,
		bytes memory signature
	) internal view returns (bool isTrustedSigner) {
		isTrustedSigner = i_signerAddress == ECDSA.recover(hash, signature);
	}
}

File 16 of 18 : DefaultOperatorFilterer.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.13;

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

/**
 * @title  DefaultOperatorFilterer
 * @notice Inherits from OperatorFilterer and automatically subscribes to the default OpenSea subscription.
 */
abstract contract DefaultOperatorFilterer is OperatorFilterer {
    address constant DEFAULT_SUBSCRIPTION = address(0x3cc6CddA760b79bAfa08dF41ECFA224f810dCeB6);

    constructor() OperatorFilterer(DEFAULT_SUBSCRIPTION, true) {}
}

File 17 of 18 : IOperatorFilterRegistry.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.13;

interface IOperatorFilterRegistry {
    function isOperatorAllowed(address registrant, address operator) external view returns (bool);
    function register(address registrant) external;
    function registerAndSubscribe(address registrant, address subscription) external;
    function registerAndCopyEntries(address registrant, address registrantToCopy) external;
    function unregister(address addr) external;
    function updateOperator(address registrant, address operator, bool filtered) external;
    function updateOperators(address registrant, address[] calldata operators, bool filtered) external;
    function updateCodeHash(address registrant, bytes32 codehash, bool filtered) external;
    function updateCodeHashes(address registrant, bytes32[] calldata codeHashes, bool filtered) external;
    function subscribe(address registrant, address registrantToSubscribe) external;
    function unsubscribe(address registrant, bool copyExistingEntries) external;
    function subscriptionOf(address addr) external returns (address registrant);
    function subscribers(address registrant) external returns (address[] memory);
    function subscriberAt(address registrant, uint256 index) external returns (address);
    function copyEntriesOf(address registrant, address registrantToCopy) external;
    function isOperatorFiltered(address registrant, address operator) external returns (bool);
    function isCodeHashOfFiltered(address registrant, address operatorWithCode) external returns (bool);
    function isCodeHashFiltered(address registrant, bytes32 codeHash) external returns (bool);
    function filteredOperators(address addr) external returns (address[] memory);
    function filteredCodeHashes(address addr) external returns (bytes32[] memory);
    function filteredOperatorAt(address registrant, uint256 index) external returns (address);
    function filteredCodeHashAt(address registrant, uint256 index) external returns (bytes32);
    function isRegistered(address addr) external returns (bool);
    function codeHashOf(address addr) external returns (bytes32);
}

File 18 of 18 : OperatorFilterer.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.13;

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

/**
 * @title  OperatorFilterer
 * @notice Abstract contract whose constructor automatically registers and optionally subscribes to or copies another
 *         registrant's entries in the OperatorFilterRegistry.
 * @dev    This smart contract is meant to be inherited by token contracts so they can use the following:
 *         - `onlyAllowedOperator` modifier for `transferFrom` and `safeTransferFrom` methods.
 *         - `onlyAllowedOperatorApproval` modifier for `approve` and `setApprovalForAll` methods.
 */
abstract contract OperatorFilterer {
    error OperatorNotAllowed(address operator);

    IOperatorFilterRegistry public constant OPERATOR_FILTER_REGISTRY =
        IOperatorFilterRegistry(0x000000000000AAeB6D7670E522A718067333cd4E);

    constructor(address subscriptionOrRegistrantToCopy, bool subscribe) {
        // If an inheriting token contract is deployed to a network without the registry deployed, the modifier
        // will not revert, but the contract will need to be registered with the registry once it is deployed in
        // order for the modifier to filter addresses.
        if (address(OPERATOR_FILTER_REGISTRY).code.length > 0) {
            if (subscribe) {
                OPERATOR_FILTER_REGISTRY.registerAndSubscribe(address(this), subscriptionOrRegistrantToCopy);
            } else {
                if (subscriptionOrRegistrantToCopy != address(0)) {
                    OPERATOR_FILTER_REGISTRY.registerAndCopyEntries(address(this), subscriptionOrRegistrantToCopy);
                } else {
                    OPERATOR_FILTER_REGISTRY.register(address(this));
                }
            }
        }
    }

    modifier onlyAllowedOperator(address from) virtual {
        // Allow spending tokens from addresses with balance
        // Note that this still allows listings and marketplaces with escrow to transfer tokens if transferred
        // from an EOA.
        if (from != msg.sender) {
            _checkFilterOperator(msg.sender);
        }
        _;
    }

    modifier onlyAllowedOperatorApproval(address operator) virtual {
        _checkFilterOperator(operator);
        _;
    }

    function _checkFilterOperator(address operator) internal view virtual {
        // Check registry code length to facilitate testing in environments without a deployed registry.
        if (address(OPERATOR_FILTER_REGISTRY).code.length > 0) {
            if (!OPERATOR_FILTER_REGISTRY.isOperatorAllowed(address(this), operator)) {
                revert OperatorNotAllowed(operator);
            }
        }
    }
}

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

Contract Security Audit

Contract ABI

[{"inputs":[],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"Alexis__EmptyVouchersArray","type":"error"},{"inputs":[],"name":"Alexis__HashComparisonFailed","type":"error"},{"inputs":[],"name":"Alexis__SignatureAlreadyUsed","type":"error"},{"inputs":[],"name":"Alexis__SignatureExpired","type":"error"},{"inputs":[],"name":"Alexis__UntrustedSigner","type":"error"},{"inputs":[],"name":"Alexis__VoucherAlreadyUsed","type":"error"},{"inputs":[],"name":"ArtDubai__ExceedingMaxSupply","type":"error"},{"inputs":[],"name":"ArtDubai__ExceedingSignatureMaxQuantity","type":"error"},{"inputs":[],"name":"ArtDubai__ExceedingTokensPerWalletLimit","type":"error"},{"inputs":[],"name":"ArtDubai__HashComparisonFailed","type":"error"},{"inputs":[],"name":"ArtDubai__InvalidInput","type":"error"},{"inputs":[],"name":"ArtDubai__NothingToWithdraw","type":"error"},{"inputs":[],"name":"ArtDubai__SignatureAlreadyUsed","type":"error"},{"inputs":[],"name":"ArtDubai__SignatureExpired","type":"error"},{"inputs":[],"name":"ArtDubai__TokenNotForSale","type":"error"},{"inputs":[],"name":"ArtDubai__UntrustedSigner","type":"error"},{"inputs":[],"name":"ArtDubai__WrongEtherAmmount","type":"error"},{"inputs":[{"internalType":"address","name":"operator","type":"address"}],"name":"OperatorNotAllowed","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"account","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":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"collection","type":"address"},{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"RewardVoucherUsed","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"operator","type":"address"},{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256[]","name":"ids","type":"uint256[]"},{"indexed":false,"internalType":"uint256[]","name":"values","type":"uint256[]"}],"name":"TransferBatch","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"operator","type":"address"},{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"id","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"TransferSingle","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"string","name":"value","type":"string"},{"indexed":true,"internalType":"uint256","name":"id","type":"uint256"}],"name":"URI","type":"event"},{"inputs":[],"name":"OPERATOR_FILTER_REGISTRY","outputs":[{"internalType":"contract IOperatorFilterRegistry","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"},{"internalType":"uint256","name":"id","type":"uint256"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address[]","name":"accounts","type":"address[]"},{"internalType":"uint256[]","name":"ids","type":"uint256[]"}],"name":"balanceOfBatch","outputs":[{"internalType":"uint256[]","name":"","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"id","type":"uint256"}],"name":"exists","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint8","name":"tokenId","type":"uint8"},{"internalType":"address","name":"wallet","type":"address"}],"name":"getMintedCount","outputs":[{"internalType":"uint16","name":"countMinted","type":"uint16"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getRewardTokenIds","outputs":[{"internalType":"uint8","name":"daytimeRewardTokenId","type":"uint8"},{"internalType":"uint8","name":"nighttimeRewardTokenId","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint8","name":"tokenId","type":"uint8"}],"name":"getSaleConditions","outputs":[{"components":[{"internalType":"uint16","name":"supplyLimit","type":"uint16"},{"internalType":"uint16","name":"maxTokensPerWallet","type":"uint16"},{"internalType":"uint256","name":"weiTokenPrice","type":"uint256"}],"internalType":"struct SaleConditions","name":"conditions","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getSignatureData","outputs":[{"internalType":"bytes8","name":"hashSalt","type":"bytes8"},{"internalType":"address","name":"signerAddress","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"},{"internalType":"address","name":"operator","type":"address"}],"name":"isApprovedForAll","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint8","name":"tokenId","type":"uint8"}],"name":"isTokenForSale","outputs":[{"internalType":"bool","name":"isForSale","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"components":[{"internalType":"bytes32","name":"hash","type":"bytes32"},{"internalType":"bytes","name":"signature","type":"bytes"},{"internalType":"uint32","name":"signatureValidUntil","type":"uint32"},{"internalType":"uint64","name":"nonce","type":"uint64"},{"internalType":"uint16","name":"maxQuantity","type":"uint16"}],"internalType":"struct SignaturePackage","name":"signaturePackage","type":"tuple"},{"internalType":"address","name":"mintTo","type":"address"},{"internalType":"uint8","name":"tokenId","type":"uint8"},{"internalType":"uint16","name":"quantity","type":"uint16"}],"name":"mint","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"components":[{"internalType":"bytes32","name":"hash","type":"bytes32"},{"internalType":"bytes","name":"signature","type":"bytes"},{"internalType":"uint32","name":"signatureValidUntil","type":"uint32"},{"internalType":"uint64","name":"nonce","type":"uint64"}],"internalType":"struct ArtDubaiAlexisChristodoulou.RewardSignaturePackage","name":"signaturePackage","type":"tuple"},{"components":[{"internalType":"address","name":"collection","type":"address"},{"internalType":"uint64","name":"tokenId","type":"uint64"}],"internalType":"struct ArtDubaiAlexisChristodoulou.RewardVoucher[]","name":"rewardVouchers","type":"tuple[]"}],"name":"mintRewardTokens","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256[]","name":"ids","type":"uint256[]"},{"internalType":"uint256[]","name":"amounts","type":"uint256[]"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"safeBatchTransferFrom","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":"uint256","name":"amount","type":"uint256"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"safeTransferFrom","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"operator","type":"address"},{"internalType":"bool","name":"approved","type":"bool"}],"name":"setApprovalForAll","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string","name":"baseUri","type":"string"}],"name":"setBaseUri","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint8","name":"daytimeRewardTokenId","type":"uint8"},{"internalType":"uint8","name":"nighttimeRewardTokenId","type":"uint8"}],"name":"setRewardTokenIds","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint8[]","name":"tokenIds","type":"uint8[]"},{"components":[{"internalType":"uint16","name":"supplyLimit","type":"uint16"},{"internalType":"uint16","name":"maxTokensPerWallet","type":"uint16"},{"internalType":"uint256","name":"weiTokenPrice","type":"uint256"}],"internalType":"struct SaleConditions[]","name":"saleConditions","type":"tuple[]"}],"name":"setSaleConditions","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes4","name":"interfaceId","type":"bytes4"}],"name":"supportsInterface","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"id","type":"uint256"}],"name":"totalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"uri","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address payable","name":"to","type":"address"}],"name":"withdraw","outputs":[],"stateMutability":"nonpayable","type":"function"}]

60c06040523480156200001157600080fd5b5067aca187d2a59b04b460c01b7389994365e8538f61a906c84cb15b6d415051fed0733cc6cdda760b79bafa08df41ecfa224f810dceb66001604051806020016040528060008152506200006b81620002fd60201b60201c565b506200008c620000806200031260201b60201c565b6200031a60201b60201c565b60006daaeb6d7670e522a718067333cd4e73ffffffffffffffffffffffffffffffffffffffff163b11156200028157801562000147576daaeb6d7670e522a718067333cd4e73ffffffffffffffffffffffffffffffffffffffff16637d3e3dbe30846040518363ffffffff1660e01b81526004016200010d92919062000425565b600060405180830381600087803b1580156200012857600080fd5b505af11580156200013d573d6000803e3d6000fd5b5050505062000280565b600073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff161462000201576daaeb6d7670e522a718067333cd4e73ffffffffffffffffffffffffffffffffffffffff1663a0af290330846040518363ffffffff1660e01b8152600401620001c792919062000425565b600060405180830381600087803b158015620001e257600080fd5b505af1158015620001f7573d6000803e3d6000fd5b505050506200027f565b6daaeb6d7670e522a718067333cd4e73ffffffffffffffffffffffffffffffffffffffff16634420e486306040518263ffffffff1660e01b81526004016200024a919062000452565b600060405180830381600087803b1580156200026557600080fd5b505af11580156200027a573d6000803e3d6000fd5b505050505b5b5b50508177ffffffffffffffffffffffffffffffffffffffffffffffff191660808177ffffffffffffffffffffffffffffffffffffffffffffffff1916815250508073ffffffffffffffffffffffffffffffffffffffff1660a08173ffffffffffffffffffffffffffffffffffffffff16815250505050620007d0565b80600290816200030e9190620006e9565b5050565b600033905090565b6000600460009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16905081600460006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055508173ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a35050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b60006200040d82620003e0565b9050919050565b6200041f8162000400565b82525050565b60006040820190506200043c600083018562000414565b6200044b602083018462000414565b9392505050565b600060208201905062000469600083018462000414565b92915050565b600081519050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b60006002820490506001821680620004f157607f821691505b602082108103620005075762000506620004a9565b5b50919050565b60008190508160005260206000209050919050565b60006020601f8301049050919050565b600082821b905092915050565b600060088302620005717fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8262000532565b6200057d868362000532565b95508019841693508086168417925050509392505050565b6000819050919050565b6000819050919050565b6000620005ca620005c4620005be8462000595565b6200059f565b62000595565b9050919050565b6000819050919050565b620005e683620005a9565b620005fe620005f582620005d1565b8484546200053f565b825550505050565b600090565b6200061562000606565b62000622818484620005db565b505050565b5b818110156200064a576200063e6000826200060b565b60018101905062000628565b5050565b601f821115620006995762000663816200050d565b6200066e8462000522565b810160208510156200067e578190505b620006966200068d8562000522565b83018262000627565b50505b505050565b600082821c905092915050565b6000620006be600019846008026200069e565b1980831691505092915050565b6000620006d98383620006ab565b9150826002028217905092915050565b620006f4826200046f565b67ffffffffffffffff81111562000710576200070f6200047a565b5b6200071c8254620004d8565b620007298282856200064e565b600060209050601f8311600181146200076157600084156200074c578287015190505b620007588582620006cb565b865550620007c8565b601f19841662000771866200050d565b60005b828110156200079b5784890151825560018201915060208501945060208101905062000774565b86831015620007bb5784890151620007b7601f891682620006ab565b8355505b6001600288020188555050505b505050505050565b60805160a051615d2e6200080b600039600081816115dd0152611d000152600081816115ba01528181611c3c015261214a0152615d2e6000f3fe6080604052600436106101655760003560e01c80638c02f79b116100d1578063bd85b0391161008a578063e985e9c511610064578063e985e9c514610552578063f006ad971461058f578063f242432a146105cc578063f2fde38b146105f557610165565b8063bd85b039146104c3578063bef98d4b14610500578063e5b8fb921461052957610165565b80638c02f79b146103c55780638da5cb5b146103ee578063900af1921461041957806390b7178e14610445578063a0bcfc7f14610471578063a22cb4651461049a57610165565b80634163933a116101235780634163933a146102c457806341f43434146102e05780634e1273f41461030b5780634f558e791461034857806351cff8d914610385578063715018a6146103ae57610165565b8062fdd58e1461016a57806301ffc9a7146101a75780630e89341c146101e457806314119bb714610221578063235f0e931461025e5780632eb2c2d61461029b575b600080fd5b34801561017657600080fd5b50610191600480360381019061018c9190613521565b61061e565b60405161019e9190613570565b60405180910390f35b3480156101b357600080fd5b506101ce60048036038101906101c991906135e3565b6106e6565b6040516101db919061362b565b60405180910390f35b3480156101f057600080fd5b5061020b60048036038101906102069190613646565b6107c8565b6040516102189190613703565b60405180910390f35b34801561022d57600080fd5b506102486004803603810190610243919061375e565b6107fc565b60405161025591906137f9565b60405180910390f35b34801561026a57600080fd5b5061028560048036038101906102809190613814565b610879565b6040516102929190613863565b60405180910390f35b3480156102a757600080fd5b506102c260048036038101906102bd9190613a7b565b6108e8565b005b6102de60048036038101906102d99190613b9a565b61093b565b005b3480156102ec57600080fd5b506102f5610e4c565b6040516103029190613c7c565b60405180910390f35b34801561031757600080fd5b50610332600480360381019061032d9190613d5a565b610e5e565b60405161033f9190613e81565b60405180910390f35b34801561035457600080fd5b5061036f600480360381019061036a9190613646565b610f77565b60405161037c919061362b565b60405180910390f35b34801561039157600080fd5b506103ac60048036038101906103a79190613ee1565b610f8b565b005b3480156103ba57600080fd5b506103c3611083565b005b3480156103d157600080fd5b506103ec60048036038101906103e79190613f88565b611097565b005b3480156103fa57600080fd5b5061040361158b565b6040516104109190614013565b60405180910390f35b34801561042557600080fd5b5061042e6115b5565b60405161043c929190614069565b60405180910390f35b34801561045157600080fd5b5061045a611602565b6040516104689291906140a1565b60405180910390f35b34801561047d57600080fd5b5061049860048036038101906104939190614120565b61162d565b005b3480156104a657600080fd5b506104c160048036038101906104bc9190614199565b61164b565b005b3480156104cf57600080fd5b506104ea60048036038101906104e59190613646565b611664565b6040516104f79190613570565b60405180910390f35b34801561050c57600080fd5b50610527600480360381019061052291906141d9565b611681565b005b34801561053557600080fd5b50610550600480360381019061054b91906142c5565b6116c3565b005b34801561055e57600080fd5b5061057960048036038101906105749190614346565b6117a3565b604051610586919061362b565b60405180910390f35b34801561059b57600080fd5b506105b660048036038101906105b1919061375e565b611837565b6040516105c3919061362b565b60405180910390f35b3480156105d857600080fd5b506105f360048036038101906105ee9190614386565b61188b565b005b34801561060157600080fd5b5061061c6004803603810190610617919061441d565b6118de565b005b60008073ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff160361068e576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610685906144bc565b60405180910390fd5b60008083815260200190815260200160002060008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054905092915050565b60007fd9b67a26000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916827bffffffffffffffffffffffffffffffffffffffffffffffffffffffff191614806107b157507f0e89341c000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916827bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916145b806107c157506107c082611961565b5b9050919050565b606060086107d5836119cb565b6040516020016107e6929190614610565b6040516020818303038152906040529050919050565b610804613450565b600660008360ff1660ff1681526020019081526020016000206040518060600160405290816000820160009054906101000a900461ffff1661ffff1661ffff1681526020016000820160029054906101000a900461ffff1661ffff1661ffff1681526020016001820154815250509050919050565b6000600760008460ff1660ff16815260200190815260200160002060008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900461ffff16905092915050565b843373ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff16146109265761092533611a99565b5b6109338686868686611b96565b505050505050565b6000600660008460ff1660ff1681526020019081526020016000206040518060600160405290816000820160009054906101000a900461ffff1661ffff1661ffff1681526020016000820160029054906101000a900461ffff1661ffff1661ffff16815260200160018201548152505090506109b683611837565b6109ec576040517fee19d49100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8161ffff168160400151610a009190614663565b3414610a38576040517f26c3439000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b806000015161ffff168261ffff16610a528560ff16611664565b610a5c91906146a5565b1115610a94576040517fd8ea507600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b806020015161ffff1682600760008660ff1660ff16815260200190815260200160002060008773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900461ffff16610b0d91906146d9565b61ffff161115610b49576040517f5b3e6f0000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b846080016020810190610b5c919061470f565b61ffff168261ffff161115610b9d576040517f8d515e9c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b42856040016020810190610bb19190614778565b63ffffffff161015610bef576040517f1eee9c0700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b610bfa858486611c37565b610c30576040517fff39ee9500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b610c908560000135868060200190610c4891906147b4565b8080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f82011690508083019250505050505050611cdc565b610cc6576040517f1490dab400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60056000866060016020810190610cdd9190614857565b67ffffffffffffffff1667ffffffffffffffff16815260200190815260200160002060009054906101000a900460ff1615610d44576040517fa2a6490500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b81600760008560ff1660ff16815260200190815260200160002060008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008282829054906101000a900461ffff16610db791906146d9565b92506101000a81548161ffff021916908361ffff160217905550600160056000876060016020810190610dea9190614857565b67ffffffffffffffff1667ffffffffffffffff16815260200190815260200160002060006101000a81548160ff021916908315150217905550610e45848460ff168461ffff1660405180602001604052806000815250611d3e565b5050505050565b6daaeb6d7670e522a718067333cd4e81565b60608151835114610ea4576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610e9b906148f6565b60405180910390fd5b6000835167ffffffffffffffff811115610ec157610ec0613883565b5b604051908082528060200260200182016040528015610eef5781602001602082028036833780820191505090505b50905060005b8451811015610f6c57610f3c858281518110610f1457610f13614916565b5b6020026020010151858381518110610f2f57610f2e614916565b5b602002602001015161061e565b828281518110610f4f57610f4e614916565b5b60200260200101818152505080610f6590614945565b9050610ef5565b508091505092915050565b600080610f8383611664565b119050919050565b610f93611eee565b600047905060008103610fd2576040517f895dcf4500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff1603611038576040517fcaa9014f00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8173ffffffffffffffffffffffffffffffffffffffff166108fc829081150290604051600060405180830381858888f1935050505015801561107e573d6000803e3d6000fd5b505050565b61108b611eee565b6110956000611f6c565b565b600082829050036110d4576040517fd643e0ef00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60006110de612032565b6110f757600960019054906101000a900460ff16611108565b600960009054906101000a900460ff165b905060005b838390508161ffff1610156112d957600084848361ffff1681811061113557611134614916565b5b90506040020180360381019061114b91906149e2565b9050600a6000826000015173ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000826020015167ffffffffffffffff16815260200190815260200160002060009054906101000a900460ff16156111f4576040517fa342934900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6001600a6000836000015173ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000836020015167ffffffffffffffff16815260200190815260200160002060006101000a81548160ff021916908315150217905550806020015167ffffffffffffffff16816000015173ffffffffffffffffffffffffffffffffffffffff167ffe6ae5739c1d6d64298f06f3740a387add6ff26d92ce81a5c93f02ee4226ab9960405160405180910390a35080806112d190614a0f565b91505061110d565b50428460400160208101906112ee9190614778565b63ffffffff16101561132c576040517fc4e9f92900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61133884848433612077565b61136e576040517f07f0245a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6113ce846000013585806020019061138691906147b4565b8080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f82011690508083019250505050505050611cdc565b611404576040517f03a5536400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6005600085606001602081019061141b9190614857565b67ffffffffffffffff1667ffffffffffffffff16815260200190815260200160002060009054906101000a900460ff1615611482576040517f4ab4c93000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b82829050600760008360ff1660ff16815260200190815260200160002060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008282829054906101000a900461ffff166114f891906146d9565b92506101000a81548161ffff021916908361ffff16021790555060016005600086606001602081019061152b9190614857565b67ffffffffffffffff1667ffffffffffffffff16815260200190815260200160002060006101000a81548160ff021916908315150217905550611585338260ff168585905060405180602001604052806000815250611d3e565b50505050565b6000600460009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16905090565b6000807f000000000000000000000000000000000000000000000000000000000000000091507f000000000000000000000000000000000000000000000000000000000000000090509091565b600080600960009054906101000a900460ff169150600960019054906101000a900460ff1690509091565b611635611eee565b818160089182611646929190614bd1565b505050565b8161165581611a99565b61165f83836121db565b505050565b600060036000838152602001908152602001600020549050919050565b611689611eee565b81600960006101000a81548160ff021916908360ff16021790555080600960016101000a81548160ff021916908360ff1602179055505050565b6116cb611eee565b81819050848490501461170a576040517fcaa9014f00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60005b848490508160ff16101561179c5782828260ff1681811061173157611730614916565b5b9050606002016006600087878560ff1681811061175157611750614916565b5b9050602002016020810190611766919061375e565b60ff1660ff16815260200190815260200160002081816117869190614e6f565b905050808061179490614e7d565b91505061170d565b5050505050565b6000600160008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff16905092915050565b6000600960009054906101000a900460ff1660ff168260ff16141580156118735750600960019054906101000a900460ff1660ff168260ff1614155b80156118845750611883826121f1565b5b9050919050565b843373ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff16146118c9576118c833611a99565b5b6118d6868686868661222b565b505050505050565b6118e6611eee565b600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff1603611955576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161194c90614f18565b60405180910390fd5b61195e81611f6c565b50565b60007f01ffc9a7000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916827bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916149050919050565b6060600060016119da846122cc565b01905060008167ffffffffffffffff8111156119f9576119f8613883565b5b6040519080825280601f01601f191660200182016040528015611a2b5781602001600182028036833780820191505090505b509050600082602001820190505b600115611a8e578080600190039150507f3031323334353637383961626364656600000000000000000000000000000000600a86061a8153600a8581611a8257611a81614f38565b5b04945060008503611a39575b819350505050919050565b60006daaeb6d7670e522a718067333cd4e73ffffffffffffffffffffffffffffffffffffffff163b1115611b93576daaeb6d7670e522a718067333cd4e73ffffffffffffffffffffffffffffffffffffffff1663c617113430836040518363ffffffff1660e01b8152600401611b10929190614f67565b602060405180830381865afa158015611b2d573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611b519190614fa5565b611b9257806040517fede71dcc000000000000000000000000000000000000000000000000000000008152600401611b899190614013565b60405180910390fd5b5b50565b611b9e61241f565b73ffffffffffffffffffffffffffffffffffffffff168573ffffffffffffffffffffffffffffffffffffffff161480611be45750611be385611bde61241f565b6117a3565b5b611c23576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401611c1a90615044565b60405180910390fd5b611c308585858585612427565b5050505050565b6000807f0000000000000000000000000000000000000000000000000000000000000000834686886080016020810190611c71919061470f565b896040016020810190611c849190614778565b8a6060016020810190611c979190614857565b604051602001611cad97969594939291906151a5565b604051602081830303815290604052905060008180519060200120905085600001358114925050509392505050565b6000611ce88383612748565b73ffffffffffffffffffffffffffffffffffffffff167f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff1614905092915050565b600073ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff1603611dad576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401611da490615298565b60405180910390fd5b6000611db761241f565b90506000611dc48561276f565b90506000611dd18561276f565b9050611de2836000898585896127e9565b8460008088815260200190815260200160002060008973ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000828254611e4191906146a5565b925050819055508673ffffffffffffffffffffffffffffffffffffffff16600073ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff167fc3d58168c5ae7397731d063d5bbf3d657854427343f4c083240f7aacaa2d0f628989604051611ebf9291906152b8565b60405180910390a4611ed6836000898585896127ff565b611ee583600089898989612807565b50505050505050565b611ef661241f565b73ffffffffffffffffffffffffffffffffffffffff16611f1461158b565b73ffffffffffffffffffffffffffffffffffffffff1614611f6a576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401611f619061532d565b60405180910390fd5b565b6000600460009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16905081600460006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055508173ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a35050565b600080610e10620151806138404261204a91906146a5565b612054919061534d565b61205e919061537e565b9050600681101580156120715750601281105b91505090565b600060608060005b868690508161ffff161015612145578287878361ffff168181106120a6576120a5614916565b5b90506040020160000160208101906120be919061441d565b6040516020016120cf9291906153f6565b60405160208183030381529060405292508187878361ffff168181106120f8576120f7614916565b5b90506040020160200160208101906121109190614857565b60405160200161212192919061541e565b6040516020818303038152906040529150808061213d90614a0f565b91505061207f565b5060007f0000000000000000000000000000000000000000000000000000000000000000854685858c60400160208101906121809190614778565b8d60600160208101906121939190614857565b6040516020016121a99796959493929190615446565b604051602081830303815290604052905060008180519060200120905088600001358114945050505050949350505050565b6121ed6121e661241f565b83836129de565b5050565b600080600660008460ff1660ff16815260200190815260200160002060000160009054906101000a900461ffff1661ffff16119050919050565b61223361241f565b73ffffffffffffffffffffffffffffffffffffffff168573ffffffffffffffffffffffffffffffffffffffff16148061227957506122788561227361241f565b6117a3565b5b6122b8576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016122af90615044565b60405180910390fd5b6122c58585858585612b4a565b5050505050565b600080600090507a184f03e93ff9f4daa797ed6e38ed64bf6a1f010000000000000000831061232a577a184f03e93ff9f4daa797ed6e38ed64bf6a1f01000000000000000083816123205761231f614f38565b5b0492506040810190505b6d04ee2d6d415b85acef81000000008310612367576d04ee2d6d415b85acef8100000000838161235d5761235c614f38565b5b0492506020810190505b662386f26fc10000831061239657662386f26fc10000838161238c5761238b614f38565b5b0492506010810190505b6305f5e10083106123bf576305f5e10083816123b5576123b4614f38565b5b0492506008810190505b61271083106123e45761271083816123da576123d9614f38565b5b0492506004810190505b6064831061240757606483816123fd576123fc614f38565b5b0492506002810190505b600a8310612416576001810190505b80915050919050565b600033905090565b815183511461246b576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161246290615531565b60405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff16036124da576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016124d1906155c3565b60405180910390fd5b60006124e461241f565b90506124f48187878787876127e9565b60005b84518110156126a557600085828151811061251557612514614916565b5b60200260200101519050600085838151811061253457612533614916565b5b60200260200101519050600080600084815260200190815260200160002060008b73ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020549050818110156125d5576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016125cc90615655565b60405180910390fd5b81810360008085815260200190815260200160002060008c73ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055508160008085815260200190815260200160002060008b73ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600082825461268a91906146a5565b925050819055505050508061269e90614945565b90506124f7565b508473ffffffffffffffffffffffffffffffffffffffff168673ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff167f4a39dc06d4c0dbc64b70af90fd698a233a518aa5d07e595d983b8c0526c8f7fb878760405161271c929190615675565b60405180910390a46127328187878787876127ff565b612740818787878787612de5565b505050505050565b60008060006127578585612fbc565b915091506127648161300d565b819250505092915050565b60606000600167ffffffffffffffff81111561278e5761278d613883565b5b6040519080825280602002602001820160405280156127bc5781602001602082028036833780820191505090505b50905082816000815181106127d4576127d3614916565b5b60200260200101818152505080915050919050565b6127f7868686868686613173565b505050505050565b505050505050565b6128268473ffffffffffffffffffffffffffffffffffffffff16613343565b156129d6578373ffffffffffffffffffffffffffffffffffffffff1663f23a6e6187878686866040518663ffffffff1660e01b815260040161286c9594939291906156f6565b6020604051808303816000875af19250505080156128a857506040513d601f19601f820116820180604052508101906128a59190615765565b60015b61294d576128b461579f565b806308c379a00361291057506128c86157c1565b806128d35750612912565b806040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016129079190613703565b60405180910390fd5b505b6040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401612944906158c3565b60405180910390fd5b63f23a6e6160e01b7bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916817bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916146129d4576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016129cb90615955565b60405180910390fd5b505b505050505050565b8173ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff1603612a4c576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401612a43906159e7565b60405180910390fd5b80600160008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548160ff0219169083151502179055508173ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff167f17307eab39ab6107e8899845ad3d59bd9653f200f220920489ca2b5937696c3183604051612b3d919061362b565b60405180910390a3505050565b600073ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff1603612bb9576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401612bb0906155c3565b60405180910390fd5b6000612bc361241f565b90506000612bd08561276f565b90506000612bdd8561276f565b9050612bed8389898585896127e9565b600080600088815260200190815260200160002060008a73ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054905085811015612c84576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401612c7b90615655565b60405180910390fd5b85810360008089815260200190815260200160002060008b73ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055508560008089815260200190815260200160002060008a73ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000828254612d3991906146a5565b925050819055508773ffffffffffffffffffffffffffffffffffffffff168973ffffffffffffffffffffffffffffffffffffffff168573ffffffffffffffffffffffffffffffffffffffff167fc3d58168c5ae7397731d063d5bbf3d657854427343f4c083240f7aacaa2d0f628a8a604051612db69291906152b8565b60405180910390a4612dcc848a8a86868a6127ff565b612dda848a8a8a8a8a612807565b505050505050505050565b612e048473ffffffffffffffffffffffffffffffffffffffff16613343565b15612fb4578373ffffffffffffffffffffffffffffffffffffffff1663bc197c8187878686866040518663ffffffff1660e01b8152600401612e4a959493929190615a07565b6020604051808303816000875af1925050508015612e8657506040513d601f19601f82011682018060405250810190612e839190615765565b60015b612f2b57612e9261579f565b806308c379a003612eee5750612ea66157c1565b80612eb15750612ef0565b806040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401612ee59190613703565b60405180910390fd5b505b6040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401612f22906158c3565b60405180910390fd5b63bc197c8160e01b7bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916817bffffffffffffffffffffffffffffffffffffffffffffffffffffffff191614612fb2576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401612fa990615955565b60405180910390fd5b505b505050505050565b6000806041835103612ffd5760008060006020860151925060408601519150606086015160001a9050612ff187828585613366565b94509450505050613006565b60006002915091505b9250929050565b6000600481111561302157613020615a6f565b5b81600481111561303457613033615a6f565b5b0315613170576001600481111561304e5761304d615a6f565b5b81600481111561306157613060615a6f565b5b036130a1576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161309890615aea565b60405180910390fd5b600260048111156130b5576130b4615a6f565b5b8160048111156130c8576130c7615a6f565b5b03613108576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016130ff90615b56565b60405180910390fd5b6003600481111561311c5761311b615a6f565b5b81600481111561312f5761312e615a6f565b5b0361316f576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161316690615be8565b60405180910390fd5b5b50565b613181868686868686613448565b600073ffffffffffffffffffffffffffffffffffffffff168573ffffffffffffffffffffffffffffffffffffffff16036132325760005b8351811015613230578281815181106131d4576131d3614916565b5b6020026020010151600360008684815181106131f3576131f2614916565b5b60200260200101518152602001908152602001600020600082825461321891906146a5565b925050819055508061322990614945565b90506131b8565b505b600073ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff160361333b5760005b835181101561333957600084828151811061328757613286614916565b5b6020026020010151905060008483815181106132a6576132a5614916565b5b602002602001015190506000600360008481526020019081526020016000205490508181101561330b576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161330290615c7a565b60405180910390fd5b81810360036000858152602001908152602001600020819055505050508061333290614945565b9050613269565b505b505050505050565b6000808273ffffffffffffffffffffffffffffffffffffffff163b119050919050565b6000807f7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a08360001c11156133a157600060039150915061343f565b6000600187878787604051600081526020016040526040516133c69493929190615cb3565b6020604051602081039080840390855afa1580156133e8573d6000803e3d6000fd5b505050602060405103519050600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff16036134365760006001925092505061343f565b80600092509250505b94509492505050565b505050505050565b6040518060600160405280600061ffff168152602001600061ffff168152602001600081525090565b6000604051905090565b600080fd5b600080fd5b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b60006134b88261348d565b9050919050565b6134c8816134ad565b81146134d357600080fd5b50565b6000813590506134e5816134bf565b92915050565b6000819050919050565b6134fe816134eb565b811461350957600080fd5b50565b60008135905061351b816134f5565b92915050565b6000806040838503121561353857613537613483565b5b6000613546858286016134d6565b92505060206135578582860161350c565b9150509250929050565b61356a816134eb565b82525050565b60006020820190506135856000830184613561565b92915050565b60007fffffffff0000000000000000000000000000000000000000000000000000000082169050919050565b6135c08161358b565b81146135cb57600080fd5b50565b6000813590506135dd816135b7565b92915050565b6000602082840312156135f9576135f8613483565b5b6000613607848285016135ce565b91505092915050565b60008115159050919050565b61362581613610565b82525050565b6000602082019050613640600083018461361c565b92915050565b60006020828403121561365c5761365b613483565b5b600061366a8482850161350c565b91505092915050565b600081519050919050565b600082825260208201905092915050565b60005b838110156136ad578082015181840152602081019050613692565b60008484015250505050565b6000601f19601f8301169050919050565b60006136d582613673565b6136df818561367e565b93506136ef81856020860161368f565b6136f8816136b9565b840191505092915050565b6000602082019050818103600083015261371d81846136ca565b905092915050565b600060ff82169050919050565b61373b81613725565b811461374657600080fd5b50565b60008135905061375881613732565b92915050565b60006020828403121561377457613773613483565b5b600061378284828501613749565b91505092915050565b600061ffff82169050919050565b6137a28161378b565b82525050565b6137b1816134eb565b82525050565b6060820160008201516137cd6000850182613799565b5060208201516137e06020850182613799565b5060408201516137f360408501826137a8565b50505050565b600060608201905061380e60008301846137b7565b92915050565b6000806040838503121561382b5761382a613483565b5b600061383985828601613749565b925050602061384a858286016134d6565b9150509250929050565b61385d8161378b565b82525050565b60006020820190506138786000830184613854565b92915050565b600080fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b6138bb826136b9565b810181811067ffffffffffffffff821117156138da576138d9613883565b5b80604052505050565b60006138ed613479565b90506138f982826138b2565b919050565b600067ffffffffffffffff82111561391957613918613883565b5b602082029050602081019050919050565b600080fd5b600061394261393d846138fe565b6138e3565b905080838252602082019050602084028301858111156139655761396461392a565b5b835b8181101561398e578061397a888261350c565b845260208401935050602081019050613967565b5050509392505050565b600082601f8301126139ad576139ac61387e565b5b81356139bd84826020860161392f565b91505092915050565b600080fd5b600067ffffffffffffffff8211156139e6576139e5613883565b5b6139ef826136b9565b9050602081019050919050565b82818337600083830152505050565b6000613a1e613a19846139cb565b6138e3565b905082815260208101848484011115613a3a57613a396139c6565b5b613a458482856139fc565b509392505050565b600082601f830112613a6257613a6161387e565b5b8135613a72848260208601613a0b565b91505092915050565b600080600080600060a08688031215613a9757613a96613483565b5b6000613aa5888289016134d6565b9550506020613ab6888289016134d6565b945050604086013567ffffffffffffffff811115613ad757613ad6613488565b5b613ae388828901613998565b935050606086013567ffffffffffffffff811115613b0457613b03613488565b5b613b1088828901613998565b925050608086013567ffffffffffffffff811115613b3157613b30613488565b5b613b3d88828901613a4d565b9150509295509295909350565b600080fd5b600060a08284031215613b6557613b64613b4a565b5b81905092915050565b613b778161378b565b8114613b8257600080fd5b50565b600081359050613b9481613b6e565b92915050565b60008060008060808587031215613bb457613bb3613483565b5b600085013567ffffffffffffffff811115613bd257613bd1613488565b5b613bde87828801613b4f565b9450506020613bef878288016134d6565b9350506040613c0087828801613749565b9250506060613c1187828801613b85565b91505092959194509250565b6000819050919050565b6000613c42613c3d613c388461348d565b613c1d565b61348d565b9050919050565b6000613c5482613c27565b9050919050565b6000613c6682613c49565b9050919050565b613c7681613c5b565b82525050565b6000602082019050613c916000830184613c6d565b92915050565b600067ffffffffffffffff821115613cb257613cb1613883565b5b602082029050602081019050919050565b6000613cd6613cd184613c97565b6138e3565b90508083825260208201905060208402830185811115613cf957613cf861392a565b5b835b81811015613d225780613d0e88826134d6565b845260208401935050602081019050613cfb565b5050509392505050565b600082601f830112613d4157613d4061387e565b5b8135613d51848260208601613cc3565b91505092915050565b60008060408385031215613d7157613d70613483565b5b600083013567ffffffffffffffff811115613d8f57613d8e613488565b5b613d9b85828601613d2c565b925050602083013567ffffffffffffffff811115613dbc57613dbb613488565b5b613dc885828601613998565b9150509250929050565b600081519050919050565b600082825260208201905092915050565b6000819050602082019050919050565b6000613e0a83836137a8565b60208301905092915050565b6000602082019050919050565b6000613e2e82613dd2565b613e388185613ddd565b9350613e4383613dee565b8060005b83811015613e74578151613e5b8882613dfe565b9750613e6683613e16565b925050600181019050613e47565b5085935050505092915050565b60006020820190508181036000830152613e9b8184613e23565b905092915050565b6000613eae8261348d565b9050919050565b613ebe81613ea3565b8114613ec957600080fd5b50565b600081359050613edb81613eb5565b92915050565b600060208284031215613ef757613ef6613483565b5b6000613f0584828501613ecc565b91505092915050565b600060808284031215613f2457613f23613b4a565b5b81905092915050565b600080fd5b60008083601f840112613f4857613f4761387e565b5b8235905067ffffffffffffffff811115613f6557613f64613f2d565b5b602083019150836040820283011115613f8157613f8061392a565b5b9250929050565b600080600060408486031215613fa157613fa0613483565b5b600084013567ffffffffffffffff811115613fbf57613fbe613488565b5b613fcb86828701613f0e565b935050602084013567ffffffffffffffff811115613fec57613feb613488565b5b613ff886828701613f32565b92509250509250925092565b61400d816134ad565b82525050565b60006020820190506140286000830184614004565b92915050565b60007fffffffffffffffff00000000000000000000000000000000000000000000000082169050919050565b6140638161402e565b82525050565b600060408201905061407e600083018561405a565b61408b6020830184614004565b9392505050565b61409b81613725565b82525050565b60006040820190506140b66000830185614092565b6140c36020830184614092565b9392505050565b60008083601f8401126140e0576140df61387e565b5b8235905067ffffffffffffffff8111156140fd576140fc613f2d565b5b6020830191508360018202830111156141195761411861392a565b5b9250929050565b6000806020838503121561413757614136613483565b5b600083013567ffffffffffffffff81111561415557614154613488565b5b614161858286016140ca565b92509250509250929050565b61417681613610565b811461418157600080fd5b50565b6000813590506141938161416d565b92915050565b600080604083850312156141b0576141af613483565b5b60006141be858286016134d6565b92505060206141cf85828601614184565b9150509250929050565b600080604083850312156141f0576141ef613483565b5b60006141fe85828601613749565b925050602061420f85828601613749565b9150509250929050565b60008083601f84011261422f5761422e61387e565b5b8235905067ffffffffffffffff81111561424c5761424b613f2d565b5b6020830191508360208202830111156142685761426761392a565b5b9250929050565b60008083601f8401126142855761428461387e565b5b8235905067ffffffffffffffff8111156142a2576142a1613f2d565b5b6020830191508360608202830111156142be576142bd61392a565b5b9250929050565b600080600080604085870312156142df576142de613483565b5b600085013567ffffffffffffffff8111156142fd576142fc613488565b5b61430987828801614219565b9450945050602085013567ffffffffffffffff81111561432c5761432b613488565b5b6143388782880161426f565b925092505092959194509250565b6000806040838503121561435d5761435c613483565b5b600061436b858286016134d6565b925050602061437c858286016134d6565b9150509250929050565b600080600080600060a086880312156143a2576143a1613483565b5b60006143b0888289016134d6565b95505060206143c1888289016134d6565b94505060406143d28882890161350c565b93505060606143e38882890161350c565b925050608086013567ffffffffffffffff81111561440457614403613488565b5b61441088828901613a4d565b9150509295509295909350565b60006020828403121561443357614432613483565b5b6000614441848285016134d6565b91505092915050565b7f455243313135353a2061646472657373207a65726f206973206e6f742061207660008201527f616c6964206f776e657200000000000000000000000000000000000000000000602082015250565b60006144a6602a8361367e565b91506144b18261444a565b604082019050919050565b600060208201905081810360008301526144d581614499565b9050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b6000600282049050600182168061452357607f821691505b602082108103614536576145356144dc565b5b50919050565b600081905092915050565b60008190508160005260206000209050919050565b600081546145698161450b565b614573818661453c565b9450600182166000811461458e57600181146145a3576145d6565b60ff19831686528115158202860193506145d6565b6145ac85614547565b60005b838110156145ce578154818901526001820191506020810190506145af565b838801955050505b50505092915050565b60006145ea82613673565b6145f4818561453c565b935061460481856020860161368f565b80840191505092915050565b600061461c828561455c565b915061462882846145df565b91508190509392505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b600061466e826134eb565b9150614679836134eb565b9250828202614687816134eb565b9150828204841483151761469e5761469d614634565b5b5092915050565b60006146b0826134eb565b91506146bb836134eb565b92508282019050808211156146d3576146d2614634565b5b92915050565b60006146e48261378b565b91506146ef8361378b565b9250828201905061ffff81111561470957614708614634565b5b92915050565b60006020828403121561472557614724613483565b5b600061473384828501613b85565b91505092915050565b600063ffffffff82169050919050565b6147558161473c565b811461476057600080fd5b50565b6000813590506147728161474c565b92915050565b60006020828403121561478e5761478d613483565b5b600061479c84828501614763565b91505092915050565b600080fd5b600080fd5b600080fd5b600080833560016020038436030381126147d1576147d06147a5565b5b80840192508235915067ffffffffffffffff8211156147f3576147f26147aa565b5b60208301925060018202360383131561480f5761480e6147af565b5b509250929050565b600067ffffffffffffffff82169050919050565b61483481614817565b811461483f57600080fd5b50565b6000813590506148518161482b565b92915050565b60006020828403121561486d5761486c613483565b5b600061487b84828501614842565b91505092915050565b7f455243313135353a206163636f756e747320616e6420696473206c656e67746860008201527f206d69736d617463680000000000000000000000000000000000000000000000602082015250565b60006148e060298361367e565b91506148eb82614884565b604082019050919050565b6000602082019050818103600083015261490f816148d3565b9050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b6000614950826134eb565b91507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff820361498257614981614634565b5b600182019050919050565b600080fd5b6000604082840312156149a8576149a761498d565b5b6149b260406138e3565b905060006149c2848285016134d6565b60008301525060206149d684828501614842565b60208301525092915050565b6000604082840312156149f8576149f7613483565b5b6000614a0684828501614992565b91505092915050565b6000614a1a8261378b565b915061ffff8203614a2e57614a2d614634565b5b600182019050919050565b600082905092915050565b60006020601f8301049050919050565b600082821b905092915050565b600060088302614a917fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff82614a54565b614a9b8683614a54565b95508019841693508086168417925050509392505050565b6000614ace614ac9614ac4846134eb565b613c1d565b6134eb565b9050919050565b6000819050919050565b614ae883614ab3565b614afc614af482614ad5565b848454614a61565b825550505050565b600090565b614b11614b04565b614b1c818484614adf565b505050565b5b81811015614b4057614b35600082614b09565b600181019050614b22565b5050565b601f821115614b8557614b5681614547565b614b5f84614a44565b81016020851015614b6e578190505b614b82614b7a85614a44565b830182614b21565b50505b505050565b600082821c905092915050565b6000614ba860001984600802614b8a565b1980831691505092915050565b6000614bc18383614b97565b9150826002028217905092915050565b614bdb8383614a39565b67ffffffffffffffff811115614bf457614bf3613883565b5b614bfe825461450b565b614c09828285614b44565b6000601f831160018114614c385760008415614c26578287013590505b614c308582614bb5565b865550614c98565b601f198416614c4686614547565b60005b82811015614c6e57848901358255600182019150602085019450602081019050614c49565b86831015614c8b5784890135614c87601f891682614b97565b8355505b6001600288020188555050505b50505050505050565b60008135614cae81613b6e565b80915050919050565b60008160001b9050919050565b600061ffff614cd284614cb7565b9350801983169250808416831791505092915050565b6000614d03614cfe614cf98461378b565b613c1d565b61378b565b9050919050565b6000819050919050565b614d1d82614ce8565b614d30614d2982614d0a565b8354614cc4565b8255505050565b60008160101b9050919050565b600063ffff0000614d5484614d37565b9350801983169250808416831791505092915050565b614d7382614ce8565b614d86614d7f82614d0a565b8354614d44565b8255505050565b60008135614d9a816134f5565b80915050919050565b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff614dcf84614cb7565b9350801983169250808416831791505092915050565b614dee82614ab3565b614e01614dfa82614ad5565b8354614da3565b8255505050565b600081016000830180614e1a81614ca1565b9050614e268184614d14565b505050600081016020830180614e3b81614ca1565b9050614e478184614d6a565b505050600181016040830180614e5c81614d8d565b9050614e688184614de5565b5050505050565b614e798282614e08565b5050565b6000614e8882613725565b915060ff8203614e9b57614e9a614634565b5b600182019050919050565b7f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160008201527f6464726573730000000000000000000000000000000000000000000000000000602082015250565b6000614f0260268361367e565b9150614f0d82614ea6565b604082019050919050565b60006020820190508181036000830152614f3181614ef5565b9050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b6000604082019050614f7c6000830185614004565b614f896020830184614004565b9392505050565b600081519050614f9f8161416d565b92915050565b600060208284031215614fbb57614fba613483565b5b6000614fc984828501614f90565b91505092915050565b7f455243313135353a2063616c6c6572206973206e6f7420746f6b656e206f776e60008201527f6572206f7220617070726f766564000000000000000000000000000000000000602082015250565b600061502e602e8361367e565b915061503982614fd2565b604082019050919050565b6000602082019050818103600083015261505d81615021565b9050919050565b6000819050919050565b61507f61507a8261402e565b615064565b82525050565b60008160601b9050919050565b600061509d82615085565b9050919050565b60006150af82615092565b9050919050565b6150c76150c2826134ad565b6150a4565b82525050565b60008160c01b9050919050565b60006150e5826150cd565b9050919050565b6150fd6150f882614817565b6150da565b82525050565b60008160f81b9050919050565b600061511b82615103565b9050919050565b61513361512e82613725565b615110565b82525050565b60008160f01b9050919050565b600061515182615139565b9050919050565b6151696151648261378b565b615146565b82525050565b60008160e01b9050919050565b60006151878261516f565b9050919050565b61519f61519a8261473c565b61517c565b82525050565b60006151b1828a61506e565b6008820191506151c182896150b6565b6014820191506151d182886150ec565b6008820191506151e18287615122565b6001820191506151f18286615158565b600282019150615201828561518e565b60048201915061521182846150ec565b60088201915081905098975050505050505050565b7f455243313135353a206d696e7420746f20746865207a65726f2061646472657360008201527f7300000000000000000000000000000000000000000000000000000000000000602082015250565b600061528260218361367e565b915061528d82615226565b604082019050919050565b600060208201905081810360008301526152b181615275565b9050919050565b60006040820190506152cd6000830185613561565b6152da6020830184613561565b9392505050565b7f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572600082015250565b600061531760208361367e565b9150615322826152e1565b602082019050919050565b600060208201905081810360008301526153468161530a565b9050919050565b6000615358826134eb565b9150615363836134eb565b92508261537357615372614f38565b5b828206905092915050565b6000615389826134eb565b9150615394836134eb565b9250826153a4576153a3614f38565b5b828204905092915050565b600081519050919050565b600081905092915050565b60006153d0826153af565b6153da81856153ba565b93506153ea81856020860161368f565b80840191505092915050565b600061540282856153c5565b915061540e82846150b6565b6014820191508190509392505050565b600061542a82856153c5565b915061543682846150ec565b6008820191508190509392505050565b6000615452828a61506e565b60088201915061546282896150b6565b60148201915061547282886150ec565b60088201915061548282876153c5565b915061548e82866153c5565b915061549a828561518e565b6004820191506154aa82846150ec565b60088201915081905098975050505050505050565b7f455243313135353a2069647320616e6420616d6f756e7473206c656e6774682060008201527f6d69736d61746368000000000000000000000000000000000000000000000000602082015250565b600061551b60288361367e565b9150615526826154bf565b604082019050919050565b6000602082019050818103600083015261554a8161550e565b9050919050565b7f455243313135353a207472616e7366657220746f20746865207a65726f20616460008201527f6472657373000000000000000000000000000000000000000000000000000000602082015250565b60006155ad60258361367e565b91506155b882615551565b604082019050919050565b600060208201905081810360008301526155dc816155a0565b9050919050565b7f455243313135353a20696e73756666696369656e742062616c616e636520666f60008201527f72207472616e7366657200000000000000000000000000000000000000000000602082015250565b600061563f602a8361367e565b915061564a826155e3565b604082019050919050565b6000602082019050818103600083015261566e81615632565b9050919050565b6000604082019050818103600083015261568f8185613e23565b905081810360208301526156a38184613e23565b90509392505050565b600082825260208201905092915050565b60006156c8826153af565b6156d281856156ac565b93506156e281856020860161368f565b6156eb816136b9565b840191505092915050565b600060a08201905061570b6000830188614004565b6157186020830187614004565b6157256040830186613561565b6157326060830185613561565b818103608083015261574481846156bd565b90509695505050505050565b60008151905061575f816135b7565b92915050565b60006020828403121561577b5761577a613483565b5b600061578984828501615750565b91505092915050565b60008160e01c9050919050565b600060033d11156157be5760046000803e6157bb600051615792565b90505b90565b600060443d1061584e576157d3613479565b60043d036004823e80513d602482011167ffffffffffffffff821117156157fb57505061584e565b808201805167ffffffffffffffff811115615819575050505061584e565b80602083010160043d03850181111561583657505050505061584e565b615845826020018501866138b2565b82955050505050505b90565b7f455243313135353a207472616e7366657220746f206e6f6e2d4552433131353560008201527f526563656976657220696d706c656d656e746572000000000000000000000000602082015250565b60006158ad60348361367e565b91506158b882615851565b604082019050919050565b600060208201905081810360008301526158dc816158a0565b9050919050565b7f455243313135353a204552433131353552656365697665722072656a6563746560008201527f6420746f6b656e73000000000000000000000000000000000000000000000000602082015250565b600061593f60288361367e565b915061594a826158e3565b604082019050919050565b6000602082019050818103600083015261596e81615932565b9050919050565b7f455243313135353a2073657474696e6720617070726f76616c2073746174757360008201527f20666f722073656c660000000000000000000000000000000000000000000000602082015250565b60006159d160298361367e565b91506159dc82615975565b604082019050919050565b60006020820190508181036000830152615a00816159c4565b9050919050565b600060a082019050615a1c6000830188614004565b615a296020830187614004565b8181036040830152615a3b8186613e23565b90508181036060830152615a4f8185613e23565b90508181036080830152615a6381846156bd565b90509695505050505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fd5b7f45434453413a20696e76616c6964207369676e61747572650000000000000000600082015250565b6000615ad460188361367e565b9150615adf82615a9e565b602082019050919050565b60006020820190508181036000830152615b0381615ac7565b9050919050565b7f45434453413a20696e76616c6964207369676e6174757265206c656e67746800600082015250565b6000615b40601f8361367e565b9150615b4b82615b0a565b602082019050919050565b60006020820190508181036000830152615b6f81615b33565b9050919050565b7f45434453413a20696e76616c6964207369676e6174757265202773272076616c60008201527f7565000000000000000000000000000000000000000000000000000000000000602082015250565b6000615bd260228361367e565b9150615bdd82615b76565b604082019050919050565b60006020820190508181036000830152615c0181615bc5565b9050919050565b7f455243313135353a206275726e20616d6f756e74206578636565647320746f7460008201527f616c537570706c79000000000000000000000000000000000000000000000000602082015250565b6000615c6460288361367e565b9150615c6f82615c08565b604082019050919050565b60006020820190508181036000830152615c9381615c57565b9050919050565b6000819050919050565b615cad81615c9a565b82525050565b6000608082019050615cc86000830187615ca4565b615cd56020830186614092565b615ce26040830185615ca4565b615cef6060830184615ca4565b9594505050505056fea2646970667358221220c9dfde0ff34e49c0f79766f3537319a7bdae6e7ea5ef9392a08a1db0aadf2ebc64736f6c63430008110033

Deployed Bytecode

0x6080604052600436106101655760003560e01c80638c02f79b116100d1578063bd85b0391161008a578063e985e9c511610064578063e985e9c514610552578063f006ad971461058f578063f242432a146105cc578063f2fde38b146105f557610165565b8063bd85b039146104c3578063bef98d4b14610500578063e5b8fb921461052957610165565b80638c02f79b146103c55780638da5cb5b146103ee578063900af1921461041957806390b7178e14610445578063a0bcfc7f14610471578063a22cb4651461049a57610165565b80634163933a116101235780634163933a146102c457806341f43434146102e05780634e1273f41461030b5780634f558e791461034857806351cff8d914610385578063715018a6146103ae57610165565b8062fdd58e1461016a57806301ffc9a7146101a75780630e89341c146101e457806314119bb714610221578063235f0e931461025e5780632eb2c2d61461029b575b600080fd5b34801561017657600080fd5b50610191600480360381019061018c9190613521565b61061e565b60405161019e9190613570565b60405180910390f35b3480156101b357600080fd5b506101ce60048036038101906101c991906135e3565b6106e6565b6040516101db919061362b565b60405180910390f35b3480156101f057600080fd5b5061020b60048036038101906102069190613646565b6107c8565b6040516102189190613703565b60405180910390f35b34801561022d57600080fd5b506102486004803603810190610243919061375e565b6107fc565b60405161025591906137f9565b60405180910390f35b34801561026a57600080fd5b5061028560048036038101906102809190613814565b610879565b6040516102929190613863565b60405180910390f35b3480156102a757600080fd5b506102c260048036038101906102bd9190613a7b565b6108e8565b005b6102de60048036038101906102d99190613b9a565b61093b565b005b3480156102ec57600080fd5b506102f5610e4c565b6040516103029190613c7c565b60405180910390f35b34801561031757600080fd5b50610332600480360381019061032d9190613d5a565b610e5e565b60405161033f9190613e81565b60405180910390f35b34801561035457600080fd5b5061036f600480360381019061036a9190613646565b610f77565b60405161037c919061362b565b60405180910390f35b34801561039157600080fd5b506103ac60048036038101906103a79190613ee1565b610f8b565b005b3480156103ba57600080fd5b506103c3611083565b005b3480156103d157600080fd5b506103ec60048036038101906103e79190613f88565b611097565b005b3480156103fa57600080fd5b5061040361158b565b6040516104109190614013565b60405180910390f35b34801561042557600080fd5b5061042e6115b5565b60405161043c929190614069565b60405180910390f35b34801561045157600080fd5b5061045a611602565b6040516104689291906140a1565b60405180910390f35b34801561047d57600080fd5b5061049860048036038101906104939190614120565b61162d565b005b3480156104a657600080fd5b506104c160048036038101906104bc9190614199565b61164b565b005b3480156104cf57600080fd5b506104ea60048036038101906104e59190613646565b611664565b6040516104f79190613570565b60405180910390f35b34801561050c57600080fd5b50610527600480360381019061052291906141d9565b611681565b005b34801561053557600080fd5b50610550600480360381019061054b91906142c5565b6116c3565b005b34801561055e57600080fd5b5061057960048036038101906105749190614346565b6117a3565b604051610586919061362b565b60405180910390f35b34801561059b57600080fd5b506105b660048036038101906105b1919061375e565b611837565b6040516105c3919061362b565b60405180910390f35b3480156105d857600080fd5b506105f360048036038101906105ee9190614386565b61188b565b005b34801561060157600080fd5b5061061c6004803603810190610617919061441d565b6118de565b005b60008073ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff160361068e576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610685906144bc565b60405180910390fd5b60008083815260200190815260200160002060008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054905092915050565b60007fd9b67a26000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916827bffffffffffffffffffffffffffffffffffffffffffffffffffffffff191614806107b157507f0e89341c000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916827bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916145b806107c157506107c082611961565b5b9050919050565b606060086107d5836119cb565b6040516020016107e6929190614610565b6040516020818303038152906040529050919050565b610804613450565b600660008360ff1660ff1681526020019081526020016000206040518060600160405290816000820160009054906101000a900461ffff1661ffff1661ffff1681526020016000820160029054906101000a900461ffff1661ffff1661ffff1681526020016001820154815250509050919050565b6000600760008460ff1660ff16815260200190815260200160002060008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900461ffff16905092915050565b843373ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff16146109265761092533611a99565b5b6109338686868686611b96565b505050505050565b6000600660008460ff1660ff1681526020019081526020016000206040518060600160405290816000820160009054906101000a900461ffff1661ffff1661ffff1681526020016000820160029054906101000a900461ffff1661ffff1661ffff16815260200160018201548152505090506109b683611837565b6109ec576040517fee19d49100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8161ffff168160400151610a009190614663565b3414610a38576040517f26c3439000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b806000015161ffff168261ffff16610a528560ff16611664565b610a5c91906146a5565b1115610a94576040517fd8ea507600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b806020015161ffff1682600760008660ff1660ff16815260200190815260200160002060008773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900461ffff16610b0d91906146d9565b61ffff161115610b49576040517f5b3e6f0000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b846080016020810190610b5c919061470f565b61ffff168261ffff161115610b9d576040517f8d515e9c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b42856040016020810190610bb19190614778565b63ffffffff161015610bef576040517f1eee9c0700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b610bfa858486611c37565b610c30576040517fff39ee9500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b610c908560000135868060200190610c4891906147b4565b8080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f82011690508083019250505050505050611cdc565b610cc6576040517f1490dab400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60056000866060016020810190610cdd9190614857565b67ffffffffffffffff1667ffffffffffffffff16815260200190815260200160002060009054906101000a900460ff1615610d44576040517fa2a6490500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b81600760008560ff1660ff16815260200190815260200160002060008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008282829054906101000a900461ffff16610db791906146d9565b92506101000a81548161ffff021916908361ffff160217905550600160056000876060016020810190610dea9190614857565b67ffffffffffffffff1667ffffffffffffffff16815260200190815260200160002060006101000a81548160ff021916908315150217905550610e45848460ff168461ffff1660405180602001604052806000815250611d3e565b5050505050565b6daaeb6d7670e522a718067333cd4e81565b60608151835114610ea4576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610e9b906148f6565b60405180910390fd5b6000835167ffffffffffffffff811115610ec157610ec0613883565b5b604051908082528060200260200182016040528015610eef5781602001602082028036833780820191505090505b50905060005b8451811015610f6c57610f3c858281518110610f1457610f13614916565b5b6020026020010151858381518110610f2f57610f2e614916565b5b602002602001015161061e565b828281518110610f4f57610f4e614916565b5b60200260200101818152505080610f6590614945565b9050610ef5565b508091505092915050565b600080610f8383611664565b119050919050565b610f93611eee565b600047905060008103610fd2576040517f895dcf4500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff1603611038576040517fcaa9014f00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8173ffffffffffffffffffffffffffffffffffffffff166108fc829081150290604051600060405180830381858888f1935050505015801561107e573d6000803e3d6000fd5b505050565b61108b611eee565b6110956000611f6c565b565b600082829050036110d4576040517fd643e0ef00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60006110de612032565b6110f757600960019054906101000a900460ff16611108565b600960009054906101000a900460ff165b905060005b838390508161ffff1610156112d957600084848361ffff1681811061113557611134614916565b5b90506040020180360381019061114b91906149e2565b9050600a6000826000015173ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000826020015167ffffffffffffffff16815260200190815260200160002060009054906101000a900460ff16156111f4576040517fa342934900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6001600a6000836000015173ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000836020015167ffffffffffffffff16815260200190815260200160002060006101000a81548160ff021916908315150217905550806020015167ffffffffffffffff16816000015173ffffffffffffffffffffffffffffffffffffffff167ffe6ae5739c1d6d64298f06f3740a387add6ff26d92ce81a5c93f02ee4226ab9960405160405180910390a35080806112d190614a0f565b91505061110d565b50428460400160208101906112ee9190614778565b63ffffffff16101561132c576040517fc4e9f92900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61133884848433612077565b61136e576040517f07f0245a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6113ce846000013585806020019061138691906147b4565b8080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f82011690508083019250505050505050611cdc565b611404576040517f03a5536400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6005600085606001602081019061141b9190614857565b67ffffffffffffffff1667ffffffffffffffff16815260200190815260200160002060009054906101000a900460ff1615611482576040517f4ab4c93000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b82829050600760008360ff1660ff16815260200190815260200160002060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008282829054906101000a900461ffff166114f891906146d9565b92506101000a81548161ffff021916908361ffff16021790555060016005600086606001602081019061152b9190614857565b67ffffffffffffffff1667ffffffffffffffff16815260200190815260200160002060006101000a81548160ff021916908315150217905550611585338260ff168585905060405180602001604052806000815250611d3e565b50505050565b6000600460009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16905090565b6000807faca187d2a59b04b400000000000000000000000000000000000000000000000091507f00000000000000000000000089994365e8538f61a906c84cb15b6d415051fed090509091565b600080600960009054906101000a900460ff169150600960019054906101000a900460ff1690509091565b611635611eee565b818160089182611646929190614bd1565b505050565b8161165581611a99565b61165f83836121db565b505050565b600060036000838152602001908152602001600020549050919050565b611689611eee565b81600960006101000a81548160ff021916908360ff16021790555080600960016101000a81548160ff021916908360ff1602179055505050565b6116cb611eee565b81819050848490501461170a576040517fcaa9014f00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60005b848490508160ff16101561179c5782828260ff1681811061173157611730614916565b5b9050606002016006600087878560ff1681811061175157611750614916565b5b9050602002016020810190611766919061375e565b60ff1660ff16815260200190815260200160002081816117869190614e6f565b905050808061179490614e7d565b91505061170d565b5050505050565b6000600160008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff16905092915050565b6000600960009054906101000a900460ff1660ff168260ff16141580156118735750600960019054906101000a900460ff1660ff168260ff1614155b80156118845750611883826121f1565b5b9050919050565b843373ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff16146118c9576118c833611a99565b5b6118d6868686868661222b565b505050505050565b6118e6611eee565b600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff1603611955576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161194c90614f18565b60405180910390fd5b61195e81611f6c565b50565b60007f01ffc9a7000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916827bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916149050919050565b6060600060016119da846122cc565b01905060008167ffffffffffffffff8111156119f9576119f8613883565b5b6040519080825280601f01601f191660200182016040528015611a2b5781602001600182028036833780820191505090505b509050600082602001820190505b600115611a8e578080600190039150507f3031323334353637383961626364656600000000000000000000000000000000600a86061a8153600a8581611a8257611a81614f38565b5b04945060008503611a39575b819350505050919050565b60006daaeb6d7670e522a718067333cd4e73ffffffffffffffffffffffffffffffffffffffff163b1115611b93576daaeb6d7670e522a718067333cd4e73ffffffffffffffffffffffffffffffffffffffff1663c617113430836040518363ffffffff1660e01b8152600401611b10929190614f67565b602060405180830381865afa158015611b2d573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611b519190614fa5565b611b9257806040517fede71dcc000000000000000000000000000000000000000000000000000000008152600401611b899190614013565b60405180910390fd5b5b50565b611b9e61241f565b73ffffffffffffffffffffffffffffffffffffffff168573ffffffffffffffffffffffffffffffffffffffff161480611be45750611be385611bde61241f565b6117a3565b5b611c23576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401611c1a90615044565b60405180910390fd5b611c308585858585612427565b5050505050565b6000807faca187d2a59b04b4000000000000000000000000000000000000000000000000834686886080016020810190611c71919061470f565b896040016020810190611c849190614778565b8a6060016020810190611c979190614857565b604051602001611cad97969594939291906151a5565b604051602081830303815290604052905060008180519060200120905085600001358114925050509392505050565b6000611ce88383612748565b73ffffffffffffffffffffffffffffffffffffffff167f00000000000000000000000089994365e8538f61a906c84cb15b6d415051fed073ffffffffffffffffffffffffffffffffffffffff1614905092915050565b600073ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff1603611dad576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401611da490615298565b60405180910390fd5b6000611db761241f565b90506000611dc48561276f565b90506000611dd18561276f565b9050611de2836000898585896127e9565b8460008088815260200190815260200160002060008973ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000828254611e4191906146a5565b925050819055508673ffffffffffffffffffffffffffffffffffffffff16600073ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff167fc3d58168c5ae7397731d063d5bbf3d657854427343f4c083240f7aacaa2d0f628989604051611ebf9291906152b8565b60405180910390a4611ed6836000898585896127ff565b611ee583600089898989612807565b50505050505050565b611ef661241f565b73ffffffffffffffffffffffffffffffffffffffff16611f1461158b565b73ffffffffffffffffffffffffffffffffffffffff1614611f6a576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401611f619061532d565b60405180910390fd5b565b6000600460009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16905081600460006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055508173ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a35050565b600080610e10620151806138404261204a91906146a5565b612054919061534d565b61205e919061537e565b9050600681101580156120715750601281105b91505090565b600060608060005b868690508161ffff161015612145578287878361ffff168181106120a6576120a5614916565b5b90506040020160000160208101906120be919061441d565b6040516020016120cf9291906153f6565b60405160208183030381529060405292508187878361ffff168181106120f8576120f7614916565b5b90506040020160200160208101906121109190614857565b60405160200161212192919061541e565b6040516020818303038152906040529150808061213d90614a0f565b91505061207f565b5060007faca187d2a59b04b4000000000000000000000000000000000000000000000000854685858c60400160208101906121809190614778565b8d60600160208101906121939190614857565b6040516020016121a99796959493929190615446565b604051602081830303815290604052905060008180519060200120905088600001358114945050505050949350505050565b6121ed6121e661241f565b83836129de565b5050565b600080600660008460ff1660ff16815260200190815260200160002060000160009054906101000a900461ffff1661ffff16119050919050565b61223361241f565b73ffffffffffffffffffffffffffffffffffffffff168573ffffffffffffffffffffffffffffffffffffffff16148061227957506122788561227361241f565b6117a3565b5b6122b8576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016122af90615044565b60405180910390fd5b6122c58585858585612b4a565b5050505050565b600080600090507a184f03e93ff9f4daa797ed6e38ed64bf6a1f010000000000000000831061232a577a184f03e93ff9f4daa797ed6e38ed64bf6a1f01000000000000000083816123205761231f614f38565b5b0492506040810190505b6d04ee2d6d415b85acef81000000008310612367576d04ee2d6d415b85acef8100000000838161235d5761235c614f38565b5b0492506020810190505b662386f26fc10000831061239657662386f26fc10000838161238c5761238b614f38565b5b0492506010810190505b6305f5e10083106123bf576305f5e10083816123b5576123b4614f38565b5b0492506008810190505b61271083106123e45761271083816123da576123d9614f38565b5b0492506004810190505b6064831061240757606483816123fd576123fc614f38565b5b0492506002810190505b600a8310612416576001810190505b80915050919050565b600033905090565b815183511461246b576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161246290615531565b60405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff16036124da576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016124d1906155c3565b60405180910390fd5b60006124e461241f565b90506124f48187878787876127e9565b60005b84518110156126a557600085828151811061251557612514614916565b5b60200260200101519050600085838151811061253457612533614916565b5b60200260200101519050600080600084815260200190815260200160002060008b73ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020549050818110156125d5576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016125cc90615655565b60405180910390fd5b81810360008085815260200190815260200160002060008c73ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055508160008085815260200190815260200160002060008b73ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600082825461268a91906146a5565b925050819055505050508061269e90614945565b90506124f7565b508473ffffffffffffffffffffffffffffffffffffffff168673ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff167f4a39dc06d4c0dbc64b70af90fd698a233a518aa5d07e595d983b8c0526c8f7fb878760405161271c929190615675565b60405180910390a46127328187878787876127ff565b612740818787878787612de5565b505050505050565b60008060006127578585612fbc565b915091506127648161300d565b819250505092915050565b60606000600167ffffffffffffffff81111561278e5761278d613883565b5b6040519080825280602002602001820160405280156127bc5781602001602082028036833780820191505090505b50905082816000815181106127d4576127d3614916565b5b60200260200101818152505080915050919050565b6127f7868686868686613173565b505050505050565b505050505050565b6128268473ffffffffffffffffffffffffffffffffffffffff16613343565b156129d6578373ffffffffffffffffffffffffffffffffffffffff1663f23a6e6187878686866040518663ffffffff1660e01b815260040161286c9594939291906156f6565b6020604051808303816000875af19250505080156128a857506040513d601f19601f820116820180604052508101906128a59190615765565b60015b61294d576128b461579f565b806308c379a00361291057506128c86157c1565b806128d35750612912565b806040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016129079190613703565b60405180910390fd5b505b6040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401612944906158c3565b60405180910390fd5b63f23a6e6160e01b7bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916817bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916146129d4576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016129cb90615955565b60405180910390fd5b505b505050505050565b8173ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff1603612a4c576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401612a43906159e7565b60405180910390fd5b80600160008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548160ff0219169083151502179055508173ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff167f17307eab39ab6107e8899845ad3d59bd9653f200f220920489ca2b5937696c3183604051612b3d919061362b565b60405180910390a3505050565b600073ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff1603612bb9576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401612bb0906155c3565b60405180910390fd5b6000612bc361241f565b90506000612bd08561276f565b90506000612bdd8561276f565b9050612bed8389898585896127e9565b600080600088815260200190815260200160002060008a73ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054905085811015612c84576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401612c7b90615655565b60405180910390fd5b85810360008089815260200190815260200160002060008b73ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055508560008089815260200190815260200160002060008a73ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000828254612d3991906146a5565b925050819055508773ffffffffffffffffffffffffffffffffffffffff168973ffffffffffffffffffffffffffffffffffffffff168573ffffffffffffffffffffffffffffffffffffffff167fc3d58168c5ae7397731d063d5bbf3d657854427343f4c083240f7aacaa2d0f628a8a604051612db69291906152b8565b60405180910390a4612dcc848a8a86868a6127ff565b612dda848a8a8a8a8a612807565b505050505050505050565b612e048473ffffffffffffffffffffffffffffffffffffffff16613343565b15612fb4578373ffffffffffffffffffffffffffffffffffffffff1663bc197c8187878686866040518663ffffffff1660e01b8152600401612e4a959493929190615a07565b6020604051808303816000875af1925050508015612e8657506040513d601f19601f82011682018060405250810190612e839190615765565b60015b612f2b57612e9261579f565b806308c379a003612eee5750612ea66157c1565b80612eb15750612ef0565b806040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401612ee59190613703565b60405180910390fd5b505b6040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401612f22906158c3565b60405180910390fd5b63bc197c8160e01b7bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916817bffffffffffffffffffffffffffffffffffffffffffffffffffffffff191614612fb2576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401612fa990615955565b60405180910390fd5b505b505050505050565b6000806041835103612ffd5760008060006020860151925060408601519150606086015160001a9050612ff187828585613366565b94509450505050613006565b60006002915091505b9250929050565b6000600481111561302157613020615a6f565b5b81600481111561303457613033615a6f565b5b0315613170576001600481111561304e5761304d615a6f565b5b81600481111561306157613060615a6f565b5b036130a1576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161309890615aea565b60405180910390fd5b600260048111156130b5576130b4615a6f565b5b8160048111156130c8576130c7615a6f565b5b03613108576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016130ff90615b56565b60405180910390fd5b6003600481111561311c5761311b615a6f565b5b81600481111561312f5761312e615a6f565b5b0361316f576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161316690615be8565b60405180910390fd5b5b50565b613181868686868686613448565b600073ffffffffffffffffffffffffffffffffffffffff168573ffffffffffffffffffffffffffffffffffffffff16036132325760005b8351811015613230578281815181106131d4576131d3614916565b5b6020026020010151600360008684815181106131f3576131f2614916565b5b60200260200101518152602001908152602001600020600082825461321891906146a5565b925050819055508061322990614945565b90506131b8565b505b600073ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff160361333b5760005b835181101561333957600084828151811061328757613286614916565b5b6020026020010151905060008483815181106132a6576132a5614916565b5b602002602001015190506000600360008481526020019081526020016000205490508181101561330b576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161330290615c7a565b60405180910390fd5b81810360036000858152602001908152602001600020819055505050508061333290614945565b9050613269565b505b505050505050565b6000808273ffffffffffffffffffffffffffffffffffffffff163b119050919050565b6000807f7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a08360001c11156133a157600060039150915061343f565b6000600187878787604051600081526020016040526040516133c69493929190615cb3565b6020604051602081039080840390855afa1580156133e8573d6000803e3d6000fd5b505050602060405103519050600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff16036134365760006001925092505061343f565b80600092509250505b94509492505050565b505050505050565b6040518060600160405280600061ffff168152602001600061ffff168152602001600081525090565b6000604051905090565b600080fd5b600080fd5b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b60006134b88261348d565b9050919050565b6134c8816134ad565b81146134d357600080fd5b50565b6000813590506134e5816134bf565b92915050565b6000819050919050565b6134fe816134eb565b811461350957600080fd5b50565b60008135905061351b816134f5565b92915050565b6000806040838503121561353857613537613483565b5b6000613546858286016134d6565b92505060206135578582860161350c565b9150509250929050565b61356a816134eb565b82525050565b60006020820190506135856000830184613561565b92915050565b60007fffffffff0000000000000000000000000000000000000000000000000000000082169050919050565b6135c08161358b565b81146135cb57600080fd5b50565b6000813590506135dd816135b7565b92915050565b6000602082840312156135f9576135f8613483565b5b6000613607848285016135ce565b91505092915050565b60008115159050919050565b61362581613610565b82525050565b6000602082019050613640600083018461361c565b92915050565b60006020828403121561365c5761365b613483565b5b600061366a8482850161350c565b91505092915050565b600081519050919050565b600082825260208201905092915050565b60005b838110156136ad578082015181840152602081019050613692565b60008484015250505050565b6000601f19601f8301169050919050565b60006136d582613673565b6136df818561367e565b93506136ef81856020860161368f565b6136f8816136b9565b840191505092915050565b6000602082019050818103600083015261371d81846136ca565b905092915050565b600060ff82169050919050565b61373b81613725565b811461374657600080fd5b50565b60008135905061375881613732565b92915050565b60006020828403121561377457613773613483565b5b600061378284828501613749565b91505092915050565b600061ffff82169050919050565b6137a28161378b565b82525050565b6137b1816134eb565b82525050565b6060820160008201516137cd6000850182613799565b5060208201516137e06020850182613799565b5060408201516137f360408501826137a8565b50505050565b600060608201905061380e60008301846137b7565b92915050565b6000806040838503121561382b5761382a613483565b5b600061383985828601613749565b925050602061384a858286016134d6565b9150509250929050565b61385d8161378b565b82525050565b60006020820190506138786000830184613854565b92915050565b600080fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b6138bb826136b9565b810181811067ffffffffffffffff821117156138da576138d9613883565b5b80604052505050565b60006138ed613479565b90506138f982826138b2565b919050565b600067ffffffffffffffff82111561391957613918613883565b5b602082029050602081019050919050565b600080fd5b600061394261393d846138fe565b6138e3565b905080838252602082019050602084028301858111156139655761396461392a565b5b835b8181101561398e578061397a888261350c565b845260208401935050602081019050613967565b5050509392505050565b600082601f8301126139ad576139ac61387e565b5b81356139bd84826020860161392f565b91505092915050565b600080fd5b600067ffffffffffffffff8211156139e6576139e5613883565b5b6139ef826136b9565b9050602081019050919050565b82818337600083830152505050565b6000613a1e613a19846139cb565b6138e3565b905082815260208101848484011115613a3a57613a396139c6565b5b613a458482856139fc565b509392505050565b600082601f830112613a6257613a6161387e565b5b8135613a72848260208601613a0b565b91505092915050565b600080600080600060a08688031215613a9757613a96613483565b5b6000613aa5888289016134d6565b9550506020613ab6888289016134d6565b945050604086013567ffffffffffffffff811115613ad757613ad6613488565b5b613ae388828901613998565b935050606086013567ffffffffffffffff811115613b0457613b03613488565b5b613b1088828901613998565b925050608086013567ffffffffffffffff811115613b3157613b30613488565b5b613b3d88828901613a4d565b9150509295509295909350565b600080fd5b600060a08284031215613b6557613b64613b4a565b5b81905092915050565b613b778161378b565b8114613b8257600080fd5b50565b600081359050613b9481613b6e565b92915050565b60008060008060808587031215613bb457613bb3613483565b5b600085013567ffffffffffffffff811115613bd257613bd1613488565b5b613bde87828801613b4f565b9450506020613bef878288016134d6565b9350506040613c0087828801613749565b9250506060613c1187828801613b85565b91505092959194509250565b6000819050919050565b6000613c42613c3d613c388461348d565b613c1d565b61348d565b9050919050565b6000613c5482613c27565b9050919050565b6000613c6682613c49565b9050919050565b613c7681613c5b565b82525050565b6000602082019050613c916000830184613c6d565b92915050565b600067ffffffffffffffff821115613cb257613cb1613883565b5b602082029050602081019050919050565b6000613cd6613cd184613c97565b6138e3565b90508083825260208201905060208402830185811115613cf957613cf861392a565b5b835b81811015613d225780613d0e88826134d6565b845260208401935050602081019050613cfb565b5050509392505050565b600082601f830112613d4157613d4061387e565b5b8135613d51848260208601613cc3565b91505092915050565b60008060408385031215613d7157613d70613483565b5b600083013567ffffffffffffffff811115613d8f57613d8e613488565b5b613d9b85828601613d2c565b925050602083013567ffffffffffffffff811115613dbc57613dbb613488565b5b613dc885828601613998565b9150509250929050565b600081519050919050565b600082825260208201905092915050565b6000819050602082019050919050565b6000613e0a83836137a8565b60208301905092915050565b6000602082019050919050565b6000613e2e82613dd2565b613e388185613ddd565b9350613e4383613dee565b8060005b83811015613e74578151613e5b8882613dfe565b9750613e6683613e16565b925050600181019050613e47565b5085935050505092915050565b60006020820190508181036000830152613e9b8184613e23565b905092915050565b6000613eae8261348d565b9050919050565b613ebe81613ea3565b8114613ec957600080fd5b50565b600081359050613edb81613eb5565b92915050565b600060208284031215613ef757613ef6613483565b5b6000613f0584828501613ecc565b91505092915050565b600060808284031215613f2457613f23613b4a565b5b81905092915050565b600080fd5b60008083601f840112613f4857613f4761387e565b5b8235905067ffffffffffffffff811115613f6557613f64613f2d565b5b602083019150836040820283011115613f8157613f8061392a565b5b9250929050565b600080600060408486031215613fa157613fa0613483565b5b600084013567ffffffffffffffff811115613fbf57613fbe613488565b5b613fcb86828701613f0e565b935050602084013567ffffffffffffffff811115613fec57613feb613488565b5b613ff886828701613f32565b92509250509250925092565b61400d816134ad565b82525050565b60006020820190506140286000830184614004565b92915050565b60007fffffffffffffffff00000000000000000000000000000000000000000000000082169050919050565b6140638161402e565b82525050565b600060408201905061407e600083018561405a565b61408b6020830184614004565b9392505050565b61409b81613725565b82525050565b60006040820190506140b66000830185614092565b6140c36020830184614092565b9392505050565b60008083601f8401126140e0576140df61387e565b5b8235905067ffffffffffffffff8111156140fd576140fc613f2d565b5b6020830191508360018202830111156141195761411861392a565b5b9250929050565b6000806020838503121561413757614136613483565b5b600083013567ffffffffffffffff81111561415557614154613488565b5b614161858286016140ca565b92509250509250929050565b61417681613610565b811461418157600080fd5b50565b6000813590506141938161416d565b92915050565b600080604083850312156141b0576141af613483565b5b60006141be858286016134d6565b92505060206141cf85828601614184565b9150509250929050565b600080604083850312156141f0576141ef613483565b5b60006141fe85828601613749565b925050602061420f85828601613749565b9150509250929050565b60008083601f84011261422f5761422e61387e565b5b8235905067ffffffffffffffff81111561424c5761424b613f2d565b5b6020830191508360208202830111156142685761426761392a565b5b9250929050565b60008083601f8401126142855761428461387e565b5b8235905067ffffffffffffffff8111156142a2576142a1613f2d565b5b6020830191508360608202830111156142be576142bd61392a565b5b9250929050565b600080600080604085870312156142df576142de613483565b5b600085013567ffffffffffffffff8111156142fd576142fc613488565b5b61430987828801614219565b9450945050602085013567ffffffffffffffff81111561432c5761432b613488565b5b6143388782880161426f565b925092505092959194509250565b6000806040838503121561435d5761435c613483565b5b600061436b858286016134d6565b925050602061437c858286016134d6565b9150509250929050565b600080600080600060a086880312156143a2576143a1613483565b5b60006143b0888289016134d6565b95505060206143c1888289016134d6565b94505060406143d28882890161350c565b93505060606143e38882890161350c565b925050608086013567ffffffffffffffff81111561440457614403613488565b5b61441088828901613a4d565b9150509295509295909350565b60006020828403121561443357614432613483565b5b6000614441848285016134d6565b91505092915050565b7f455243313135353a2061646472657373207a65726f206973206e6f742061207660008201527f616c6964206f776e657200000000000000000000000000000000000000000000602082015250565b60006144a6602a8361367e565b91506144b18261444a565b604082019050919050565b600060208201905081810360008301526144d581614499565b9050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b6000600282049050600182168061452357607f821691505b602082108103614536576145356144dc565b5b50919050565b600081905092915050565b60008190508160005260206000209050919050565b600081546145698161450b565b614573818661453c565b9450600182166000811461458e57600181146145a3576145d6565b60ff19831686528115158202860193506145d6565b6145ac85614547565b60005b838110156145ce578154818901526001820191506020810190506145af565b838801955050505b50505092915050565b60006145ea82613673565b6145f4818561453c565b935061460481856020860161368f565b80840191505092915050565b600061461c828561455c565b915061462882846145df565b91508190509392505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b600061466e826134eb565b9150614679836134eb565b9250828202614687816134eb565b9150828204841483151761469e5761469d614634565b5b5092915050565b60006146b0826134eb565b91506146bb836134eb565b92508282019050808211156146d3576146d2614634565b5b92915050565b60006146e48261378b565b91506146ef8361378b565b9250828201905061ffff81111561470957614708614634565b5b92915050565b60006020828403121561472557614724613483565b5b600061473384828501613b85565b91505092915050565b600063ffffffff82169050919050565b6147558161473c565b811461476057600080fd5b50565b6000813590506147728161474c565b92915050565b60006020828403121561478e5761478d613483565b5b600061479c84828501614763565b91505092915050565b600080fd5b600080fd5b600080fd5b600080833560016020038436030381126147d1576147d06147a5565b5b80840192508235915067ffffffffffffffff8211156147f3576147f26147aa565b5b60208301925060018202360383131561480f5761480e6147af565b5b509250929050565b600067ffffffffffffffff82169050919050565b61483481614817565b811461483f57600080fd5b50565b6000813590506148518161482b565b92915050565b60006020828403121561486d5761486c613483565b5b600061487b84828501614842565b91505092915050565b7f455243313135353a206163636f756e747320616e6420696473206c656e67746860008201527f206d69736d617463680000000000000000000000000000000000000000000000602082015250565b60006148e060298361367e565b91506148eb82614884565b604082019050919050565b6000602082019050818103600083015261490f816148d3565b9050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b6000614950826134eb565b91507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff820361498257614981614634565b5b600182019050919050565b600080fd5b6000604082840312156149a8576149a761498d565b5b6149b260406138e3565b905060006149c2848285016134d6565b60008301525060206149d684828501614842565b60208301525092915050565b6000604082840312156149f8576149f7613483565b5b6000614a0684828501614992565b91505092915050565b6000614a1a8261378b565b915061ffff8203614a2e57614a2d614634565b5b600182019050919050565b600082905092915050565b60006020601f8301049050919050565b600082821b905092915050565b600060088302614a917fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff82614a54565b614a9b8683614a54565b95508019841693508086168417925050509392505050565b6000614ace614ac9614ac4846134eb565b613c1d565b6134eb565b9050919050565b6000819050919050565b614ae883614ab3565b614afc614af482614ad5565b848454614a61565b825550505050565b600090565b614b11614b04565b614b1c818484614adf565b505050565b5b81811015614b4057614b35600082614b09565b600181019050614b22565b5050565b601f821115614b8557614b5681614547565b614b5f84614a44565b81016020851015614b6e578190505b614b82614b7a85614a44565b830182614b21565b50505b505050565b600082821c905092915050565b6000614ba860001984600802614b8a565b1980831691505092915050565b6000614bc18383614b97565b9150826002028217905092915050565b614bdb8383614a39565b67ffffffffffffffff811115614bf457614bf3613883565b5b614bfe825461450b565b614c09828285614b44565b6000601f831160018114614c385760008415614c26578287013590505b614c308582614bb5565b865550614c98565b601f198416614c4686614547565b60005b82811015614c6e57848901358255600182019150602085019450602081019050614c49565b86831015614c8b5784890135614c87601f891682614b97565b8355505b6001600288020188555050505b50505050505050565b60008135614cae81613b6e565b80915050919050565b60008160001b9050919050565b600061ffff614cd284614cb7565b9350801983169250808416831791505092915050565b6000614d03614cfe614cf98461378b565b613c1d565b61378b565b9050919050565b6000819050919050565b614d1d82614ce8565b614d30614d2982614d0a565b8354614cc4565b8255505050565b60008160101b9050919050565b600063ffff0000614d5484614d37565b9350801983169250808416831791505092915050565b614d7382614ce8565b614d86614d7f82614d0a565b8354614d44565b8255505050565b60008135614d9a816134f5565b80915050919050565b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff614dcf84614cb7565b9350801983169250808416831791505092915050565b614dee82614ab3565b614e01614dfa82614ad5565b8354614da3565b8255505050565b600081016000830180614e1a81614ca1565b9050614e268184614d14565b505050600081016020830180614e3b81614ca1565b9050614e478184614d6a565b505050600181016040830180614e5c81614d8d565b9050614e688184614de5565b5050505050565b614e798282614e08565b5050565b6000614e8882613725565b915060ff8203614e9b57614e9a614634565b5b600182019050919050565b7f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160008201527f6464726573730000000000000000000000000000000000000000000000000000602082015250565b6000614f0260268361367e565b9150614f0d82614ea6565b604082019050919050565b60006020820190508181036000830152614f3181614ef5565b9050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b6000604082019050614f7c6000830185614004565b614f896020830184614004565b9392505050565b600081519050614f9f8161416d565b92915050565b600060208284031215614fbb57614fba613483565b5b6000614fc984828501614f90565b91505092915050565b7f455243313135353a2063616c6c6572206973206e6f7420746f6b656e206f776e60008201527f6572206f7220617070726f766564000000000000000000000000000000000000602082015250565b600061502e602e8361367e565b915061503982614fd2565b604082019050919050565b6000602082019050818103600083015261505d81615021565b9050919050565b6000819050919050565b61507f61507a8261402e565b615064565b82525050565b60008160601b9050919050565b600061509d82615085565b9050919050565b60006150af82615092565b9050919050565b6150c76150c2826134ad565b6150a4565b82525050565b60008160c01b9050919050565b60006150e5826150cd565b9050919050565b6150fd6150f882614817565b6150da565b82525050565b60008160f81b9050919050565b600061511b82615103565b9050919050565b61513361512e82613725565b615110565b82525050565b60008160f01b9050919050565b600061515182615139565b9050919050565b6151696151648261378b565b615146565b82525050565b60008160e01b9050919050565b60006151878261516f565b9050919050565b61519f61519a8261473c565b61517c565b82525050565b60006151b1828a61506e565b6008820191506151c182896150b6565b6014820191506151d182886150ec565b6008820191506151e18287615122565b6001820191506151f18286615158565b600282019150615201828561518e565b60048201915061521182846150ec565b60088201915081905098975050505050505050565b7f455243313135353a206d696e7420746f20746865207a65726f2061646472657360008201527f7300000000000000000000000000000000000000000000000000000000000000602082015250565b600061528260218361367e565b915061528d82615226565b604082019050919050565b600060208201905081810360008301526152b181615275565b9050919050565b60006040820190506152cd6000830185613561565b6152da6020830184613561565b9392505050565b7f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572600082015250565b600061531760208361367e565b9150615322826152e1565b602082019050919050565b600060208201905081810360008301526153468161530a565b9050919050565b6000615358826134eb565b9150615363836134eb565b92508261537357615372614f38565b5b828206905092915050565b6000615389826134eb565b9150615394836134eb565b9250826153a4576153a3614f38565b5b828204905092915050565b600081519050919050565b600081905092915050565b60006153d0826153af565b6153da81856153ba565b93506153ea81856020860161368f565b80840191505092915050565b600061540282856153c5565b915061540e82846150b6565b6014820191508190509392505050565b600061542a82856153c5565b915061543682846150ec565b6008820191508190509392505050565b6000615452828a61506e565b60088201915061546282896150b6565b60148201915061547282886150ec565b60088201915061548282876153c5565b915061548e82866153c5565b915061549a828561518e565b6004820191506154aa82846150ec565b60088201915081905098975050505050505050565b7f455243313135353a2069647320616e6420616d6f756e7473206c656e6774682060008201527f6d69736d61746368000000000000000000000000000000000000000000000000602082015250565b600061551b60288361367e565b9150615526826154bf565b604082019050919050565b6000602082019050818103600083015261554a8161550e565b9050919050565b7f455243313135353a207472616e7366657220746f20746865207a65726f20616460008201527f6472657373000000000000000000000000000000000000000000000000000000602082015250565b60006155ad60258361367e565b91506155b882615551565b604082019050919050565b600060208201905081810360008301526155dc816155a0565b9050919050565b7f455243313135353a20696e73756666696369656e742062616c616e636520666f60008201527f72207472616e7366657200000000000000000000000000000000000000000000602082015250565b600061563f602a8361367e565b915061564a826155e3565b604082019050919050565b6000602082019050818103600083015261566e81615632565b9050919050565b6000604082019050818103600083015261568f8185613e23565b905081810360208301526156a38184613e23565b90509392505050565b600082825260208201905092915050565b60006156c8826153af565b6156d281856156ac565b93506156e281856020860161368f565b6156eb816136b9565b840191505092915050565b600060a08201905061570b6000830188614004565b6157186020830187614004565b6157256040830186613561565b6157326060830185613561565b818103608083015261574481846156bd565b90509695505050505050565b60008151905061575f816135b7565b92915050565b60006020828403121561577b5761577a613483565b5b600061578984828501615750565b91505092915050565b60008160e01c9050919050565b600060033d11156157be5760046000803e6157bb600051615792565b90505b90565b600060443d1061584e576157d3613479565b60043d036004823e80513d602482011167ffffffffffffffff821117156157fb57505061584e565b808201805167ffffffffffffffff811115615819575050505061584e565b80602083010160043d03850181111561583657505050505061584e565b615845826020018501866138b2565b82955050505050505b90565b7f455243313135353a207472616e7366657220746f206e6f6e2d4552433131353560008201527f526563656976657220696d706c656d656e746572000000000000000000000000602082015250565b60006158ad60348361367e565b91506158b882615851565b604082019050919050565b600060208201905081810360008301526158dc816158a0565b9050919050565b7f455243313135353a204552433131353552656365697665722072656a6563746560008201527f6420746f6b656e73000000000000000000000000000000000000000000000000602082015250565b600061593f60288361367e565b915061594a826158e3565b604082019050919050565b6000602082019050818103600083015261596e81615932565b9050919050565b7f455243313135353a2073657474696e6720617070726f76616c2073746174757360008201527f20666f722073656c660000000000000000000000000000000000000000000000602082015250565b60006159d160298361367e565b91506159dc82615975565b604082019050919050565b60006020820190508181036000830152615a00816159c4565b9050919050565b600060a082019050615a1c6000830188614004565b615a296020830187614004565b8181036040830152615a3b8186613e23565b90508181036060830152615a4f8185613e23565b90508181036080830152615a6381846156bd565b90509695505050505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fd5b7f45434453413a20696e76616c6964207369676e61747572650000000000000000600082015250565b6000615ad460188361367e565b9150615adf82615a9e565b602082019050919050565b60006020820190508181036000830152615b0381615ac7565b9050919050565b7f45434453413a20696e76616c6964207369676e6174757265206c656e67746800600082015250565b6000615b40601f8361367e565b9150615b4b82615b0a565b602082019050919050565b60006020820190508181036000830152615b6f81615b33565b9050919050565b7f45434453413a20696e76616c6964207369676e6174757265202773272076616c60008201527f7565000000000000000000000000000000000000000000000000000000000000602082015250565b6000615bd260228361367e565b9150615bdd82615b76565b604082019050919050565b60006020820190508181036000830152615c0181615bc5565b9050919050565b7f455243313135353a206275726e20616d6f756e74206578636565647320746f7460008201527f616c537570706c79000000000000000000000000000000000000000000000000602082015250565b6000615c6460288361367e565b9150615c6f82615c08565b604082019050919050565b60006020820190508181036000830152615c9381615c57565b9050919050565b6000819050919050565b615cad81615c9a565b82525050565b6000608082019050615cc86000830187615ca4565b615cd56020830186614092565b615ce26040830185615ca4565b615cef6060830184615ca4565b9594505050505056fea2646970667358221220c9dfde0ff34e49c0f79766f3537319a7bdae6e7ea5ef9392a08a1db0aadf2ebc64736f6c63430008110033

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.