ETH Price: $3,006.06 (+3.12%)
Gas: 2 Gwei

Token

fiveoutofnine (♞)
 

Overview

Max Total Supply

321

Holders

104

Market

Volume (24H)

N/A

Min Price (24H)

N/A

Max Price (24H)

N/A
Filtered by Token Holder
dyd.eth
Balance
1 ♞
0x056590f16d5b314a132bbcfb1283fec5d5c6e670
Loading...
Loading
Loading...
Loading
Loading...
Loading

OVERVIEW

fiveoutofnine is the first 100% on-chain chess engine, where minters play against the smart contract. Each move is minted as an NFT, and accompanied by a generative art piece.

# Exchange Pair Price  24H Volume % Volume

Contract Source Code Verified (Exact Match)

Contract Name:
fiveoutofnine

Compiler Version
v0.8.9+commit.e5eed63a

Optimization Enabled:
Yes with 500 runs

Other Settings:
default evmVersion
File 1 of 16 : fiveoutofnine.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.9;

import "@openzeppelin/contracts/token/ERC721/ERC721.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts/security/ReentrancyGuard.sol";
import "@openzeppelin/contracts/utils/Strings.sol";

import { Chess } from "./Chess.sol";
import { Engine } from "./Engine.sol";
import { fiveoutofnineART } from "./fiveoutofnineART.sol";

/// @title fiveoutofnine NFT - the first 100% on-chain chess engine.
/// @author fiveoutofnine
/// @notice This file has few docstrings (by choice) because most of it is standard. Refer to
/// {Chess}, {Engine}, and {fiveoutofnineART} for thorough documentation.
contract fiveoutofnine is ERC721, Ownable, ReentrancyGuard {
    using Chess for uint256;
    using Strings for uint256;

    uint256 public board;
    uint256 private internalId;

    mapping(uint256 => uint256) public tokenInternalIds;
    mapping(uint256 => Chess.Move) public tokenMoves;

    uint256 public totalSupply;
    string private baseURI;

    constructor() ERC721("fiveoutofnine", unicode"♞") {
        honorableMints();
        board = 0x32562300110101000010010000000C0099999000BCDE0B000000001;
        internalId = (1 << 0x80) | 2;
        totalSupply = 11;
    }

    function mintMove(uint256 _move, uint256 _depth) external payable nonReentrant {
        require(_depth >= 3 && _depth <= 10);
        require((internalId >> 0x80) < 59 && uint128(internalId) < 59);

        playMove(_move, _depth);
        _safeMint(msg.sender, totalSupply++);
    }

    function playMove(uint256 _move, uint256 _depth) internal {
        unchecked {
            uint256 inMemoryBoard = board;
            require(inMemoryBoard.isLegalMove(_move));

            inMemoryBoard = inMemoryBoard.applyMove(_move);
            (uint256 bestMove, bool isWhiteCheckmated) = Engine.searchMove(inMemoryBoard, _depth);

            tokenInternalIds[totalSupply] = internalId++;
            tokenMoves[totalSupply] = Chess.Move(board, (_depth << 24) | (_move << 12) | bestMove);

            if (bestMove == 0 || uint128(internalId) >= 59) {
                resetBoard();
            } else {
                board = inMemoryBoard.applyMove(bestMove);
                if (isWhiteCheckmated) {
                    resetBoard();
                }
            }
        }
    }

    function resetBoard() internal {
        board = 0x3256230011111100000000000000000099999900BCDECB000000001;
        internalId = ((internalId >> 0x80) + 1) << 0x80;
    }

    function tokenURI(uint256 _tokenId) public view virtual override returns (string memory) {
        return bytes(baseURI).length == 0
            ? _tokenURI(_tokenId)
            : string(abi.encodePacked(baseURI, _tokenId.toString()));
    }

    function _tokenURI(uint256 _tokenId) public view returns (string memory) {
        return fiveoutofnineART.getMetadata(tokenInternalIds[_tokenId], tokenMoves[_tokenId]);
    }

    function setBaseURI(string memory _baseURI) external onlyOwner {
        baseURI = _baseURI;
    }

    function honorableMints() internal {
        _safeMint(0xA85572Cd96f1643458f17340b6f0D6549Af482F5, 0);
        tokenInternalIds[0] = 0;
        tokenMoves[0] = Chess.Move(
            0x3256230011111100000000000000000099999900BCDECB000000001,
            0x851C4A2
        );

        _safeMint(0x3759328b1CE944642d36a61F06783f2865212515, 1);
        tokenInternalIds[1] = 1;
        tokenMoves[1] = Chess.Move(
            0x3256230010111100000000000190000099099900BCDECB000000001,
            0x759E51C
        );

        _safeMint(0xFD8eA0F05dB884A78B1A1C1B3767B9E5D6664764, 2);
        tokenInternalIds[2] = 2;
        tokenMoves[2] = Chess.Move(
            0x3256230010101100000100009190000009099900BCDECB000000001,
            0x64DB565
        );

        _safeMint(0x174787a207BF4eD4D8db0945602e49f42c146474, 3);
        tokenInternalIds[3] = 3;
        tokenMoves[3] = Chess.Move(
            0x3256230010100100000100009199100009009900BCDECB000000001,
            0x645A725
        );

        _safeMint(0x6dEa5dCFa64DC0bb4E5AC53A375A4377CF4eD0Ee, 4);
        tokenInternalIds[4] = 4;
        tokenMoves[4] = Chess.Move(
            0x3256230010100100000000009199100009009000BCDECB000000001,
            0x631A4DB
        );

        _safeMint(0x333601a803CAc32B7D17A38d32c9728A93b422f4, 5);
        tokenInternalIds[5] = 5;
        tokenMoves[5] = Chess.Move(
            0x3256230010000100001000009199D00009009000BC0ECB000000001,
            0x6693315
        );

        _safeMint(0x530cF036Ed4Fa58f7301a9C788C9806624ceFD19, 6);
        tokenInternalIds[6] = 6;
        tokenMoves[6] = Chess.Move(
            0x32502300100061000010000091990000090D9000BC0ECB000000001,
            0x64E1554
        );

        _safeMint(0xD6A9cB7aB95293a7D38f416Cd3A4Fe9059CCd5B2, 7);
        tokenInternalIds[7] = 7;
        tokenMoves[7] = Chess.Move(
            0x325023001006010000100D009199000009009000BC0ECB000000001,
            0x63532A5
        );

        _safeMint(0xaFDc1A3EF3992f53C10fC798d242E15E2F0DF51A, 8);
        tokenInternalIds[8] = 8;
        tokenMoves[8] = Chess.Move(
            0x305023001006010000100D0091992000090C9000B00ECB000000001,
            0x66E4000
        );

        _safeMint(0xC1A80D351232fD07EE5733b5F581E01C269068A9, 9);
        tokenInternalIds[9] = 1 << 0x80;
        tokenMoves[9] = Chess.Move(
            0x3256230011111100000000000000000099999900BCDECB000000001,
            0x646155E
        );

        _safeMint(0xF42D1c0c0165AF5625b2ecD5027c5C5554e5b039, 10);
        tokenInternalIds[10] = (1 << 0x80) | 1;
        tokenMoves[10] = Chess.Move(
            0x3256230011110100000001000000000099999000BCDECB000000001,
            0x62994DB
        );
    }
}

File 2 of 16 : ERC721.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.0 (token/ERC721/ERC721.sol)

pragma solidity ^0.8.0;

import "./IERC721.sol";
import "./IERC721Receiver.sol";
import "./extensions/IERC721Metadata.sol";
import "../../utils/Address.sol";
import "../../utils/Context.sol";
import "../../utils/Strings.sol";
import "../../utils/introspection/ERC165.sol";

/**
 * @dev Implementation of https://eips.ethereum.org/EIPS/eip-721[ERC721] Non-Fungible Token Standard, including
 * the Metadata extension, but not including the Enumerable extension, which is available separately as
 * {ERC721Enumerable}.
 */
