ERC-721
Overview
Max Total Supply
337 ♞
Holders
105
Market
Volume (24H)
N/A
Min Price (24H)
N/A
Max Price (24H)
N/A
Other Info
Token Contract
Balance
1 ♞Loading...
Loading
Loading...
Loading
Loading...
Loading
# | Exchange | Pair | Price | 24H Volume | % Volume |
---|
Contract Name:
fiveoutofnine
Compiler Version
v0.8.9+commit.e5eed63a
Optimization Enabled:
Yes with 500 runs
Other Settings:
default evmVersion
Contract Source Code (Solidity Standard Json-Input format)
// 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 ); } }
// 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 {} }
// 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); } }
// 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; } }
// 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); } }
// 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; } } } }
// 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; } }
// 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"·"; } }
// 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; }
// 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); }
// 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); }
// 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); } } } }
// 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; } }
// 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; } }
// 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); }
// 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; } }
{ "optimizer": { "enabled": true, "runs": 500 }, "outputSelection": { "*": { "*": [ "evm.bytecode", "evm.deployedBytecode", "devdoc", "userdoc", "metadata", "abi" ] } }, "libraries": {} }
Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
[{"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"}]
Contract Creation Code
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.