contract ERC721 is Context, ERC165, IERC721, IERC721Metadata {
    using Address for address;
    using Strings for uint256;

    // Token name
    string private _name;

    // Token symbol
    string private _symbol;

    // Mapping from token ID to owner address
    mapping(uint256 => address) private _owners;

    // Mapping owner address to token count
    mapping(address => uint256) private _balances;

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

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

    /**
     * @dev Initializes the contract by setting a `name` and a `symbol` to the token collection.
     */
    constructor(string memory name_, string memory symbol_) {
        _name = name_;
        _symbol = symbol_;
    }

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

    /**
     * @dev See {IERC721-balanceOf}.
     */
    function balanceOf(address owner) public view virtual override returns (uint256) {
        require(owner != address(0), "ERC721: balance query for the zero address");
        return _balances[owner];
    }

    /**
     * @dev See {IERC721-ownerOf}.
     */
    function ownerOf(uint256 tokenId) public view virtual override returns (address) {
        address owner = _owners[tokenId];
        require(owner != address(0), "ERC721: owner query for nonexistent token");
        return owner;
    }

    /**
     * @dev See {IERC721Metadata-name}.
     */
    function name() public view virtual override returns (string memory) {
        return _name;
    }

    /**
     * @dev See {IERC721Metadata-symbol}.
     */
    function symbol() public view virtual override returns (string memory) {
        return _symbol;
    }

    /**
     * @dev See {IERC721Metadata-tokenURI}.
     */
    function tokenURI(uint256 tokenId) public view virtual override returns (string memory) {
        require(_exists(tokenId), "ERC721Metadata: URI query for nonexistent token");

        string memory baseURI = _baseURI();
        return bytes(baseURI).length > 0 ? string(abi.encodePacked(baseURI, tokenId.toString())) : "";
    }

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

    /**
     * @dev See {IERC721-approve}.
     */
    function approve(address to, uint256 tokenId) public virtual override {
        address owner = ERC721.ownerOf(tokenId);
        require(to != owner, "ERC721: approval to current owner");

        require(
            _msgSender() == owner || isApprovedForAll(owner, _msgSender()),
            "ERC721: approve caller is not owner nor approved for all"
        );

        _approve(to, tokenId);
    }

    /**
     * @dev See {IERC721-getApproved}.
     */
    function getApproved(uint256 tokenId) public view virtual override returns (address) {
        require(_exists(tokenId), "ERC721: approved query for nonexistent token");

        return _tokenApprovals[tokenId];
    }

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

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

    /**
     * @dev See {IERC721-transferFrom}.
     */
    function transferFrom(
        address from,
        address to,
        uint256 tokenId
    ) public virtual override {
        //solhint-disable-next-line max-line-length
        require(_isApprovedOrOwner(_msgSender(), tokenId), "ERC721: transfer caller is not owner nor approved");

        _transfer(from, to, tokenId);
    }

    /**
     * @dev See {IERC721-safeTransferFrom}.
     */
    function safeTransferFrom(
        address from,
        address to,
        uint256 tokenId
    ) public virtual override {
        safeTransferFrom(from, to, tokenId, "");
    }

    /**
     * @dev See {IERC721-safeTransferFrom}.
     */
    function safeTransferFrom(
        address from,
        address to,
        uint256 tokenId,
        bytes memory _data
    ) public virtual override {
        require(_isApprovedOrOwner(_msgSender(), tokenId), "ERC721: transfer caller is not owner nor approved");
        _safeTransfer(from, to, tokenId, _data);
    }

    /**
     * @dev Safely transfers `tokenId` token from `from` to `to`, checking first that contract recipients
     * are aware of the ERC721 protocol to prevent tokens from being forever locked.
     *
     * `_data` is additional data, it has no specified format and it is sent in call to `to`.
     *
     * This internal function is equivalent to {safeTransferFrom}, and can be used to e.g.
     * implement alternative mechanisms to perform token transfer, such as signature-based.
     *
     * Requirements:
     *
     * - `from` cannot be the zero address.
     * - `to` cannot be the zero address.
     * - `tokenId` token must exist and be owned by `from`.
     * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
     *
     * Emits a {Transfer} event.
     */
    function _safeTransfer(
        address from,
        address to,
        uint256 tokenId,
        bytes memory _data
    ) internal virtual {
        _transfer(from, to, tokenId);
        require(_checkOnERC721Received(from, to, tokenId, _data), "ERC721: transfer to non ERC721Receiver implementer");
    }

    /**
     * @dev Returns whether `tokenId` exists.
     *
     * Tokens can be managed by their owner or approved accounts via {approve} or {setApprovalForAll}.
     *
     * Tokens start existing when they are minted (`_mint`),
     * and stop existing when they are burned (`_burn`).
     */
    function _exists(uint256 tokenId) internal view virtual returns (bool) {
        return _owners[tokenId] != address(0);
    }

    /**
     * @dev Returns whether `spender` is allowed to manage `tokenId`.
     *
     * Requirements:
     *
     * - `tokenId` must exist.
     */
    function _isApprovedOrOwner(address spender, uint256 tokenId) internal view virtual returns (bool) {
        require(_exists(tokenId), "ERC721: operator query for nonexistent token");
        address owner = ERC721.ownerOf(tokenId);
        return (spender == owner || getApproved(tokenId) == spender || isApprovedForAll(owner, spender));
    }

    /**
     * @dev Safely mints `tokenId` and transfers it to `to`.
     *
     * Requirements:
     *
     * - `tokenId` must not exist.
     * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
     *
     * Emits a {Transfer} event.
     */
    function _safeMint(address to, uint256 tokenId) internal virtual {
        _safeMint(to, tokenId, "");
    }

    /**
     * @dev Same as {xref-ERC721-_safeMint-address-uint256-}[`_safeMint`], with an additional `data` parameter which is
     * forwarded in {IERC721Receiver-onERC721Received} to contract recipients.
     */
    function _safeMint(
        address to,
        uint256 tokenId,
        bytes memory _data
    ) internal virtual {
        _mint(to, tokenId);
        require(
            _checkOnERC721Received(address(0), to, tokenId, _data),
            "ERC721: transfer to non ERC721Receiver implementer"
        );
    }

    /**
     * @dev Mints `tokenId` and transfers it to `to`.
     *
     * WARNING: Usage of this method is discouraged, use {_safeMint} whenever possible
     *
     * Requirements:
     *
     * - `tokenId` must not exist.
     * - `to` cannot be the zero address.
     *
     * Emits a {Transfer} event.
     */
    function _mint(address to, uint256 tokenId) internal virtual {
        require(to != address(0), "ERC721: mint to the zero address");
        require(!_exists(tokenId), "ERC721: token already minted");

        _beforeTokenTransfer(address(0), to, tokenId);

        _balances[to] += 1;
        _owners[tokenId] = to;

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

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

        _beforeTokenTransfer(owner, address(0), tokenId);

        // Clear approvals
        _approve(address(0), tokenId);

        _balances[owner] -= 1;
        delete _owners[tokenId];

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

    /**
     * @dev Transfers `tokenId` from `from` to `to`.
     *  As opposed to {transferFrom}, this imposes no restrictions on msg.sender.
     *
     * Requirements:
     *
     * - `to` cannot be the zero address.
     * - `tokenId` token must be owned by `from`.
     *
     * Emits a {Transfer} event.
     */
    function _transfer(
        address from,
        address to,
        uint256 tokenId
    ) internal virtual {
        require(ERC721.ownerOf(tokenId) == from, "ERC721: transfer of token that is not own");
        require(to != address(0), "ERC721: transfer to the zero address");

        _beforeTokenTransfer(from, to, tokenId);

        // Clear approvals from the previous owner
        _approve(address(0), tokenId);

        _balances[from] -= 1;
        _balances[to] += 1;
        _owners[tokenId] = to;

        emit Transfer(from, to, tokenId);
    }

    /**
     * @dev Approve `to` to operate on `tokenId`
     *
     * Emits a {Approval} event.
     */
    function _approve(address to, uint256 tokenId) internal virtual {
        _tokenApprovals[tokenId] = to;
        emit Approval(ERC721.ownerOf(tokenId), to, tokenId);
    }

    /**
     * @dev Approve `operator` to operate on all of `owner` tokens
     *
     * Emits a {ApprovalForAll} event.
     */
    function _setApprovalForAll(
        address owner,
        address operator,
        bool approved
    ) internal virtual {
        require(owner != operator, "ERC721: approve to caller");
        _operatorApprovals[owner][operator] = approved;
        emit ApprovalForAll(owner, operator, approved);
    }

    /**
     * @dev Internal function to invoke {IERC721Receiver-onERC721Received} on a target address.
     * The call is not executed if the target address is not a contract.
     *
     * @param from address representing the previous owner of the given token ID
     * @param to target address that will receive the tokens
     * @param tokenId uint256 ID of the token to be transferred
     * @param _data bytes optional data to send along with the call
     * @return bool whether the call correctly returned the expected magic value
     */
    function _checkOnERC721Received(
        address from,
        address to,
        uint256 tokenId,
        bytes memory _data
    ) private returns (bool) {
        if (to.isContract()) {
            try IERC721Receiver(to).onERC721Received(_msgSender(), from, tokenId, _data) returns (bytes4 retval) {
                return retval == IERC721Receiver.onERC721Received.selector;
            } catch (bytes memory reason) {
                if (reason.length == 0) {
                    revert("ERC721: transfer to non ERC721Receiver implementer");
                } else {
                    assembly {
                        revert(add(32, reason), mload(reason))
                    }
                }
            }
        } else {
            return true;
        }
    }

    /**
     * @dev Hook that is called before any token transfer. This includes minting
     * and burning.
     *
     * Calling conditions:
     *
     * - When `from` and `to` are both non-zero, ``from``'s `tokenId` will be
     * transferred to `to`.
     * - When `from` is zero, `tokenId` will be minted for `to`.
     * - When `to` is zero, ``from``'s `tokenId` will be burned.
     * - `from` and `to` are never both zero.
     *
     * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
     */
    function _beforeTokenTransfer(
        address from,
        address to,
        uint256 tokenId
    ) internal virtual {}
}

File 3 of 16 : Ownable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.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 Returns the address of the current owner.
     */
    function owner() public view virtual returns (address) {
        return _owner;
    }

    /**
     * @dev Throws if called by any account other than the owner.
     */
    modifier onlyOwner() {
        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 4 of 16 : ReentrancyGuard.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.0 (security/ReentrancyGuard.sol)

pragma solidity ^0.8.0;

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

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

    uint256 private _status;

    constructor() {
        _status = _NOT_ENTERED;
    }

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

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

        _;

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

File 5 of 16 : Strings.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.0 (utils/Strings.sol)

pragma solidity ^0.8.0;

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

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

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

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

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

File 6 of 16 : Chess.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.9;

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

/// @title Utils library for fiveoutofnine (a 100% on-chain 6x6 chess engine)
/// @author fiveoutofnine
/// @dev Understand the representations of the chess pieces, board, and moves very carefully before
/// using this library:
/// ======================================Piece Representation======================================
/// Each chess piece is defined with 4 bits as follows:
///     * The first bit denotes the color (0 means black; 1 means white).
///     * The last 3 bits denote the type:
///         | Bits | # | Type   |
///         | ---- | - | ------ |
///         | 000  | 0 | Empty  |
///         | 001  | 1 | Pawn   |
///         | 010  | 2 | Bishop |
///         | 011  | 3 | Rook   |
///         | 100  | 4 | Knight |
///         | 101  | 5 | Queen  |
///         | 110  | 6 | King   |
/// ======================================Board Representation======================================
/// The board is an 8x8 representation of a 6x6 chess board. For efficiency, all information is
/// bitpacked into a single uint256. Thus, unlike typical implementations, board positions are
/// accessed via bit shifts and bit masks, as opposed to array accesses. Since each piece is 4 bits,
/// there are 64 ``indices'' to access:
///                                     63 62 61 60 59 58 57 56
///                                     55 54 53 52 51 50 49 48
///                                     47 46 45 44 43 42 41 40
///                                     39 38 37 36 35 34 33 32
///                                     31 30 29 28 27 26 25 24
///                                     23 22 21 20 19 18 17 16
///                                     15 14 13 12 11 10 09 08
///                                     07 06 05 04 03 02 01 00
/// All numbers in the figure above are in decimal representation.
/// For example, the piece at index 27 is accessed with ``(board >> (27 << 2)) & 0xF''.
///
/// The top/bottom rows and left/right columns are treated as sentinel rows/columns for efficient
/// boundary validation (see {Chess-generateMoves} and {Chess-isValid}). i.e., (63, ..., 56),
/// (07, ..., 00), (63, ..., 07), and (56, ..., 00) never contain pieces. Every bit in those rows
/// and columns should be ignored, except for the last bit. The last bit denotes whose turn it is to
/// play (0 means black's turn; 1 means white's turn). e.g. a potential starting position:
///                                Black
///                       00 00 00 00 00 00 00 00                    Black
///                       00 03 02 05 06 02 03 00                 ♜ ♝ ♛ ♚ ♝ ♜
///                       00 01 01 01 01 01 01 00                 ♟ ♟ ♟ ♟ ♟ ♟
///                       00 00 00 00 00 00 00 00     denotes
///                       00 00 00 00 00 00 00 00    the board
///                       00 09 09 09 09 09 09 00                 ♙ ♙ ♙ ♙ ♙ ♙
///                       00 11 12 13 14 12 11 00                 ♖ ♘ ♕ ♔ ♘ ♖
///                       00 00 00 00 00 00 00 01                    White
///                                White
/// All numbers in the example above are in decimal representation.
/// ======================================Move Representation=======================================
/// Each move is allocated 12 bits. The first 6 bits are the index the piece is moving from, and the
/// last 6 bits are the index the piece is moving to. Since the index representing a square is at
/// most 54, 6 bits sufficiently represents any index (0b111111 = 63 > 54). e.g. 1243 denotes a move
/// from index 19 to 27 (1243 = (19 << 6) | 27).
///
/// Since the board is represented by a uint256, consider including ``using Chess for uint256''.
library Chess {
    using Chess for uint256;
    using Chess for Chess.MovesArray;

    /// The depth, white's move, and black's move are bitpacked in that order as `metadata` for
    /// efficiency. As explained above, 12 bits sufficiently describe a move, so both white's and
    /// black's moves are allocated 12 bits each.
    struct Move {
        uint256 board;
        uint256 metadata;
    }

    /// ``moves'' are bitpacked into uint256s for efficiency. Since every move is defined by at most
    /// 12 bits, a uint256 can contain up to 21 moves via bitpacking (21 * 12 = 252 < 256).
    /// Therefore, `items` can contain up to 21 * 5 = 105 moves. 105 is a safe upper bound for the
    /// number of possible moves a given side may have during a real game, but be wary because there
    /// is no formal proof of the upper bound being less than or equal to 105.
    struct MovesArray {
        uint256 index;
        uint256[5] items;
    }

    /// @notice Takes in a board position, and applies the move `_move` to it.
    /// @dev After applying the move, the board's perspective is updated (see {rotate}). Thus,
    /// engines with symmterical search algorithms -- like negamax search -- probably work best.
    /// @param _board The board to apply the move to.
    /// @param _move The move to apply.
    /// @return The reversed board after applying `_move` to `_board`.
    function applyMove(uint256 _board, uint256 _move) internal pure returns (uint256) {
        unchecked {
            // Get piece at the from index
            uint256 piece = (_board >> ((_move >> 6) << 2)) & 0xF;
            // Replace 4 bits at the from index with 0000
            _board &= type(uint256).max ^ (0xF << ((_move >> 6) << 2));
            // Replace 4 bits at the to index with 0000
            _board &= type(uint256).max ^ (0xF << ((_move & 0x3F) << 2));
            // Place the piece at the to index
            _board |= (piece << ((_move & 0x3F) << 2));

            return _board.rotate();
        }
    }

    /// @notice Switches the perspective of the board by reversing its 4-bit subdivisions (e.g.
    /// 1100-0011 would become 0011-1100).
    /// @dev Since the last bit exchanges positions with the 4th bit, the turn identifier is updated
    /// as well.
    /// @param _board The board to reverse the perspective on.
    /// @return `_board` reversed.
    function rotate(uint256 _board) internal pure returns (uint256) {
        uint256 rotatedBoard;

        unchecked {
            for (uint256 i; i < 64; ++i) {
                rotatedBoard = (rotatedBoard << 4) | (_board & 0xF);
                _board >>= 4;
            }
        }

        return rotatedBoard;
    }

    /// @notice Generates all possible pseudolegal moves for a given position and color.
    /// @dev The last bit denotes which color to generate the moves for (see {Chess}). Also, the
    /// function errors if more than 105 moves are found (see {Chess-MovesArray}). All moves are
    /// expressed in code as shifts respective to the board's 8x8 representation (see {Chess}).
    /// @param _board The board position to generate moves for.
    /// @return Bitpacked uint256(s) containing moves.
    function generateMoves(uint256 _board) internal pure returns (uint256[5] memory) {
        Chess.MovesArray memory movesArray;
        uint256 move;
        uint256 moveTo;

        unchecked {
            // `0xDB5D33CB1BADB2BAA99A59238A179D71B69959551349138D30B289` is a mapping of indices
            // relative to the 6x6 board to indices relative to the 8x8 representation (see
            // {Chess-getAdjustedIndex}).
            for (
                uint256 index = 0xDB5D33CB1BADB2BAA99A59238A179D71B69959551349138D30B289;
                index != 0;
                index >>= 6
            ) {
                uint256 adjustedIndex = index & 0x3F;
                uint256 adjustedBoard = _board >> (adjustedIndex << 2);
                uint256 piece = adjustedBoard & 0xF;
                // Skip if square is empty or not the color of the board the function call is
                // analyzing.
                if (piece == 0 || piece >> 3 != _board & 1) continue;
                // The first bit can be discarded because the if statement above catches all
                // redundant squares.
                piece &= 7;

                if (piece == 1) { // Piece is a pawn.
                    // 1 square in front of the pawn is empty.
                    if ((adjustedBoard >> 0x20) & 0xF == 0) {
                        movesArray.append(adjustedIndex, adjustedIndex + 8);
                        // The pawn is in its starting row and 2 squares in front is empty. This
                        // must be nested because moving 2 squares would not be valid if there was
                        // an obstruction 1 square in front (i.e. pawns can not jump over pieces).
                        if (adjustedIndex >> 3 == 2 && (adjustedBoard >> 0x40) & 0xF == 0) {
                            movesArray.append(adjustedIndex, adjustedIndex + 0x10);
                        }
                    }
                    // Moving to the right diagonal by 1 captures a piece.
                    if (_board.isCapture(adjustedBoard >> 0x1C)) {
                        movesArray.append(adjustedIndex, adjustedIndex + 7); 
                    }
                    // Moving to the left diagonal by 1 captures a piece.
                    if (_board.isCapture(adjustedBoard >> 0x24)) {
                        movesArray.append(adjustedIndex, adjustedIndex + 9);
                    }
                } else if (piece > 3 && piece & 1 == 0) { // Piece is a knight or a king.
                    // Knights and kings always only have 8 positions to check relative to their
                    // current position, and the relative distances are always the same. For
                    // knights, positions to check are ±{6, 10, 15, 17}. This is bitpacked into
                    // `0x060A0F11` to reduce code redundancy. Similarly, the positions to check for
                    // kings are ±{1, 7, 8, 9}, which is `0x01070809` when bitpacked.
                    for (move = piece == 4 ? 0x060A0F11 : 0x01070809; move != 0; move >>= 8) {
                        if (_board.isValid(moveTo = adjustedIndex + (move & 0xFF))) {
                            movesArray.append(adjustedIndex, moveTo);
                        }
                        if (move <= adjustedIndex
                            && _board.isValid(moveTo = adjustedIndex - (move & 0xFF)))
                        {
                            movesArray.append(adjustedIndex, moveTo);
                        }
                    }
                } else {
                    // This else block generates moves for all sliding pieces. All of the 8 for
                    // loops terminate
                    //     * before a sliding piece makes an illegal move
                    //     * or after a sliding piece captures a piece.
                    if (piece != 2) { // Ortholinear pieces (i.e. rook and queen)
                        for (move = adjustedIndex + 1; _board.isValid(move); move += 1) {
                            movesArray.append(adjustedIndex, move);
                            if (_board.isCapture(_board >> (move << 2))) break;
                        }
                        for (move = adjustedIndex - 1; _board.isValid(move); move -= 1) {
                            movesArray.append(adjustedIndex, move);
                            if (_board.isCapture(_board >> (move << 2))) break;
                        }
                        for (move = adjustedIndex + 8; _board.isValid(move); move += 8) {
                            movesArray.append(adjustedIndex, move);
                            if (_board.isCapture(_board >> (move << 2))) break;
                        }
                        for (move = adjustedIndex - 8; _board.isValid(move); move -= 8) {
                            movesArray.append(adjustedIndex, move);
                            if (_board.isCapture(_board >> (move << 2))) break;
                        }
                    }
                    if (piece != 3) { // Diagonal pieces (i.e. bishop and queen)
                        for (move = adjustedIndex + 7; _board.isValid(move); move += 7) {
                            movesArray.append(adjustedIndex, move);
                            if (_board.isCapture(_board >> (move << 2))) break;
                        }
                        for (move = adjustedIndex - 7; _board.isValid(move); move -= 7) {
                            movesArray.append(adjustedIndex, move);
                            if (_board.isCapture(_board >> (move << 2))) break;
                        }
                        for (move = adjustedIndex + 9; _board.isValid(move); move += 9) {
                            movesArray.append(adjustedIndex, move);
                            if (_board.isCapture(_board >> (move << 2))) break;
                        }
                        for (move = adjustedIndex - 9; _board.isValid(move); move -= 9) {
                            // Handles the edge case where a white bishop believes it can capture
                            // the ``piece'' at index 0, when it is actually the turn identifier It
                            // would mistakenly believe it is valid move via capturing a black pawn.
                            if (move == 0) break;
                            movesArray.append(adjustedIndex, move);
                            if (_board.isCapture(_board >> (move << 2))) break;
                        }
                    }
                }
            }
        }

        return movesArray.items;
    }

    /// @notice Determines whether a move is a legal move or not (includes checking whether king is
    /// checked or not after the move).
    /// @param _board The board to analyze.
    /// @param _move The move to check.
    /// @return Whether the move is legal or not.
    function isLegalMove(uint256 _board, uint256 _move) internal pure returns (bool) {
        unchecked {
            uint256 fromIndex = _move >> 6;
            uint256 toIndex = _move & 0x3F;
            if ((0x7E7E7E7E7E7E00 >> fromIndex) & 1 == 0) return false;
            if ((0x7E7E7E7E7E7E00 >> toIndex) & 1 == 0) return false;

            uint256 pieceAtFromIndex = (_board >> (fromIndex << 2)) & 0xF;
            if (pieceAtFromIndex == 0) return false;
            if (pieceAtFromIndex >> 3 != _board & 1) return false;
            pieceAtFromIndex &= 7;

            uint256 adjustedBoard = _board >> (toIndex << 2);
            uint256 indexChange = toIndex < fromIndex
                    ? fromIndex - toIndex
                    : toIndex - fromIndex;
            if (pieceAtFromIndex == 1) {
                if (toIndex <= fromIndex) return false;
                indexChange = toIndex - fromIndex;
                if ((indexChange == 7 || indexChange == 9)) {
                    if (!_board.isCapture(adjustedBoard)) return false;
                } else if (indexChange == 8) {
                    if (!isValid(_board, toIndex)) return false;
                } else if (indexChange == 0x10) {
                    if (!isValid(_board, toIndex - 8) || !isValid(_board, toIndex)) return false;
                } else {
                    return false;
                }
            } else if (pieceAtFromIndex == 4 || pieceAtFromIndex == 6) {
                if (((pieceAtFromIndex == 4 ? 0x28440 : 0x382) >> indexChange) & 1 == 0) {
                    return false;
                }
                if (!isValid(_board, toIndex)) return false;
            } else {
                bool rayFound;
                if (pieceAtFromIndex != 2) {
                    rayFound = searchRay(_board, fromIndex, toIndex, 1)
                        || searchRay(_board, fromIndex, toIndex, 8);
                }
                if (pieceAtFromIndex != 3) {
                    rayFound = rayFound
                        || searchRay(_board, fromIndex, toIndex, 7)
                        || searchRay(_board, fromIndex, toIndex, 9);
                }
                if (!rayFound) return false;
            }

            if (Engine.negaMax(_board.applyMove(_move), 1) < -1_260) return false;

            return true;
        }
    }

    /// @notice Determines whether there is a clear path along a direction vector from one index to
    /// another index on the board.
    /// @dev The board's representation essentially flattens it from 2D to 1D, so `_directionVector`
    /// should be the change in index that represents the direction vector.
    /// @param _board The board to analyze.
    /// @param _fromIndex The index of the starting piece.
    /// @param _toIndex The index of the ending piece.
    /// @param _directionVector The direction vector of the ray.
    /// @return Whether there is a clear path between `_fromIndex` and `_toIndex` or not.
    function searchRay(
        uint256 _board,
        uint256 _fromIndex,
        uint256 _toIndex,
        uint256 _directionVector
    )
        internal pure
        returns (bool)
    {
        unchecked {
            uint256 indexChange;
            uint256 rayStart;
            uint256 rayEnd;
            if (_fromIndex < _toIndex) {
                indexChange = _toIndex - _fromIndex;
                rayStart = _fromIndex + _directionVector;
                rayEnd = _toIndex;
            } else {
                indexChange = _fromIndex - _toIndex;
                rayStart = _toIndex;
                rayEnd = _fromIndex - _directionVector;
            }
            if (indexChange % _directionVector != 0) return false;

            for (
                rayStart = rayStart;
                rayStart < rayEnd;
                rayStart += _directionVector
            ) {
                if (!isValid(_board, rayStart)) return false;
                if (isCapture(_board, _board >> (rayStart << 2))) return false;
            }

            if (!isValid(_board, rayStart)) return false;

            return rayStart == rayEnd;
        }
    }

    /// @notice Determines whether a move results in a capture or not.
    /// @param _board The board prior to the potential capture.
    /// @param _indexAdjustedBoard The board bitshifted to the to index to consider.
    /// @return Whether the move is a capture or not.
    function isCapture(uint256 _board, uint256 _indexAdjustedBoard) internal pure returns (bool) {
        unchecked {
            return (_indexAdjustedBoard & 0xF) != 0 // The square is not empty.
                && (_indexAdjustedBoard & 0xF) >> 3 != _board & 1; // The piece is opposite color.
        }
    }

    /// @notice Determines whether a move is valid or not (i.e. within bounds and not capturing
    /// same colored piece).
    /// @dev As mentioned above, the board representation has 2 sentinel rows and columns for
    /// efficient boundary validation as follows:
    ///                                           0 0 0 0 0 0 0 0
    ///                                           0 1 1 1 1 1 1 0
    ///                                           0 1 1 1 1 1 1 0
    ///                                           0 1 1 1 1 1 1 0
    ///                                           0 1 1 1 1 1 1 0
    ///                                           0 1 1 1 1 1 1 0
    ///                                           0 1 1 1 1 1 1 0
    ///                                           0 0 0 0 0 0 0 0,
    /// where 1 means a piece is within the board, and 0 means the piece is out of bounds. The bits
    /// are bitpacked into a uint256 (i.e. ``0x7E7E7E7E7E7E00 = 0 << 63 | ... | 0 << 0'') for
    /// efficiency.
    ///
    /// Moves that overflow the uint256 are computed correctly because bitshifting more than bits
    /// available results in 0. However, moves that underflow the uint256 (i.e. applying the move
    /// results in a negative index) must be checked beforehand.
    /// @param _board The board on which to consider whether the move is valid.
    /// @param _toIndex The to index of the move.
    /// @return Whether the move is valid or not.
    function isValid(uint256 _board, uint256 _toIndex) internal pure returns (bool) {
        unchecked {
            return (0x7E7E7E7E7E7E00 >> _toIndex) & 1 == 1 // Move is within bounds.
                && ((_board >> (_toIndex << 2)) & 0xF == 0 // Square is empty.
                    || (((_board >> (_toIndex << 2)) & 0xF) >> 3) != _board & 1); // Piece captured.
        }
    }

    /// @notice Maps an index relative to the 6x6 board to the index relative to the 8x8
    /// representation.
    /// @dev The indices are mapped as follows:
    ///                           35 34 33 32 31 30              54 53 52 51 50 49
    ///                           29 28 27 26 25 24              46 45 44 43 42 41
    ///                           23 22 21 20 19 18    mapped    38 37 36 35 34 33
    ///                           17 16 15 14 13 12      to      30 29 28 27 26 25
    ///                           11 10 09 08 07 06              22 21 20 19 18 17
    ///                           05 04 03 02 01 00              14 13 12 11 10 09
    /// All numbers in the figure above are in decimal representation. The bits are bitpacked into a
    /// uint256 (i.e. ``0xDB5D33CB1BADB2BAA99A59238A179D71B69959551349138D30B289 = 54 << (6 * 35) |
    /// ... | 9 << (6 * 0)'') for efficiency.
    /// @param _index Index relative to the 6x6 board.
    /// @return Index relative to the 8x8 representation.
    function getAdjustedIndex(uint256 _index) internal pure returns (uint256) {
        unchecked {
            return (
                (0xDB5D33CB1BADB2BAA99A59238A179D71B69959551349138D30B289 >> (_index * 6)) & 0x3F
            );
        }
    }

    /// @notice Appends a move to a {Chess-MovesArray} object.
    /// @dev Since each uint256 fits at most 21 moves (see {Chess-MovesArray}), {Chess-append}
    /// bitpacks 21 moves per uint256 before moving on to the next uint256.
    /// @param _movesArray {Chess-MovesArray} object to append the new move to.
    /// @param _fromMoveIndex Index the piece moves from.
    /// @param _toMoveIndex Index the piece moves to.
    function append(MovesArray memory _movesArray, uint256 _fromMoveIndex, uint256 _toMoveIndex)
        internal pure
    {
        unchecked {
            uint256 currentIndex = _movesArray.index;
            uint256 currentPartition = _movesArray.items[currentIndex];

            if (currentPartition > (1 << 0xF6)) {
                _movesArray.items[++_movesArray.index] = (_fromMoveIndex << 6) | _toMoveIndex;
            } else {
                _movesArray.items[currentIndex] = (currentPartition << 0xC)
                    | (_fromMoveIndex << 6)
                    | _toMoveIndex;
            }
        }
    }
}

File 7 of 16 : Engine.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.9;

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

/// @title A 6x6 chess engine with negamax search
/// @author fiveoutofnine
/// @notice Docstrings below are written from the perspective of black (i.e. written as if the
/// engine is always black). However, due to negamax's symmetric nature, the engine may be used for
/// white as well.
library Engine {
    using Chess for uint256;
    using Engine for uint256;

    /// @notice Searches for the ``best'' move.
    /// @dev The ply depth must be at least 3 because game ending scenarios are determined lazily.
    /// This is because {generateMoves} generates pseudolegal moves. Consider the following:
    ///     1. In the case of white checkmates black, depth 2 is necessary:
    ///         * Depth 1: This is the move black plays after considering depth 2.
    ///         * Depth 2: Check whether white captures black's king within 1 turn for every such
    ///           move. If so, white has checkmated black.
    ///     2. In the case of black checkmates white, depth 3 is necessary:
    ///         * Depth 1: This is the move black plays after considering depths 2 and 3.
    ///         * Depth 2: Generate all pseudolegal moves for white in response to black's move.
    ///         * Depth 3: Check whether black captures white's king within 1 turn for every such
    ///         * move. If so, black has checkmated white.
    /// The minimum depth required to cover all the cases above is 3. For simplicity, stalemates
    /// are treated as checkmates.
    ///
    /// The function returns 0 if the game is over after white's move (no collision with any
    /// potentially real moves because 0 is not a valid index), and returns true if the game is over
    /// after black's move.
    /// @param _board The board position to analyze.
    /// @param _depth The ply depth to analyze to. Must be at least 3.
    /// @return The best move for the player (denoted by the last bit in `_board`).
    /// @return Whether white is checkmated or not.
    function searchMove(uint256 _board, uint256 _depth) internal pure returns (uint256, bool) {
        uint256[5] memory moves = _board.generateMoves();
        if (moves[0] == 0) return (0, false);
        // See {Engine-negaMax} for explanation on why `bestScore` is set to -4_196.
        int256 bestScore = -4_196;
        int256 currentScore;
        uint256 bestMove;

        unchecked {
            for (uint256 i; moves[i] != 0; ++i) {
                for (uint256 movePartition = moves[i]; movePartition != 0; movePartition >>= 0xC) {
                    currentScore = _board.evaluateMove(movePartition & 0xFFF)
                        + negaMax(_board.applyMove(movePartition & 0xFFF), _depth - 1);
                    if (currentScore > bestScore) {
                        bestScore = currentScore;
                        bestMove = movePartition & 0xFFF;
                    }
                }
            }
        }

        // 1_260 is equivalent to 7 queens (7 * 180 = 1260). Since a king's capture is equivalent to
        // an evaluation of 4_000, ±1_260 catches all lines that include the capture of a king.
        if (bestScore < -1_260) return (0, false);
        return (bestMove, bestScore > 1_260);
    }

    /// @notice Searches and evaluates moves using a variant of the negamax search algorithm.
    /// @dev For efficiency, the function evaluates how good moves are and sums them up, rather than
    /// evaluating entire board positions. Thus, the only pruning the algorithm performs is when a
    /// king is captured. If a king is captured, it always returns -4,000, which is the king's value
    /// (see {Chess}) because there is nothing more to consider.
    /// @param _board The board position to analyze.
    /// @param _depth The ply depth to analyze to.
    /// @return The cumulative score searched to a ply depth of `_depth`, assuming each side picks
    /// their ``best'' (as decided by {Engine-evaluateMove}) moves.
    function negaMax(uint256 _board, uint256 _depth) internal pure returns (int256) {
        // Base case for the recursion.
        if (_depth == 0) return 0;
        uint256[5] memory moves = _board.generateMoves();
        // There is no ``best'' score if there are no moves to play.
        if (moves[0] == 0) return 0;
        // `bestScore` is initially set to -4_196 because no line will result in a cumulative
        // evaluation of <-4_195. -4_195 occurs, for example. when the engine's king is captured
        // (-4000), and the player captures an engine's queen on index 35 (-181) with knight from
        // index 52 (-14).
        int256 bestScore = -4_196;
        int256 currentScore;
        uint256 bestMove;

        unchecked {
            for (uint256 i; moves[i] != 0; ++i) {
                for (uint256 movePartition = moves[i]; movePartition != 0; movePartition >>= 0xC) {
                    currentScore = _board.evaluateMove(movePartition & 0xFFF);
                    if (currentScore > bestScore) {
                        bestScore = currentScore;
                        bestMove = movePartition & 0xFFF;
                    }
                }
            }

            // If a king is captured, stop the recursive call stack and return a score of -4_000.
            // There is nothing more to consider.
            if (((_board >> ((bestMove & 0x3F) << 2)) & 7) == 6) return -4_000;
            return _board & 1 == 0
                ? bestScore + negaMax(_board.applyMove(bestMove), _depth - 1)
                : -bestScore + negaMax(_board.applyMove(bestMove), _depth - 1);
        }
    }

    /// @notice Uses piece-square tables (PSTs) to evaluate how ``good'' a move is.
    /// @dev The PSTs were selected semi-arbitrarily with chess strategies in mind (e.g. pawns are
    /// good in the center). Updating them changes the way the engine ``thinks.'' Each piece's PST
    /// is bitpacked into as few uint256s as possible for efficiency (see {Engine-getPst} and
    /// {Engine-getPstTwo}):
    ///          Pawn                Bishop               Knight                   Rook
    ///    20 20 20 20 20 20    62 64 64 64 64 62    54 56 54 54 56 58    100 100 100 100 100 100
    ///    30 30 30 30 30 30    64 66 66 66 66 64    56 60 64 64 60 56    101 102 102 102 102 101
    ///    20 22 24 24 22 20    64 67 68 68 67 64    58 64 68 68 64 58     99 100 100 100 100  99
    ///    21 20 26 26 20 21    64 68 68 68 68 64    58 65 68 68 65 58     99 100 100 100 100  99
    ///    21 30 16 16 30 21    64 67 66 66 67 64    56 60 65 65 60 56     99 100 100 100 100  99
    ///    20 20 20 20 20 20    62 64 64 64 64 62    54 56 58 58 56 54    100 100 101 101 100 100
    ///                            Queen                         King
    ///                   176 178 179 179 178 176    3994 3992 3990 3990 3992 3994
    ///                   178 180 180 180 180 178    3994 3992 3990 3990 3992 3994
    ///                   179 180 181 181 180 179    3996 3994 3992 3992 3994 3995
    ///                   179 181 181 181 180 179    3998 3996 3996 3996 3996 3998
    ///                   178 180 181 180 180 178    4001 4001 4000 4000 4001 4001
    ///                   176 178 179 179 178 176    4004 4006 4002 4002 4006 4004
    /// All entries in the figure above are in decimal representation.
    ///
    /// Each entry in the pawn's, bishop's, knight's, and rook's PSTs uses 7 bits, and each entry in
    /// the queen's and king's PSTs uses 12 bits. Additionally, each piece is valued as following:
    ///                                      | Type   | Value |
    ///                                      | ------ | ----- |
    ///                                      | Pawn   | 20    |
    ///                                      | Bishop | 66    |
    ///                                      | Knight | 64    |
    ///                                      | Rook   | 100   |
    ///                                      | Queen  | 180   |
    ///                                      | King   | 4000  |
    /// The king's value just has to be sufficiently larger than 180 * 7 = 1260 (i.e. equivalent to
    /// 7 queens) because check/checkmates are detected lazily (see {Engine-generateMoves}).
    ///
    /// The evaluation of a move is given by
    ///                Δ(PST value of the moved piece) + (PST value of any captured pieces).
    /// @param _board The board to apply the move to.
    /// @param _move The move to evaluate.
    /// @return The evaluation of the move applied to the given position.
    function evaluateMove(uint256 _board, uint256 _move) internal pure returns (int256) {
        unchecked {
            uint256 fromIndex = 6 * (_move >> 9) + ((_move >> 6) & 7) - 7;
            uint256 toIndex = 6 * ((_move & 0x3F) >> 3) + ((_move & 0x3F) & 7) - 7;
            uint256 pieceAtFromIndex = (_board >> ((_move >> 6) << 2)) & 7;
            uint256 pieceAtToIndex = (_board >> ((_move & 0x3F) << 2)) & 7;
            uint256 oldPst;
            uint256 newPst;
            uint256 captureValue;

            if (pieceAtToIndex != 0) {
                if (pieceAtToIndex < 5) { // Piece is not a queen or king
                    captureValue = (getPst(pieceAtToIndex) >> (7 * (0x23 - toIndex))) & 0x7F;
                } else if (toIndex < 0x12) { // Piece is queen or king and in the closer half
                    captureValue = (getPst(pieceAtToIndex) >> (0xC * (0x11 - toIndex))) & 0xFFF;
                } else { // Piece is queen or king and in the further half
                    captureValue = (getPstTwo(pieceAtToIndex) >> (0xC * (0x23 - toIndex))) & 0xFFF;
                }
            }
            if (pieceAtFromIndex < 5) { // Piece is not a queen or king
                oldPst = (getPst(pieceAtFromIndex) >> (7 * fromIndex)) & 0x7F;
                newPst = (getPst(pieceAtFromIndex) >> (7 * toIndex)) & 0x7F;
            } else if (fromIndex < 0x12) { // Piece is queen or king and in the closer half
                oldPst = (getPstTwo(pieceAtFromIndex) >> (0xC * fromIndex)) & 0xFFF;
                newPst = (getPstTwo(pieceAtFromIndex) >> (0xC * toIndex)) & 0xFFF;
            } else { // Piece is queen or king and in the further half
                oldPst = (getPst(pieceAtFromIndex) >> (0xC * (fromIndex - 0x12))) & 0xFFF;
                newPst = (getPst(pieceAtFromIndex) >> (0xC * (toIndex - 0x12))) & 0xFFF;
            }

            return int256(captureValue + newPst) - int256(oldPst);
        }
    }

    /// @notice Maps a given piece type to its PST (see {Engine-evaluateMove} for details on the
    /// PSTs and {Chess} for piece representation).
    /// @dev The queen's and king's PSTs do not fit in 1 uint256, so their PSTs are split into 2
    /// uint256s each. {Chess-getPst} contains the first half, and {Chess-getPstTwo} contains the
    /// second half.
    /// @param _type A piece type defined in {Chess}.
    /// @return The PST corresponding to `_type`.
    function getPst(uint256 _type) internal pure returns (uint256) {
        if (_type == 1) return 0x2850A142850F1E3C78F1E2858C182C50A943468A152A788103C54A142850A14;
        if (_type == 2) return 0x7D0204080FA042850A140810E24487020448912240810E1428701F40810203E;
        if (_type == 3) return 0xC993264C9932E6CD9B365C793264C98F1E4C993263C793264C98F264CB97264;
        if (_type == 4) return 0x6CE1B3670E9C3C8101E38750224480E9D4189120BA70F20C178E1B3874E9C36;
        if (_type == 5) return 0xB00B20B30B30B20B00B20B40B40B40B40B20B30B40B50B50B40B3;
        return 0xF9AF98F96F96F98F9AF9AF98F96F96F98F9AF9CF9AF98F98F9AF9B;
    }

    /// @notice Maps a queen or king to the second half of its PST (see {Engine-getPst}).
    /// @param _type A piece type defined in {Chess}. Must be a queen or a king (see
    /// {Engine-getPst}).
    /// @return The PST corresponding to `_type`.
    function getPstTwo(uint256 _type) internal pure returns (uint256) {
        return _type == 5
            ? 0xB30B50B50B50B40B30B20B40B50B40B40B20B00B20B30B30B20B0
            : 0xF9EF9CF9CF9CF9CF9EFA1FA1FA0FA0FA1FA1FA4FA6FA2FA2FA6FA4;
    }
}

File 8 of 16 : fiveoutofnineART.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.9;

import "@openzeppelin/contracts/utils/Strings.sol";

import { Chess } from "./Chess.sol";
import { Base64 } from "./Base64.sol";

/// @title A library that generates HTML art for fiveoutofnine (an on-chain 6x6 chess engine)
/// @author fiveoutofnine
/// @notice Below details how the metadata and art are generated:
/// ==============================================Name==============================================
/// Expressed as Python3 f-strings below, token names generate as
///                     ``f"fiveoutofnine - Game #{game_id}, Move #{move_id}"''.
/// ==========================================Description===========================================
/// Token descriptions describe white's move in algebraic notation and black's move in algebraic
/// notation. If white's move results in checkmating black or a stalemate, the description will say
/// black resigned (for simplicity, stalemates are treated as checkmates). Since the engine always
/// plays black, and the player always plays white, white is indicated as ``Player'', and black is
/// indicated as ``fiveoutofnine''. Additionally, for every non game-ending turn, a string graphic
/// is generated after the moves' descriptions. An example:
///                             Player plays e4 rook captures e5 queen.
///                             6 · · ♜ · ♚ ♜
///                             5 · ♟ · · ♖ ♟
///                             4 ♟ ♙ ♟ ♙ * ♙
///                             3 ♙ · ♙ · · ·
///                             2 · · · · ♖ ·
///                             1 · ♘ · ♔ · ·
///                               a b c d e f
///
///                             fiveoutofnine resigns.
/// * indicates the square the piece moved from.
/// ==============================================Art===============================================
/// The art is generated as HTML code with in-line CSS (0 JS) according to the following table:
///  | Property       | Name      | Value/Description                       | Determination       |
///  | ============== | ========= | ======================================= | =================== |
///  | Dimension      | 1 × 1     | 1 × 1 pillars                           | Player moved king   |
///  | (6 traits)     | 2 × 2     | 2 × 2 pillars                           | Player moved rook   |
///  |                | 3 × 3     | 3 × 3 pillars                           | Engine moved bishop |
///  |                | 4 × 4     | 4 × 4 pillars                           | Player moved knight |
///  |                | 6 × 6     | 6 × 6 pillars                           | Player moved pawn   |
///  |                | 12 × 12   | 12 × 12 pillars                         | Player moved queen  |
///  | -------------- | --------- | --------------------------------------- | ------------------- |
///  | Height         | Plane     | 8px pillar height                       | 1 / 64 chance[^0]   |
///  | (5 traits)     | 1/4       | 98px pillar height                      | 10 / 64 chance[^0]  |
///  |                | 1/2       | 197px pillar height                     | 10 / 64 chance[^0]  |
///  |                | Cube      | 394px pillar height                     | 40 / 64 chance[^0]  |
///  |                | Infinite  | 1000px pillar height                    | 3 / 64 chance[^0]   |
///  | -------------- | --------- | --------------------------------------- | ------------------- |
///  | Gap[^1]        | None      | 0px gap between the pillars             | 4 / 16 chance[^0]   |
///  | (4 traits)     | Narrow    | 2px gap between the pillars             | 9 / 16 chance[^0]   |
///  |                | Wide      | 12px gap between the pillars            | 2 / 16 chance[^0]   |
///  |                | Ultrawide | 24px gap between the pillars            | 1 / 16 chance[^0]   |
///  | -------------- | --------- | --------------------------------------- | ------------------- |
///  | Color          | Uniform   | All faces are the same color            | 7 / 32 chance[^0]   |
///  | Generation[^2] | Shades    | Faces get darker anticlockwise          | 7 / 32 chance[^0]   |
///  | (6 traits)     | Tints     | Faces get lighter anticlockwise         | 7 / 32 chance[^0]   |
///  |                | Eclipse   | Left face is white; black face is black | 3 / 32 chance[^0]   |
///  |                | Void      | Left and right face are black           | 1 / 32 chance[^0]   |
///  |                | Curated   | One of 8 color themes (see below)       | 7 / 32 chance[^0]   |
///  | -------------- | --------- | --------------------------------------- | ------------------- |
///  | Color          | Nord      | 0x8FBCBBEBCB8BD087705E81ACB48EAD        | 1 / 8 chance[^0]    |
///  | Theme[^3]      | B/W       | 0x000000FFFFFFFFFFFFFFFFFF000000        | 1 / 8 chance[^0]    |
///  | (8 traits)     | Candycorn | 0x0D3B66F4D35EEE964BFAF0CAF95738        | 1 / 8 chance[^0]    |
///  |                | RGB       | 0xFFFF0000FF000000FFFF0000FFFF00        | 1 / 8 chance[^0]    |
///  |                | VSCode    | 0x1E1E1E569CD6D2D1A2BA7FB54DC4AC        | 1 / 8 chance[^0]    |
///  |                | Neon      | 0x00FFFFFFFF000000FF00FF00FF00FF        | 1 / 8 chance[^0]    |
///  |                | Jungle    | 0xBE3400015045020D22EABAACBE3400        | 1 / 8 chance[^0]    |
///  |                | Corn      | 0xF9C233705860211A28346830F9C233        | 1 / 8 chance[^0]    |
///  | -------------- | --------- | --------------------------------------- | ------------------- |
///  | Bit Border[^4] | True      | The bits have a 1px solid black border  | Any pieces captured |
///  | (2 traits)     | False     | The bits don't have any border          | No pieces captuered |
///  | ============== | ========= | ======================================= | =================== |
///  | [^0]: Determined from `_seed`.                                                             |
///  | [^1]: Gap is omitted when dimension is 1 x 1.                                              |
///  | [^2]: The first 5 color generation traits are algorithms. A base color is generated from   |
///  | `seed`, and the remaining colors are generated according to the selected algorithm. The    |
///  | color of the bits is always the complement of the randomly generated base color, and the   |
///  | background color depends on the algorithm:                                                 |
///  |     * Uniform: same as the base color;                                                     |
///  |     * Shades: darkest shade of the base color;                                             |
///  |     * Tints: lightest shade of the base color;                                             |
///  |     * Eclipse: same as the base color;                                                     |
///  |     * Void: complement of the base color.                                                  |
///  | If the selected color generation trait is "Curated," 1 of 8 pre-curated themes is randomly |
///  | selected.                                                                                  |
///  | [^3]: The entries in the 3rd column are bitpacked integers where                           |
///  |     * the first 24 bits represent the background color,                                    |
///  |     * the second 24 bits represent the left face's color,                                  |
///  |     * the third 24 bits represent the right face's color,                                  |
///  |     * the fourth 24 bits represent the top face's color,                                   |
///  |     * and the last 24 bits represent the bits' color.                                      |
///  | [^4]: Bit border is omitted when dimension is 12 x 12.                                     |
library fiveoutofnineART {
    using Strings for uint256;
    using Chess for uint256;

    string internal constant SVG_STYLES = "--n:calc((394px - (var(--b) - 1)*var(--c))/var(--b));--o"
        ":calc(106px + var(--n));--p:calc(var(--a)/2)}section{height:var(--a);width:var(--a);backgr"
        "ound:var(--e);position:absolute;left:0;top:0;right:0;bottom:0;overflow:hidden}.c{height:0;"
        "width:0;position:absolute;transition:0.25s}.c:hover{transform:translate(0px,-64px);transit"
        "ion:0.25s}.c>*{height:var(--n);width:var(--n);border-bottom:4px solid black;border-right:4"
        "px solid black;border-left:1px solid black;border-top:1px solid black;transform-origin:0 0"
        ";position:relative;box-sizing:border-box}.c>*:nth-child(1){width:var(--d);background-color"
        ":var(--f);transform:rotate(90deg)skewX(-30deg)scaleY(0.864)}.c>*:nth-child(2){height:var(-"
        "-d);bottom:var(--n);background-color:var(--g);transform:rotate(-30deg)skewX(-30deg)scaleY("
        "0.864)}#h{background-color:var(--h)}#i{background-color:var(--i)}.c>*:nth-child(3){bottom:"
        "calc(var(--d) + var(--n));background-color:var(--h);display:grid;grid-template-columns:rep"
        "eat(";
    bytes32 internal constant HEXADECIMAL_DIGITS = "0123456789ABCDEF";
    bytes32 internal constant FILE_NAMES = "abcdef";

    /// @notice Takes in data for a given fiveoutofnine NFT and outputs its metadata in JSON form.
    /// Refer to {fiveoutofnineART} for details.
    /// @dev The output is base 64-encoded.
    /// @param _internalId A bitpacked uint256 where the first 128 bits are the game ID, and the
    /// last 128 bits are the move ID within the game.
    /// @param _move A struct with information about the player's move and engine's response (see
    /// {Chess-Move}).
    /// @return Base 64-encoded JSON of metadata generated from `_internalId` and `_move`.
    function getMetadata(uint256 _internalId, Chess.Move memory _move)
        internal pure
        returns (string memory)
    {
        string memory description;
        string memory image;
        string memory attributes;
        uint256 whiteMove;
        uint256 blackMove;
        uint256 boardAfterWhiteMove;
        uint256 boardAfterBlackMove;
        bool whiteCaptures;
        bool blackCaptures;
        uint256 depth;

        {
            whiteMove = (_move.metadata >> 0xC) & 0xFFF;
            blackMove = _move.metadata & 0xFFF;

            boardAfterWhiteMove = _move.board.applyMove(whiteMove);
            boardAfterBlackMove = boardAfterWhiteMove.applyMove(blackMove);

            whiteCaptures = _move.board.isCapture(
                _move.board >> ((whiteMove & 0x3F) << 2)
            );
            blackCaptures = boardAfterWhiteMove.isCapture(
                boardAfterWhiteMove >> ((blackMove & 0x3F) << 2)
            );

            depth = _move.metadata >> 0x18;
        }

        {
            uint256 numSquares;
            {
                uint256 whitePieceType = (_move.board >> ((whiteMove >> 6) << 2)) & 7;
                uint256 blackPieceType = (boardAfterWhiteMove >> ((blackMove >> 6) << 2)) & 7;

                if (whitePieceType == 1) numSquares = 6;
                else if (whitePieceType == 3) numSquares = 2;
                else if (whitePieceType == 4) numSquares = 4;
                else if (whitePieceType == 5) numSquares = 12;
                else numSquares = 1;
                if (blackPieceType == 2) numSquares = 3;
            }

            uint256 seed = uint256(
                keccak256(abi.encodePacked(_internalId, boardAfterBlackMove, _move.metadata))
            );

            (image, attributes) = getImage(
                boardAfterBlackMove,
                numSquares,
                seed,
                whiteCaptures || blackCaptures
            );
        }

        // Lots of unusual identation and braces to get around the 16 local variable limitation.
        {
            description = string(
                abi.encodePacked(
                    "---\\n\\n**Player** plays **`",
                    indexToPosition(whiteMove >> 6, true),
                    "` ",
                    getPieceName((_move.board >> ((whiteMove >> 6) << 2)) & 7),
                    "**",
                    whiteCaptures
                        ? " captures "
                        : " to ",
                    "**`",
                    indexToPosition(whiteMove & 0x3F, true)
                )
            );
        }
        {
            description = string(
                abi.encodePacked(
                    description,
                    "`",
                    whiteCaptures
                        ? " "
                        : "",
                    whiteCaptures
                        ? getPieceName((_move.board >> ((whiteMove & 0x3F) << 2)) & 7)
                        : "",
                    "**.\\n\\n",
                    drawMove(boardAfterWhiteMove, whiteMove >> 6),
                    "\\n\\n---\\n\\n**fiveoutofnine** "
                )
            );
        }

        {
            if (blackMove == 0) {
                description = string(abi.encodePacked(description, "**resigns**."));
            } else {
                description = string(
                    abi.encodePacked(
                        description,
                        "responds with **`",
                        indexToPosition(blackMove >> 6, false),
                        "` ",
                        getPieceName((boardAfterWhiteMove >> ((blackMove >> 6) << 2)) & 7),
                        "**",
                        blackCaptures
                            ? " captures "
                            : " to ",
                        "**`",
                        indexToPosition(blackMove & 0x3F, false),
                        "`",
                        blackCaptures
                            ? " "
                            : "",
                        blackCaptures
                            ? getPieceName((boardAfterWhiteMove>> ((blackMove & 0x3F) << 2)) & 7)
                            : "",
                        "**.\\n\\n",
                        drawMove(boardAfterBlackMove, blackMove >> 6)
                    )
                );
            }
        }

        return string(
            abi.encodePacked(
                "data:application/json;base64,",
                Base64.encode(
                    abi.encodePacked(
                        '{"name":"Game #',
                        Strings.toString(_internalId >> 0x80),
                        ", Move #",
                        Strings.toString(uint128(_internalId)),
                        '",'
                        '"description":"',
                        description,
                        '","animation_url":"data:text/html;base64,',
                        image,
                        '","attributes":[{"trait_type":"Depth","value":',
                        depth.toString(),
                        "},",
                        attributes,
                        "]}"
                    )
                )
            )
        );
    }

    /// @notice Generates the HTML image and its attributes for a given board/seed according to the
    /// table described in {fiveoutofnineART}.
    /// @dev The output of the image is base 64-encoded.
    /// @param _board The board after the player's and engine's move are played.
    /// @param _numSquares The dimension of the board.
    /// @param _seed A hash of the game ID, move ID, board position, and metadata.
    /// @param _pieceCaptured Whether or not any piees were captured.
    /// @return Base 64-encoded image (in HTML) and its attributes.
    function getImage(uint256 _board, uint256 _numSquares, uint256 _seed, bool _pieceCaptured)
        internal pure
        returns (string memory, string memory)
    {
        string memory attributes = string(
            abi.encodePacked(
                '{"trait_type":"Dimension","value":"',
                _numSquares.toString(),
                unicode" × ",
                _numSquares.toString(),
                '"}'
            )
        );
        string memory styles = string(
            abi.encodePacked(
                "<style>:root{--a:1000px;--b:",
                _numSquares.toString(),
                ";--c:"
            )
        );

        {
            string memory tempAttribute;
            string memory tempValue = "0";
            if (_numSquares != 1) {
                if (_seed & 0xF < 4) { (tempAttribute, tempValue) = ("None", "0"); }
                else if (_seed & 0xF < 13) { (tempAttribute, tempValue) = ("Narrow", "2"); }
                else if (_seed & 0xF < 15) { (tempAttribute, tempValue) = ("Wide", "12"); }
                else { (tempAttribute, tempValue) = ("Ultrawide", "24"); }

                attributes = string(
                    abi.encodePacked(
                        attributes,
                        ',{"trait_type":"Gap","value":"',
                        tempAttribute,
                        '"}'
                    )
                );
            }
            styles = string(abi.encodePacked(styles, tempValue, "px;--d:"));
        }
        _seed >>= 4;

        {
            string memory tempAttribute;
            string memory tempValue;
            if (_seed & 0x3F < 1) { (tempAttribute, tempValue) = ("Plane", "8"); }
            else if (_seed & 0x3F < 11) { (tempAttribute, tempValue) = ("1/4", "98"); }
            else if (_seed & 0x3F < 21) { (tempAttribute, tempValue) = ("1/2", "197"); }
            else if (_seed & 0x3F < 51) { (tempAttribute, tempValue) = ("Cube", "394"); }
            else { (tempAttribute, tempValue) = ("Infinite", "1000"); }

            attributes = string(
                abi.encodePacked(
                    attributes,
                    ',{"trait_type":"Height","value":"',
                    tempAttribute,
                    '"}'
                )
            );
            styles = string(abi.encodePacked(styles, tempValue, "px;"));
        }
        _seed >>= 6;

        {
            string memory tempAttribute;
            uint256 colorTheme;
            if (_seed & 0x1F < 25) {
                colorTheme = (_seed >> 5) & 0xFFFFFF;
                attributes = string(
                    abi.encodePacked(
                        attributes,
                        ',{"trait_type":"Base Color","value":',
                        colorTheme.toString(),
                        "}"
                    )
                );
                if (_seed & 0x1F < 7) {
                    tempAttribute = "Uniform";
                    colorTheme = (colorTheme << 0x60)
                        | (colorTheme << 0x48)
                        | (colorTheme << 0x30)
                        | (colorTheme << 0x18)
                        | complementColor(colorTheme);
                } else if (_seed & 0x1F < 14) {
                    tempAttribute = "Shades";
                    colorTheme = (darkenColor(colorTheme, 3) << 0x60)
                        | (darkenColor(colorTheme, 1) << 0x48)
                        | (darkenColor(colorTheme, 2) << 0x30)
                        | (colorTheme << 0x18)
                        | complementColor(colorTheme);
                } else if (_seed & 0x1F < 21) {
                    tempAttribute = "Tints";
                    colorTheme = (brightenColor(colorTheme, 3) << 0x60)
                        | (brightenColor(colorTheme, 1) << 0x48)
                        | (brightenColor(colorTheme, 2) << 0x30)
                        | (colorTheme << 0x18)
                        | complementColor(colorTheme);
                } else if (_seed & 0x1F < 24) {
                    tempAttribute = "Eclipse";
                    colorTheme = (colorTheme << 0x60)
                        | (0xFFFFFF << 0x48)
                        | (colorTheme << 0x18)
                        | complementColor(colorTheme);
                } else {
                    tempAttribute = "Void";
                    colorTheme = (complementColor(colorTheme) << 0x60)
                        | (colorTheme << 0x18)
                        | complementColor(colorTheme);
                }
            } else {
                tempAttribute = "Curated";
                _seed >>= 5;

                attributes = string(
                    abi.encodePacked(
                        attributes,
                        ',{"trait_type":"Color Theme","value":"',
                        ["Nord", "B/W", "Candycorn", "RGB", "VSCode", "Neon", "Jungle", "Corn"]
                        [_seed & 7],
                        '"}'
                    )
                );

                colorTheme = [
                    0x8FBCBBEBCB8BD087705E81ACB48EAD000000FFFFFFFFFFFFFFFFFF000000,
                    0x0D3B66F4D35EEE964BFAF0CAF95738FFFF0000FF000000FFFF0000FFFF00,
                    0x1E1E1E569CD6D2D1A2BA7FB54DC4AC00FFFFFFFF000000FF00FF00FF00FF,
                    0xBE3400015045020D22EABAACBE3400F9C233705860211A28346830F9C233
                ][(_seed & 7) >> 1];
                colorTheme = _seed & 1 == 0
                    ? colorTheme >> 0x78
                    : colorTheme & 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF;
            }
            attributes = string(
                abi.encodePacked(
                    attributes,
                    ',{"trait_type":"Color Generation","value":"',
                    tempAttribute,
                    '"}'
                )
            );
            styles = string(
                abi.encodePacked(
                    styles,
                    "--e:",
                    toColorHexString(colorTheme >> 0x60),
                    ";--f:",
                    toColorHexString((colorTheme >> 0x48) & 0xFFFFFF),
                    ";--g:",
                    toColorHexString((colorTheme >> 0x30) & 0xFFFFFF),
                    ";--h:",
                    toColorHexString((colorTheme >> 0x18) & 0xFFFFFF),
                    ";--i:",
                    toColorHexString(colorTheme & 0xFFFFFF),
                    ";"
                )
            );
        }

        {
            string memory tempAttribute;
            styles = string(
                abi.encodePacked(
                    styles,
                    SVG_STYLES,
                    Strings.toString(12 / _numSquares),
                    ",1fr);grid-template-rows:repeat(",
                    Strings.toString(12 / _numSquares),
                    ",1fr);transform:rotate(210deg)skew(-30deg)scaleY(0.864)}"
                )
            );
            if (_numSquares != 12) {
                if (_pieceCaptured) {
                    tempAttribute = "True";
                    styles = string(
                        abi.encodePacked(
                            styles,
                            ".c>*:nth-child(3)>div{border: 1px solid black}"
                        )
                    );
                } else {
                    tempAttribute = "False";
                }
                attributes = string(
                    abi.encodePacked(
                        attributes,
                        ',{"trait_type":"Bit Border","value":"',
                        tempAttribute,
                        '"}'
                    )
                );
            }
        }

        unchecked {
            for (uint256 i; i < 23; ++i) {
                styles = string(
                    abi.encodePacked(
                        styles,
                        ".r",
                        i.toString(),
                        "{top:calc(var(--o) + ",
                        i.toString(),
                        "*(var(--n)/2 + var(--c)))}"
                        ".c",
                        i.toString(),
                        "{left:calc(var(--p) ",
                        i < 11 ? "-" : "+",
                        " 0.866*",
                        i < 11 ? (11 - i).toString() : (i - 11).toString(),
                        "*(var(--n) + var(--c)))}"
                    )
                );
            }

            string memory image;
            for (uint256 row; row < (_numSquares << 1) - 1; ++row) {
                uint256 tempCol = row <= _numSquares - 1
                    ? 11 - row
                    : 11 - ((_numSquares << 1) - 2 - row);
                for (
                    uint256 col = tempCol;
                    col <= (row <= _numSquares - 1
                        ? tempCol + (row << 1)
                        : tempCol + (((_numSquares << 1) - 2 - row) << 1));
                    col = col + 2
                ) {
                    image = string(
                        abi.encodePacked(
                            image,
                            getPillarHtml(_board, 12 / _numSquares, row, col)
                        )
                    );
                }
            }

            return (
                Base64.encode(
                    abi.encodePacked(
                        styles,
                        "</style><section>",
                        image,
                        "</section>"
                    )
                ),
                attributes
            );
        }
    }

    /// @notice Returns the HTML for a particular pillar within the image.
    /// @param _board The board after the player's and engine's move are played.
    /// @param _dim The dimension of the bits within a pillar.
    /// @param _row The row index of the pillar.
    /// @param _col The column index of the pillar.
    /// @return The HTML for the pillar described by the parameters.
    function getPillarHtml(uint256 _board, uint256 _dim, uint256 _row, uint256 _col)
        internal pure
        returns (string memory)
    {
        string memory pillar = string(
            abi.encodePacked(
                '<div class="c r',
                _row.toString(),
                " c",
                _col.toString(),
                '"><div></div><div></div><div>'
            )
        );

        uint256 x;
        uint256 y;
        uint256 colOffset;
        uint256 rowOffset;
        unchecked {
            for (
                uint256 subRow = _row * _dim + ((_dim - 1) << 1);
                subRow >= _row * _dim + (_dim - 1);
                --subRow
            ) {
                rowOffset = 0;
                uint256 tempSubCol = _col <= 11
                    ? 11 - _dim * (11 - _col) + colOffset
                    : 11 + _dim * (_col - 11) + colOffset;
                for (
                    uint256 subCol = tempSubCol;
                    subCol >= tempSubCol + 1 - _dim;
                    --subCol
                ) {
                    x = 11 - ((11 + subCol - (subRow - rowOffset)) >> 1);
                    y = 16 - ((subCol + subRow - rowOffset) >> 1);
                    pillar = string(
                        abi.encodePacked(
                            pillar,
                            '<div id="',
                            (
                                _board
                                >> (Chess.getAdjustedIndex(6 * (y >> 1) + (x >> 1)) << 2)
                                >> (((0xD8 >> ((x & 1) << 2)) >> ((y & 1) << 1)) & 3)
                            )
                            & 1 == 0
                                ? "h"
                                : "i",
                            '"></div>'
                        )
                    );
                    rowOffset++;
                    if (subCol == 0) { break; }
                }
                colOffset++;
                if (subRow == 0) { break; }
            }
        }

        return string(abi.encodePacked(pillar, "</div></div>"));
    }

    /// @notice Draws out a move being played out on a board position as a string with unicode
    /// characters to represent pieces. Files and rows are labeled with standard algebraic
    /// notation. For example:
    /// ```
    /// 6 ♜ ♝ ♛ ♚ ♝ ♜
    /// 5 ♟ ♟ ♟ ♟ ♟ ♟
    /// 4 · · · · · ·
    /// 3 · · ♙ · · ·
    /// 2 ♙ ♙ * ♙ ♙ ♙
    /// 1 ♖ ♘ ♕ ♔ ♘ ♖
    ///  a b c d e f
    /// ```
    /// * indicates the square the piece moved from.
    /// @param _board The board the move is played on.
    /// @param _fromIndex The from index of the move.
    /// @return The string showing the move played out on the board.
    function drawMove(uint256 _board, uint256 _fromIndex) internal pure returns (string memory) {
        string memory boardString = "```\\n";

        if (_board & 1 == 0) _board = _board.rotate();
        else _fromIndex = ((7 - (_fromIndex >> 3)) << 3) + (7 - (_fromIndex & 7));

        for (
            uint256 index = 0x24A2CC34E4524D455665A6DC75E8628E4966A6AAECB6EC72CF4D76;
            index != 0;
            index >>= 6
        ) {
            uint256 indexToDraw = index & 0x3F;
            boardString = string(
                abi.encodePacked(
                    boardString,
                    indexToDraw & 7 == 6
                        ? string(abi.encodePacked(Strings.toString((indexToDraw >> 3)), " "))
                        : "",
                    indexToDraw == _fromIndex
                        ? "*"
                        : getPieceChar((_board >> (indexToDraw << 2)) & 0xF),
                    indexToDraw & 7 == 1 && indexToDraw != 9
                        ? "\\n"
                        : indexToDraw != 9
                            ? " "
                            : ""
                )
            );
        }

        boardString = string(
            abi.encodePacked(
                boardString,
                "\\n  a b c d e f\\n```"
                )
            );

        return boardString;
    }

    /// @notice Computes the complement of 24-bit colors.
    /// @param _color A 24-bit color.
    /// @return The complement of `_color`.
    function complementColor(uint256 _color) internal pure returns (uint256) {
        unchecked {
            return 0xFFFFFF - _color;
        }
    }

    /// @notice Darkens 24-bit colors.
    /// @param _color A 24-bit color.
    /// @param _num The number of shades to darken by.
    /// @return `_color` darkened `_num` times.
    function darkenColor(uint256 _color, uint256 _num) internal pure returns (uint256) {
        return (((_color >> 0x10) >> _num) << 0x10)
            | ((((_color >> 8) & 0xFF) >> _num) << 8)
            | ((_color & 0xFF) >> _num);
    }

    /// @notice Brightens 24-bit colors.
    /// @param _color A 24-bit color.
    /// @param _num The number of tints to brighten by.
    /// @return `_color` brightened `_num` times.
    function brightenColor(uint256 _color, uint256 _num) internal pure returns (uint256) {
        unchecked {
            return ((0xFF - ((0xFF - (_color >> 0x10)) >> _num)) << 0x10)
                | ((0xFF - ((0xFF - ((_color >> 8) & 0xFF)) >> _num)) << 8)
                | (0xFF - ((0xFF - (_color & 0xFF)) >> _num));
        }
    }

    /// @notice Returns the color hex string of a 24-bit color.
    /// @param _integer A 24-bit color.
    /// @return The color hex string of `_integer`.
    function toColorHexString(uint256 _integer) internal pure returns (string memory) {
        return string(
            abi.encodePacked(
                "#",
                HEXADECIMAL_DIGITS[(_integer >> 0x14) & 0xF],
                HEXADECIMAL_DIGITS[(_integer >> 0x10) & 0xF],
                HEXADECIMAL_DIGITS[(_integer >> 0xC) & 0xF],
                HEXADECIMAL_DIGITS[(_integer >> 8) & 0xF],
                HEXADECIMAL_DIGITS[(_integer >> 4) & 0xF],
                HEXADECIMAL_DIGITS[_integer & 0xF]
            )
        );
    }

    /// @notice Maps piece type to its corresponding name.
    /// @param _type A piece type defined in {Chess}.
    /// @return The name corresponding to `_type`.
    function getPieceName(uint256 _type) internal pure returns (string memory) {
        if (_type == 1) return "pawn";
        else if (_type == 2) return "bishop";
        else if (_type == 3) return "rook";
        else if (_type == 4) return "knight";
        else if (_type == 5) return "queen";
        return "king";
    }

    /// @notice Converts a position's index to algebraic notation.
    /// @param _index The index of the position.
    /// @param _isWhite Whether the piece is being determined for a white piece or not.
    /// @return The algebraic notation of `_index`.
    function indexToPosition(uint256 _index, bool _isWhite) internal pure returns (string memory) {
        unchecked {
            return _isWhite
                ? string(
                    abi.encodePacked(
                        FILE_NAMES[6 - (_index & 7)],
                        Strings.toString(_index >> 3))
                )
                : string(
                    abi.encodePacked(
                        FILE_NAMES[(_index & 7) - 1],
                        Strings.toString(7 - (_index >> 3))
                    )
                );
        }
    }

    /// @notice Maps pieces to its corresponding unicode character.
    /// @param _piece A piece.
    /// @return The unicode character corresponding to `_piece`. It returns ``.'' otherwise.
    function getPieceChar(uint256 _piece) internal pure returns (string memory) {
        if (_piece == 1) return unicode"♟";
        if (_piece == 2) return unicode"♝";
        if (_piece == 3) return unicode"♜";
        if (_piece == 4) return unicode"♞";
        if (_piece == 5) return unicode"♛";
        if (_piece == 6) return unicode"♚";
        if (_piece == 9) return unicode"♙";
        if (_piece == 0xA) return unicode"♗";
        if (_piece == 0xB) return unicode"♖";
        if (_piece == 0xC) return unicode"♘";
        if (_piece == 0xD) return unicode"♕";
        if (_piece == 0xE) return unicode"♔";
        return unicode"·";
    }
}

File 9 of 16 : IERC721.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.0 (token/ERC721/IERC721.sol)

pragma solidity ^0.8.0;

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

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

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

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

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

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

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

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

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

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

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

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

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

File 10 of 16 : IERC721Receiver.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.0 (token/ERC721/IERC721Receiver.sol)

pragma solidity ^0.8.0;

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

File 11 of 16 : IERC721Metadata.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.0 (token/ERC721/extensions/IERC721Metadata.sol)

pragma solidity ^0.8.0;

import "../IERC721.sol";

/**
 * @title ERC-721 Non-Fungible Token Standard, optional metadata extension
 * @dev See https://eips.ethereum.org/EIPS/eip-721
 */
interface IERC721Metadata is IERC721 {
    /**
     * @dev Returns the token collection name.
     */
    function name() external view returns (string memory);

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

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

File 12 of 16 : Address.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.0 (utils/Address.sol)

pragma solidity ^0.8.0;

/**
 * @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
     * ====
     */
    function isContract(address account) internal view returns (bool) {
        // This method relies on extcodesize, which returns 0 for contracts in
        // construction, since the code is only stored at the end of the
        // constructor execution.

        uint256 size;
        assembly {
            size := extcodesize(account)
        }
        return size > 0;
    }

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

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

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

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

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

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

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

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

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

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

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

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

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

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

                assembly {
                    let returndata_size := mload(returndata)
                    revert(add(32, returndata), returndata_size)
                }
            } else {
                revert(errorMessage);
            }
        }
    }
}

File 13 of 16 : Context.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.0 (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 14 of 16 : ERC165.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.0 (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 15 of 16 : IERC165.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.0 (utils/introspection/IERC165.sol)

pragma solidity ^0.8.0;

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

File 16 of 16 : Base64.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.9;

/// @title Base64
/// @author Brecht Devos - <[email protected]>
/// @notice Provides a function for encoding some bytes in base64
library Base64 {
    string internal constant TABLE = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz012345678"
        "9+/";

    function encode(bytes memory data) internal pure returns (string memory) {
        if (data.length == 0) return "";
        string memory table = TABLE;
        uint256 encodedLength = ((data.length + 2) / 3) << 2;
        string memory result = new string(encodedLength + 0x20);

        assembly {
            mstore(result, encodedLength)
            let tablePtr := add(table, 1)
            let dataPtr := data
            let endPtr := add(dataPtr, mload(data))
            let resultPtr := add(result, 0x20)
            for {} lt(dataPtr, endPtr) {}
            {
               dataPtr := add(dataPtr, 3)
               let input := mload(dataPtr)
               mstore(resultPtr, shl(0xF8, mload(add(tablePtr, and(shr(0x12, input), 0x3F)))))
               resultPtr := add(resultPtr, 1)
               mstore(resultPtr, shl(0xF8, mload(add(tablePtr, and(shr(0xC, input), 0x3F)))))
               resultPtr := add(resultPtr, 1)
               mstore(resultPtr, shl(0xF8, mload(add(tablePtr, and(shr(6, input), 0x3F)))))
               resultPtr := add(resultPtr, 1)
               mstore(resultPtr, shl(0xF8, mload(add(tablePtr, and(input, 0x3F)))))
               resultPtr := add(resultPtr, 1)
            }
            switch mod(mload(data), 3)
            case 1 { mstore(sub(resultPtr, 2), shl(0xF0, 0x3D3D)) }
            case 2 { mstore(sub(resultPtr, 1), shl(0xF8, 0x3D)) }
        }

        return result;
    }
}

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

Contract Security Audit

Contract ABI

[{"inputs":[],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"approved","type":"address"},{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"operator","type":"address"},{"indexed":false,"internalType":"bool","name":"approved","type":"bool"}],"name":"ApprovalForAll","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"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":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"Transfer","type":"event"},{"inputs":[{"internalType":"uint256","name":"_tokenId","type":"uint256"}],"name":"_tokenURI","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"approve","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"board","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"getApproved","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"operator","type":"address"}],"name":"isApprovedForAll","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_move","type":"uint256"},{"internalType":"uint256","name":"_depth","type":"uint256"}],"name":"mintMove","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"ownerOf","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"safeTransferFrom","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"bytes","name":"_data","type":"bytes"}],"name":"safeTransferFrom","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"operator","type":"address"},{"internalType":"bool","name":"approved","type":"bool"}],"name":"setApprovalForAll","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string","name":"_baseURI","type":"string"}],"name":"setBaseURI","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes4","name":"interfaceId","type":"bytes4"}],"name":"supportsInterface","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"tokenInternalIds","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"tokenMoves","outputs":[{"internalType":"uint256","name":"board","type":"uint256"},{"internalType":"uint256","name":"metadata","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_tokenId","type":"uint256"}],"name":"tokenURI","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"transferFrom","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"}]

60806040523480156200001157600080fd5b50604080518082018252600d81526c666976656f75746f666e696e6560981b602080830191825283518085019094526003845262714ccf60e91b908401528151919291620000629160009162000d79565b5080516200007890600190602084019062000d79565b505050620000956200008f620000e460201b60201c565b620000e8565b6001600755620000a46200013a565b7b032562300110101000010010000000c0099999000bcde0b000000001600855700100000000000000000000000000000002600955600b600c5562000f31565b3390565b600680546001600160a01b038381166001600160a01b0319831681179093556040519116919082907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a35050565b6200015b73a85572cd96f1643458f17340b6f0d6549af482f5600062000a20565b60007f13da86008ba1c6922daee3e07db95305ef49ebced9f5467a0b8613fcc6b343e3819055604080518082019091527b03256230011111100000000000000000099999900bcdecb0000000018152630851c4a26020828101918252928052600b909252517fdf7de25b7f1fd6d0b5205f0e18f1f35bd7b8d84cce336588d184533ce43a6f7655517fdf7de25b7f1fd6d0b5205f0e18f1f35bd7b8d84cce336588d184533ce43a6f775562000226733759328b1ce944642d36a61f06783f2865212515600162000a20565b60017fbbc70db1b6c7afd11e79c0fb0051300458f1a3acb8ee9789d9b6b26c61ad9bc7819055604080518082019091527b03256230010111100000000000190000099099900bcdecb0000000018152630759e51c6020828101918252600093909352600b909252517f72c6bfb7988af3a1efa6568f02a999bc52252641c659d85961ca3d372b57d5cf55517f72c6bfb7988af3a1efa6568f02a999bc52252641c659d85961ca3d372b57d5d055620002f473fd8ea0f05db884a78b1a1c1b3767b9e5d6664764600262000a20565b60027fbff4442b8ed600beeb8e26b1279a0f0d14c6edfaec26d968ee13c86f7d4c2ba8819055604080518082019091527b03256230010101100000100009190000009099900bcdecb000000001815263064db5656020828101918252600093909352600b909252517fa50eece07c7db1631545c0069bd8f5f54d5935e215d59097edf258a44ba9163455517fa50eece07c7db1631545c0069bd8f5f54d5935e215d59097edf258a44ba9163555620003c273174787a207bf4ed4d8db0945602e49f42c146474600362000a20565b60037fa856840544dc26124927add067d799967eac11be13e14d82cc281ea46fa39759819055604080518082019091527b03256230010100100000100009199100009009900bcdecb0000000018152630645a7256020828101918252600093909352600b909252517f64c15cc42be7899b001f818cf4433057002112c418d1d3a67cd5cb453051d33e55517f64c15cc42be7899b001f818cf4433057002112c418d1d3a67cd5cb453051d33f5562000490736dea5dcfa64dc0bb4e5ac53a375a4377cf4ed0ee600462000a20565b60047fe1eb2b2161a492c07c5a334e48012567cba93ec021043f53c1955516a3c5a841819055604080518082019091527b03256230010100100000000009199100009009000bcdecb0000000018152630631a4db6020828101918252600093909352600b909252517f12d0c11577e2f0950f57c455c117796550b79f444811db8ba2f69c57b646c78455517f12d0c11577e2f0950f57c455c117796550b79f444811db8ba2f69c57b646c785556200055e73333601a803cac32b7d17a38d32c9728a93b422f4600562000a20565b60057ff35035bc2b01d44bd35a1dcdc552315cffb73da35cfd60570b7b777f98036f9f819055604080518082019091527b03256230010000100001000009199d00009009000bc0ecb000000001815263066933156020828101918252600093909352600b909252517febae6141bae5521e99e0a8d610356b0f501fea54980b59c84841db43ba7204f455517febae6141bae5521e99e0a8d610356b0f501fea54980b59c84841db43ba7204f5556200062c73530cf036ed4fa58f7301a9c788c9806624cefd19600662000a20565b60067f10d9dd018e4cae503383c9f804c1c1603ada5856ee7894375d9b97cd8c8b27db819055604080518082019091527b032502300100061000010000091990000090d9000bc0ecb000000001815263064e15546020828101918252600093909352600b909252517f0387e9d1203691d8e3362a7e4c6723de358a4010d7f31ecbec3fbfc61d1c75fc55517f0387e9d1203691d8e3362a7e4c6723de358a4010d7f31ecbec3fbfc61d1c75fd55620006fa73d6a9cb7ab95293a7d38f416cd3a4fe9059ccd5b2600762000a20565b60077f22e39f61d1e4986b4f116cea9067f62cc77d74dff1780ae9c8b5166d1dd28829819055604080518082019091527b0325023001006010000100d009199000009009000bc0ecb000000001815263063532a56020828101918252600093909352600b909252517ff5559028dc9ba50d75343c779b2f75e13a84a14662932fc67a486f263ca31a9655517ff5559028dc9ba50d75343c779b2f75e13a84a14662932fc67a486f263ca31a9755620007c873afdc1a3ef3992f53c10fc798d242e15e2f0df51a600862000a20565b60087f2c1fd36ba11b13b555f58753742999069764391f450ca8727fe8a3eeffe67775819055604080518082019091527b0305023001006010000100d0091992000090c9000b00ecb000000001815263066e40006020828101918252600093909352600b909252517f71f482bdabd1ea844d62c952b094e632959690d7448ca2aab34034ec9856935855517f71f482bdabd1ea844d62c952b094e632959690d7448ca2aab34034ec98569359556200089673c1a80d351232fd07ee5733b5f581e01c269068a9600962000a20565b600160801b7f825eb4cda6b8b44578c55770496c59e6dc3cf2235f690bcdaf51a61898ceb28455604080518082019091527b03256230011111100000000000000000099999900bcdecb0000000018152630646155e60208281019182526009600052600b905290517fe12d5ff10640a555479d85b7ab5f83b5dbd3cf6ac615eec77e24e0984b75a03855517fe12d5ff10640a555479d85b7ab5f83b5dbd3cf6ac615eec77e24e0984b75a039556200096473f42d1c0c0165af5625b2ecd5027c5c5554e5b039600a62000a20565b7001000000000000000000000000000000017f3e57c57b03145299956be61386751c5b285d460d484d5c2403a6be086d9d6baa55604080518082019091527b03256230011110100000001000000000099999000bcdecb000000001815263062994db6020828101918252600a600052600b905290517fc47c2f4ab42fe2617dd76ca1eb9781d09fced5e5671df71824e2f8a8f694e02455517fc47c2f4ab42fe2617dd76ca1eb9781d09fced5e5671df71824e2f8a8f694e02555565b62000a4282826040518060200160405280600081525062000a4660201b60201c565b5050565b62000a52838362000ac2565b62000a61600084848462000c0a565b62000abd5760405162461bcd60e51b81526020600482015260326024820152600080516020620066d483398151915260448201527131b2b4bb32b91034b6b83632b6b2b73a32b960711b60648201526084015b60405180910390fd5b505050565b6001600160a01b03821662000b1a5760405162461bcd60e51b815260206004820181905260248201527f4552433732313a206d696e7420746f20746865207a65726f2061646472657373604482015260640162000ab4565b6000818152600260205260409020546001600160a01b03161562000b815760405162461bcd60e51b815260206004820152601c60248201527f4552433732313a20746f6b656e20616c7265616479206d696e74656400000000604482015260640162000ab4565b6001600160a01b038216600090815260036020526040812080546001929062000bac90849062000e1f565b909155505060008181526002602052604080822080546001600160a01b0319166001600160a01b03861690811790915590518392907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef908290a45050565b600062000c2b846001600160a01b031662000d7360201b62000ca21760201c565b1562000d6757604051630a85bd0160e11b81526001600160a01b0385169063150b7a029062000c6590339089908890889060040162000e46565b602060405180830381600087803b15801562000c8057600080fd5b505af192505050801562000cb3575060408051601f3d908101601f1916820190925262000cb09181019062000ec1565b60015b62000d4c573d80801562000ce4576040519150601f19603f3d011682016040523d82523d6000602084013e62000ce9565b606091505b50805162000d445760405162461bcd60e51b81526020600482015260326024820152600080516020620066d483398151915260448201527131b2b4bb32b91034b6b83632b6b2b73a32b960711b606482015260840162000ab4565b805181602001fd5b6001600160e01b031916630a85bd0160e11b14905062000d6b565b5060015b949350505050565b3b151590565b82805462000d879062000ef4565b90600052602060002090601f01602090048101928262000dab576000855562000df6565b82601f1062000dc657805160ff191683800117855562000df6565b8280016001018555821562000df6579182015b8281111562000df657825182559160200191906001019062000dd9565b5062000e0492915062000e08565b5090565b5b8082111562000e04576000815560010162000e09565b6000821982111562000e4157634e487b7160e01b600052601160045260246000fd5b500190565b600060018060a01b038087168352602081871681850152856040850152608060608501528451915081608085015260005b8281101562000e955785810182015185820160a00152810162000e77565b8281111562000ea857600060a084870101525b5050601f01601f19169190910160a00195945050505050565b60006020828403121562000ed457600080fd5b81516001600160e01b03198116811462000eed57600080fd5b9392505050565b600181811c9082168062000f0957607f821691505b6020821081141562000f2b57634e487b7160e01b600052602260045260246000fd5b50919050565b6157938062000f416000396000f3fe6080604052600436106101705760003560e01c806370a08231116100d6578063a22cb4651161007f578063d5f31d4e11610059578063d5f31d4e14610408578063e985e9c514610451578063f2fde38b1461049a57600080fd5b8063a22cb465146103a8578063b88d4fde146103c8578063c87b56dd146103e857600080fd5b80638d17e712116100b05780638d17e712146103555780638da5cb5b1461037557806395d89b411461039357600080fd5b806370a082311461030a578063715018a61461032a5780637cd3229a1461033f57600080fd5b806318160ddd1161013857806342842e0e1161011257806342842e0e146102aa57806355f804b3146102ca5780636352211e146102ea57600080fd5b806318160ddd1461026157806323b872dd1461027757806335df5d9f1461029757600080fd5b806301ffc9a71461017557806305931cb1146101aa57806306fdde03146101e5578063081812fc14610207578063095ea7b31461023f575b600080fd5b34801561018157600080fd5b50610195610190366004613fd5565b6104ba565b60405190151581526020015b60405180910390f35b3480156101b657600080fd5b506101d76101c5366004613ff2565b600a6020526000908152604090205481565b6040519081526020016101a1565b3480156101f157600080fd5b506101fa61050c565b6040516101a19190614063565b34801561021357600080fd5b50610227610222366004613ff2565b61059e565b6040516001600160a01b0390911681526020016101a1565b34801561024b57600080fd5b5061025f61025a366004614092565b610638565b005b34801561026d57600080fd5b506101d7600c5481565b34801561028357600080fd5b5061025f6102923660046140bc565b61076c565b61025f6102a53660046140f8565b6107e7565b3480156102b657600080fd5b5061025f6102c53660046140bc565b6108bf565b3480156102d657600080fd5b5061025f6102e53660046141a6565b6108da565b3480156102f657600080fd5b50610227610305366004613ff2565b61094b565b34801561031657600080fd5b506101d76103253660046141ef565b6109c2565b34801561033657600080fd5b5061025f610a49565b34801561034b57600080fd5b506101d760085481565b34801561036157600080fd5b506101fa610370366004613ff2565b610aaf565b34801561038157600080fd5b506006546001600160a01b0316610227565b34801561039f57600080fd5b506101fa610aec565b3480156103b457600080fd5b5061025f6103c336600461420a565b610afb565b3480156103d457600080fd5b5061025f6103e3366004614246565b610b06565b3480156103f457600080fd5b506101fa610403366004613ff2565b610b88565b34801561041457600080fd5b5061043c610423366004613ff2565b600b602052600090815260409020805460019091015482565b604080519283526020830191909152016101a1565b34801561045d57600080fd5b5061019561046c3660046142c2565b6001600160a01b03918216600090815260056020908152604080832093909416825291909152205460ff1690565b3480156104a657600080fd5b5061025f6104b53660046141ef565b610bd7565b60006001600160e01b031982166380ac58cd60e01b14806104eb57506001600160e01b03198216635b5e139f60e01b145b8061050657506301ffc9a760e01b6001600160e01b03198316145b92915050565b60606000805461051b906142f5565b80601f0160208091040260200160405190810160405280929190818152602001828054610547906142f5565b80156105945780601f1061056957610100808354040283529160200191610594565b820191906000526020600020905b81548152906001019060200180831161057757829003601f168201915b5050505050905090565b6000818152600260205260408120546001600160a01b031661061c5760405162461bcd60e51b815260206004820152602c60248201527f4552433732313a20617070726f76656420717565727920666f72206e6f6e657860448201526b34b9ba32b73a103a37b5b2b760a11b60648201526084015b60405180910390fd5b506000908152600460205260409020546001600160a01b031690565b60006106438261094b565b9050806001600160a01b0316836001600160a01b031614156106b15760405162461bcd60e51b815260206004820152602160248201527f4552433732313a20617070726f76616c20746f2063757272656e74206f776e656044820152603960f91b6064820152608401610613565b336001600160a01b03821614806106eb57506001600160a01b038116600090815260056020908152604080832033845290915290205460ff165b61075d5760405162461bcd60e51b815260206004820152603860248201527f4552433732313a20617070726f76652063616c6c6572206973206e6f74206f7760448201527f6e6572206e6f7220617070726f76656420666f7220616c6c00000000000000006064820152608401610613565b6107678383610ca8565b505050565b6107763382610d16565b6107dc5760405162461bcd60e51b815260206004820152603160248201527f4552433732313a207472616e736665722063616c6c6572206973206e6f74206f6044820152701ddb995c881b9bdc88185c1c1c9bdd9959607a1b6064820152608401610613565b610767838383610e0d565b6002600754141561083a5760405162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c006044820152606401610613565b6002600755600381108015906108515750600a8111155b61085a57600080fd5b603b6080600954901c1080156108845750603b6009546fffffffffffffffffffffffffffffffff16105b61088d57600080fd5b6108978282610fad565b600c80546108b69133919060006108ad83614346565b9190505561108d565b50506001600755565b61076783838360405180602001604052806000815250610b06565b6006546001600160a01b031633146109345760405162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e65726044820152606401610613565b805161094790600d906020840190613ee9565b5050565b6000818152600260205260408120546001600160a01b0316806105065760405162461bcd60e51b815260206004820152602960248201527f4552433732313a206f776e657220717565727920666f72206e6f6e657869737460448201526832b73a103a37b5b2b760b91b6064820152608401610613565b60006001600160a01b038216610a2d5760405162461bcd60e51b815260206004820152602a60248201527f4552433732313a2062616c616e636520717565727920666f7220746865207a65604482015269726f206164647265737360b01b6064820152608401610613565b506001600160a01b031660009081526003602052604090205490565b6006546001600160a01b03163314610aa35760405162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e65726044820152606401610613565b610aad60006110a7565b565b6000818152600a6020908152604080832054600b8352928190208151808301909252805482526001015491810191909152606091610506916110f9565b60606001805461051b906142f5565b61094733838361157d565b610b103383610d16565b610b765760405162461bcd60e51b815260206004820152603160248201527f4552433732313a207472616e736665722063616c6c6572206973206e6f74206f6044820152701ddb995c881b9bdc88185c1c1c9bdd9959607a1b6064820152608401610613565b610b828484848461164c565b50505050565b6060600d8054610b97906142f5565b159050610bce57600d610ba9836116ca565b604051602001610bba92919061437d565b604051602081830303815290604052610506565b61050682610aaf565b6006546001600160a01b03163314610c315760405162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e65726044820152606401610613565b6001600160a01b038116610c965760405162461bcd60e51b815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201526564647265737360d01b6064820152608401610613565b610c9f816110a7565b50565b3b151590565b600081815260046020526040902080546001600160a01b0319166001600160a01b0384169081179091558190610cdd8261094b565b6001600160a01b03167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92560405160405180910390a45050565b6000818152600260205260408120546001600160a01b0316610d8f5760405162461bcd60e51b815260206004820152602c60248201527f4552433732313a206f70657261746f7220717565727920666f72206e6f6e657860448201526b34b9ba32b73a103a37b5b2b760a11b6064820152608401610613565b6000610d9a8361094b565b9050806001600160a01b0316846001600160a01b03161480610dd55750836001600160a01b0316610dca8461059e565b6001600160a01b0316145b80610e0557506001600160a01b0380821660009081526005602090815260408083209388168352929052205460ff165b949350505050565b826001600160a01b0316610e208261094b565b6001600160a01b031614610e885760405162461bcd60e51b815260206004820152602960248201527f4552433732313a207472616e73666572206f6620746f6b656e2074686174206960448201526839903737ba1037bbb760b91b6064820152608401610613565b6001600160a01b038216610eea5760405162461bcd60e51b8152602060048201526024808201527f4552433732313a207472616e7366657220746f20746865207a65726f206164646044820152637265737360e01b6064820152608401610613565b610ef5600082610ca8565b6001600160a01b0383166000908152600360205260408120805460019290610f1e908490614424565b90915550506001600160a01b0382166000908152600360205260408120805460019290610f4c90849061443b565b909155505060008181526002602052604080822080546001600160a01b0319166001600160a01b0386811691821790925591518493918716917fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef91a4505050565b600854610fba81846117e0565b610fc357600080fd5b610fcd8184611a37565b9050600080610fdc8385611a7e565b600980546001808201909255600c80546000908152600a602090815260408083209490945583518085018552600854815260188c901b8d851b17881781830190815293548352600b909152929020915182555191015590925090508115806110595750603b6009546fffffffffffffffffffffffffffffffff1610155b1561106b57611066611b66565b611086565b6110758383611a37565b600855801561108657611086611b66565b5050505050565b610947828260405180602001604052806000815250611ba0565b600680546001600160a01b038381166001600160a01b0319831681179093556040519116919082907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a35050565b60208101518151606091829182918291610fff600c83901c8116921690600090819081908190819061112b9088611a37565b94506111378587611a37565b8c519094506111509060fc60028a901b1681901c611c1e565b92506111668560fc600289901b1681901c611c1e565b60208d01518d5191935060181c915060009060076004600160fc1b0360048b811c82169390931c8216928a901c1688901c1660018214156111aa57600692506111e5565b81600314156111bc57600292506111e5565b81600414156111ce57600492506111e5565b81600514156111e057600c92506111e5565b600192505b80600214156111f357600392505b505060008e868f60200151604051602001611221939291909283526020830191909152604082015260600190565b6040516020818303038152906040528051906020012060001c9050611250868383888061124b5750875b611c43565b909b509950611267915050600688901c600161285b565b611282600260068a901c901b8e60000151901c600716612916565b846112a957604051806040016040528060048152602001630103a37960e51b8152506112cd565b6040518060400160405280600a81526020016901031b0b83a3ab932b9960b51b8152505b6112db8a603f16600161285b565b6040516020016112ee9493929190614453565b6040516020818303038152906040529950898361131a5760405180602001604052806000815250611335565b604051806040016040528060018152602001600160fd1b8152505b8461134f5760405180602001604052806000815250611369565b61136960028a603f16901b8f60000151901c600716612916565b6113778860068c901c612a0a565b60405160200161138a9493929190614502565b604051602081830303815290604052995085600014156113cb57896040516020016113b591906145a8565b60405160208183030381529060405299506114ec565b896113db600688901c600061285b565b6113f760076004600160fc1b0360048b901c1689901c16612916565b8461141e57604051806040016040528060048152602001630103a37960e51b815250611442565b6040518060400160405280600a81526020016901031b0b83a3ab932b9960b51b8152505b6114508a603f16600061285b565b8661146a5760405180602001604052806000815250611485565b604051806040016040528060018152602001600160fd1b8152505b8761149f57604051806020016040528060008152506114b5565b6114b560028d603f16901b8c901c600716612916565b6114c38b60068f901c612a0a565b6040516020016114da9897969594939291906145d8565b60405160208183030381529060405299505b61154c6114fc60808f901c6116ca565b6115178f6fffffffffffffffffffffffffffffffff166116ca565b8c8c611522866116ca565b8d604051602001611538969594939291906146e5565b604051602081830303815290604052612be4565b60405160200161155c919061486d565b6040516020818303038152906040529a505050505050505050505092915050565b816001600160a01b0316836001600160a01b031614156115df5760405162461bcd60e51b815260206004820152601960248201527f4552433732313a20617070726f766520746f2063616c6c6572000000000000006044820152606401610613565b6001600160a01b03838116600081815260056020908152604080832094871680845294825291829020805460ff191686151590811790915591519182527f17307eab39ab6107e8899845ad3d59bd9653f200f220920489ca2b5937696c31910160405180910390a3505050565b611657848484610e0d565b61166384848484612d45565b610b825760405162461bcd60e51b815260206004820152603260248201527f4552433732313a207472616e7366657220746f206e6f6e20455243373231526560448201527131b2b4bb32b91034b6b83632b6b2b73a32b960711b6064820152608401610613565b6060816116ee5750506040805180820190915260018152600360fc1b602082015290565b8160005b8115611718578061170281614346565b91506117119050600a836148c8565b91506116f2565b60008167ffffffffffffffff8111156117335761173361411a565b6040519080825280601f01601f19166020018201604052801561175d576020820181803683370190505b5090505b8415610e0557611772600183614424565b915061177f600a866148dc565b61178a90603061443b565b60f81b81838151811061179f5761179f6148f0565b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a9053506117d9600a866148c8565b9450611761565b6000600682901c603f83166001667e7e7e7e7e7e00831c1661180757600092505050610506565b6001667e7e7e7e7e7e00821c1661182357600092505050610506565b600f600283901b86901c168061183f5760009350505050610506565b85600116600382901c146118595760009350505050610506565b600716600282901b86901c600084841061187557848403611879565b8385035b9050826001141561192f5784841161189957600095505050505050610506565b5083830360078114806118ac5750806009145b156118d2576118bb8883611c1e565b6118cd57600095505050505050610506565b6119fd565b80600814156118e5576118bb8885612e9d565b8060101415611921576118fb8860088603612e9d565b158061190e575061190c8885612e9d565b155b156118cd57600095505050505050610506565b600095505050505050610506565b826004148061193e5750826006145b1561198857808360041461195457610382611959565b620284405b62ffffff16901c60011662ffffff166000141561197e57600095505050505050610506565b6118bb8885612e9d565b6000836002146119b55761199f8987876001612edd565b806119b257506119b28987876008612edd565b90505b836003146119e75780806119d157506119d18987876007612edd565b806119e457506119e48987876009612edd565b90505b806119fb5760009650505050505050610506565b505b6104eb19611a15611a0e8a8a611a37565b6001612f98565b1215611a2957600095505050505050610506565b506001979650505050505050565b600f6004600160fc1b03600483901c1683811c821660fc600285901b1681811b9084901b6000199081189490931b92909218909416919091161791600090610e05846130aa565b6000806000611a8c856130d9565b8051909150611aa2576000809250925050611b5f565b61106319600080805b848160058110611abd57611abd6148f0565b602002015115611b38576000858260058110611adb57611adb6148f0565b602002015190505b8015611b2f57611b03611afa8b610fff8416611a37565b60018b03612f98565b611b118b610fff84166134df565b01935084841315611b275783945080610fff1692505b600c1c611ae3565b50600101611aab565b506104eb19831215611b54576000809550955050505050611b5f565b9450506104ec129150505b9250929050565b7b03256230011111100000000000000000099999900bcdecb000000001600855600954608090611b9990821c600161443b565b901b600955565b611baa8383613645565b611bb76000848484612d45565b6107675760405162461bcd60e51b815260206004820152603260248201527f4552433732313a207472616e7366657220746f206e6f6e20455243373231526560448201527131b2b4bb32b91034b6b83632b6b2b73a32b960711b6064820152608401610613565b6000600f821615801590611c3c575082600116600383600f16901c14155b9392505050565b6060806000611c51866116ca565b611c5a876116ca565b604051602001611c6b929190614906565b60405160208183030381529060405290506000611c87876116ca565b604051602001611c979190614989565b60408051601f198184030181528282019091526001808352600360fc1b6020840152909250606091908914611e0057600488600f161015611d0d57505060408051808201825260048152634e6f6e6560e01b602080830191909152825180840190935260018352600360fc1b9083015290611ddb565b600d88600f161015611d5657505060408051808201825260068152654e6172726f7760d01b602080830191909152825180840190935260018352601960f91b9083015290611ddb565b600f88600f161015611d9e57505060408051808201825260048152635769646560e01b60208083019190915282518084019093526002835261189960f11b9083015290611ddb565b50506040805180820182526009815268556c7472617769646560b81b602080830191909152825180840190935260028352610c8d60f21b90830152905b8382604051602001611dee9291906149dd565b60405160208183030381529060405293505b8281604051602001611e13929190614a44565b60405160208183030381529060405292505050600486901c9550606080600188603f161015611e785750506040805180820182526005815264506c616e6560d81b602080830191909152825180840190935260018352600760fb1b9083015290611f8e565b600b88603f161015611ebf57505060408051808201825260038152620c4bcd60ea1b60208083019190915282518084019093526002835261072760f31b9083015290611f8e565b601588603f161015611f0757505060408051808201825260038082526218979960e91b60208084019190915283518085019094529083526231393760e81b9083015290611f8e565b603388603f161015611f5057505060408051808201825260048152634375626560e01b602080830191909152825180840190935260038352620cce4d60ea1b9083015290611f8e565b50506040805180820182526008815267496e66696e69746560c01b602080830191909152825180840190935260048352630313030360e41b90830152905b8382604051602001611fa1929190614a85565b60405160208183030381529060405293508281604051602001611fc5929190614af7565b60405160208183030381529060405292505050600686901c955060606000601988601f1610156122cd575062ffffff600588901c1683612004826116ca565b604051602001612015929190614b34565b6040516020818303038152906040529350600788601f16101561207f5760405180604001604052806007815260200166556e69666f726d60c81b81525091506120608162ffffff0390565b601882901b603083901b604884901b606085901b171717179050612526565b600e88601f161015612174576040518060400160405280600681526020016553686164657360d01b81525091506120b88162ffffff0390565b601882901b60307f3fffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000600285901c908116613f00821617603f90911617901b60487f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000600186901c908116617f00821617607f90911617901b60607f1fffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000600387901c908116611f00821617601f909116175b901b171717179050612526565b601588601f161015612229576040518060400160405280600581526020016454696e747360d81b81525091506121ac8162ffffff0390565b601882901b601083811c60ff908103600281811c8303841b600888811c8516850380841c8603821b92909217858a1686039384901c86031760301b95600185811c8703821b84821c8803841b179085901c87031760481b95606095600390811c820390921b93821c810390921b929092179290911c900317612167565b601888601f161015612284576040518060400160405280600781526020016645636c6970736560c81b81525091506122638162ffffff0390565b601882901b606083901b6bffffff0000000000000000001717179050612526565b60405180604001604052806004815260200163159bda5960e21b81525091506122af8162ffffff0390565b601882901b60606122c28462ffffff0390565b901b17179050612526565b60408051808201825260078082526610dd5c985d195960ca1b6020808401919091528351610140810185526004610100820181815263139bdc9960e21b610120840152825285518087018752600380825262422f5760e81b828601528385019190915286518088018852600981526821b0b7323cb1b7b93760b91b818601528388015286518088018852908152622923a160e91b818501526060830152855180870187526006808252655653436f646560d01b82860152608084019190915286518088018852828152632732b7b760e11b8186015260a084015286518088018852908152654a756e676c6560d01b8185015260c0830152855180870190965285526321b7b93760e11b9185019190915260e081019390935260059a909a1c999093508591908a1660088110612404576124046148f0565b602002015160405160200161241a929190614ba8565b60408051601f198184030181526080830182527d8fbcbbebcb8bd087705e81acb48ead000000ffffffffffffffffff00000083527d0d3b66f4d35eee964bfaf0caf95738ffff0000ff000000ffff0000ffff0060208401527d1e1e1e569cd6d2d1a2ba7fb54dc4ac00ffffffff000000ff00ff00ff00ff918301919091527dbe3400015045020d22eabaacbe3400f9c233705860211a28346830f9c23360608301529450600360018a901c16600481106124d6576124d66148f0565b60200201517dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff169050600188161561251d57806effffffffffffffffffffffffffffff16612523565b607881901c5b90505b8382604051602001612539929190614c1f565b604051602081830303815290604052935082612558606083901c613787565b61256a604884901c62ffffff16613787565b61257c603085901c62ffffff16613787565b61258e601886901c62ffffff16613787565b61259c8662ffffff16613787565b6040516020016125b196959493929190614c9b565b60408051601f198184030181526103e083019091526103c08083529094506060935084925061539e60208301396125f16125ec8b600c6148c8565b6116ca565b6125ff6125ec8c600c6148c8565b6040516020016126129493929190614d7d565b604051602081830303815290604052915087600c146126ba57851561267757604051806040016040528060048152602001635472756560e01b8152509050816040516020016126619190614e49565b6040516020818303038152906040529150612695565b5060408051808201909152600581526446616c736560d81b60208201525b82816040516020016126a8929190614eb0565b60405160208183030381529060405292505b5060005b601781101561277957816126d1826116ca565b6126da836116ca565b6126e3846116ca565b600b851061270a57604051806040016040528060018152602001602b60f81b815250612725565b604051806040016040528060018152602001602d60f81b8152505b600b861061273e57612739600b87036116ca565b61274a565b61274a86600b036116ca565b60405160200161275f96959493929190614f26565b60408051601f1981840301815291905291506001016126be565b50606060005b6001808a901b0381101561283557600060018a038211156127ac5781600260018c901b0303600b036127b1565b81600b035b9050805b60018b038311156127d557600183600260018e901b0303901b82016127dd565b600183901b82015b811161282b57836128008d8d600c816127f8576127f86148b2565b048685613904565b60405160200161281192919061505f565b60408051601f1981840301815291905293506002016127b5565b505060010161277f565b5061284c828260405160200161153892919061508e565b99929850919650505050505050565b6060816128bc576530b131b232b360d11b6001846007160360208110612883576128836148f0565b1a60f81b612897600385901c6007036116ca565b6040516020016128a89291906150fd565b604051602081830303815290604052611c3c565b6530b131b232b360d11b83600716600603602081106128dd576128dd6148f0565b1a60f81b6128ee600385901c6116ca565b6040516020016128ff9291906150fd565b604051602081830303815290604052905092915050565b606081600114156129415750506040805180820190915260048152633830bbb760e11b602082015290565b816002141561296c5750506040805180820190915260068152650626973686f760d41b602082015290565b8160031415612995575050604080518082019091526004815263726f6f6b60e01b602082015290565b81600414156129c05750506040805180820190915260068152651adb9a59da1d60d21b602082015290565b81600514156129ea57505060408051808201909152600581526438bab2b2b760d91b602082015290565b50506040805180820190915260048152636b696e6760e01b602082015290565b6040805180820190915260058152643030302e3760d91b602082015260609060018416612a4157612a3a846130aa565b9350612a6c565b612a4f600780851690614424565b6003612a5e85821c6007614424565b612a6992911b61443b565b92505b7a24a2cc34e4524d455665a6dc75e8628e4966a6aaecb6ec72cf4d765b8015612bba57603f81168260066007841614612ab45760405180602001604052806000815250612ae1565b612ac1600383901c6116ca565b604051602001612ad1919061512e565b6040516020818303038152906040525b868314612b0057612afb600284901b89901c600f16613ae2565b612b1b565b604051806040016040528060018152602001601560f91b8152505b836007166001148015612b2f575083600914155b612b70578360091415612b515760405180602001604052806000815250612b8c565b604051806040016040528060018152602001600160fd1b815250612b8c565b604051806040016040528060028152602001612e3760f11b8152505b604051602001612b9f9493929190615153565b60408051601f1981840301815291905292505060061c612a89565b5080604051602001612bcc91906151aa565b60408051808303601f19018152919052949350505050565b6060815160001415612c0457505060408051602081019091526000815290565b600060405180606001604052806040815260200161535e60409139905060006002600385516002612c35919061443b565b612c3f91906148c8565b901b90506000612c5082602061443b565b67ffffffffffffffff811115612c6857612c6861411a565b6040519080825280601f01601f191660200182016040528015612c92576020820181803683370190505b509050818152600183018586518101602084015b81831015612d005760039283018051603f601282901c811687015160f890811b8552600c83901c8216880151811b6001860152600683901c8216880151811b60028601529116860151901b93820193909352600401612ca6565b600389510660018114612d1a5760028114612d2b57612d37565b613d3d60f01b600119830152612d37565b603d60f81b6000198301525b509398975050505050505050565b60006001600160a01b0384163b15612e9257604051630a85bd0160e11b81526001600160a01b0385169063150b7a0290612d899033908990889088906004016151eb565b602060405180830381600087803b158015612da357600080fd5b505af1925050508015612dd3575060408051601f3d908101601f19168201909252612dd091810190615227565b60015b612e78573d808015612e01576040519150601f19603f3d011682016040523d82523d6000602084013e612e06565b606091505b508051612e705760405162461bcd60e51b815260206004820152603260248201527f4552433732313a207472616e7366657220746f206e6f6e20455243373231526560448201527131b2b4bb32b91034b6b83632b6b2b73a32b960711b6064820152608401610613565b805181602001fd5b6001600160e01b031916630a85bd0160e11b149050610e05565b506001949350505050565b60006001667e7e7e7e7e7e00831c8116148015611c3c5750600f600283901b84901c161580611c3c57505060021b81901c60031c60019081169116141590565b60008060008085871015612efa5750505083830382850184612f05565b505050828403838386035b848381612f1457612f146148b2565b0615612f265760009350505050610e05565b80821015612f7357612f388883612e9d565b612f485760009350505050610e05565b612f5988600284901b8a901c611c1e565b15612f6a5760009350505050610e05565b90840190612f26565b612f7d8883612e9d565b612f8d5760009350505050610e05565b149695505050505050565b600081612fa757506000610506565b6000612fb2846130d9565b8051909150612fc5576000915050610506565b61106319600080805b848160058110612fe057612fe06148f0565b602002015115613040576000858260058110612ffe57612ffe6148f0565b602002015190505b80156130375761301a89610fff83166134df565b93508484131561302f5783945080610fff1692505b600c1c613006565b50600101612fce565b50600281603f16901b87901c6007166006141561306657610f9f19945050505050610506565b600187161561308f5761308561307c8883611a37565b60018803612f98565b836000030161309f565b61309c61307c8883611a37565b83015b979650505050505050565b60008060005b60408110156130d257600484811c94600f1692901b91909117906001016130b0565b5092915050565b6130e1613f6d565b6130e9613f8b565b6000807adb5d33cb1badb2baa99a59238a179d71b69959551349138d30b2895b80156134d257603f811660fc600283901b1687901c600f8116801580613136575088600116600382901c14155b15613143575050506134ca565b60071660018114156131dc57600f602083901c1661319557613169878460088101613ce2565b600383901c60021480156131825750600f604083901c16155b1561319557613195878460108101613ce2565b6131a389601c84901c611c1e565b156131b6576131b6878460078101613ce2565b6131c489602484901c611c1e565b156131d7576131d7878460098101613ce2565b6134c6565b6003811180156131ed575060018116155b15613274578060041461320457630107080961320a565b63060a0f115b63ffffffff1695505b85156131d75760ff86168301945061322b8986612e9d565b1561323b5761323b878487613ce2565b828611158015613258575060ff8616830394506132588986612e9d565b1561326857613268878487613ce2565b600886901c9550613213565b80600214613398578260010195505b61328d8987612e9d565b156132c35761329d878488613ce2565b6132ae89600288901b81901c611c1e565b156132b8576132c3565b600186019550613283565b6001830395505b6132d48987612e9d565b1561330a576132e4878488613ce2565b6132f589600288901b81901c611c1e565b156132ff5761330a565b6001860395506132ca565b8260080195505b61331b8987612e9d565b156133515761332b878488613ce2565b61333c89600288901b81901c611c1e565b1561334657613351565b600886019550613311565b6008830395505b6133628987612e9d565b1561339857613372878488613ce2565b61338389600288901b81901c611c1e565b1561338d57613398565b600886039550613358565b806003146134c6578260070195505b6133b18987612e9d565b156133e7576133c1878488613ce2565b6133d289600288901b81901c611c1e565b156133dc576133e7565b6007860195506133a7565b6007830395505b6133f88987612e9d565b1561342e57613408878488613ce2565b61341989600288901b81901c611c1e565b156134235761342e565b6007860395506133ee565b8260090195505b61343f8987612e9d565b156134755761344f878488613ce2565b61346089600288901b81901c611c1e565b1561346a57613475565b600986019550613435565b6009830395505b6134868987612e9d565b156134c65785613495576134c6565b6134a0878488613ce2565b6134b189600288901b81901c611c1e565b156134bb576134c6565b60098603955061347c565b5050505b60061c613109565b5050506020015192915050565b60006007600683811c8216600985901c82020160061990810192808616600387901c8216909302929092010190600485901c6004600160fc1b031686901c811690600286901b60fc1687901c16848080831561359957600584101561355a578560230360070261354e85613d6f565b901c607f169050613599565b60128610156135805785601103600c0261357385613d6f565b901c610fff169050613599565b85602303600c0261359085613e7e565b901c610fff1690505b60058510156135cf57866007026135af86613d6f565b901c607f169250856007026135c386613d6f565b901c607f169150613638565b60128710156136075786600c026135e586613e7e565b901c610fff16925085600c026135fa86613e7e565b901c610fff169150613638565b60128703600c0261361786613d6f565b901c610fff16925060128603600c0261362f86613d6f565b901c610fff1691505b0103979650505050505050565b6001600160a01b03821661369b5760405162461bcd60e51b815260206004820181905260248201527f4552433732313a206d696e7420746f20746865207a65726f20616464726573736044820152606401610613565b6000818152600260205260409020546001600160a01b0316156137005760405162461bcd60e51b815260206004820152601c60248201527f4552433732313a20746f6b656e20616c7265616479206d696e746564000000006044820152606401610613565b6001600160a01b038216600090815260036020526040812080546001929061372990849061443b565b909155505060008181526002602052604080822080546001600160a01b0319166001600160a01b03861690811790915590518392907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef908290a45050565b60606f181899199a1a9b1b9c1ca0a121a222a360811b601483901c600f16602081106137b5576137b56148f0565b1a60f81b6f181899199a1a9b1b9c1ca0a121a222a360811b601084901c600f16602081106137e5576137e56148f0565b1a60f81b6f181899199a1a9b1b9c1ca0a121a222a360811b600c85901c600f1660208110613815576138156148f0565b1a60f81b6f181899199a1a9b1b9c1ca0a121a222a360811b600886901c600f1660208110613845576138456148f0565b1a60f81b6f181899199a1a9b1b9c1ca0a121a222a360811b600487901c600f1660208110613875576138756148f0565b1a60f81b6f181899199a1a9b1b9c1ca0a121a222a360811b87600f16602081106138a1576138a16148f0565b604051602360f81b60208201526001600160f81b0319978816602182015295871660228701529386166023860152918516602485015284166025840152901a60f81b90911660268201526027016040516020818303038152906040529050919050565b60606000613911846116ca565b61391a846116ca565b60405160200161392b929190615244565b60408051601f19818403018152919052905060008080808789026000198a0160011b015b60018a038a8a02018110613ab257600091506000600b89111561397c5783600b8a038c02600b0101613988565b8389600b038c02600b03015b9050805b8b82600101038110613a9657600184840382600b0103901c600b03965060018484830103901c601003955087600187600116901b600289600116901b60d8901c901c6003166002613a0f60018b901c60018b901c60060201603f7adb5d33cb1badb2baa99a59238a179d71b69959551349138d30b28960069092029190911c1690565b901b8f901c901c600116600014613a3f57604051806040016040528060018152602001606960f81b815250613a5a565b604051806040016040528060018152602001600d60fb1b8152505b604051602001613a6b9291906152d4565b60408051601f19818403018152919052975060019093019280613a8d57613a96565b6000190161398c565b5060019093019281613aa85750613ab2565b506000190161394f565b5084604051602001613ac4919061532d565b60405160208183030381529060405295505050505050949350505050565b60608160011415613b0c575050604080518082019091526003815262e2999f60e81b602082015290565b8160021415613b34575050604080518082019091526003815262e2999d60e81b602082015290565b8160031415613b5c57505060408051808201909152600381526238a66760ea1b602082015290565b8160041415613b84575050604080518082019091526003815262714ccf60e91b602082015290565b8160051415613bac575050604080518082019091526003815262e2999b60e81b602082015290565b8160061415613bd4575050604080518082019091526003815262714ccd60e91b602082015290565b8160091415613bfc575050604080518082019091526003815262e2999960e81b602082015290565b81600a1415613c24575050604080518082019091526003815262e2999760e81b602082015290565b81600b1415613c4c575050604080518082019091526003815262714ccb60e91b602082015290565b81600c1415613c745750506040805180820190915260038152621c533360eb1b602082015290565b81600d1415613c9c575050604080518082019091526003815262e2999560e81b602082015290565b81600e1415613cc457505060408051808201909152600381526238a66560ea1b602082015290565b5050604080518082019091526002815261c2b760f01b602082015290565b825160208401516000908260058110613cfd57613cfd6148f0565b60200201519050600160f61b811115613d405760208501518551600101808752600686901b8517919060058110613d3657613d366148f0565b6020020152611086565b82600685901b600c83901b171785602001518360058110613d6357613d636148f0565b60200201525050505050565b60008160011415613da157507f02850a142850f1e3c78f1e2858c182c50a943468a152a788103c54a142850a14919050565b8160021415613dd157507f07d0204080fa042850a140810e24487020448912240810e1428701f40810203e919050565b8160031415613e0157507f0c993264c9932e6cd9b365c793264c98f1e4c993263c793264c98f264cb97264919050565b8160041415613e3157507f06ce1b3670e9c3c8101e38750224480e9d4189120ba70f20c178e1b3874e9c36919050565b8160051415613e5c57507a0b00b20b30b30b20b00b20b40b40b40b40b20b30b40b50b50b40b3919050565b507af9af98f96f96f98f9af9af98f96f96f98f9af9cf9af98f98f9af9b919050565b600081600514613ea9577af9ef9cf9cf9cf9cf9efa1fa1fa0fa0fa1fa1fa4fa6fa2fa2fa6fa4613ec6565b7a0b30b50b50b50b40b30b20b40b50b40b40b20b00b20b30b30b20b05b7affffffffffffffffffffffffffffffffffffffffffffffffffffff1692915050565b828054613ef5906142f5565b90600052602060002090601f016020900481019282613f175760008555613f5d565b82601f10613f3057805160ff1916838001178555613f5d565b82800160010185558215613f5d579182015b82811115613f5d578251825591602001919060010190613f42565b50613f69929150613faa565b5090565b6040518060a001604052806005906020820280368337509192915050565b604051806040016040528060008152602001613fa5613f6d565b905290565b5b80821115613f695760008155600101613fab565b6001600160e01b031981168114610c9f57600080fd5b600060208284031215613fe757600080fd5b8135611c3c81613fbf565b60006020828403121561400457600080fd5b5035919050565b60005b8381101561402657818101518382015260200161400e565b83811115610b825750506000910152565b6000815180845261404f81602086016020860161400b565b601f01601f19169290920160200192915050565b602081526000611c3c6020830184614037565b80356001600160a01b038116811461408d57600080fd5b919050565b600080604083850312156140a557600080fd5b6140ae83614076565b946020939093013593505050565b6000806000606084860312156140d157600080fd5b6140da84614076565b92506140e860208501614076565b9150604084013590509250925092565b6000806040838503121561410b57600080fd5b50508035926020909101359150565b634e487b7160e01b600052604160045260246000fd5b600067ffffffffffffffff8084111561414b5761414b61411a565b604051601f8501601f19908116603f011681019082821181831017156141735761417361411a565b8160405280935085815286868601111561418c57600080fd5b858560208301376000602087830101525050509392505050565b6000602082840312156141b857600080fd5b813567ffffffffffffffff8111156141cf57600080fd5b8201601f810184136141e057600080fd5b610e0584823560208401614130565b60006020828403121561420157600080fd5b611c3c82614076565b6000806040838503121561421d57600080fd5b61422683614076565b91506020830135801515811461423b57600080fd5b809150509250929050565b6000806000806080858703121561425c57600080fd5b61426585614076565b935061427360208601614076565b925060408501359150606085013567ffffffffffffffff81111561429657600080fd5b8501601f810187136142a757600080fd5b6142b687823560208401614130565b91505092959194509250565b600080604083850312156142d557600080fd5b6142de83614076565b91506142ec60208401614076565b90509250929050565b600181811c9082168061430957607f821691505b6020821081141561432a57634e487b7160e01b600052602260045260246000fd5b50919050565b634e487b7160e01b600052601160045260246000fd5b600060001982141561435a5761435a614330565b5060010190565b6000815161437381856020860161400b565b9290920192915050565b600080845481600182811c91508083168061439957607f831692505b60208084108214156143b957634e487b7160e01b86526022600452602486fd5b8180156143cd57600181146143de5761440b565b60ff1986168952848901965061440b565b60008b81526020902060005b868110156144035781548b8201529085019083016143ea565b505084890196505b50505050505061441b8185614361565b95945050505050565b60008282101561443657614436614330565b500390565b6000821982111561444e5761444e614330565b500190565b7f2d2d2d5c6e5c6e2a2a506c617965722a2a20706c617973202a2a60000000000081526000855161448b81601b850160208a0161400b565b61030160f51b601b9184019182015285516144ad81601d840160208a0161400b565b61151560f11b601d929091019182015284516144d081601f84016020890161400b565b6201515360ed1b601f929091019182015283516144f481602284016020880161400b565b016022019695505050505050565b60008551614514818460208a0161400b565b600360fd1b9083019081528551614532816001840160208a0161400b565b855191019061454881600184016020890161400b565b661515172e372e3760c91b60019290910191820152835161457081600884016020880161400b565b7f5c6e5c6e2d2d2d5c6e5c6e2a2a666976656f75746f666e696e652a2a20000000600892909101918201526025019695505050505050565b600082516145ba81846020870161400b565b6b15153932b9b4b3b73995151760a11b920191825250600c01919050565b600089516145ea818460208e0161400b565b7f726573706f6e64732077697468202a2a600000000000000000000000000000009083019081528951614624816011840160208e0161400b565b61030160f51b601192909101918201528851614647816013840160208d0161400b565b61151560f11b60139290910191820152875161466a816015840160208c0161400b565b6201515360ed1b60159290910191820152865161468e816018840160208b0161400b565b6146d56146cf6146bc6146b66146b0601886880101600360fd1b815260010190565b8b614361565b89614361565b661515172e372e3760c91b815260070190565b86614361565b9c9b505050505050505050505050565b7f7b226e616d65223a2247616d652023000000000000000000000000000000000081526000875161471d81600f850160208c0161400b565b672c204d6f7665202360c01b600f918401918201528751614745816017840160208c0161400b565b7f222c226465736372697074696f6e223a22000000000000000000000000000000601792909101918201528651614783816028840160208b0161400b565b7f222c22616e696d6174696f6e5f75726c223a22646174613a746578742f68746d60289290910191820152681b0ed8985cd94d8d0b60ba1b604882015285516147d3816051840160208a0161400b565b61485f61485161484b61483d6148376051868801017f222c2261747472696275746573223a5b7b2274726169745f74797065223a224481527f65707468222c2276616c7565223a0000000000000000000000000000000000006020820152602e0190565b8a614361565b611f4b60f21b815260020190565b87614361565b615d7d60f01b815260020190565b9a9950505050505050505050565b7f646174613a6170706c69636174696f6e2f6a736f6e3b6261736536342c0000008152600082516148a581601d85016020870161400b565b91909101601d0192915050565b634e487b7160e01b600052601260045260246000fd5b6000826148d7576148d76148b2565b500490565b6000826148eb576148eb6148b2565b500690565b634e487b7160e01b600052603260045260246000fd5b7f7b2274726169745f74797065223a2244696d656e73696f6e222c2276616c7565815262111d1160e91b60208201526000835161494a81602385016020880161400b565b6301061cb960e51b602391840191820152835161496e81602784016020880161400b565b61227d60f01b60279290910191820152602901949350505050565b7f3c7374796c653e3a726f6f747b2d2d613a3130303070783b2d2d623a000000008152600082516149c181601c85016020870161400b565b641d9696b19d60d91b601c939091019283015250602101919050565b600083516149ef81846020880161400b565b7f2c7b2274726169745f74797065223a22476170222c2276616c7565223a2200009083019081528351614a2981601e84016020880161400b565b61227d60f01b601e9290910191820152602001949350505050565b60008351614a5681846020880161400b565b835190830190614a6a81836020880161400b565b66383c1d9696b21d60c91b9101908152600701949350505050565b60008351614a9781846020880161400b565b80830190507f2c7b2274726169745f74797065223a22486569676874222c2276616c7565223a8152601160f91b60208201528351614adc81602184016020880161400b565b61227d60f01b60219290910191820152602301949350505050565b60008351614b0981846020880161400b565b835190830190614b1d81836020880161400b565b6270783b60e81b9101908152600301949350505050565b60008351614b4681846020880161400b565b80830190507f2c7b2274726169745f74797065223a224261736520436f6c6f72222c2276616c8152633ab2911d60e11b60208201528351614b8e81602484016020880161400b565b607d60f81b60249290910191820152602501949350505050565b60008351614bba81846020880161400b565b80830190507f2c7b2274726169745f74797065223a22436f6c6f72205468656d65222c227661815265363ab2911d1160d11b60208201528351614c0481602684016020880161400b565b61227d60f01b60269290910191820152602801949350505050565b60008351614c3181846020880161400b565b80830190507f2c7b2274726169745f74797065223a22436f6c6f722047656e65726174696f6e81526a1116113b30b63ab2911d1160a91b60208201528351614c8081602b84016020880161400b565b61227d60f01b602b9290910191820152602d01949350505050565b60008751614cad818460208c0161400b565b631696b29d60e11b9083019081528751614cce816004840160208c0161400b565b641d9696b31d60d91b600492909101918201528651614cf4816009840160208b0161400b565b641d9696b39d60d91b600992909101918201528551614d1a81600e840160208a0161400b565b641d9696b41d60d91b600e92909101918201528451614d4081601384016020890161400b565b641d9696b49d60d91b60139290910191820152614d70614d636018830186614361565b603b60f81b815260010190565b9998505050505050505050565b600085516020614d908285838b0161400b565b865191840191614da38184848b0161400b565b8651920191614db58184848a0161400b565b7f2c316672293b677269642d74656d706c6174652d726f77733a7265706561742892019182528451614dec8183850184890161400b565b7f2c316672293b7472616e73666f726d3a726f746174652832313064656729736b9201908101919091527f6577282d3330646567297363616c655928302e383634297d000000000000000060408201526058019695505050505050565b60008251614e5b81846020870161400b565b7f2e633e2a3a6e74682d6368696c642833293e6469767b626f726465723a2031709201918252507f7820736f6c696420626c61636b7d0000000000000000000000000000000000006020820152602e01919050565b60008351614ec281846020880161400b565b80830190507f2c7b2274726169745f74797065223a2242697420426f72646572222c2276616c8152643ab2911d1160d91b60208201528351614f0b81602584016020880161400b565b61227d60f01b60259290910191820152602701949350505050565b60008751614f38818460208c0161400b565b61173960f11b9083019081528751614f57816002840160208c0161400b565b7f7b746f703a63616c6328766172282d2d6f29202b200000000000000000000000600292909101918201528651614f95816017840160208b0161400b565b7f2a28766172282d2d6e292f32202b20766172282d2d632929297d2e6300000000601792909101918201528551614fd3816033840160208a0161400b565b7f7b6c6566743a63616c6328766172282d2d70292000000000000000000000000060339290910191820152845161501181604784016020890161400b565b661018171c1b1b1560c91b60479290910191820152614d70615036604e830186614361565b7f2a28766172282d2d6e29202b20766172282d2d632929297d0000000000000000815260180190565b6000835161507181846020880161400b565b83519083019061508581836020880161400b565b01949350505050565b600083516150a081846020880161400b565b7f3c2f7374796c653e3c73656374696f6e3e00000000000000000000000000000090830190815283516150da81601184016020880161400b565b691e17b9b2b1ba34b7b71f60b11b60119290910191820152601b01949350505050565b6001600160f81b031983168152815160009061512081600185016020870161400b565b919091016001019392505050565b6000825161514081846020870161400b565b600160fd1b920191825250600101919050565b60008551615165818460208a0161400b565b855190830190615179818360208a0161400b565b855191019061518c81836020890161400b565b845191019061519f81836020880161400b565b019695505050505050565b600082516151bc81846020870161400b565b7f5c6e202061206220632064206520665c6e606060000000000000000000000000920191825250601401919050565b60006001600160a01b0380871683528086166020840152508360408301526080606083015261521d6080830184614037565b9695505050505050565b60006020828403121561523957600080fd5b8151611c3c81613fbf565b7f3c64697620636c6173733d22632072000000000000000000000000000000000081526000835161527c81600f85016020880161400b565b61206360f01b600f91840191820152835161529e81601184016020880161400b565b7f223e3c6469763e3c2f6469763e3c6469763e3c2f6469763e3c6469763e00000060119290910191820152602e01949350505050565b600083516152e681846020880161400b565b681e3234bb1034b21e9160b91b908301908152835161530c81600984016020880161400b565b67111f1e17b234bb1f60c11b60099290910191820152601101949350505050565b6000825161533f81846020870161400b565b6b1e17b234bb1f1e17b234bb1f60a11b920191825250600c0191905056fe4142434445464748494a4b4c4d4e4f505152535455565758595a6162636465666768696a6b6c6d6e6f707172737475767778797a303132333435363738392b2f2d2d6e3a63616c6328283339347078202d2028766172282d2d6229202d2031292a766172282d2d6329292f766172282d2d6229293b2d2d6f3a63616c63283130367078202b20766172282d2d6e29293b2d2d703a63616c6328766172282d2d61292f32297d73656374696f6e7b6865696768743a766172282d2d61293b77696474683a766172282d2d61293b6261636b67726f756e643a766172282d2d65293b706f736974696f6e3a6162736f6c7574653b6c6566743a303b746f703a303b72696768743a303b626f74746f6d3a303b6f766572666c6f773a68696464656e7d2e637b6865696768743a303b77696474683a303b706f736974696f6e3a6162736f6c7574653b7472616e736974696f6e3a302e3235737d2e633a686f7665727b7472616e73666f726d3a7472616e736c617465283070782c2d36347078293b7472616e736974696f6e3a302e3235737d2e633e2a7b6865696768743a766172282d2d6e293b77696474683a766172282d2d6e293b626f726465722d626f74746f6d3a34707820736f6c696420626c61636b3b626f726465722d72696768743a34707820736f6c696420626c61636b3b626f726465722d6c6566743a31707820736f6c696420626c61636b3b626f726465722d746f703a31707820736f6c696420626c61636b3b7472616e73666f726d2d6f726967696e3a3020303b706f736974696f6e3a72656c61746976653b626f782d73697a696e673a626f726465722d626f787d2e633e2a3a6e74682d6368696c642831297b77696474683a766172282d2d64293b6261636b67726f756e642d636f6c6f723a766172282d2d66293b7472616e73666f726d3a726f7461746528393064656729736b657758282d3330646567297363616c655928302e383634297d2e633e2a3a6e74682d6368696c642832297b6865696768743a766172282d2d64293b626f74746f6d3a766172282d2d6e293b6261636b67726f756e642d636f6c6f723a766172282d2d67293b7472616e73666f726d3a726f74617465282d333064656729736b657758282d3330646567297363616c655928302e383634297d23687b6261636b67726f756e642d636f6c6f723a766172282d2d68297d23697b6261636b67726f756e642d636f6c6f723a766172282d2d69297d2e633e2a3a6e74682d6368696c642833297b626f74746f6d3a63616c6328766172282d2d6429202b20766172282d2d6e29293b6261636b67726f756e642d636f6c6f723a766172282d2d68293b646973706c61793a677269643b677269642d74656d706c6174652d636f6c756d6e733a72657065617428a264697066735822122089c3af8cdf009c81d0ecf69510fd9cd2c4270ad84ba43d39c9e4c4262a28ee3964736f6c634300080900334552433732313a207472616e7366657220746f206e6f6e204552433732315265

Deployed Bytecode

0x6080604052600436106101705760003560e01c806370a08231116100d6578063a22cb4651161007f578063d5f31d4e11610059578063d5f31d4e14610408578063e985e9c514610451578063f2fde38b1461049a57600080fd5b8063a22cb465146103a8578063b88d4fde146103c8578063c87b56dd146103e857600080fd5b80638d17e712116100b05780638d17e712146103555780638da5cb5b1461037557806395d89b411461039357600080fd5b806370a082311461030a578063715018a61461032a5780637cd3229a1461033f57600080fd5b806318160ddd1161013857806342842e0e1161011257806342842e0e146102aa57806355f804b3146102ca5780636352211e146102ea57600080fd5b806318160ddd1461026157806323b872dd1461027757806335df5d9f1461029757600080fd5b806301ffc9a71461017557806305931cb1146101aa57806306fdde03146101e5578063081812fc14610207578063095ea7b31461023f575b600080fd5b34801561018157600080fd5b50610195610190366004613fd5565b6104ba565b60405190151581526020015b60405180910390f35b3480156101b657600080fd5b506101d76101c5366004613ff2565b600a6020526000908152604090205481565b6040519081526020016101a1565b3480156101f157600080fd5b506101fa61050c565b6040516101a19190614063565b34801561021357600080fd5b50610227610222366004613ff2565b61059e565b6040516001600160a01b0390911681526020016101a1565b34801561024b57600080fd5b5061025f61025a366004614092565b610638565b005b34801561026d57600080fd5b506101d7600c5481565b34801561028357600080fd5b5061025f6102923660046140bc565b61076c565b61025f6102a53660046140f8565b6107e7565b3480156102b657600080fd5b5061025f6102c53660046140bc565b6108bf565b3480156102d657600080fd5b5061025f6102e53660046141a6565b6108da565b3480156102f657600080fd5b50610227610305366004613ff2565b61094b565b34801561031657600080fd5b506101d76103253660046141ef565b6109c2565b34801561033657600080fd5b5061025f610a49565b34801561034b57600080fd5b506101d760085481565b34801561036157600080fd5b506101fa610370366004613ff2565b610aaf565b34801561038157600080fd5b506006546001600160a01b0316610227565b34801561039f57600080fd5b506101fa610aec565b3480156103b457600080fd5b5061025f6103c336600461420a565b610afb565b3480156103d457600080fd5b5061025f6103e3366004614246565b610b06565b3480156103f457600080fd5b506101fa610403366004613ff2565b610b88565b34801561041457600080fd5b5061043c610423366004613ff2565b600b602052600090815260409020805460019091015482565b604080519283526020830191909152016101a1565b34801561045d57600080fd5b5061019561046c3660046142c2565b6001600160a01b03918216600090815260056020908152604080832093909416825291909152205460ff1690565b3480156104a657600080fd5b5061025f6104b53660046141ef565b610bd7565b60006001600160e01b031982166380ac58cd60e01b14806104eb57506001600160e01b03198216635b5e139f60e01b145b8061050657506301ffc9a760e01b6001600160e01b03198316145b92915050565b60606000805461051b906142f5565b80601f0160208091040260200160405190810160405280929190818152602001828054610547906142f5565b80156105945780601f1061056957610100808354040283529160200191610594565b820191906000526020600020905b81548152906001019060200180831161057757829003601f168201915b5050505050905090565b6000818152600260205260408120546001600160a01b031661061c5760405162461bcd60e51b815260206004820152602c60248201527f4552433732313a20617070726f76656420717565727920666f72206e6f6e657860448201526b34b9ba32b73a103a37b5b2b760a11b60648201526084015b60405180910390fd5b506000908152600460205260409020546001600160a01b031690565b60006106438261094b565b9050806001600160a01b0316836001600160a01b031614156106b15760405162461bcd60e51b815260206004820152602160248201527f4552433732313a20617070726f76616c20746f2063757272656e74206f776e656044820152603960f91b6064820152608401610613565b336001600160a01b03821614806106eb57506001600160a01b038116600090815260056020908152604080832033845290915290205460ff165b61075d5760405162461bcd60e51b815260206004820152603860248201527f4552433732313a20617070726f76652063616c6c6572206973206e6f74206f7760448201527f6e6572206e6f7220617070726f76656420666f7220616c6c00000000000000006064820152608401610613565b6107678383610ca8565b505050565b6107763382610d16565b6107dc5760405162461bcd60e51b815260206004820152603160248201527f4552433732313a207472616e736665722063616c6c6572206973206e6f74206f6044820152701ddb995c881b9bdc88185c1c1c9bdd9959607a1b6064820152608401610613565b610767838383610e0d565b6002600754141561083a5760405162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c006044820152606401610613565b6002600755600381108015906108515750600a8111155b61085a57600080fd5b603b6080600954901c1080156108845750603b6009546fffffffffffffffffffffffffffffffff16105b61088d57600080fd5b6108978282610fad565b600c80546108b69133919060006108ad83614346565b9190505561108d565b50506001600755565b61076783838360405180602001604052806000815250610b06565b6006546001600160a01b031633146109345760405162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e65726044820152606401610613565b805161094790600d906020840190613ee9565b5050565b6000818152600260205260408120546001600160a01b0316806105065760405162461bcd60e51b815260206004820152602960248201527f4552433732313a206f776e657220717565727920666f72206e6f6e657869737460448201526832b73a103a37b5b2b760b91b6064820152608401610613565b60006001600160a01b038216610a2d5760405162461bcd60e51b815260206004820152602a60248201527f4552433732313a2062616c616e636520717565727920666f7220746865207a65604482015269726f206164647265737360b01b6064820152608401610613565b506001600160a01b031660009081526003602052604090205490565b6006546001600160a01b03163314610aa35760405162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e65726044820152606401610613565b610aad60006110a7565b565b6000818152600a6020908152604080832054600b8352928190208151808301909252805482526001015491810191909152606091610506916110f9565b60606001805461051b906142f5565b61094733838361157d565b610b103383610d16565b610b765760405162461bcd60e51b815260206004820152603160248201527f4552433732313a207472616e736665722063616c6c6572206973206e6f74206f6044820152701ddb995c881b9bdc88185c1c1c9bdd9959607a1b6064820152608401610613565b610b828484848461164c565b50505050565b6060600d8054610b97906142f5565b159050610bce57600d610ba9836116ca565b604051602001610bba92919061437d565b604051602081830303815290604052610506565b61050682610aaf565b6006546001600160a01b03163314610c315760405162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e65726044820152606401610613565b6001600160a01b038116610c965760405162461bcd60e51b815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201526564647265737360d01b6064820152608401610613565b610c9f816110a7565b50565b3b151590565b600081815260046020526040902080546001600160a01b0319166001600160a01b0384169081179091558190610cdd8261094b565b6001600160a01b03167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92560405160405180910390a45050565b6000818152600260205260408120546001600160a01b0316610d8f5760405162461bcd60e51b815260206004820152602c60248201527f4552433732313a206f70657261746f7220717565727920666f72206e6f6e657860448201526b34b9ba32b73a103a37b5b2b760a11b6064820152608401610613565b6000610d9a8361094b565b9050806001600160a01b0316846001600160a01b03161480610dd55750836001600160a01b0316610dca8461059e565b6001600160a01b0316145b80610e0557506001600160a01b0380821660009081526005602090815260408083209388168352929052205460ff165b949350505050565b826001600160a01b0316610e208261094b565b6001600160a01b031614610e885760405162461bcd60e51b815260206004820152602960248201527f4552433732313a207472616e73666572206f6620746f6b656e2074686174206960448201526839903737ba1037bbb760b91b6064820152608401610613565b6001600160a01b038216610eea5760405162461bcd60e51b8152602060048201526024808201527f4552433732313a207472616e7366657220746f20746865207a65726f206164646044820152637265737360e01b6064820152608401610613565b610ef5600082610ca8565b6001600160a01b0383166000908152600360205260408120805460019290610f1e908490614424565b90915550506001600160a01b0382166000908152600360205260408120805460019290610f4c90849061443b565b909155505060008181526002602052604080822080546001600160a01b0319166001600160a01b0386811691821790925591518493918716917fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef91a4505050565b600854610fba81846117e0565b610fc357600080fd5b610fcd8184611a37565b9050600080610fdc8385611a7e565b600980546001808201909255600c80546000908152600a602090815260408083209490945583518085018552600854815260188c901b8d851b17881781830190815293548352600b909152929020915182555191015590925090508115806110595750603b6009546fffffffffffffffffffffffffffffffff1610155b1561106b57611066611b66565b611086565b6110758383611a37565b600855801561108657611086611b66565b5050505050565b610947828260405180602001604052806000815250611ba0565b600680546001600160a01b038381166001600160a01b0319831681179093556040519116919082907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a35050565b60208101518151606091829182918291610fff600c83901c8116921690600090819081908190819061112b9088611a37565b94506111378587611a37565b8c519094506111509060fc60028a901b1681901c611c1e565b92506111668560fc600289901b1681901c611c1e565b60208d01518d5191935060181c915060009060076004600160fc1b0360048b811c82169390931c8216928a901c1688901c1660018214156111aa57600692506111e5565b81600314156111bc57600292506111e5565b81600414156111ce57600492506111e5565b81600514156111e057600c92506111e5565b600192505b80600214156111f357600392505b505060008e868f60200151604051602001611221939291909283526020830191909152604082015260600190565b6040516020818303038152906040528051906020012060001c9050611250868383888061124b5750875b611c43565b909b509950611267915050600688901c600161285b565b611282600260068a901c901b8e60000151901c600716612916565b846112a957604051806040016040528060048152602001630103a37960e51b8152506112cd565b6040518060400160405280600a81526020016901031b0b83a3ab932b9960b51b8152505b6112db8a603f16600161285b565b6040516020016112ee9493929190614453565b6040516020818303038152906040529950898361131a5760405180602001604052806000815250611335565b604051806040016040528060018152602001600160fd1b8152505b8461134f5760405180602001604052806000815250611369565b61136960028a603f16901b8f60000151901c600716612916565b6113778860068c901c612a0a565b60405160200161138a9493929190614502565b604051602081830303815290604052995085600014156113cb57896040516020016113b591906145a8565b60405160208183030381529060405299506114ec565b896113db600688901c600061285b565b6113f760076004600160fc1b0360048b901c1689901c16612916565b8461141e57604051806040016040528060048152602001630103a37960e51b815250611442565b6040518060400160405280600a81526020016901031b0b83a3ab932b9960b51b8152505b6114508a603f16600061285b565b8661146a5760405180602001604052806000815250611485565b604051806040016040528060018152602001600160fd1b8152505b8761149f57604051806020016040528060008152506114b5565b6114b560028d603f16901b8c901c600716612916565b6114c38b60068f901c612a0a565b6040516020016114da9897969594939291906145d8565b60405160208183030381529060405299505b61154c6114fc60808f901c6116ca565b6115178f6fffffffffffffffffffffffffffffffff166116ca565b8c8c611522866116ca565b8d604051602001611538969594939291906146e5565b604051602081830303815290604052612be4565b60405160200161155c919061486d565b6040516020818303038152906040529a505050505050505050505092915050565b816001600160a01b0316836001600160a01b031614156115df5760405162461bcd60e51b815260206004820152601960248201527f4552433732313a20617070726f766520746f2063616c6c6572000000000000006044820152606401610613565b6001600160a01b03838116600081815260056020908152604080832094871680845294825291829020805460ff191686151590811790915591519182527f17307eab39ab6107e8899845ad3d59bd9653f200f220920489ca2b5937696c31910160405180910390a3505050565b611657848484610e0d565b61166384848484612d45565b610b825760405162461bcd60e51b815260206004820152603260248201527f4552433732313a207472616e7366657220746f206e6f6e20455243373231526560448201527131b2b4bb32b91034b6b83632b6b2b73a32b960711b6064820152608401610613565b6060816116ee5750506040805180820190915260018152600360fc1b602082015290565b8160005b8115611718578061170281614346565b91506117119050600a836148c8565b91506116f2565b60008167ffffffffffffffff8111156117335761173361411a565b6040519080825280601f01601f19166020018201604052801561175d576020820181803683370190505b5090505b8415610e0557611772600183614424565b915061177f600a866148dc565b61178a90603061443b565b60f81b81838151811061179f5761179f6148f0565b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a9053506117d9600a866148c8565b9450611761565b6000600682901c603f83166001667e7e7e7e7e7e00831c1661180757600092505050610506565b6001667e7e7e7e7e7e00821c1661182357600092505050610506565b600f600283901b86901c168061183f5760009350505050610506565b85600116600382901c146118595760009350505050610506565b600716600282901b86901c600084841061187557848403611879565b8385035b9050826001141561192f5784841161189957600095505050505050610506565b5083830360078114806118ac5750806009145b156118d2576118bb8883611c1e565b6118cd57600095505050505050610506565b6119fd565b80600814156118e5576118bb8885612e9d565b8060101415611921576118fb8860088603612e9d565b158061190e575061190c8885612e9d565b155b156118cd57600095505050505050610506565b600095505050505050610506565b826004148061193e5750826006145b1561198857808360041461195457610382611959565b620284405b62ffffff16901c60011662ffffff166000141561197e57600095505050505050610506565b6118bb8885612e9d565b6000836002146119b55761199f8987876001612edd565b806119b257506119b28987876008612edd565b90505b836003146119e75780806119d157506119d18987876007612edd565b806119e457506119e48987876009612edd565b90505b806119fb5760009650505050505050610506565b505b6104eb19611a15611a0e8a8a611a37565b6001612f98565b1215611a2957600095505050505050610506565b506001979650505050505050565b600f6004600160fc1b03600483901c1683811c821660fc600285901b1681811b9084901b6000199081189490931b92909218909416919091161791600090610e05846130aa565b6000806000611a8c856130d9565b8051909150611aa2576000809250925050611b5f565b61106319600080805b848160058110611abd57611abd6148f0565b602002015115611b38576000858260058110611adb57611adb6148f0565b602002015190505b8015611b2f57611b03611afa8b610fff8416611a37565b60018b03612f98565b611b118b610fff84166134df565b01935084841315611b275783945080610fff1692505b600c1c611ae3565b50600101611aab565b506104eb19831215611b54576000809550955050505050611b5f565b9450506104ec129150505b9250929050565b7b03256230011111100000000000000000099999900bcdecb000000001600855600954608090611b9990821c600161443b565b901b600955565b611baa8383613645565b611bb76000848484612d45565b6107675760405162461bcd60e51b815260206004820152603260248201527f4552433732313a207472616e7366657220746f206e6f6e20455243373231526560448201527131b2b4bb32b91034b6b83632b6b2b73a32b960711b6064820152608401610613565b6000600f821615801590611c3c575082600116600383600f16901c14155b9392505050565b6060806000611c51866116ca565b611c5a876116ca565b604051602001611c6b929190614906565b60405160208183030381529060405290506000611c87876116ca565b604051602001611c979190614989565b60408051601f198184030181528282019091526001808352600360fc1b6020840152909250606091908914611e0057600488600f161015611d0d57505060408051808201825260048152634e6f6e6560e01b602080830191909152825180840190935260018352600360fc1b9083015290611ddb565b600d88600f161015611d5657505060408051808201825260068152654e6172726f7760d01b602080830191909152825180840190935260018352601960f91b9083015290611ddb565b600f88600f161015611d9e57505060408051808201825260048152635769646560e01b60208083019190915282518084019093526002835261189960f11b9083015290611ddb565b50506040805180820182526009815268556c7472617769646560b81b602080830191909152825180840190935260028352610c8d60f21b90830152905b8382604051602001611dee9291906149dd565b60405160208183030381529060405293505b8281604051602001611e13929190614a44565b60405160208183030381529060405292505050600486901c9550606080600188603f161015611e785750506040805180820182526005815264506c616e6560d81b602080830191909152825180840190935260018352600760fb1b9083015290611f8e565b600b88603f161015611ebf57505060408051808201825260038152620c4bcd60ea1b60208083019190915282518084019093526002835261072760f31b9083015290611f8e565b601588603f161015611f0757505060408051808201825260038082526218979960e91b60208084019190915283518085019094529083526231393760e81b9083015290611f8e565b603388603f161015611f5057505060408051808201825260048152634375626560e01b602080830191909152825180840190935260038352620cce4d60ea1b9083015290611f8e565b50506040805180820182526008815267496e66696e69746560c01b602080830191909152825180840190935260048352630313030360e41b90830152905b8382604051602001611fa1929190614a85565b60405160208183030381529060405293508281604051602001611fc5929190614af7565b60405160208183030381529060405292505050600686901c955060606000601988601f1610156122cd575062ffffff600588901c1683612004826116ca565b604051602001612015929190614b34565b6040516020818303038152906040529350600788601f16101561207f5760405180604001604052806007815260200166556e69666f726d60c81b81525091506120608162ffffff0390565b601882901b603083901b604884901b606085901b171717179050612526565b600e88601f161015612174576040518060400160405280600681526020016553686164657360d01b81525091506120b88162ffffff0390565b601882901b60307f3fffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000600285901c908116613f00821617603f90911617901b60487f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000600186901c908116617f00821617607f90911617901b60607f1fffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000600387901c908116611f00821617601f909116175b901b171717179050612526565b601588601f161015612229576040518060400160405280600581526020016454696e747360d81b81525091506121ac8162ffffff0390565b601882901b601083811c60ff908103600281811c8303841b600888811c8516850380841c8603821b92909217858a1686039384901c86031760301b95600185811c8703821b84821c8803841b179085901c87031760481b95606095600390811c820390921b93821c810390921b929092179290911c900317612167565b601888601f161015612284576040518060400160405280600781526020016645636c6970736560c81b81525091506122638162ffffff0390565b601882901b606083901b6bffffff0000000000000000001717179050612526565b60405180604001604052806004815260200163159bda5960e21b81525091506122af8162ffffff0390565b601882901b60606122c28462ffffff0390565b901b17179050612526565b60408051808201825260078082526610dd5c985d195960ca1b6020808401919091528351610140810185526004610100820181815263139bdc9960e21b610120840152825285518087018752600380825262422f5760e81b828601528385019190915286518088018852600981526821b0b7323cb1b7b93760b91b818601528388015286518088018852908152622923a160e91b818501526060830152855180870187526006808252655653436f646560d01b82860152608084019190915286518088018852828152632732b7b760e11b8186015260a084015286518088018852908152654a756e676c6560d01b8185015260c0830152855180870190965285526321b7b93760e11b9185019190915260e081019390935260059a909a1c999093508591908a1660088110612404576124046148f0565b602002015160405160200161241a929190614ba8565b60408051601f198184030181526080830182527d8fbcbbebcb8bd087705e81acb48ead000000ffffffffffffffffff00000083527d0d3b66f4d35eee964bfaf0caf95738ffff0000ff000000ffff0000ffff0060208401527d1e1e1e569cd6d2d1a2ba7fb54dc4ac00ffffffff000000ff00ff00ff00ff918301919091527dbe3400015045020d22eabaacbe3400f9c233705860211a28346830f9c23360608301529450600360018a901c16600481106124d6576124d66148f0565b60200201517dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff169050600188161561251d57806effffffffffffffffffffffffffffff16612523565b607881901c5b90505b8382604051602001612539929190614c1f565b604051602081830303815290604052935082612558606083901c613787565b61256a604884901c62ffffff16613787565b61257c603085901c62ffffff16613787565b61258e601886901c62ffffff16613787565b61259c8662ffffff16613787565b6040516020016125b196959493929190614c9b565b60408051601f198184030181526103e083019091526103c08083529094506060935084925061539e60208301396125f16125ec8b600c6148c8565b6116ca565b6125ff6125ec8c600c6148c8565b6040516020016126129493929190614d7d565b604051602081830303815290604052915087600c146126ba57851561267757604051806040016040528060048152602001635472756560e01b8152509050816040516020016126619190614e49565b6040516020818303038152906040529150612695565b5060408051808201909152600581526446616c736560d81b60208201525b82816040516020016126a8929190614eb0565b60405160208183030381529060405292505b5060005b601781101561277957816126d1826116ca565b6126da836116ca565b6126e3846116ca565b600b851061270a57604051806040016040528060018152602001602b60f81b815250612725565b604051806040016040528060018152602001602d60f81b8152505b600b861061273e57612739600b87036116ca565b61274a565b61274a86600b036116ca565b60405160200161275f96959493929190614f26565b60408051601f1981840301815291905291506001016126be565b50606060005b6001808a901b0381101561283557600060018a038211156127ac5781600260018c901b0303600b036127b1565b81600b035b9050805b60018b038311156127d557600183600260018e901b0303901b82016127dd565b600183901b82015b811161282b57836128008d8d600c816127f8576127f86148b2565b048685613904565b60405160200161281192919061505f565b60408051601f1981840301815291905293506002016127b5565b505060010161277f565b5061284c828260405160200161153892919061508e565b99929850919650505050505050565b6060816128bc576530b131b232b360d11b6001846007160360208110612883576128836148f0565b1a60f81b612897600385901c6007036116ca565b6040516020016128a89291906150fd565b604051602081830303815290604052611c3c565b6530b131b232b360d11b83600716600603602081106128dd576128dd6148f0565b1a60f81b6128ee600385901c6116ca565b6040516020016128ff9291906150fd565b604051602081830303815290604052905092915050565b606081600114156129415750506040805180820190915260048152633830bbb760e11b602082015290565b816002141561296c5750506040805180820190915260068152650626973686f760d41b602082015290565b8160031415612995575050604080518082019091526004815263726f6f6b60e01b602082015290565b81600414156129c05750506040805180820190915260068152651adb9a59da1d60d21b602082015290565b81600514156129ea57505060408051808201909152600581526438bab2b2b760d91b602082015290565b50506040805180820190915260048152636b696e6760e01b602082015290565b6040805180820190915260058152643030302e3760d91b602082015260609060018416612a4157612a3a846130aa565b9350612a6c565b612a4f600780851690614424565b6003612a5e85821c6007614424565b612a6992911b61443b565b92505b7a24a2cc34e4524d455665a6dc75e8628e4966a6aaecb6ec72cf4d765b8015612bba57603f81168260066007841614612ab45760405180602001604052806000815250612ae1565b612ac1600383901c6116ca565b604051602001612ad1919061512e565b6040516020818303038152906040525b868314612b0057612afb600284901b89901c600f16613ae2565b612b1b565b604051806040016040528060018152602001601560f91b8152505b836007166001148015612b2f575083600914155b612b70578360091415612b515760405180602001604052806000815250612b8c565b604051806040016040528060018152602001600160fd1b815250612b8c565b604051806040016040528060028152602001612e3760f11b8152505b604051602001612b9f9493929190615153565b60408051601f1981840301815291905292505060061c612a89565b5080604051602001612bcc91906151aa565b60408051808303601f19018152919052949350505050565b6060815160001415612c0457505060408051602081019091526000815290565b600060405180606001604052806040815260200161535e60409139905060006002600385516002612c35919061443b565b612c3f91906148c8565b901b90506000612c5082602061443b565b67ffffffffffffffff811115612c6857612c6861411a565b6040519080825280601f01601f191660200182016040528015612c92576020820181803683370190505b509050818152600183018586518101602084015b81831015612d005760039283018051603f601282901c811687015160f890811b8552600c83901c8216880151811b6001860152600683901c8216880151811b60028601529116860151901b93820193909352600401612ca6565b600389510660018114612d1a5760028114612d2b57612d37565b613d3d60f01b600119830152612d37565b603d60f81b6000198301525b509398975050505050505050565b60006001600160a01b0384163b15612e9257604051630a85bd0160e11b81526001600160a01b0385169063150b7a0290612d899033908990889088906004016151eb565b602060405180830381600087803b158015612da357600080fd5b505af1925050508015612dd3575060408051601f3d908101601f19168201909252612dd091810190615227565b60015b612e78573d808015612e01576040519150601f19603f3d011682016040523d82523d6000602084013e612e06565b606091505b508051612e705760405162461bcd60e51b815260206004820152603260248201527f4552433732313a207472616e7366657220746f206e6f6e20455243373231526560448201527131b2b4bb32b91034b6b83632b6b2b73a32b960711b6064820152608401610613565b805181602001fd5b6001600160e01b031916630a85bd0160e11b149050610e05565b506001949350505050565b60006001667e7e7e7e7e7e00831c8116148015611c3c5750600f600283901b84901c161580611c3c57505060021b81901c60031c60019081169116141590565b60008060008085871015612efa5750505083830382850184612f05565b505050828403838386035b848381612f1457612f146148b2565b0615612f265760009350505050610e05565b80821015612f7357612f388883612e9d565b612f485760009350505050610e05565b612f5988600284901b8a901c611c1e565b15612f6a5760009350505050610e05565b90840190612f26565b612f7d8883612e9d565b612f8d5760009350505050610e05565b149695505050505050565b600081612fa757506000610506565b6000612fb2846130d9565b8051909150612fc5576000915050610506565b61106319600080805b848160058110612fe057612fe06148f0565b602002015115613040576000858260058110612ffe57612ffe6148f0565b602002015190505b80156130375761301a89610fff83166134df565b93508484131561302f5783945080610fff1692505b600c1c613006565b50600101612fce565b50600281603f16901b87901c6007166006141561306657610f9f19945050505050610506565b600187161561308f5761308561307c8883611a37565b60018803612f98565b836000030161309f565b61309c61307c8883611a37565b83015b979650505050505050565b60008060005b60408110156130d257600484811c94600f1692901b91909117906001016130b0565b5092915050565b6130e1613f6d565b6130e9613f8b565b6000807adb5d33cb1badb2baa99a59238a179d71b69959551349138d30b2895b80156134d257603f811660fc600283901b1687901c600f8116801580613136575088600116600382901c14155b15613143575050506134ca565b60071660018114156131dc57600f602083901c1661319557613169878460088101613ce2565b600383901c60021480156131825750600f604083901c16155b1561319557613195878460108101613ce2565b6131a389601c84901c611c1e565b156131b6576131b6878460078101613ce2565b6131c489602484901c611c1e565b156131d7576131d7878460098101613ce2565b6134c6565b6003811180156131ed575060018116155b15613274578060041461320457630107080961320a565b63060a0f115b63ffffffff1695505b85156131d75760ff86168301945061322b8986612e9d565b1561323b5761323b878487613ce2565b828611158015613258575060ff8616830394506132588986612e9d565b1561326857613268878487613ce2565b600886901c9550613213565b80600214613398578260010195505b61328d8987612e9d565b156132c35761329d878488613ce2565b6132ae89600288901b81901c611c1e565b156132b8576132c3565b600186019550613283565b6001830395505b6132d48987612e9d565b1561330a576132e4878488613ce2565b6132f589600288901b81901c611c1e565b156132ff5761330a565b6001860395506132ca565b8260080195505b61331b8987612e9d565b156133515761332b878488613ce2565b61333c89600288901b81901c611c1e565b1561334657613351565b600886019550613311565b6008830395505b6133628987612e9d565b1561339857613372878488613ce2565b61338389600288901b81901c611c1e565b1561338d57613398565b600886039550613358565b806003146134c6578260070195505b6133b18987612e9d565b156133e7576133c1878488613ce2565b6133d289600288901b81901c611c1e565b156133dc576133e7565b6007860195506133a7565b6007830395505b6133f88987612e9d565b1561342e57613408878488613ce2565b61341989600288901b81901c611c1e565b156134235761342e565b6007860395506133ee565b8260090195505b61343f8987612e9d565b156134755761344f878488613ce2565b61346089600288901b81901c611c1e565b1561346a57613475565b600986019550613435565b6009830395505b6134868987612e9d565b156134c65785613495576134c6565b6134a0878488613ce2565b6134b189600288901b81901c611c1e565b156134bb576134c6565b60098603955061347c565b5050505b60061c613109565b5050506020015192915050565b60006007600683811c8216600985901c82020160061990810192808616600387901c8216909302929092010190600485901c6004600160fc1b031686901c811690600286901b60fc1687901c16848080831561359957600584101561355a578560230360070261354e85613d6f565b901c607f169050613599565b60128610156135805785601103600c0261357385613d6f565b901c610fff169050613599565b85602303600c0261359085613e7e565b901c610fff1690505b60058510156135cf57866007026135af86613d6f565b901c607f169250856007026135c386613d6f565b901c607f169150613638565b60128710156136075786600c026135e586613e7e565b901c610fff16925085600c026135fa86613e7e565b901c610fff169150613638565b60128703600c0261361786613d6f565b901c610fff16925060128603600c0261362f86613d6f565b901c610fff1691505b0103979650505050505050565b6001600160a01b03821661369b5760405162461bcd60e51b815260206004820181905260248201527f4552433732313a206d696e7420746f20746865207a65726f20616464726573736044820152606401610613565b6000818152600260205260409020546001600160a01b0316156137005760405162461bcd60e51b815260206004820152601c60248201527f4552433732313a20746f6b656e20616c7265616479206d696e746564000000006044820152606401610613565b6001600160a01b038216600090815260036020526040812080546001929061372990849061443b565b909155505060008181526002602052604080822080546001600160a01b0319166001600160a01b03861690811790915590518392907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef908290a45050565b60606f181899199a1a9b1b9c1ca0a121a222a360811b601483901c600f16602081106137b5576137b56148f0565b1a60f81b6f181899199a1a9b1b9c1ca0a121a222a360811b601084901c600f16602081106137e5576137e56148f0565b1a60f81b6f181899199a1a9b1b9c1ca0a121a222a360811b600c85901c600f1660208110613815576138156148f0565b1a60f81b6f181899199a1a9b1b9c1ca0a121a222a360811b600886901c600f1660208110613845576138456148f0565b1a60f81b6f181899199a1a9b1b9c1ca0a121a222a360811b600487901c600f1660208110613875576138756148f0565b1a60f81b6f181899199a1a9b1b9c1ca0a121a222a360811b87600f16602081106138a1576138a16148f0565b604051602360f81b60208201526001600160f81b0319978816602182015295871660228701529386166023860152918516602485015284166025840152901a60f81b90911660268201526027016040516020818303038152906040529050919050565b60606000613911846116ca565b61391a846116ca565b60405160200161392b929190615244565b60408051601f19818403018152919052905060008080808789026000198a0160011b015b60018a038a8a02018110613ab257600091506000600b89111561397c5783600b8a038c02600b0101613988565b8389600b038c02600b03015b9050805b8b82600101038110613a9657600184840382600b0103901c600b03965060018484830103901c601003955087600187600116901b600289600116901b60d8901c901c6003166002613a0f60018b901c60018b901c60060201603f7adb5d33cb1badb2baa99a59238a179d71b69959551349138d30b28960069092029190911c1690565b901b8f901c901c600116600014613a3f57604051806040016040528060018152602001606960f81b815250613a5a565b604051806040016040528060018152602001600d60fb1b8152505b604051602001613a6b9291906152d4565b60408051601f19818403018152919052975060019093019280613a8d57613a96565b6000190161398c565b5060019093019281613aa85750613ab2565b506000190161394f565b5084604051602001613ac4919061532d565b60405160208183030381529060405295505050505050949350505050565b60608160011415613b0c575050604080518082019091526003815262e2999f60e81b602082015290565b8160021415613b34575050604080518082019091526003815262e2999d60e81b602082015290565b8160031415613b5c57505060408051808201909152600381526238a66760ea1b602082015290565b8160041415613b84575050604080518082019091526003815262714ccf60e91b602082015290565b8160051415613bac575050604080518082019091526003815262e2999b60e81b602082015290565b8160061415613bd4575050604080518082019091526003815262714ccd60e91b602082015290565b8160091415613bfc575050604080518082019091526003815262e2999960e81b602082015290565b81600a1415613c24575050604080518082019091526003815262e2999760e81b602082015290565b81600b1415613c4c575050604080518082019091526003815262714ccb60e91b602082015290565b81600c1415613c745750506040805180820190915260038152621c533360eb1b602082015290565b81600d1415613c9c575050604080518082019091526003815262e2999560e81b602082015290565b81600e1415613cc457505060408051808201909152600381526238a66560ea1b602082015290565b5050604080518082019091526002815261c2b760f01b602082015290565b825160208401516000908260058110613cfd57613cfd6148f0565b60200201519050600160f61b811115613d405760208501518551600101808752600686901b8517919060058110613d3657613d366148f0565b6020020152611086565b82600685901b600c83901b171785602001518360058110613d6357613d636148f0565b60200201525050505050565b60008160011415613da157507f02850a142850f1e3c78f1e2858c182c50a943468a152a788103c54a142850a14919050565b8160021415613dd157507f07d0204080fa042850a140810e24487020448912240810e1428701f40810203e919050565b8160031415613e0157507f0c993264c9932e6cd9b365c793264c98f1e4c993263c793264c98f264cb97264919050565b8160041415613e3157507f06ce1b3670e9c3c8101e38750224480e9d4189120ba70f20c178e1b3874e9c36919050565b8160051415613e5c57507a0b00b20b30b30b20b00b20b40b40b40b40b20b30b40b50b50b40b3919050565b507af9af98f96f96f98f9af9af98f96f96f98f9af9cf9af98f98f9af9b919050565b600081600514613ea9577af9ef9cf9cf9cf9cf9efa1fa1fa0fa0fa1fa1fa4fa6fa2fa2fa6fa4613ec6565b7a0b30b50b50b50b40b30b20b40b50b40b40b20b00b20b30b30b20b05b7affffffffffffffffffffffffffffffffffffffffffffffffffffff1692915050565b828054613ef5906142f5565b90600052602060002090601f016020900481019282613f175760008555613f5d565b82601f10613f3057805160ff1916838001178555613f5d565b82800160010185558215613f5d579182015b82811115613f5d578251825591602001919060010190613f42565b50613f69929150613faa565b5090565b6040518060a001604052806005906020820280368337509192915050565b604051806040016040528060008152602001613fa5613f6d565b905290565b5b80821115613f695760008155600101613fab565b6001600160e01b031981168114610c9f57600080fd5b600060208284031215613fe757600080fd5b8135611c3c81613fbf565b60006020828403121561400457600080fd5b5035919050565b60005b8381101561402657818101518382015260200161400e565b83811115610b825750506000910152565b6000815180845261404f81602086016020860161400b565b601f01601f19169290920160200192915050565b602081526000611c3c6020830184614037565b80356001600160a01b038116811461408d57600080fd5b919050565b600080604083850312156140a557600080fd5b6140ae83614076565b946020939093013593505050565b6000806000606084860312156140d157600080fd5b6140da84614076565b92506140e860208501614076565b9150604084013590509250925092565b6000806040838503121561410b57600080fd5b50508035926020909101359150565b634e487b7160e01b600052604160045260246000fd5b600067ffffffffffffffff8084111561414b5761414b61411a565b604051601f8501601f19908116603f011681019082821181831017156141735761417361411a565b8160405280935085815286868601111561418c57600080fd5b858560208301376000602087830101525050509392505050565b6000602082840312156141b857600080fd5b813567ffffffffffffffff8111156141cf57600080fd5b8201601f810184136141e057600080fd5b610e0584823560208401614130565b60006020828403121561420157600080fd5b611c3c82614076565b6000806040838503121561421d57600080fd5b61422683614076565b91506020830135801515811461423b57600080fd5b809150509250929050565b6000806000806080858703121561425c57600080fd5b61426585614076565b935061427360208601614076565b925060408501359150606085013567ffffffffffffffff81111561429657600080fd5b8501601f810187136142a757600080fd5b6142b687823560208401614130565b91505092959194509250565b600080604083850312156142d557600080fd5b6142de83614076565b91506142ec60208401614076565b90509250929050565b600181811c9082168061430957607f821691505b6020821081141561432a57634e487b7160e01b600052602260045260246000fd5b50919050565b634e487b7160e01b600052601160045260246000fd5b600060001982141561435a5761435a614330565b5060010190565b6000815161437381856020860161400b565b9290920192915050565b600080845481600182811c91508083168061439957607f831692505b60208084108214156143b957634e487b7160e01b86526022600452602486fd5b8180156143cd57600181146143de5761440b565b60ff1986168952848901965061440b565b60008b81526020902060005b868110156144035781548b8201529085019083016143ea565b505084890196505b50505050505061441b8185614361565b95945050505050565b60008282101561443657614436614330565b500390565b6000821982111561444e5761444e614330565b500190565b7f2d2d2d5c6e5c6e2a2a506c617965722a2a20706c617973202a2a60000000000081526000855161448b81601b850160208a0161400b565b61030160f51b601b9184019182015285516144ad81601d840160208a0161400b565b61151560f11b601d929091019182015284516144d081601f84016020890161400b565b6201515360ed1b601f929091019182015283516144f481602284016020880161400b565b016022019695505050505050565b60008551614514818460208a0161400b565b600360fd1b9083019081528551614532816001840160208a0161400b565b855191019061454881600184016020890161400b565b661515172e372e3760c91b60019290910191820152835161457081600884016020880161400b565b7f5c6e5c6e2d2d2d5c6e5c6e2a2a666976656f75746f666e696e652a2a20000000600892909101918201526025019695505050505050565b600082516145ba81846020870161400b565b6b15153932b9b4b3b73995151760a11b920191825250600c01919050565b600089516145ea818460208e0161400b565b7f726573706f6e64732077697468202a2a600000000000000000000000000000009083019081528951614624816011840160208e0161400b565b61030160f51b601192909101918201528851614647816013840160208d0161400b565b61151560f11b60139290910191820152875161466a816015840160208c0161400b565b6201515360ed1b60159290910191820152865161468e816018840160208b0161400b565b6146d56146cf6146bc6146b66146b0601886880101600360fd1b815260010190565b8b614361565b89614361565b661515172e372e3760c91b815260070190565b86614361565b9c9b505050505050505050505050565b7f7b226e616d65223a2247616d652023000000000000000000000000000000000081526000875161471d81600f850160208c0161400b565b672c204d6f7665202360c01b600f918401918201528751614745816017840160208c0161400b565b7f222c226465736372697074696f6e223a22000000000000000000000000000000601792909101918201528651614783816028840160208b0161400b565b7f222c22616e696d6174696f6e5f75726c223a22646174613a746578742f68746d60289290910191820152681b0ed8985cd94d8d0b60ba1b604882015285516147d3816051840160208a0161400b565b61485f61485161484b61483d6148376051868801017f222c2261747472696275746573223a5b7b2274726169745f74797065223a224481527f65707468222c2276616c7565223a0000000000000000000000000000000000006020820152602e0190565b8a614361565b611f4b60f21b815260020190565b87614361565b615d7d60f01b815260020190565b9a9950505050505050505050565b7f646174613a6170706c69636174696f6e2f6a736f6e3b6261736536342c0000008152600082516148a581601d85016020870161400b565b91909101601d0192915050565b634e487b7160e01b600052601260045260246000fd5b6000826148d7576148d76148b2565b500490565b6000826148eb576148eb6148b2565b500690565b634e487b7160e01b600052603260045260246000fd5b7f7b2274726169745f74797065223a2244696d656e73696f6e222c2276616c7565815262111d1160e91b60208201526000835161494a81602385016020880161400b565b6301061cb960e51b602391840191820152835161496e81602784016020880161400b565b61227d60f01b60279290910191820152602901949350505050565b7f3c7374796c653e3a726f6f747b2d2d613a3130303070783b2d2d623a000000008152600082516149c181601c85016020870161400b565b641d9696b19d60d91b601c939091019283015250602101919050565b600083516149ef81846020880161400b565b7f2c7b2274726169745f74797065223a22476170222c2276616c7565223a2200009083019081528351614a2981601e84016020880161400b565b61227d60f01b601e9290910191820152602001949350505050565b60008351614a5681846020880161400b565b835190830190614a6a81836020880161400b565b66383c1d9696b21d60c91b9101908152600701949350505050565b60008351614a9781846020880161400b565b80830190507f2c7b2274726169745f74797065223a22486569676874222c2276616c7565223a8152601160f91b60208201528351614adc81602184016020880161400b565b61227d60f01b60219290910191820152602301949350505050565b60008351614b0981846020880161400b565b835190830190614b1d81836020880161400b565b6270783b60e81b9101908152600301949350505050565b60008351614b4681846020880161400b565b80830190507f2c7b2274726169745f74797065223a224261736520436f6c6f72222c2276616c8152633ab2911d60e11b60208201528351614b8e81602484016020880161400b565b607d60f81b60249290910191820152602501949350505050565b60008351614bba81846020880161400b565b80830190507f2c7b2274726169745f74797065223a22436f6c6f72205468656d65222c227661815265363ab2911d1160d11b60208201528351614c0481602684016020880161400b565b61227d60f01b60269290910191820152602801949350505050565b60008351614c3181846020880161400b565b80830190507f2c7b2274726169745f74797065223a22436f6c6f722047656e65726174696f6e81526a1116113b30b63ab2911d1160a91b60208201528351614c8081602b84016020880161400b565b61227d60f01b602b9290910191820152602d01949350505050565b60008751614cad818460208c0161400b565b631696b29d60e11b9083019081528751614cce816004840160208c0161400b565b641d9696b31d60d91b600492909101918201528651614cf4816009840160208b0161400b565b641d9696b39d60d91b600992909101918201528551614d1a81600e840160208a0161400b565b641d9696b41d60d91b600e92909101918201528451614d4081601384016020890161400b565b641d9696b49d60d91b60139290910191820152614d70614d636018830186614361565b603b60f81b815260010190565b9998505050505050505050565b600085516020614d908285838b0161400b565b865191840191614da38184848b0161400b565b8651920191614db58184848a0161400b565b7f2c316672293b677269642d74656d706c6174652d726f77733a7265706561742892019182528451614dec8183850184890161400b565b7f2c316672293b7472616e73666f726d3a726f746174652832313064656729736b9201908101919091527f6577282d3330646567297363616c655928302e383634297d000000000000000060408201526058019695505050505050565b60008251614e5b81846020870161400b565b7f2e633e2a3a6e74682d6368696c642833293e6469767b626f726465723a2031709201918252507f7820736f6c696420626c61636b7d0000000000000000000000000000000000006020820152602e01919050565b60008351614ec281846020880161400b565b80830190507f2c7b2274726169745f74797065223a2242697420426f72646572222c2276616c8152643ab2911d1160d91b60208201528351614f0b81602584016020880161400b565b61227d60f01b60259290910191820152602701949350505050565b60008751614f38818460208c0161400b565b61173960f11b9083019081528751614f57816002840160208c0161400b565b7f7b746f703a63616c6328766172282d2d6f29202b200000000000000000000000600292909101918201528651614f95816017840160208b0161400b565b7f2a28766172282d2d6e292f32202b20766172282d2d632929297d2e6300000000601792909101918201528551614fd3816033840160208a0161400b565b7f7b6c6566743a63616c6328766172282d2d70292000000000000000000000000060339290910191820152845161501181604784016020890161400b565b661018171c1b1b1560c91b60479290910191820152614d70615036604e830186614361565b7f2a28766172282d2d6e29202b20766172282d2d632929297d0000000000000000815260180190565b6000835161507181846020880161400b565b83519083019061508581836020880161400b565b01949350505050565b600083516150a081846020880161400b565b7f3c2f7374796c653e3c73656374696f6e3e00000000000000000000000000000090830190815283516150da81601184016020880161400b565b691e17b9b2b1ba34b7b71f60b11b60119290910191820152601b01949350505050565b6001600160f81b031983168152815160009061512081600185016020870161400b565b919091016001019392505050565b6000825161514081846020870161400b565b600160fd1b920191825250600101919050565b60008551615165818460208a0161400b565b855190830190615179818360208a0161400b565b855191019061518c81836020890161400b565b845191019061519f81836020880161400b565b019695505050505050565b600082516151bc81846020870161400b565b7f5c6e202061206220632064206520665c6e606060000000000000000000000000920191825250601401919050565b60006001600160a01b0380871683528086166020840152508360408301526080606083015261521d6080830184614037565b9695505050505050565b60006020828403121561523957600080fd5b8151611c3c81613fbf565b7f3c64697620636c6173733d22632072000000000000000000000000000000000081526000835161527c81600f85016020880161400b565b61206360f01b600f91840191820152835161529e81601184016020880161400b565b7f223e3c6469763e3c2f6469763e3c6469763e3c2f6469763e3c6469763e00000060119290910191820152602e01949350505050565b600083516152e681846020880161400b565b681e3234bb1034b21e9160b91b908301908152835161530c81600984016020880161400b565b67111f1e17b234bb1f60c11b60099290910191820152601101949350505050565b6000825161533f81846020870161400b565b6b1e17b234bb1f1e17b234bb1f60a11b920191825250600c0191905056fe4142434445464748494a4b4c4d4e4f505152535455565758595a6162636465666768696a6b6c6d6e6f707172737475767778797a303132333435363738392b2f2d2d6e3a63616c6328283339347078202d2028766172282d2d6229202d2031292a766172282d2d6329292f766172282d2d6229293b2d2d6f3a63616c63283130367078202b20766172282d2d6e29293b2d2d703a63616c6328766172282d2d61292f32297d73656374696f6e7b6865696768743a766172282d2d61293b77696474683a766172282d2d61293b6261636b67726f756e643a766172282d2d65293b706f736974696f6e3a6162736f6c7574653b6c6566743a303b746f703a303b72696768743a303b626f74746f6d3a303b6f766572666c6f773a68696464656e7d2e637b6865696768743a303b77696474683a303b706f736974696f6e3a6162736f6c7574653b7472616e736974696f6e3a302e3235737d2e633a686f7665727b7472616e73666f726d3a7472616e736c617465283070782c2d36347078293b7472616e736974696f6e3a302e3235737d2e633e2a7b6865696768743a766172282d2d6e293b77696474683a766172282d2d6e293b626f726465722d626f74746f6d3a34707820736f6c696420626c61636b3b626f726465722d72696768743a34707820736f6c696420626c61636b3b626f726465722d6c6566743a31707820736f6c696420626c61636b3b626f726465722d746f703a31707820736f6c696420626c61636b3b7472616e73666f726d2d6f726967696e3a3020303b706f736974696f6e3a72656c61746976653b626f782d73697a696e673a626f726465722d626f787d2e633e2a3a6e74682d6368696c642831297b77696474683a766172282d2d64293b6261636b67726f756e642d636f6c6f723a766172282d2d66293b7472616e73666f726d3a726f7461746528393064656729736b657758282d3330646567297363616c655928302e383634297d2e633e2a3a6e74682d6368696c642832297b6865696768743a766172282d2d64293b626f74746f6d3a766172282d2d6e293b6261636b67726f756e642d636f6c6f723a766172282d2d67293b7472616e73666f726d3a726f74617465282d333064656729736b657758282d3330646567297363616c655928302e383634297d23687b6261636b67726f756e642d636f6c6f723a766172282d2d68297d23697b6261636b67726f756e642d636f6c6f723a766172282d2d69297d2e633e2a3a6e74682d6368696c642833297b626f74746f6d3a63616c6328766172282d2d6429202b20766172282d2d6e29293b6261636b67726f756e642d636f6c6f723a766172282d2d68293b646973706c61793a677269643b677269642d74656d706c6174652d636f6c756d6e733a72657065617428a264697066735822122089c3af8cdf009c81d0ecf69510fd9cd2c4270ad84ba43d39c9e4c4262a28ee3964736f6c63430008090033

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.