ETH Price: $3,132.06 (+0.66%)
 

Overview

Max Total Supply

121

Holders

0

Transfers

-
0

Market

Price

$0.00 @ 0.000000 ETH

Onchain Market Cap

-

Circulating Supply Market Cap

-

Other Info

Token Contract (WITH 0 Decimals)

Loading...
Loading
Loading...
Loading
Loading...
Loading

Click here to update the token information / general information

Contract Source Code Verified (Exact Match)

Contract Name:
Straylight

Compiler Version
v0.8.15+commit.e14f2714

Optimization Enabled:
No with 200 runs

Other Settings:
default evmVersion
//SPDX-License-Identifier: Unlicense
pragma solidity ^0.8.0;

import "./metadata.sol";
import "./EnumerableMate.sol";

//                           STRAYLIGHT PROTOCOL v.01
//
//                                .         .
//                                  ., ... .,
//                                  \%%&%%&./
//                         ,,.      /@&&%&&&\     ,,
//                            *,   (##%&&%##)  .*.
//                              (,  (#%%%%.)   %,
//                               ,% (#(##(%.(/
//                                 %((#%%%##*
//                 ..**,*/***///*,..,#%%%%*..,*\\\***\*,**..
//                                   /%#%%
//                              /###(/%&%/(##%(,
//                           ,/    (%###%%&%,   **
//                         .#.    *@&%###%%&)     \\
//                        /       *&&%###%&@#       *.
//                      ,*         (%#%###%#?)       .*.
//                                 ./%%###%%#,
//                                  .,(((##,.
//
//

/// @title Straylight
/// @notice The main point of interaction for Straylight - relies on Gamboard, Metadata, Turmitesv4 and EnumerableMate
/// @author @brachlandberlin / plsdlr.net
/// @dev facilitates minting, moving of individual turmites, reprogramming and even external logic of turmites and overwrites tokenUri()

contract Straylight is EnumerableMate, Metadata {
    event TurmiteReprogramm(uint256 indexed tokenId, bytes12 indexed newrule);
    event TurmiteMint(uint256 indexed tokenId, bytes12 indexed rule, uint256 boardId);

    uint256 public boardcounter = 0;
    uint256 private turmitecounter = 0;
    uint256 public maxnumbturmites;
    uint256[4] startx = [36, 72, 72, 108];
    uint256[4] starty = [72, 36, 108, 72];
    address minterContract;
    address haecceityContract;
    address admin;
    mapping(uint256 => bool) public haecceity;

    constructor(
        address _minterContract,
        uint256 maxAmount,
        string memory network
    ) Metadata(network) EnumerableMate("Straylight", "STR") {
        minterContract = _minterContract;
        maxnumbturmites = maxAmount;
        admin = msg.sender;
    }

    /// @dev public Mint function should only be called from external Minter Contract
    /// @param mintTo the address the token should be minted to
    /// @param rule the inital rule the turmite is minted with
    /// @param moves the inital number of rules the turmite is minted with
    function publicmint(
        address mintTo,
        bytes12 rule,
        uint256 moves
    ) external {
        require(turmitecounter < maxnumbturmites, "MINT_OVER");
        require(validateNewRule(rule) == true, "INVALID_RULE");
        require(msg.sender == minterContract, "ONLY_MINTABLE_FROM_MINT_CONTRACT");

        boardcounter = (turmitecounter / 4) + 1;
        uint256 startposx = startx[turmitecounter % 4];
        uint256 startposy = starty[turmitecounter % 4];
        _addTokenToOwnerEnumeration(mintTo, turmitecounter);
        _addTokenToAllTokensEnumeration(turmitecounter);
        _mint(mintTo, turmitecounter);
        createTurmite(turmitecounter, uint8(startposx), uint8(startposy), 1, uint8(boardcounter), rule);
        emit TurmiteMint(turmitecounter, rule, boardcounter);
        if (moves > 0) {
            calculateTurmiteMove(turmitecounter, moves);
        }
        haecceity[turmitecounter] = false;
        turmitecounter = turmitecounter + 1;
    }

    /// @dev overwrites the tokenURI function from ERC721 Solmate
    /// @param id the id of the NFT
    function tokenURI(uint256 id) public view override returns (string memory) {
        return
            fullMetadata(
                id,
                turmites[id].boardnumber,
                turmites[id].rule,
                turmites[id].state,
                turmites[id].turposx,
                turmites[id].turposy,
                turmites[id].orientation
            );
    }

    /// @dev helper Function to render board without turmites
    /// @param number Board number
    function renderBoard(uint8 number) public view returns (string memory) {
        return getSvg(number, 0, 0, false);
    }

    function moveTurmite(uint256[2] calldata idmoves) external {
        require(msg.sender == ownerOf(idmoves[0]), "NOT_AUTHORIZED");
        if (idmoves[1] > 0) {
            calculateTurmiteMove(idmoves[0], idmoves[1]);
        }
    }

    /// @dev function to validate that an new input from user is in the "gramma" of the rules
    /// @param rule a bytes12 rule - to understand the specific gramma of rules take a look at turmitev4 contract
    function validateNewRule(bytes12 rule) public pure returns (bool allowed) {
        //Normal Format Example: 0xff0801ff0201ff0000000001
        //we dont test against direction bc direction never writes
        //bool firstbit = (rule[0] == 0xFF || rule[0] == 0x00);
        //bool secondbit = (rule[3] == 0xFF || rule[3] == 0x00);
        bool colorfieldbit = ((rule[0] == 0xFF || rule[0] == 0x00) &&
            (rule[3] == 0xFF || rule[3] == 0x00) &&
            (rule[6] == 0xFF || rule[6] == 0x00) &&
            (rule[9] == 0xFF || rule[9] == 0x00));
        bool statebit = ((rule[2] == 0x01 || rule[2] == 0x00) &&
            (rule[5] == 0x01 || rule[5] == 0x00) &&
            (rule[8] == 0x01 || rule[8] == 0x00) &&
            (rule[11] == 0x01 || rule[11] == 0x00));
        return bool(statebit && colorfieldbit);
    }

    /// @notice WE EXPECT THAT YOU KNOW WHAT YOU ARE DOING BEFORE CALLING THIS FUNCTION MANUALY
    /// @notice PLEASE CONSULT THE DOCUMENTATION
    /// @dev function to reprogramm your turmite | DANGERZONE | if you don't use the interface consult the documentation before wasting gas
    /// @param id ID of the turmite
    /// @param rule a bytes12 rule - to understand the specific gramma of rules take a look at turmitev4 contract
    function reprogrammTurmite(uint256 id, bytes12 rule) external {
        require(msg.sender == ownerOf(id), "NOT_AUTHORIZED");
        require(validateNewRule(rule) == true, "INVALID_RULE");
        turmites[id].rule = rule;
        emit TurmiteReprogramm(id, rule);
    }

    /// @dev function for the admin to set external HA Contract
    /// @param _haecceityContract address of contract
    function setHaecceityContract(address _haecceityContract) external {
        require(msg.sender == admin, "NOT_AUTHORIZED");
        haecceityContract = _haecceityContract;
    }

    /// @dev get the position(x and y values) and the state of the current field for a turmite (all handy encoded)
    /// @param id the id of the token / turmite
    function getPosField(uint256 id) public view returns (bytes memory encodedData) {
        bytes32 sour;
        uint8 _x;
        uint8 _y;
        bytes memory data = new bytes(32);
        turmite storage dataTurmite = turmites[id];
        assembly {
            sour := sload(dataTurmite.slot)
            _x := and(sour, 0xFF)
            _y := and(shr(8, sour), 0xFF)
        }
        bytes1 stateOfField = getByte(_x, _y, (id / 4) + 1);
        assembly {
            mstore8(add(data, 32), _x)
            mstore8(add(data, 33), _y)
            mstore(add(data, 34), stateOfField)
        }
        return (data);
    }

    //  _   _   _____          _   _  _____ ______ _____   __________  _   _ ______   _   _
    // | | | | |  __ \   /\   | \ | |/ ____|  ____|  __ \ |___  / __ \| \ | |  ____| | | | |
    // | | | | | |  | | /  \  |  \| | |  __| |__  | |__) |   / / |  | |  \| | |__    | | | |
    // | | | | | |  | |/ /\ \ | . ` | | |_ |  __| |  _  /   / /| |  | | . ` |  __|   | | | |
    // |_| |_| | |__| / ____ \| |\  | |__| | |____| | \ \  / /_| |__| | |\  | |____  |_| |_|
    // (_) (_) |_____/_/    \_\_| \_|\_____|______|_|  \_\/_____\____/|_| \_|______| (_) (_)

    /// @notice WE EXPECT THAT YOU KNOW WHAT YOU ARE DOING BEFORE CALLING THIS FUNCTION
    /// @notice PLEASE CONSULT THE DOCUMENTATION
    /// @dev should be called by user to unlock external control
    /// @param id the id of the token / turmite the user what to hand logic control over to external smart contract
    function setHaecceityMode(uint256 id) external {
        require(haecceityContract != address(0), "CONTRACT_IS_ZEROADDRESS");
        require(msg.sender == ownerOf(id), "NOT_AUTHORIZED");
        haecceity[id] = true;
    }

    /// @dev internal deocde function
    ///  @param data data to decode
    function decode(bytes memory data)
        internal
        pure
        returns (
            uint8 x,
            uint8 y,
            bytes1 field
        )
    {
        assembly {
            x := mload(add(data, 1))
            y := mload(add(data, 2))
            field := mload(add(data, 34))
        }
    }

    /// @notice WE EXPECT THAT YOU KNOW WHAT YOU ARE DOING BEFORE CALLING THIS FUNCTION
    /// @notice PLEASE CONSULT THE DOCUMENTATION
    /// @dev function should be called by external haecceity Contract which allows external control of turmites by user deployed smart contracts
    /// @dev this function sets the field
    /// @param id the id of the turmite
    /// @param data the encoded data of the next step
    function setByteHaMode(uint256 id, bytes calldata data) external {
        require(haecceityContract != address(0), "CONTRACT_IS_ZEROADDRESS");
        require(haecceity[id] == true, "CONTRACT_NOT_INITALIZED_BY_NFT_OWNER");
        require(msg.sender == haecceityContract, "CALL_ONLY_FROM_HACONTRACT");
        (uint8 x, uint8 y, bytes1 stateOfField) = decode(data);
        setByte(x, y, stateOfField, turmites[id].boardnumber);
    }

    /// @notice WE EXPECT THAT YOU KNOW WHAT YOU ARE DOING BEFORE CALLING THIS FUNCTION
    /// @notice PLEASE CONSULT THE DOCUMENTATION
    /// @dev function should be called by external haecceity Contract which allows external control of turmites by user deployed smart contracts
    /// @dev this function sets the end position after moving
    /// @param id the id of the turmite
    /// @param data the encoded data of the position
    function setPositionHaMode(uint256 id, bytes calldata data) external {
        require(haecceityContract != address(0), "CONTRACT_IS_ZEROADDRESS");
        require(haecceity[id] == true, "CONTRACT_NOT_INITALIZED_BY_NFT_OWNER");
        require(msg.sender == haecceityContract, "CALL_ONLY_FROM_HACONTRACT");
        (uint8 x, uint8 y, ) = decode(data);
        turmites[id].turposx = x;
        turmites[id].turposy = y;
    }

    /// @dev overwriting transfer functions to add extension from here

    /// @notice after every transfer we reset the permission for external control
    /// @dev resets permission after every transfer
    function _transferResetHAMode(uint256 tokenId) internal {
        if (haecceity[tokenId] == true) {
            haecceity[tokenId] = false;
        }
    }

    function transferFrom(
        address from,
        address to,
        uint256 tokenId
    ) public virtual override {
        _transferResetHAMode(tokenId);
        _removeTokenFromOwnerEnumeration(from, tokenId);
        _addTokenToOwnerEnumeration(to, tokenId);
        super.transferFrom(from, to, tokenId);
    }

    function safeTransferFrom(
        address from,
        address to,
        uint256 tokenId
    ) public virtual override {
        _transferResetHAMode(tokenId);
        _removeTokenFromOwnerEnumeration(from, tokenId);
        _addTokenToOwnerEnumeration(to, tokenId);
        super.safeTransferFrom(from, to, tokenId);
    }

    function safeTransferFrom(
        address from,
        address to,
        uint256 tokenId,
        bytes calldata data
    ) public virtual override {
        _transferResetHAMode(tokenId);
        _removeTokenFromOwnerEnumeration(from, tokenId);
        _addTokenToOwnerEnumeration(to, tokenId);
        super.safeTransferFrom(from, to, tokenId, data);
    }
}

//SPDX-License-Identifier: Unlicense
pragma solidity ^0.8.0;

import "./turmitev4.sol";
import "solady/src/utils/LibString.sol";

/// @title Metadata
/// @notice Renders the dynamic metadata of every NFT via the Turmite v4.
/// @author @brachlandberlin / plsdlr.net
/// @dev Generates the metadata as JSON String and encodes it with base64 and data:application/json;base64,

contract Metadata is Turmite {
    string private network;

    constructor(string memory _network) {
        network = _network;
    }

    /// @dev generates the dynamic metadata
    /// @param tokenId the tokenId of the Turmite
    /// @param boardNumber the Board Number
    /// @param rule the rule of the turmite
    /// @param state current state
    /// @param turposx x position of the turmite
    /// @param turposy y position of the turmite
    /// @param orientation orientation of the turmite
    function fullMetadata(
        uint256 tokenId,
        uint8 boardNumber,
        bytes12 rule,
        bytes1 state,
        uint8 turposx,
        uint8 turposy,
        uint8 orientation
    ) internal view returns (string memory) {
        return
            string(
                abi.encodePacked(
                    "data:application/json;base64,",
                    Base64.encode(
                        abi.encodePacked(
                            '{"name":"',
                            generateName(tokenId, boardNumber),
                            '", "description":"',
                            "Onchain Mutiplayer Art",
                            '", "image": "',
                            getSvg(boardNumber, turposx, turposy, true),
                            '",',
                            '"attributes": ',
                            generateAttributes(boardNumber, rule, state, turposx, turposy, orientation),
                            "}"
                        )
                    )
                )
            );
    }

    /// @dev generates the Name of the turmite as a string
    /// @param tokenId the tokenId of the Turmite
    /// @param boardNumber the Board Number
    function generateName(uint256 tokenId, uint8 boardNumber) internal pure returns (string memory) {
        return
            string(
                abi.encodePacked("Turmite ", LibString.toString(tokenId), " World ", LibString.toString(boardNumber))
            );
    }

    /// @dev generates the dynamic attributes as JSON String, for param see fullMetadata()
    function generateAttributes(
        uint8 boardNumber,
        bytes12 rule,
        bytes1 state,
        uint8 turposx,
        uint8 turposy,
        uint8 orientation
    ) internal view returns (string memory) {
        return
            string(
                abi.encodePacked(
                    '[{"trait_type":"World","value":"',
                    LibString.toString(boardNumber),
                    '"},',
                    '{"trait_type":"Rule",',
                    '"value":"',
                    bytes12ToString(rule),
                    '"},',
                    '{"trait_type":"State",',
                    '"value":"',
                    LibString.toString(uint8(state)),
                    '"},',
                    '{"trait_type":"POS X",',
                    '"value":"',
                    LibString.toString(turposx),
                    '"},',
                    '{"trait_type":"POS Y",',
                    '"value":"',
                    LibString.toString(turposy),
                    '"},',
                    '{"trait_type":"Direction",',
                    '"value":"',
                    LibString.toString(orientation),
                    '"},{"trait_type":"Network","value":"',
                    network,
                    '"}]'
                )
            );
    }

    /// @dev helper function to create a String from a byte12
    /// @param _bytes12 the input value
    function bytes12ToString(bytes12 _bytes12) internal pure returns (string memory) {
        uint8 i = 0;
        bytes memory bytesArray = new bytes(24);
        for (i = 0; i < bytesArray.length; i++) {
            uint8 _f = uint8(_bytes12[i / 2] & 0x0f);
            uint8 _l = uint8(_bytes12[i / 2] >> 4);

            bytesArray[i] = toByte(_l);
            i = i + 1;
            bytesArray[i] = toByte(_f);
        }
        return string(bytesArray);
    }

    /// @dev helper function to convert from uint8 to byte1
    /// @param _uint8 the input value
    function toByte(uint8 _uint8) internal pure returns (bytes1) {
        if (_uint8 < 10) {
            return bytes1(_uint8 + 48);
        } else {
            return bytes1(_uint8 + 87);
        }
    }
}

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.13;

import "@rari-capital/solmate/src/tokens/ERC721.sol";

/// @title EnumerableMate
/// @notice slightly reduced version of ERC721Enumerable.sol - mainly optimizing the transfer function
/// @author @brachlandberlin / plsdlr.net
/// @author OpenZeppelin Contracts (last updated v4.5.0) (token/ERC721/extensions/IERC721Enumerable.sol)
/// @dev Comments are mostly taken over from OpenZeppelin

abstract contract EnumerableMate is ERC721 {
    // Mapping from owner to list of owned token IDs
    mapping(address => mapping(uint256 => uint256)) private _ownedTokens;

    // Mapping from token ID to index of the owner tokens list
    mapping(uint256 => uint256) private _ownedTokensIndex;

    // Array with all token ids, used for enumeration
    uint256[] private _allTokens;

    // Mapping from token id to position in the allTokens array
    mapping(uint256 => uint256) private _allTokensIndex;

    constructor(string memory name, string memory s) ERC721(name, s) {}

    /// getter
    function tokenOfOwnerByIndex(address owner, uint256 index) public view virtual returns (uint256) {
        require(index < ERC721.balanceOf(owner), "ERC721Enumerable: owner index out of bounds");
        return _ownedTokens[owner][index];
    }

    /**
     * @dev See {IERC721Enumerable-totalSupply}.
     */
    function totalSupply() public view virtual returns (uint256) {
        return _allTokens.length;
    }

    /**
     * @dev See {IERC721Enumerable-tokenByIndex}.
     */
    function tokenByIndex(uint256 index) public view virtual returns (uint256) {
        require(index < EnumerableMate.totalSupply(), "ERC721Enumerable: global index out of bounds");
        return _allTokens[index];
    }

    function _addTokenToOwnerEnumeration(address to, uint256 tokenId) internal {
        uint256 length = ERC721.balanceOf(to);
        _ownedTokens[to][length] = tokenId;
        _ownedTokensIndex[tokenId] = length;
    }

    function _addTokenToAllTokensEnumeration(uint256 tokenId) internal {
        _allTokensIndex[tokenId] = _allTokens.length;
        _allTokens.push(tokenId);
    }

    function _removeTokenFromOwnerEnumeration(address from, uint256 tokenId) internal {
        uint256 lastTokenIndex = ERC721.balanceOf(from) - 1;
        uint256 tokenIndex = _ownedTokensIndex[tokenId];

        // When the token to delete is the last token, the swap operation is unnecessary
        if (tokenIndex != lastTokenIndex) {
            uint256 lastTokenId = _ownedTokens[from][lastTokenIndex];

            _ownedTokens[from][tokenIndex] = lastTokenId; // Move the last token to the slot of the to-delete token
            _ownedTokensIndex[lastTokenId] = tokenIndex; // Update the moved token's index
        }

        // This also deletes the contents at the last position of the array
        delete _ownedTokensIndex[tokenId];
        delete _ownedTokens[from][lastTokenIndex];
    }

    function _removeTokenFromAllTokensEnumeration(uint256 tokenId) internal {
        uint256 lastTokenIndex = _allTokens.length - 1;
        uint256 tokenIndex = _allTokensIndex[tokenId];
        uint256 lastTokenId = _allTokens[lastTokenIndex];
        _allTokens[tokenIndex] = lastTokenId; // Move the last token to the slot of the to-delete token
        _allTokensIndex[lastTokenId] = tokenIndex; // Update the moved token's index
        delete _allTokensIndex[tokenId];
        _allTokens.pop();
    }
}

//SPDX-License-Identifier: Unlicense
pragma solidity ^0.8.0;

import "./gameboard.sol";

/// @title Turmite v4
/// @notice Implementation of Turmite logic for Straylight Protocoll.
/// @author @brachlandberlin / plsdlr.net
/// @dev Every Turmite rule is for simplicity represented as 12 Bytes. For easy acess by render functions all individual turmite data is safed in an struct.

contract Turmite is Gameboard {
    // using Base64 for string;    //note what is this used for?

    mapping(uint256 => turmite) public turmites;

    event TurmiteMove(uint256 indexed tokenId, uint8 indexed boardnumber, uint256 indexed moves);

    /// @dev individual slot for every turmite
    // Layout from Example:
    // _____________________________________________________________________________________________________
    // Empty Space :)                   Rule                     State  Boardnumber  Orientation   Y     X
    // _____________________________________________________________________________________________________
    // 0x000000000000000000000000000000 ff0801ff0201ff0000000001 01     01           01            32    3a
    // _____________________________________________________________________________________________________
    //
    struct turmite {
        uint8 turposx;
        uint8 turposy;
        uint8 orientation;
        uint8 boardnumber;
        bytes1 state;
        bytes12 rule;
    }

    /// @dev grammar for the rules
    // Layout from example rule
    // Rule:
    // _________________________________________________
    // ff0801ff0201ff0000000001                            2 states / 4 rulessegments
    // _________________________________________________
    // | ff0801 | ff0201 | ff0000 | 000001 |               4 * 3 Bytes
    // _________________________________________________
    // | c d s  |  c d s |  ...                            c = color, d = direction, s = state
    // _________________________________________________
    // | c = ff  ,  d = 08  ,  s = 01 | ...
    // _________________________________________________
    //
    // written as context-sensitive grammar, with start symbol S:
    //
    //     S    	→       a  a  a
    //     a        →       c  d  s
    //     c        →       ff | 00
    //     d        →       02 | 08 | 04
    //     s        →       01 | 00

    /// @dev creates an Turmite Struct and mapping to id, every turmite gets initalized with state 0
    /// @param posx the x position of the turmite on the board
    /// @param posy the y position on the turmite on the board
    /// @param startdirection the startdirection of the turmite
    /// @param boardNumber the boardNumber number
    /// @param rule 12 Byte rule which defines behavior of the turmite
    function createTurmite(
        uint256 id,
        uint8 posx,
        uint8 posy,
        uint8 startdirection,
        uint8 boardNumber,
        bytes12 rule
    ) internal {
        bytes1 state = hex"00";
        turmites[id] = turmite(posx, posy, startdirection, boardNumber, state, rule);
    }

    /// @dev main computational logic of turmite
    /// @dev this function is internal because there should be a check to validate ownership of the turmite
    /// @param id the id of the turmite to move
    /// @param moves the number of moves
    function calculateTurmiteMove(uint256 id, uint256 moves) internal {
        bytes1 colorField;
        uint8 _x;
        uint8 _y;
        uint8 _boardNumber;
        bytes32 sour;

        turmite storage data = turmites[id];
        assembly {
            sour := sload(data.slot)
        }
        for (uint256 z = 0; z < moves; ) {
            assembly {
                _x := and(sour, 0xFF)
                _y := and(shr(8, sour), 0xFF)
                _boardNumber := shr(24, sour)
            }
            bytes1 stateOfField = getByte(_x, _y, _boardNumber);
            assembly {
                let maskedRule := and(sour, 0x000000000000000000000000000000ffffffffffffffffffffffff0000000000)

                let _orientation := and(
                    shr(16, sour),
                    0x00000000000000000000000000000000000000000000000000000000000000ff
                )

                let newState
                let newDirection

                if and(
                    eq(shr(248, stateOfField), 0x00),
                    eq(shr(32, and(sour, 0x000000000000000000000000000000000000000000000000000000ff00000000)), 0x00)
                ) {
                    colorField := shl(120, maskedRule)
                    newDirection := and(shr(120, maskedRule), 0xFF)
                    newState := and(shr(112, maskedRule), 0xFF)
                }
                if and(
                    eq(shr(248, stateOfField), 0xff),
                    eq(shr(32, and(sour, 0x000000000000000000000000000000000000000000000000000000ff00000000)), 0x00)
                ) {
                    colorField := shl(144, maskedRule)
                    newDirection := and(shr(96, maskedRule), 0xFF)
                    newState := and(shr(88, maskedRule), 0xFF)
                }
                if and(
                    eq(shr(248, stateOfField), 0x00),
                    eq(shr(32, and(sour, 0x000000000000000000000000000000000000000000000000000000ff00000000)), 0x01)
                ) {
                    colorField := shl(168, maskedRule)
                    newDirection := and(shr(72, maskedRule), 0xFF)
                    newState := and(shr(64, maskedRule), 0xFF)
                }
                if and(
                    eq(shr(248, stateOfField), 0xff),
                    eq(shr(32, and(sour, 0x000000000000000000000000000000000000000000000000000000ff00000000)), 0x01)
                ) {
                    colorField := shl(192, maskedRule)
                    newDirection := and(shr(48, maskedRule), 0xFF)
                    newState := and(shr(40, maskedRule), 0xFF)
                }

                let newOrientation
                switch newDirection
                case 0x02 {
                    newOrientation := addmod(_orientation, 1, 4)
                }
                case 0x08 {
                    switch _orientation
                    case 0 {
                        newOrientation := 3
                    }
                    default {
                        newOrientation := mod(sub(_orientation, 1), 4)
                    }
                }
                case 0x04 {
                    newOrientation := mod(add(_orientation, 2), 4)
                }
                default {
                    newOrientation := _orientation
                }

                let buffer := mload(0x40)

                switch newOrientation
                case 0x00 {
                    mstore8(add(buffer, 31), addmod(_x, 1, 144))
                    mstore8(add(buffer, 30), _y)
                }
                case 0x02 {
                    switch _x
                    case 0 {
                        mstore8(add(buffer, 31), 143)
                        mstore8(add(buffer, 30), _y)
                    }
                    default {
                        mstore8(add(buffer, 31), sub(_x, 1))
                        mstore8(add(buffer, 30), _y)
                    }
                }
                case 0x03 {
                    mstore8(add(buffer, 31), _x)
                    mstore8(add(buffer, 30), addmod(_y, 1, 144))
                }
                case 0x01 {
                    switch _y
                    case 0 {
                        mstore8(add(buffer, 31), _x)
                        mstore8(add(buffer, 30), 143)
                    }
                    default {
                        mstore8(add(buffer, 31), _x)
                        mstore8(add(buffer, 30), sub(_y, 1))
                    }
                }

                //  128   120  112  104   96   88   80   72   64   56   48  40
                // 0xff    08   01   ff   02   01   ff   00   00   00   44  21

                mstore8(add(buffer, 29), newOrientation)
                mstore8(add(buffer, 28), _boardNumber)
                mstore8(add(buffer, 27), newState)
                sour := or(mload(buffer), maskedRule)
            }

            // note that we pass here the "old" x & y
            setByte(_x, _y, colorField, _boardNumber);
            unchecked {
                z += 1;
            }
        }
        assembly {
            sstore(data.slot, sour)
        }
        emit TurmiteMove(id, _boardNumber, moves);
    }
}

File 5 of 9 : LibString.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;

/// @notice Library for converting numbers into strings and other string operations.
/// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/LibString.sol)
/// @author Modified from Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/LibString.sol)
library LibString {
    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                        CUSTOM ERRORS                       */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev The `length` of the output is too small to contain all the hex digits.
    error HexLengthInsufficient();

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                         CONSTANTS                          */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev The constant returned when the `search` is not found in the string.
    uint256 internal constant NOT_FOUND = type(uint256).max;

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                     DECIMAL OPERATIONS                     */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Returns the base 10 decimal representation of `value`.
    function toString(uint256 value) internal pure returns (string memory str) {
        /// @solidity memory-safe-assembly
        assembly {
            // The maximum value of a uint256 contains 78 digits (1 byte per digit), but
            // we allocate 0xa0 bytes to keep the free memory pointer 32-byte word aligned.
            // We will need 1 word for the trailing zeros padding, 1 word for the length,
            // and 3 words for a maximum of 78 digits. Total: 5 * 0x20 = 0xa0.
            let m := add(mload(0x40), 0xa0)
            // Update the free memory pointer to allocate.
            mstore(0x40, m)
            // Assign the `str` to the end.
            str := sub(m, 0x20)
            // Zeroize the slot after the string.
            mstore(str, 0)

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

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

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

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                   HEXADECIMAL OPERATIONS                   */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Returns the hexadecimal representation of `value`,
    /// left-padded to an input length of `length` bytes.
    /// The output is prefixed with "0x" encoded using 2 hexadecimal digits per byte,
    /// giving a total length of `length * 2 + 2` bytes.
    /// Reverts if `length` is too small for the output to contain all the digits.
    function toHexString(uint256 value, uint256 length) internal pure returns (string memory str) {
        str = toHexStringNoPrefix(value, length);
        /// @solidity memory-safe-assembly
        assembly {
            let strLength := add(mload(str), 2) // Compute the length.
            mstore(str, 0x3078) // Write the "0x" prefix.
            str := sub(str, 2) // Move the pointer.
            mstore(str, strLength) // Write the length.
        }
    }

    /// @dev Returns the hexadecimal representation of `value`,
    /// left-padded to an input length of `length` bytes.
    /// The output is prefixed with "0x" encoded using 2 hexadecimal digits per byte,
    /// giving a total length of `length * 2` bytes.
    /// Reverts if `length` is too small for the output to contain all the digits.
    function toHexStringNoPrefix(uint256 value, uint256 length) internal pure returns (string memory str) {
        /// @solidity memory-safe-assembly
        assembly {
            let start := mload(0x40)
            // We need 0x20 bytes for the trailing zeros padding, `length * 2` bytes
            // for the digits, 0x02 bytes for the prefix, and 0x20 bytes for the length.
            // We add 0x20 to the total and round down to a multiple of 0x20.
            // (0x20 + 0x20 + 0x02 + 0x20) = 0x62.
            let m := add(start, and(add(shl(1, length), 0x62), not(0x1f)))
            // Allocate the memory.
            mstore(0x40, m)
            // Assign the `str` to the end.
            str := sub(m, 0x20)
            // Zeroize the slot after the string.
            mstore(str, 0)

            // Cache the end to calculate the length later.
            let end := str
            // Store "0123456789abcdef" in scratch space.
            mstore(0x0f, 0x30313233343536373839616263646566)

            let temp := value
            // We write the string from rightmost digit to leftmost digit.
            // The following is essentially a do-while loop that also handles the zero case.
            // prettier-ignore
            for {} 1 {} {
                str := sub(str, 2)
                mstore8(add(str, 1), mload(and(temp, 15)))
                mstore8(str, mload(and(shr(4, temp), 15)))
                temp := shr(8, temp)
                length := sub(length, 1)
                // prettier-ignore
                if iszero(length) { break }
            }

            if temp {
                // Store the function selector of `HexLengthInsufficient()`.
                mstore(0x00, 0x2194895a)
                // Revert with (offset, size).
                revert(0x1c, 0x04)
            }

            // Compute the string's length.
            let strLength := sub(end, str)
            // Move the pointer and write the length.
            str := sub(str, 0x20)
            mstore(str, strLength)
        }
    }

    /// @dev Returns the hexadecimal representation of `value`.
    /// The output is prefixed with "0x" and encoded using 2 hexadecimal digits per byte.
    /// As address are 20 bytes long, the output will left-padded to have
    /// a length of `20 * 2 + 2` bytes.
    function toHexString(uint256 value) internal pure returns (string memory str) {
        str = toHexStringNoPrefix(value);
        /// @solidity memory-safe-assembly
        assembly {
            let strLength := add(mload(str), 2) // Compute the length.
            mstore(str, 0x3078) // Write the "0x" prefix.
            str := sub(str, 2) // Move the pointer.
            mstore(str, strLength) // Write the length.
        }
    }

    /// @dev Returns the hexadecimal representation of `value`.
    /// The output is encoded using 2 hexadecimal digits per byte.
    /// As address are 20 bytes long, the output will left-padded to have
    /// a length of `20 * 2` bytes.
    function toHexStringNoPrefix(uint256 value) internal pure returns (string memory str) {
        /// @solidity memory-safe-assembly
        assembly {
            let start := mload(0x40)
            // We need 0x20 bytes for the trailing zeros padding, 0x20 bytes for the length,
            // 0x02 bytes for the prefix, and 0x40 bytes for the digits.
            // The next multiple of 0x20 above (0x20 + 0x20 + 0x02 + 0x40) is 0xa0.
            let m := add(start, 0xa0)
            // Allocate the memory.
            mstore(0x40, m)
            // Assign the `str` to the end.
            str := sub(m, 0x20)
            // Zeroize the slot after the string.
            mstore(str, 0)

            // Cache the end to calculate the length later.
            let end := str
            // Store "0123456789abcdef" in scratch space.
            mstore(0x0f, 0x30313233343536373839616263646566)

            // We write the string from rightmost digit to leftmost digit.
            // The following is essentially a do-while loop that also handles the zero case.
            // prettier-ignore
            for { let temp := value } 1 {} {
                str := sub(str, 2)
                mstore8(add(str, 1), mload(and(temp, 15)))
                mstore8(str, mload(and(shr(4, temp), 15)))
                temp := shr(8, temp)
                // prettier-ignore
                if iszero(temp) { break }
            }

            // Compute the string's length.
            let strLength := sub(end, str)
            // Move the pointer and write the length.
            str := sub(str, 0x20)
            mstore(str, strLength)
        }
    }

    /// @dev Returns the hexadecimal representation of `value`.
    /// The output is prefixed with "0x", encoded using 2 hexadecimal digits per byte,
    /// and the alphabets are capitalized conditionally according to
    /// https://eips.ethereum.org/EIPS/eip-55
    function toHexStringChecksumed(address value) internal pure returns (string memory str) {
        str = toHexString(value);
        /// @solidity memory-safe-assembly
        assembly {
            let mask := shl(6, div(not(0), 255)) // `0b010000000100000000 ...`
            let o := add(str, 0x22)
            let hashed := and(keccak256(o, 40), mul(34, mask)) // `0b10001000 ... `
            let t := shl(240, 136) // `0b10001000 << 240`
            // prettier-ignore
            for { let i := 0 } 1 {} {
                mstore(add(i, i), mul(t, byte(i, hashed)))
                i := add(i, 1)
                // prettier-ignore
                if eq(i, 20) { break }
            }
            mstore(o, xor(mload(o), shr(1, and(mload(0x00), and(mload(o), mask)))))
            o := add(o, 0x20)
            mstore(o, xor(mload(o), shr(1, and(mload(0x20), and(mload(o), mask)))))
        }
    }

    /// @dev Returns the hexadecimal representation of `value`.
    /// The output is prefixed with "0x" and encoded using 2 hexadecimal digits per byte.
    function toHexString(address value) internal pure returns (string memory str) {
        str = toHexStringNoPrefix(value);
        /// @solidity memory-safe-assembly
        assembly {
            let strLength := add(mload(str), 2) // Compute the length.
            mstore(str, 0x3078) // Write the "0x" prefix.
            str := sub(str, 2) // Move the pointer.
            mstore(str, strLength) // Write the length.
        }
    }

    /// @dev Returns the hexadecimal representation of `value`.
    /// The output is encoded using 2 hexadecimal digits per byte.
    function toHexStringNoPrefix(address value) internal pure returns (string memory str) {
        /// @solidity memory-safe-assembly
        assembly {
            str := mload(0x40)

            // Allocate the memory.
            // We need 0x20 bytes for the trailing zeros padding, 0x20 bytes for the length,
            // 0x02 bytes for the prefix, and 0x28 bytes for the digits.
            // The next multiple of 0x20 above (0x20 + 0x20 + 0x02 + 0x28) is 0x80.
            mstore(0x40, add(str, 0x80))

            // Store "0123456789abcdef" in scratch space.
            mstore(0x0f, 0x30313233343536373839616263646566)

            str := add(str, 2)
            mstore(str, 40)

            let o := add(str, 0x20)
            mstore(add(o, 40), 0)

            value := shl(96, value)

            // We write the string from rightmost digit to leftmost digit.
            // The following is essentially a do-while loop that also handles the zero case.
            // prettier-ignore
            for { let i := 0 } 1 {} {
                let p := add(o, add(i, i))
                let temp := byte(i, value)
                mstore8(add(p, 1), mload(and(temp, 15)))
                mstore8(p, mload(shr(4, temp)))
                i := add(i, 1)
                // prettier-ignore
                if eq(i, 20) { break }
            }
        }
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                   RUNE STRING OPERATIONS                   */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Returns the number of UTF characters in the string.
    function runeCount(string memory s) internal pure returns (uint256 result) {
        /// @solidity memory-safe-assembly
        assembly {
            if mload(s) {
                mstore(0x00, div(not(0), 255))
                mstore(0x20, 0x0202020202020202020202020202020202020202020202020303030304040506)
                let o := add(s, 0x20)
                let end := add(o, mload(s))
                // prettier-ignore
                for { result := 1 } 1 { result := add(result, 1) } {
                    o := add(o, byte(0, mload(shr(250, mload(o)))))
                    // prettier-ignore
                    if iszero(lt(o, end)) { break }
                }
            }
        }
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                   BYTE STRING OPERATIONS                   */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    // For performance and bytecode compactness, all indices of the following operations
    // are byte (ASCII) offsets, not UTF character offsets.

    /// @dev Returns `subject` all occurrences of `search` replaced with `replacement`.
    function replace(
        string memory subject,
        string memory search,
        string memory replacement
    ) internal pure returns (string memory result) {
        /// @solidity memory-safe-assembly
        assembly {
            let subjectLength := mload(subject)
            let searchLength := mload(search)
            let replacementLength := mload(replacement)

            subject := add(subject, 0x20)
            search := add(search, 0x20)
            replacement := add(replacement, 0x20)
            result := add(mload(0x40), 0x20)

            let subjectEnd := add(subject, subjectLength)
            if iszero(gt(searchLength, subjectLength)) {
                let subjectSearchEnd := add(sub(subjectEnd, searchLength), 1)
                let h := 0
                if iszero(lt(searchLength, 32)) {
                    h := keccak256(search, searchLength)
                }
                let m := shl(3, sub(32, and(searchLength, 31)))
                let s := mload(search)
                // prettier-ignore
                for {} 1 {} {
                    let t := mload(subject)
                    // Whether the first `searchLength % 32` bytes of 
                    // `subject` and `search` matches.
                    if iszero(shr(m, xor(t, s))) {
                        if h {
                            if iszero(eq(keccak256(subject, searchLength), h)) {
                                mstore(result, t)
                                result := add(result, 1)
                                subject := add(subject, 1)
                                // prettier-ignore
                                if iszero(lt(subject, subjectSearchEnd)) { break }
                                continue
                            }
                        }
                        // Copy the `replacement` one word at a time.
                        // prettier-ignore
                        for { let o := 0 } 1 {} {
                            mstore(add(result, o), mload(add(replacement, o)))
                            o := add(o, 0x20)
                            // prettier-ignore
                            if iszero(lt(o, replacementLength)) { break }
                        }
                        result := add(result, replacementLength)
                        subject := add(subject, searchLength)
                        if searchLength {
                            // prettier-ignore
                            if iszero(lt(subject, subjectSearchEnd)) { break }
                            continue
                        }
                    }
                    mstore(result, t)
                    result := add(result, 1)
                    subject := add(subject, 1)
                    // prettier-ignore
                    if iszero(lt(subject, subjectSearchEnd)) { break }
                }
            }

            let resultRemainder := result
            result := add(mload(0x40), 0x20)
            let k := add(sub(resultRemainder, result), sub(subjectEnd, subject))
            // Copy the rest of the string one word at a time.
            // prettier-ignore
            for {} lt(subject, subjectEnd) {} {
                mstore(resultRemainder, mload(subject))
                resultRemainder := add(resultRemainder, 0x20)
                subject := add(subject, 0x20)
            }
            result := sub(result, 0x20)
            // Zeroize the slot after the string.
            let last := add(add(result, 0x20), k)
            mstore(last, 0)
            // Allocate memory for the length and the bytes,
            // rounded up to a multiple of 32.
            mstore(0x40, and(add(last, 31), not(31)))
            // Store the length of the result.
            mstore(result, k)
        }
    }

    /// @dev Returns the byte index of the first location of `search` in `subject`,
    /// searching from left to right, starting from `from`.
    /// Returns `NOT_FOUND` (i.e. `type(uint256).max`) if the `search` is not found.
    function indexOf(
        string memory subject,
        string memory search,
        uint256 from
    ) internal pure returns (uint256 result) {
        /// @solidity memory-safe-assembly
        assembly {
            // prettier-ignore
            for { let subjectLength := mload(subject) } 1 {} {
                if iszero(mload(search)) {
                    // `result = min(from, subjectLength)`.
                    result := xor(from, mul(xor(from, subjectLength), lt(subjectLength, from)))
                    break
                }
                let searchLength := mload(search)
                let subjectStart := add(subject, 0x20)    
                
                result := not(0) // Initialize to `NOT_FOUND`.

                subject := add(subjectStart, from)
                let subjectSearchEnd := add(sub(add(subjectStart, subjectLength), searchLength), 1)

                let m := shl(3, sub(32, and(searchLength, 31)))
                let s := mload(add(search, 0x20))

                // prettier-ignore
                if iszero(lt(subject, subjectSearchEnd)) { break }

                if iszero(lt(searchLength, 32)) {
                    // prettier-ignore
                    for { let h := keccak256(add(search, 0x20), searchLength) } 1 {} {
                        if iszero(shr(m, xor(mload(subject), s))) {
                            if eq(keccak256(subject, searchLength), h) {
                                result := sub(subject, subjectStart)
                                break
                            }
                        }
                        subject := add(subject, 1)
                        // prettier-ignore
                        if iszero(lt(subject, subjectSearchEnd)) { break }
                    }
                    break
                }
                // prettier-ignore
                for {} 1 {} {
                    if iszero(shr(m, xor(mload(subject), s))) {
                        result := sub(subject, subjectStart)
                        break
                    }
                    subject := add(subject, 1)
                    // prettier-ignore
                    if iszero(lt(subject, subjectSearchEnd)) { break }
                }
                break
            }
        }
    }

    /// @dev Returns the byte index of the first location of `search` in `subject`,
    /// searching from left to right.
    /// Returns `NOT_FOUND` (i.e. `type(uint256).max`) if the `search` is not found.
    function indexOf(string memory subject, string memory search) internal pure returns (uint256 result) {
        result = indexOf(subject, search, 0);
    }

    /// @dev Returns the byte index of the first location of `search` in `subject`,
    /// searching from right to left, starting from `from`.
    /// Returns `NOT_FOUND` (i.e. `type(uint256).max`) if the `search` is not found.
    function lastIndexOf(
        string memory subject,
        string memory search,
        uint256 from
    ) internal pure returns (uint256 result) {
        /// @solidity memory-safe-assembly
        assembly {
            // prettier-ignore
            for {} 1 {} {
                let searchLength := mload(search)
                let fromMax := sub(mload(subject), searchLength)
                if iszero(gt(fromMax, from)) {
                    from := fromMax
                }
                if iszero(mload(search)) {
                    result := from
                    break
                }
                result := not(0) // Initialize to `NOT_FOUND`.

                let subjectSearchEnd := sub(add(subject, 0x20), 1)

                subject := add(add(subject, 0x20), from)
                // prettier-ignore
                if iszero(gt(subject, subjectSearchEnd)) { break }
                // As this function is not too often used,
                // we shall simply use keccak256 for smaller bytecode size.
                // prettier-ignore
                for { let h := keccak256(add(search, 0x20), searchLength) } 1 {} {
                    if eq(keccak256(subject, searchLength), h) {
                        result := sub(subject, add(subjectSearchEnd, 1))
                        break
                    }
                    subject := sub(subject, 1)
                    // prettier-ignore
                    if iszero(gt(subject, subjectSearchEnd)) { break }
                }
                break
            }
        }
    }

    /// @dev Returns the byte index of the first location of `search` in `subject`,
    /// searching from right to left.
    /// Returns `NOT_FOUND` (i.e. `type(uint256).max`) if the `search` is not found.
    function lastIndexOf(string memory subject, string memory search) internal pure returns (uint256 result) {
        result = lastIndexOf(subject, search, uint256(int256(-1)));
    }

    /// @dev Returns whether `subject` starts with `search`.
    function startsWith(string memory subject, string memory search) internal pure returns (bool result) {
        /// @solidity memory-safe-assembly
        assembly {
            let searchLength := mload(search)
            // Just using keccak256 directly is actually cheaper.
            result := and(
                iszero(gt(searchLength, mload(subject))),
                eq(keccak256(add(subject, 0x20), searchLength), keccak256(add(search, 0x20), searchLength))
            )
        }
    }

    /// @dev Returns whether `subject` ends with `search`.
    function endsWith(string memory subject, string memory search) internal pure returns (bool result) {
        /// @solidity memory-safe-assembly
        assembly {
            let searchLength := mload(search)
            let subjectLength := mload(subject)
            // Whether `search` is not longer than `subject`.
            let withinRange := iszero(gt(searchLength, subjectLength))
            // Just using keccak256 directly is actually cheaper.
            result := and(
                withinRange,
                eq(
                    keccak256(
                        // `subject + 0x20 + max(subjectLength - searchLength, 0)`.
                        add(add(subject, 0x20), mul(withinRange, sub(subjectLength, searchLength))),
                        searchLength
                    ),
                    keccak256(add(search, 0x20), searchLength)
                )
            )
        }
    }

    /// @dev Returns `subject` repeated `times`.
    function repeat(string memory subject, uint256 times) internal pure returns (string memory result) {
        /// @solidity memory-safe-assembly
        assembly {
            let subjectLength := mload(subject)
            if iszero(or(iszero(times), iszero(subjectLength))) {
                subject := add(subject, 0x20)
                result := mload(0x40)
                let output := add(result, 0x20)
                // prettier-ignore
                for {} 1 {} {
                    // Copy the `subject` one word at a time.
                    // prettier-ignore
                    for { let o := 0 } 1 {} {
                        mstore(add(output, o), mload(add(subject, o)))
                        o := add(o, 0x20)
                        // prettier-ignore
                        if iszero(lt(o, subjectLength)) { break }
                    }
                    output := add(output, subjectLength)
                    times := sub(times, 1)
                    // prettier-ignore
                    if iszero(times) { break }
                }
                // Zeroize the slot after the string.
                mstore(output, 0)
                // Store the length.
                let resultLength := sub(output, add(result, 0x20))
                mstore(result, resultLength)
                // Allocate memory for the length and the bytes,
                // rounded up to a multiple of 32.
                mstore(0x40, add(result, and(add(resultLength, 63), not(31))))
            }
        }
    }

    /// @dev Returns a copy of `subject` sliced from `start` to `end` (exclusive).
    /// `start` and `end` are byte offsets.
    function slice(
        string memory subject,
        uint256 start,
        uint256 end
    ) internal pure returns (string memory result) {
        /// @solidity memory-safe-assembly
        assembly {
            let subjectLength := mload(subject)
            if iszero(gt(subjectLength, end)) {
                end := subjectLength
            }
            if iszero(gt(subjectLength, start)) {
                start := subjectLength
            }
            if lt(start, end) {
                result := mload(0x40)
                let resultLength := sub(end, start)
                mstore(result, resultLength)
                subject := add(subject, start)
                let w := not(31)
                // Copy the `subject` one word at a time, backwards.
                // prettier-ignore
                for { let o := and(add(resultLength, 31), w) } 1 {} {
                    mstore(add(result, o), mload(add(subject, o)))
                    o := add(o, w) // `sub(o, 0x20)`.
                    // prettier-ignore
                    if iszero(o) { break }
                }
                // Zeroize the slot after the string.
                mstore(add(add(result, 0x20), resultLength), 0)
                // Allocate memory for the length and the bytes,
                // rounded up to a multiple of 32.
                mstore(0x40, add(result, and(add(resultLength, 63), w)))
            }
        }
    }

    /// @dev Returns a copy of `subject` sliced from `start` to the end of the string.
    /// `start` is a byte offset.
    function slice(string memory subject, uint256 start) internal pure returns (string memory result) {
        result = slice(subject, start, uint256(int256(-1)));
    }

    /// @dev Returns all the indices of `search` in `subject`.
    /// The indices are byte offsets.
    function indicesOf(string memory subject, string memory search) internal pure returns (uint256[] memory result) {
        /// @solidity memory-safe-assembly
        assembly {
            let subjectLength := mload(subject)
            let searchLength := mload(search)

            if iszero(gt(searchLength, subjectLength)) {
                subject := add(subject, 0x20)
                search := add(search, 0x20)
                result := add(mload(0x40), 0x20)

                let subjectStart := subject
                let subjectSearchEnd := add(sub(add(subject, subjectLength), searchLength), 1)
                let h := 0
                if iszero(lt(searchLength, 32)) {
                    h := keccak256(search, searchLength)
                }
                let m := shl(3, sub(32, and(searchLength, 31)))
                let s := mload(search)
                // prettier-ignore
                for {} 1 {} {
                    let t := mload(subject)
                    // Whether the first `searchLength % 32` bytes of 
                    // `subject` and `search` matches.
                    if iszero(shr(m, xor(t, s))) {
                        if h {
                            if iszero(eq(keccak256(subject, searchLength), h)) {
                                subject := add(subject, 1)
                                // prettier-ignore
                                if iszero(lt(subject, subjectSearchEnd)) { break }
                                continue
                            }
                        }
                        // Append to `result`.
                        mstore(result, sub(subject, subjectStart))
                        result := add(result, 0x20)
                        // Advance `subject` by `searchLength`.
                        subject := add(subject, searchLength)
                        if searchLength {
                            // prettier-ignore
                            if iszero(lt(subject, subjectSearchEnd)) { break }
                            continue
                        }
                    }
                    subject := add(subject, 1)
                    // prettier-ignore
                    if iszero(lt(subject, subjectSearchEnd)) { break }
                }
                let resultEnd := result
                // Assign `result` to the free memory pointer.
                result := mload(0x40)
                // Store the length of `result`.
                mstore(result, shr(5, sub(resultEnd, add(result, 0x20))))
                // Allocate memory for result.
                // We allocate one more word, so this array can be recycled for {split}.
                mstore(0x40, add(resultEnd, 0x20))
            }
        }
    }

    /// @dev Returns a arrays of strings based on the `delimiter` inside of the `subject` string.
    function split(string memory subject, string memory delimiter) internal pure returns (string[] memory result) {
        uint256[] memory indices = indicesOf(subject, delimiter);
        /// @solidity memory-safe-assembly
        assembly {
            let w := not(31)
            let indexPtr := add(indices, 0x20)
            let indicesEnd := add(indexPtr, shl(5, add(mload(indices), 1)))
            mstore(add(indicesEnd, w), mload(subject))
            mstore(indices, add(mload(indices), 1))
            let prevIndex := 0
            // prettier-ignore
            for {} 1 {} {
                let index := mload(indexPtr)
                mstore(indexPtr, 0x60)                        
                if iszero(eq(index, prevIndex)) {
                    let element := mload(0x40)
                    let elementLength := sub(index, prevIndex)
                    mstore(element, elementLength)
                    // Copy the `subject` one word at a time, backwards.
                    // prettier-ignore
                    for { let o := and(add(elementLength, 31), w) } 1 {} {
                        mstore(add(element, o), mload(add(add(subject, prevIndex), o)))
                        o := add(o, w) // `sub(o, 0x20)`.
                        // prettier-ignore
                        if iszero(o) { break }
                    }
                    // Zeroize the slot after the string.
                    mstore(add(add(element, 0x20), elementLength), 0)
                    // Allocate memory for the length and the bytes,
                    // rounded up to a multiple of 32.
                    mstore(0x40, add(element, and(add(elementLength, 63), w)))
                    // Store the `element` into the array.
                    mstore(indexPtr, element)                        
                }
                prevIndex := add(index, mload(delimiter))
                indexPtr := add(indexPtr, 0x20)
                // prettier-ignore
                if iszero(lt(indexPtr, indicesEnd)) { break }
            }
            result := indices
            if iszero(mload(delimiter)) {
                result := add(indices, 0x20)
                mstore(result, sub(mload(indices), 2))
            }
        }
    }

    /// @dev Returns a concatenated string of `a` and `b`.
    /// Cheaper than `string.concat()` and does not de-align the free memory pointer.
    function concat(string memory a, string memory b) internal pure returns (string memory result) {
        /// @solidity memory-safe-assembly
        assembly {
            let w := not(31)
            result := mload(0x40)
            let aLength := mload(a)
            // Copy `a` one word at a time, backwards.
            // prettier-ignore
            for { let o := and(add(mload(a), 32), w) } 1 {} {
                mstore(add(result, o), mload(add(a, o)))
                o := add(o, w) // `sub(o, 0x20)`.
                // prettier-ignore
                if iszero(o) { break }
            }
            let bLength := mload(b)
            let output := add(result, mload(a))
            // Copy `b` one word at a time, backwards.
            // prettier-ignore
            for { let o := and(add(bLength, 32), w) } 1 {} {
                mstore(add(output, o), mload(add(b, o)))
                o := add(o, w) // `sub(o, 0x20)`.
                // prettier-ignore
                if iszero(o) { break }
            }
            let totalLength := add(aLength, bLength)
            let last := add(add(result, 0x20), totalLength)
            // Zeroize the slot after the string.
            mstore(last, 0)
            // Stores the length.
            mstore(result, totalLength)
            // Allocate memory for the length and the bytes,
            // rounded up to a multiple of 32.
            mstore(0x40, and(add(last, 31), w))
        }
    }

    /// @dev Returns a copy of the string in either lowercase or UPPERCASE.
    function toCase(string memory subject, bool toUpper) internal pure returns (string memory result) {
        /// @solidity memory-safe-assembly
        assembly {
            let length := mload(subject)
            if length {
                result := add(mload(0x40), 0x20)
                subject := add(subject, 1)
                let flags := shl(add(70, shl(5, toUpper)), 67108863)
                let w := not(0)
                // prettier-ignore
                for { let o := length } 1 {} {
                    o := add(o, w)
                    let b := and(0xff, mload(add(subject, o)))
                    mstore8(add(result, o), xor(b, and(shr(b, flags), 0x20)))
                    // prettier-ignore
                    if iszero(o) { break }
                }
                // Restore the result.
                result := mload(0x40)
                // Stores the string length.
                mstore(result, length)
                // Zeroize the slot after the string.
                let last := add(add(result, 0x20), length)
                mstore(last, 0)
                // Allocate memory for the length and the bytes,
                // rounded up to a multiple of 32.
                mstore(0x40, and(add(last, 31), not(31)))
            }
        }
    }

    /// @dev Returns a lowercased copy of the string.
    function lower(string memory subject) internal pure returns (string memory result) {
        result = toCase(subject, false);
    }

    /// @dev Returns an UPPERCASED copy of the string.
    function upper(string memory subject) internal pure returns (string memory result) {
        result = toCase(subject, true);
    }

    /// @dev Escapes the string to be used within HTML tags.
    function escapeHTML(string memory s) internal pure returns (string memory result) {
        /// @solidity memory-safe-assembly
        assembly {
            // prettier-ignore
            for {
                let end := add(s, mload(s))
                result := add(mload(0x40), 0x20)
                // Store the bytes of the packed offsets and strides into the scratch space.
                // `packed = (stride << 5) | offset`. Max offset is 20. Max stride is 6.
                mstore(0x1f, 0x900094)
                mstore(0x08, 0xc0000000a6ab)
                // Store "&quot;&amp;&#39;&lt;&gt;" into the scratch space.
                mstore(0x00, shl(64, 0x2671756f743b26616d703b262333393b266c743b2667743b))
            } iszero(eq(s, end)) {} {
                s := add(s, 1)
                let c := and(mload(s), 0xff)
                // Not in `["\"","'","&","<",">"]`.
                if iszero(and(shl(c, 1), 0x500000c400000000)) { 
                    mstore8(result, c)
                    result := add(result, 1)
                    continue    
                }
                let t := shr(248, mload(c))
                mstore(result, mload(and(t, 31)))
                result := add(result, shr(5, t))
            }
            let last := result
            // Zeroize the slot after the string.
            mstore(last, 0)
            // Restore the result to the start of the free memory.
            result := mload(0x40)
            // Store the length of the result.
            mstore(result, sub(last, add(result, 0x20)))
            // Allocate memory for the length and the bytes,
            // rounded up to a multiple of 32.
            mstore(0x40, and(add(last, 31), not(31)))
        }
    }

    /// @dev Escapes the string to be used within double-quotes in a JSON.
    function escapeJSON(string memory s) internal pure returns (string memory result) {
        /// @solidity memory-safe-assembly
        assembly {
            // prettier-ignore
            for {
                let end := add(s, mload(s))
                result := add(mload(0x40), 0x20)
                // Store "\\u0000" in scratch space.
                // Store "0123456789abcdef" in scratch space.
                // Also, store `{0x08:"b", 0x09:"t", 0x0a:"n", 0x0c:"f", 0x0d:"r"}`.
                // into the scratch space.
                mstore(0x15, 0x5c75303030303031323334353637383961626364656662746e006672)
                // Bitmask for detecting `["\"","\\"]`.
                let e := or(shl(0x22, 1), shl(0x5c, 1))
            } iszero(eq(s, end)) {} {
                s := add(s, 1)
                let c := and(mload(s), 0xff)
                if iszero(lt(c, 0x20)) {
                    if iszero(and(shl(c, 1), e)) { // Not in `["\"","\\"]`.
                        mstore8(result, c)
                        result := add(result, 1)
                        continue    
                    }
                    mstore8(result, 0x5c) // "\\".
                    mstore8(add(result, 1), c) 
                    result := add(result, 2)
                    continue
                }
                if iszero(and(shl(c, 1), 0x3700)) { // Not in `["\b","\t","\n","\f","\d"]`.
                    mstore8(0x1d, mload(shr(4, c))) // Hex value.
                    mstore8(0x1e, mload(and(c, 15))) // Hex value.
                    mstore(result, mload(0x19)) // "\\u00XX".
                    result := add(result, 6)    
                    continue
                }
                mstore8(result, 0x5c) // "\\".
                mstore8(add(result, 1), mload(add(c, 8)))
                result := add(result, 2)
            }
            let last := result
            // Zeroize the slot after the string.
            mstore(last, 0)
            // Restore the result to the start of the free memory.
            result := mload(0x40)
            // Store the length of the result.
            mstore(result, sub(last, add(result, 0x20)))
            // Allocate memory for the length and the bytes,
            // rounded up to a multiple of 32.
            mstore(0x40, and(add(last, 31), not(31)))
        }
    }

    /// @dev Returns whether `a` equals `b`.
    function eq(string memory a, string memory b) internal pure returns (bool result) {
        assembly {
            result := eq(keccak256(add(a, 0x20), mload(a)), keccak256(add(b, 0x20), mload(b)))
        }
    }

    /// @dev Packs a single string with its length into a single word.
    /// Returns `bytes32(0)` if the length is zero or greater than 31.
    function packOne(string memory a) internal pure returns (bytes32 result) {
        /// @solidity memory-safe-assembly
        assembly {
            // We don't need to zero right pad the string,
            // since this is our own custom non-standard packing scheme.
            result := mul(
                // Load the length and the bytes.
                mload(add(a, 0x1f)),
                // `length != 0 && length < 32`. Abuses underflow.
                // Assumes that the length is valid and within the block gas limit.
                lt(sub(mload(a), 1), 0x1f)
            )
        }
    }

    /// @dev Unpacks a string packed using {packOne}.
    /// Returns the empty string if `packed` is `bytes32(0)`.
    /// If `packed` is not an output of {packOne}, the output behaviour is undefined.
    function unpackOne(bytes32 packed) internal pure returns (string memory result) {
        /// @solidity memory-safe-assembly
        assembly {
            // Grab the free memory pointer.
            result := mload(0x40)
            // Allocate 2 words (1 for the length, 1 for the bytes).
            mstore(0x40, add(result, 0x40))
            // Zeroize the length slot.
            mstore(result, 0)
            // Store the length and bytes.
            mstore(add(result, 0x1f), packed)
            // Right pad with zeroes.
            mstore(add(add(result, 0x20), mload(result)), 0)
        }
    }

    /// @dev Packs two strings with their lengths into a single word.
    /// Returns `bytes32(0)` if combined length is zero or greater than 30.
    function packTwo(string memory a, string memory b) internal pure returns (bytes32 result) {
        /// @solidity memory-safe-assembly
        assembly {
            let aLength := mload(a)
            // We don't need to zero right pad the strings,
            // since this is our own custom non-standard packing scheme.
            result := mul(
                // Load the length and the bytes of `a` and `b`.
                or(shl(shl(3, sub(0x1f, aLength)), mload(add(a, aLength))), mload(sub(add(b, 0x1e), aLength))),
                // `totalLength != 0 && totalLength < 31`. Abuses underflow.
                // Assumes that the lengths are valid and within the block gas limit.
                lt(sub(add(aLength, mload(b)), 1), 0x1e)
            )
        }
    }

    /// @dev Unpacks strings packed using {packTwo}.
    /// Returns the empty strings if `packed` is `bytes32(0)`.
    /// If `packed` is not an output of {packTwo}, the output behaviour is undefined.
    function unpackTwo(bytes32 packed) internal pure returns (string memory resultA, string memory resultB) {
        /// @solidity memory-safe-assembly
        assembly {
            // Grab the free memory pointer.
            resultA := mload(0x40)
            resultB := add(resultA, 0x40)
            // Allocate 2 words for each string (1 for the length, 1 for the byte). Total 4 words.
            mstore(0x40, add(resultB, 0x40))
            // Zeroize the length slots.
            mstore(resultA, 0)
            mstore(resultB, 0)
            // Store the lengths and bytes.
            mstore(add(resultA, 0x1f), packed)
            mstore(add(resultB, 0x1f), mload(add(add(resultA, 0x20), mload(resultA))))
            // Right pad with zeroes.
            mstore(add(add(resultA, 0x20), mload(resultA)), 0)
            mstore(add(add(resultB, 0x20), mload(resultB)), 0)
        }
    }

    /// @dev Directly returns `a` without copying.
    function directReturn(string memory a) internal pure {
        assembly {
            // Assumes that the string does not start from the scratch space.
            let retStart := sub(a, 0x20)
            let retSize := add(mload(a), 0x40)
            // Right pad with zeroes. Just in case the string is produced
            // by a method that doesn't zero right pad.
            mstore(add(retStart, retSize), 0)
            // Store the return offset.
            mstore(retStart, 0x20)
            // End the transaction, returning the string.
            return(retStart, retSize)
        }
    }
}

//SPDX-License-Identifier: Unlicense
pragma solidity ^0.8.0;

import "solidity-bytes-utils/contracts/BytesLib.sol";
import "base64-sol/base64.sol";

/// @title Gameboard for inchain turmites
/// @notice Implementation of an Gameboard for Straylight Protocoll.
/// @notice we are all standing on the shoulders of giants - this board is inspired by how CryptoGhost is drawing boards
/// @author @brachlandberlin / plsdlr.net
/// @dev bytesLib & base64 are required for formating

contract Gameboard {
    using BytesLib for bytes;
    mapping(uint256 => gameboard) gameboards;

    struct gameboard {
        bytes1[144][144] board;
    }

    /// @dev an explicit function to get a byte with x,y,board
    /// @param x the x position on the board
    /// @param y the y position on the board
    /// @param boardNumber the boardNumber number
    function getByte(
        uint256 x,
        uint256 y,
        uint256 boardNumber
    ) public view returns (bytes1) {
        return gameboards[boardNumber].board[x][y];
    }

    /// @dev an explicit function to set a byte with x,y,value,board
    /// @param x the x position on the board
    /// @param y the y position on the board
    /// @param value the byte1 value to set
    /// @param boardNumber the board number
    function setByte(
        uint256 x,
        uint256 y,
        bytes1 value,
        uint256 boardNumber
    ) internal {
        gameboards[boardNumber].board[x][y] = value;
    }

    /// @dev function to generate the Bitmap Base64 encoded with boardNumber, position x, position y and boolean if turmite should be rendered
    /// @param boardNumber the board number
    /// @param posx the x position of the turmite on the board
    /// @param posy the y position on the turmite on the board
    /// @param renderTurmite boolean to render turmite
    function getBitmapBase64(
        uint8 boardNumber,
        uint8 posx,
        uint8 posy,
        bool renderTurmite
    ) public view returns (string memory) {
        return
            string(
                abi.encodePacked(
                    "data:image/bmp;base64,",
                    Base64.encode(getBitmap(boardNumber, posx, posy, renderTurmite))
                )
            );
    }

    /// @dev function to generate a SVG String with boardNumber, position x, position y and boolean if turmite should be rendered
    /// @dev same parameters as getBitmapBase64
    function getSvg(
        uint8 boardNumber,
        uint8 posx,
        uint8 posy,
        bool renderTurmite
    ) public view returns (string memory) {
        return
            string(
                abi.encodePacked(
                    "data:image/svg+xml;base64,",
                    Base64.encode(
                        abi.encodePacked(
                            '<svg class="svgBGG" xmlns="http://www.w3.org/2000/svg" version="1.1" width="500" height="500"><defs id="someDefs"><style id="style1999"> .svgBGG { width: 500px;height: 500px;background-image: url(',
                            getBitmapBase64(boardNumber, posx, posy, renderTurmite),
                            "); background-repeat: no-repeat; background-size: 100%; image-rendering: -webkit-optimize-contrast; -ms-interpolation-mode: nearest-neighbor; image-rendering: -moz-crisp-edges; image-rendering: pixelated;}</style></defs></svg>"
                        )
                    )
                )
            );
    }

    /// @dev function to generate byte representation of the Board, position x, position y and boolean if turmite should be rendered
    /// @dev BMP Header is generated externaly
    /// @dev same parameters as getBitmapBase64
    function getBitmap(
        uint8 boardNumber,
        uint8 posx,
        uint8 posy,
        bool renderTurmite
    ) public view returns (bytes memory) {
        bytes
            memory headers = hex"424D385500000000000036040000280000009000000090000000010008000000000002510000120B0000120B00000000000000000000000000000101010002020200030303000404040005050500060606000707070008080800090909000A0A0A000B0B0B000C0C0C000D0D0D000E0E0E000F0F0F00101010001111110012121200131313001414140015151500161616001717170018181800191919001A1A1A001B1B1B001C1C1C001D1D1D001E1E1E001F1F1F00202020002121210022222200232323002424240025252500262626002727270028282800292929002A2A2A002B2B2B002C2C2C002D2D2D002E2E2E002F2F2F00303030003131310032323200333333003434340035353500363636003737370038383800393939003A3A3A003B3B3B003C3C3C003D3D3D003E3E3E003F3F3F00404040004141410042424200434343004444440045454500464646004747470048484800494949004A4A4A004B4B4B004C4C4C004D4D4D004E4E4E004F4F4F00505050005151510052525200535353005454540055555500565656005757570058585800595959005A5A5A005B5B5B005C5C5C005D5D5D005E5E5E005F5F5F00606060006161610062626200636363006464640065656500666666006767670068686800696969006A6A6A006B6B6B006C6C6C006D6D6D006E6E6E006F6F6F00707070007171710072727200737373007474740075757500767676007777770078787800797979007A7A7A007B7B7B007C7C7C007D7D7D007E7E7E007F7F7F00808080008181810082828200838383008484840085858500868686008787870088888800898989008A8A8A008B8B8B008C8C8C008D8D8D008E8E8E008F8F8F00909090009191910092929200939393009494940095959500969696009797970098989800999999009A9A9A009B9B9B009C9C9C009D9D9D009E9E9E009F9F9F00A0A0A000A1A1A100A2A2A200A3A3A300A4A4A400A5A5A500A6A6A600A7A7A700A8A8A800A9A9A900AAAAAA00ABABAB00ACACAC00ADADAD00AEAEAE00AFAFAF00B0B0B000B1B1B100B2B2B200B3B3B300B4B4B400B5B5B500B6B6B600B7B7B700B8B8B800B9B9B900BABABA00BBBBBB00BCBCBC00BDBDBD00BEBEBE00BFBFBF00C0C0C000C1C1C100C2C2C200C3C3C300C4C4C400C5C5C500C6C6C600C7C7C700C8C8C800C9C9C900CACACA00CBCBCB00CCCCCC00CDCDCD00CECECE00CFCFCF00D0D0D000D1D1D100D2D2D200D3D3D300D4D4D400D5D5D500D6D6D600D7D7D700D8D8D800D9D9D900DADADA00DBDBDB00DCDCDC00DDDDDD00DEDEDE00DFDFDF00E0E0E000E1E1E100E2E2E200E3E3E300E4E4E400E5E5E500E6E6E600E7E7E700E8E8E800E9E9E900EAEAEA00EBEBEB00ECECEC00EDEDED00EEEEEE00EFEFEF00F0F0F000F1F1F100F2F2F200F3F3F300F4F4F400F5F5F500F6F6F600F7F7F700F8F8F800F9F9F900FAFAFA00FBFBFB00FCFCFC00FDFDFD00FEFEFE00FFFFFF00";
        bytes memory returngameboard = new bytes(20736);
        for (uint256 xFill = 0; xFill < 144; ++xFill) {
            for (uint256 yFill = 0; yFill < 144; ++yFill) {
                uint256 index = xFill + 144 * yFill;
                returngameboard[index] = gameboards[boardNumber].board[xFill][yFill];
            }
        }
        if (renderTurmite == true) {
            uint256 index2 = uint256(posx) + 144 * uint256(posy);
            returngameboard[index2] = bytes1(uint8(165));
        }
        return headers.concat(returngameboard);
    }
}

// SPDX-License-Identifier: Unlicense
/*
 * @title Solidity Bytes Arrays Utils
 * @author Gonçalo Sá <[email protected]>
 *
 * @dev Bytes tightly packed arrays utility library for ethereum contracts written in Solidity.
 *      The library lets you concatenate, slice and type cast bytes arrays both in memory and storage.
 */
pragma solidity >=0.8.0 <0.9.0;


library BytesLib {
    function concat(
        bytes memory _preBytes,
        bytes memory _postBytes
    )
        internal
        pure
        returns (bytes memory)
    {
        bytes memory tempBytes;

        assembly {
            // Get a location of some free memory and store it in tempBytes as
            // Solidity does for memory variables.
            tempBytes := mload(0x40)

            // Store the length of the first bytes array at the beginning of
            // the memory for tempBytes.
            let length := mload(_preBytes)
            mstore(tempBytes, length)

            // Maintain a memory counter for the current write location in the
            // temp bytes array by adding the 32 bytes for the array length to
            // the starting location.
            let mc := add(tempBytes, 0x20)
            // Stop copying when the memory counter reaches the length of the
            // first bytes array.
            let end := add(mc, length)

            for {
                // Initialize a copy counter to the start of the _preBytes data,
                // 32 bytes into its memory.
                let cc := add(_preBytes, 0x20)
            } lt(mc, end) {
                // Increase both counters by 32 bytes each iteration.
                mc := add(mc, 0x20)
                cc := add(cc, 0x20)
            } {
                // Write the _preBytes data into the tempBytes memory 32 bytes
                // at a time.
                mstore(mc, mload(cc))
            }

            // Add the length of _postBytes to the current length of tempBytes
            // and store it as the new length in the first 32 bytes of the
            // tempBytes memory.
            length := mload(_postBytes)
            mstore(tempBytes, add(length, mload(tempBytes)))

            // Move the memory counter back from a multiple of 0x20 to the
            // actual end of the _preBytes data.
            mc := end
            // Stop copying when the memory counter reaches the new combined
            // length of the arrays.
            end := add(mc, length)

            for {
                let cc := add(_postBytes, 0x20)
            } lt(mc, end) {
                mc := add(mc, 0x20)
                cc := add(cc, 0x20)
            } {
                mstore(mc, mload(cc))
            }

            // Update the free-memory pointer by padding our last write location
            // to 32 bytes: add 31 bytes to the end of tempBytes to move to the
            // next 32 byte block, then round down to the nearest multiple of
            // 32. If the sum of the length of the two arrays is zero then add
            // one before rounding down to leave a blank 32 bytes (the length block with 0).
            mstore(0x40, and(
              add(add(end, iszero(add(length, mload(_preBytes)))), 31),
              not(31) // Round down to the nearest 32 bytes.
            ))
        }

        return tempBytes;
    }

    function concatStorage(bytes storage _preBytes, bytes memory _postBytes) internal {
        assembly {
            // Read the first 32 bytes of _preBytes storage, which is the length
            // of the array. (We don't need to use the offset into the slot
            // because arrays use the entire slot.)
            let fslot := sload(_preBytes.slot)
            // Arrays of 31 bytes or less have an even value in their slot,
            // while longer arrays have an odd value. The actual length is
            // the slot divided by two for odd values, and the lowest order
            // byte divided by two for even values.
            // If the slot is even, bitwise and the slot with 255 and divide by
            // two to get the length. If the slot is odd, bitwise and the slot
            // with -1 and divide by two.
            let slength := div(and(fslot, sub(mul(0x100, iszero(and(fslot, 1))), 1)), 2)
            let mlength := mload(_postBytes)
            let newlength := add(slength, mlength)
            // slength can contain both the length and contents of the array
            // if length < 32 bytes so let's prepare for that
            // v. http://solidity.readthedocs.io/en/latest/miscellaneous.html#layout-of-state-variables-in-storage
            switch add(lt(slength, 32), lt(newlength, 32))
            case 2 {
                // Since the new array still fits in the slot, we just need to
                // update the contents of the slot.
                // uint256(bytes_storage) = uint256(bytes_storage) + uint256(bytes_memory) + new_length
                sstore(
                    _preBytes.slot,
                    // all the modifications to the slot are inside this
                    // next block
                    add(
                        // we can just add to the slot contents because the
                        // bytes we want to change are the LSBs
                        fslot,
                        add(
                            mul(
                                div(
                                    // load the bytes from memory
                                    mload(add(_postBytes, 0x20)),
                                    // zero all bytes to the right
                                    exp(0x100, sub(32, mlength))
                                ),
                                // and now shift left the number of bytes to
                                // leave space for the length in the slot
                                exp(0x100, sub(32, newlength))
                            ),
                            // increase length by the double of the memory
                            // bytes length
                            mul(mlength, 2)
                        )
                    )
                )
            }
            case 1 {
                // The stored value fits in the slot, but the combined value
                // will exceed it.
                // get the keccak hash to get the contents of the array
                mstore(0x0, _preBytes.slot)
                let sc := add(keccak256(0x0, 0x20), div(slength, 32))

                // save new length
                sstore(_preBytes.slot, add(mul(newlength, 2), 1))

                // The contents of the _postBytes array start 32 bytes into
                // the structure. Our first read should obtain the `submod`
                // bytes that can fit into the unused space in the last word
                // of the stored array. To get this, we read 32 bytes starting
                // from `submod`, so the data we read overlaps with the array
                // contents by `submod` bytes. Masking the lowest-order
                // `submod` bytes allows us to add that value directly to the
                // stored value.

                let submod := sub(32, slength)
                let mc := add(_postBytes, submod)
                let end := add(_postBytes, mlength)
                let mask := sub(exp(0x100, submod), 1)

                sstore(
                    sc,
                    add(
                        and(
                            fslot,
                            0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00
                        ),
                        and(mload(mc), mask)
                    )
                )

                for {
                    mc := add(mc, 0x20)
                    sc := add(sc, 1)
                } lt(mc, end) {
                    sc := add(sc, 1)
                    mc := add(mc, 0x20)
                } {
                    sstore(sc, mload(mc))
                }

                mask := exp(0x100, sub(mc, end))

                sstore(sc, mul(div(mload(mc), mask), mask))
            }
            default {
                // get the keccak hash to get the contents of the array
                mstore(0x0, _preBytes.slot)
                // Start copying to the last used word of the stored array.
                let sc := add(keccak256(0x0, 0x20), div(slength, 32))

                // save new length
                sstore(_preBytes.slot, add(mul(newlength, 2), 1))

                // Copy over the first `submod` bytes of the new data as in
                // case 1 above.
                let slengthmod := mod(slength, 32)
                let mlengthmod := mod(mlength, 32)
                let submod := sub(32, slengthmod)
                let mc := add(_postBytes, submod)
                let end := add(_postBytes, mlength)
                let mask := sub(exp(0x100, submod), 1)

                sstore(sc, add(sload(sc), and(mload(mc), mask)))

                for {
                    sc := add(sc, 1)
                    mc := add(mc, 0x20)
                } lt(mc, end) {
                    sc := add(sc, 1)
                    mc := add(mc, 0x20)
                } {
                    sstore(sc, mload(mc))
                }

                mask := exp(0x100, sub(mc, end))

                sstore(sc, mul(div(mload(mc), mask), mask))
            }
        }
    }

    function slice(
        bytes memory _bytes,
        uint256 _start,
        uint256 _length
    )
        internal
        pure
        returns (bytes memory)
    {
        require(_length + 31 >= _length, "slice_overflow");
        require(_bytes.length >= _start + _length, "slice_outOfBounds");

        bytes memory tempBytes;

        assembly {
            switch iszero(_length)
            case 0 {
                // Get a location of some free memory and store it in tempBytes as
                // Solidity does for memory variables.
                tempBytes := mload(0x40)

                // The first word of the slice result is potentially a partial
                // word read from the original array. To read it, we calculate
                // the length of that partial word and start copying that many
                // bytes into the array. The first word we copy will start with
                // data we don't care about, but the last `lengthmod` bytes will
                // land at the beginning of the contents of the new array. When
                // we're done copying, we overwrite the full first word with
                // the actual length of the slice.
                let lengthmod := and(_length, 31)

                // The multiplication in the next line is necessary
                // because when slicing multiples of 32 bytes (lengthmod == 0)
                // the following copy loop was copying the origin's length
                // and then ending prematurely not copying everything it should.
                let mc := add(add(tempBytes, lengthmod), mul(0x20, iszero(lengthmod)))
                let end := add(mc, _length)

                for {
                    // The multiplication in the next line has the same exact purpose
                    // as the one above.
                    let cc := add(add(add(_bytes, lengthmod), mul(0x20, iszero(lengthmod))), _start)
                } lt(mc, end) {
                    mc := add(mc, 0x20)
                    cc := add(cc, 0x20)
                } {
                    mstore(mc, mload(cc))
                }

                mstore(tempBytes, _length)

                //update free-memory pointer
                //allocating the array padded to 32 bytes like the compiler does now
                mstore(0x40, and(add(mc, 31), not(31)))
            }
            //if we want a zero-length slice let's just return a zero-length array
            default {
                tempBytes := mload(0x40)
                //zero out the 32 bytes slice we are about to return
                //we need to do it because Solidity does not garbage collect
                mstore(tempBytes, 0)

                mstore(0x40, add(tempBytes, 0x20))
            }
        }

        return tempBytes;
    }

    function toAddress(bytes memory _bytes, uint256 _start) internal pure returns (address) {
        require(_bytes.length >= _start + 20, "toAddress_outOfBounds");
        address tempAddress;

        assembly {
            tempAddress := div(mload(add(add(_bytes, 0x20), _start)), 0x1000000000000000000000000)
        }

        return tempAddress;
    }

    function toUint8(bytes memory _bytes, uint256 _start) internal pure returns (uint8) {
        require(_bytes.length >= _start + 1 , "toUint8_outOfBounds");
        uint8 tempUint;

        assembly {
            tempUint := mload(add(add(_bytes, 0x1), _start))
        }

        return tempUint;
    }

    function toUint16(bytes memory _bytes, uint256 _start) internal pure returns (uint16) {
        require(_bytes.length >= _start + 2, "toUint16_outOfBounds");
        uint16 tempUint;

        assembly {
            tempUint := mload(add(add(_bytes, 0x2), _start))
        }

        return tempUint;
    }

    function toUint32(bytes memory _bytes, uint256 _start) internal pure returns (uint32) {
        require(_bytes.length >= _start + 4, "toUint32_outOfBounds");
        uint32 tempUint;

        assembly {
            tempUint := mload(add(add(_bytes, 0x4), _start))
        }

        return tempUint;
    }

    function toUint64(bytes memory _bytes, uint256 _start) internal pure returns (uint64) {
        require(_bytes.length >= _start + 8, "toUint64_outOfBounds");
        uint64 tempUint;

        assembly {
            tempUint := mload(add(add(_bytes, 0x8), _start))
        }

        return tempUint;
    }

    function toUint96(bytes memory _bytes, uint256 _start) internal pure returns (uint96) {
        require(_bytes.length >= _start + 12, "toUint96_outOfBounds");
        uint96 tempUint;

        assembly {
            tempUint := mload(add(add(_bytes, 0xc), _start))
        }

        return tempUint;
    }

    function toUint128(bytes memory _bytes, uint256 _start) internal pure returns (uint128) {
        require(_bytes.length >= _start + 16, "toUint128_outOfBounds");
        uint128 tempUint;

        assembly {
            tempUint := mload(add(add(_bytes, 0x10), _start))
        }

        return tempUint;
    }

    function toUint256(bytes memory _bytes, uint256 _start) internal pure returns (uint256) {
        require(_bytes.length >= _start + 32, "toUint256_outOfBounds");
        uint256 tempUint;

        assembly {
            tempUint := mload(add(add(_bytes, 0x20), _start))
        }

        return tempUint;
    }

    function toBytes32(bytes memory _bytes, uint256 _start) internal pure returns (bytes32) {
        require(_bytes.length >= _start + 32, "toBytes32_outOfBounds");
        bytes32 tempBytes32;

        assembly {
            tempBytes32 := mload(add(add(_bytes, 0x20), _start))
        }

        return tempBytes32;
    }

    function equal(bytes memory _preBytes, bytes memory _postBytes) internal pure returns (bool) {
        bool success = true;

        assembly {
            let length := mload(_preBytes)

            // if lengths don't match the arrays are not equal
            switch eq(length, mload(_postBytes))
            case 1 {
                // cb is a circuit breaker in the for loop since there's
                //  no said feature for inline assembly loops
                // cb = 1 - don't breaker
                // cb = 0 - break
                let cb := 1

                let mc := add(_preBytes, 0x20)
                let end := add(mc, length)

                for {
                    let cc := add(_postBytes, 0x20)
                // the next line is the loop condition:
                // while(uint256(mc < end) + cb == 2)
                } eq(add(lt(mc, end), cb), 2) {
                    mc := add(mc, 0x20)
                    cc := add(cc, 0x20)
                } {
                    // if any of these checks fails then arrays are not equal
                    if iszero(eq(mload(mc), mload(cc))) {
                        // unsuccess:
                        success := 0
                        cb := 0
                    }
                }
            }
            default {
                // unsuccess:
                success := 0
            }
        }

        return success;
    }

    function equalStorage(
        bytes storage _preBytes,
        bytes memory _postBytes
    )
        internal
        view
        returns (bool)
    {
        bool success = true;

        assembly {
            // we know _preBytes_offset is 0
            let fslot := sload(_preBytes.slot)
            // Decode the length of the stored array like in concatStorage().
            let slength := div(and(fslot, sub(mul(0x100, iszero(and(fslot, 1))), 1)), 2)
            let mlength := mload(_postBytes)

            // if lengths don't match the arrays are not equal
            switch eq(slength, mlength)
            case 1 {
                // slength can contain both the length and contents of the array
                // if length < 32 bytes so let's prepare for that
                // v. http://solidity.readthedocs.io/en/latest/miscellaneous.html#layout-of-state-variables-in-storage
                if iszero(iszero(slength)) {
                    switch lt(slength, 32)
                    case 1 {
                        // blank the last byte which is the length
                        fslot := mul(div(fslot, 0x100), 0x100)

                        if iszero(eq(fslot, mload(add(_postBytes, 0x20)))) {
                            // unsuccess:
                            success := 0
                        }
                    }
                    default {
                        // cb is a circuit breaker in the for loop since there's
                        //  no said feature for inline assembly loops
                        // cb = 1 - don't breaker
                        // cb = 0 - break
                        let cb := 1

                        // get the keccak hash to get the contents of the array
                        mstore(0x0, _preBytes.slot)
                        let sc := keccak256(0x0, 0x20)

                        let mc := add(_postBytes, 0x20)
                        let end := add(mc, mlength)

                        // the next line is the loop condition:
                        // while(uint256(mc < end) + cb == 2)
                        for {} eq(add(lt(mc, end), cb), 2) {
                            sc := add(sc, 1)
                            mc := add(mc, 0x20)
                        } {
                            if iszero(eq(sload(sc), mload(mc))) {
                                // unsuccess:
                                success := 0
                                cb := 0
                            }
                        }
                    }
                }
            }
            default {
                // unsuccess:
                success := 0
            }
        }

        return success;
    }
}

// SPDX-License-Identifier: MIT

pragma solidity >=0.6.0;

/// @title Base64
/// @author Brecht Devos - <[email protected]>
/// @notice Provides functions for encoding/decoding base64
library Base64 {
    string internal constant TABLE_ENCODE = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
    bytes  internal constant TABLE_DECODE = hex"0000000000000000000000000000000000000000000000000000000000000000"
                                            hex"00000000000000000000003e0000003f3435363738393a3b3c3d000000000000"
                                            hex"00000102030405060708090a0b0c0d0e0f101112131415161718190000000000"
                                            hex"001a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132330000000000";

    function encode(bytes memory data) internal pure returns (string memory) {
        if (data.length == 0) return '';

        // load the table into memory
        string memory table = TABLE_ENCODE;

        // multiply by 4/3 rounded up
        uint256 encodedLen = 4 * ((data.length + 2) / 3);

        // add some extra buffer at the end required for the writing
        string memory result = new string(encodedLen + 32);

        assembly {
            // set the actual output length
            mstore(result, encodedLen)

            // prepare the lookup table
            let tablePtr := add(table, 1)

            // input ptr
            let dataPtr := data
            let endPtr := add(dataPtr, mload(data))

            // result ptr, jump over length
            let resultPtr := add(result, 32)

            // run over the input, 3 bytes at a time
            for {} lt(dataPtr, endPtr) {}
            {
                // read 3 bytes
                dataPtr := add(dataPtr, 3)
                let input := mload(dataPtr)

                // write 4 characters
                mstore8(resultPtr, mload(add(tablePtr, and(shr(18, input), 0x3F))))
                resultPtr := add(resultPtr, 1)
                mstore8(resultPtr, mload(add(tablePtr, and(shr(12, input), 0x3F))))
                resultPtr := add(resultPtr, 1)
                mstore8(resultPtr, mload(add(tablePtr, and(shr( 6, input), 0x3F))))
                resultPtr := add(resultPtr, 1)
                mstore8(resultPtr, mload(add(tablePtr, and(        input,  0x3F))))
                resultPtr := add(resultPtr, 1)
            }

            // padding with '='
            switch mod(mload(data), 3)
            case 1 { mstore(sub(resultPtr, 2), shl(240, 0x3d3d)) }
            case 2 { mstore(sub(resultPtr, 1), shl(248, 0x3d)) }
        }

        return result;
    }

    function decode(string memory _data) internal pure returns (bytes memory) {
        bytes memory data = bytes(_data);

        if (data.length == 0) return new bytes(0);
        require(data.length % 4 == 0, "invalid base64 decoder input");

        // load the table into memory
        bytes memory table = TABLE_DECODE;

        // every 4 characters represent 3 bytes
        uint256 decodedLen = (data.length / 4) * 3;

        // add some extra buffer at the end required for the writing
        bytes memory result = new bytes(decodedLen + 32);

        assembly {
            // padding with '='
            let lastBytes := mload(add(data, mload(data)))
            if eq(and(lastBytes, 0xFF), 0x3d) {
                decodedLen := sub(decodedLen, 1)
                if eq(and(lastBytes, 0xFFFF), 0x3d3d) {
                    decodedLen := sub(decodedLen, 1)
                }
            }

            // set the actual output length
            mstore(result, decodedLen)

            // prepare the lookup table
            let tablePtr := add(table, 1)

            // input ptr
            let dataPtr := data
            let endPtr := add(dataPtr, mload(data))

            // result ptr, jump over length
            let resultPtr := add(result, 32)

            // run over the input, 4 characters at a time
            for {} lt(dataPtr, endPtr) {}
            {
               // read 4 characters
               dataPtr := add(dataPtr, 4)
               let input := mload(dataPtr)

               // write 3 bytes
               let output := add(
                   add(
                       shl(18, and(mload(add(tablePtr, and(shr(24, input), 0xFF))), 0xFF)),
                       shl(12, and(mload(add(tablePtr, and(shr(16, input), 0xFF))), 0xFF))),
                   add(
                       shl( 6, and(mload(add(tablePtr, and(shr( 8, input), 0xFF))), 0xFF)),
                               and(mload(add(tablePtr, and(        input , 0xFF))), 0xFF)
                    )
                )
                mstore(resultPtr, shl(232, output))
                resultPtr := add(resultPtr, 3)
            }
        }

        return result;
    }
}

// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity >=0.8.0;

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

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

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

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

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

    string public name;

    string public symbol;

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

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

    mapping(uint256 => address) internal _ownerOf;

    mapping(address => uint256) internal _balanceOf;

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

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

        return _balanceOf[owner];
    }

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

    mapping(uint256 => address) public getApproved;

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

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

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

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

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

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

        getApproved[id] = spender;

        emit Approval(owner, spender, id);
    }

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

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

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

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

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

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

            _balanceOf[to]++;
        }

        _ownerOf[id] = to;

        delete getApproved[id];

        emit Transfer(from, to, id);
    }

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

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

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

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

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

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

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

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

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

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

        _ownerOf[id] = to;

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

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

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

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

        delete _ownerOf[id];

        delete getApproved[id];

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

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

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

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

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

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

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

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

Contract Security Audit

Contract ABI

API
[{"inputs":[{"internalType":"address","name":"_minterContract","type":"address"},{"internalType":"uint256","name":"maxAmount","type":"uint256"},{"internalType":"string","name":"network","type":"string"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"spender","type":"address"},{"indexed":true,"internalType":"uint256","name":"id","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"operator","type":"address"},{"indexed":false,"internalType":"bool","name":"approved","type":"bool"}],"name":"ApprovalForAll","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":true,"internalType":"uint256","name":"id","type":"uint256"}],"name":"Transfer","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"},{"indexed":true,"internalType":"bytes12","name":"rule","type":"bytes12"},{"indexed":false,"internalType":"uint256","name":"boardId","type":"uint256"}],"name":"TurmiteMint","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"},{"indexed":true,"internalType":"uint8","name":"boardnumber","type":"uint8"},{"indexed":true,"internalType":"uint256","name":"moves","type":"uint256"}],"name":"TurmiteMove","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"},{"indexed":true,"internalType":"bytes12","name":"newrule","type":"bytes12"}],"name":"TurmiteReprogramm","type":"event"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"id","type":"uint256"}],"name":"approve","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"boardcounter","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"getApproved","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint8","name":"boardNumber","type":"uint8"},{"internalType":"uint8","name":"posx","type":"uint8"},{"internalType":"uint8","name":"posy","type":"uint8"},{"internalType":"bool","name":"renderTurmite","type":"bool"}],"name":"getBitmap","outputs":[{"internalType":"bytes","name":"","type":"bytes"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint8","name":"boardNumber","type":"uint8"},{"internalType":"uint8","name":"posx","type":"uint8"},{"internalType":"uint8","name":"posy","type":"uint8"},{"internalType":"bool","name":"renderTurmite","type":"bool"}],"name":"getBitmapBase64","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"x","type":"uint256"},{"internalType":"uint256","name":"y","type":"uint256"},{"internalType":"uint256","name":"boardNumber","type":"uint256"}],"name":"getByte","outputs":[{"internalType":"bytes1","name":"","type":"bytes1"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"id","type":"uint256"}],"name":"getPosField","outputs":[{"internalType":"bytes","name":"encodedData","type":"bytes"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint8","name":"boardNumber","type":"uint8"},{"internalType":"uint8","name":"posx","type":"uint8"},{"internalType":"uint8","name":"posy","type":"uint8"},{"internalType":"bool","name":"renderTurmite","type":"bool"}],"name":"getSvg","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"haecceity","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"isApprovedForAll","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"maxnumbturmites","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256[2]","name":"idmoves","type":"uint256[2]"}],"name":"moveTurmite","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"id","type":"uint256"}],"name":"ownerOf","outputs":[{"internalType":"address","name":"owner","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"mintTo","type":"address"},{"internalType":"bytes12","name":"rule","type":"bytes12"},{"internalType":"uint256","name":"moves","type":"uint256"}],"name":"publicmint","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint8","name":"number","type":"uint8"}],"name":"renderBoard","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"id","type":"uint256"},{"internalType":"bytes12","name":"rule","type":"bytes12"}],"name":"reprogrammTurmite","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":"uint256","name":"id","type":"uint256"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"setByteHaMode","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_haecceityContract","type":"address"}],"name":"setHaecceityContract","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"id","type":"uint256"}],"name":"setHaecceityMode","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"id","type":"uint256"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"setPositionHaMode","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":"index","type":"uint256"}],"name":"tokenByIndex","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"uint256","name":"index","type":"uint256"}],"name":"tokenOfOwnerByIndex","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"id","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":"uint256","name":"","type":"uint256"}],"name":"turmites","outputs":[{"internalType":"uint8","name":"turposx","type":"uint8"},{"internalType":"uint8","name":"turposy","type":"uint8"},{"internalType":"uint8","name":"orientation","type":"uint8"},{"internalType":"uint8","name":"boardnumber","type":"uint8"},{"internalType":"bytes1","name":"state","type":"bytes1"},{"internalType":"bytes12","name":"rule","type":"bytes12"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes12","name":"rule","type":"bytes12"}],"name":"validateNewRule","outputs":[{"internalType":"bool","name":"allowed","type":"bool"}],"stateMutability":"pure","type":"function"}]

60806040526000600d556000600e556040518060800160405280602460ff168152602001604860ff168152602001604860ff168152602001606c60ff1681525060109060046200005192919062000205565b506040518060800160405280604860ff168152602001602460ff168152602001606c60ff168152602001604860ff1681525060149060046200009592919062000205565b50348015620000a357600080fd5b506040516200678d3803806200678d8339818101604052810190620000c99190620004ab565b806040518060400160405280600a81526020017f53747261796c69676874000000000000000000000000000000000000000000008152506040518060400160405280600381526020017f53545200000000000000000000000000000000000000000000000000000000008152508181816000908162000149919062000767565b5080600190816200015b919062000767565b505050505080600c908162000171919062000767565b505082601860006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555081600f8190555033601a60006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055505050506200084e565b82600481019282156200023c579160200282015b828111156200023b578251829060ff1690559160200191906001019062000219565b5b5090506200024b91906200024f565b5090565b5b808211156200026a57600081600090555060010162000250565b5090565b6000604051905090565b600080fd5b600080fd5b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b6000620002af8262000282565b9050919050565b620002c181620002a2565b8114620002cd57600080fd5b50565b600081519050620002e181620002b6565b92915050565b6000819050919050565b620002fc81620002e7565b81146200030857600080fd5b50565b6000815190506200031c81620002f1565b92915050565b600080fd5b600080fd5b6000601f19601f8301169050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b62000377826200032c565b810181811067ffffffffffffffff821117156200039957620003986200033d565b5b80604052505050565b6000620003ae6200026e565b9050620003bc82826200036c565b919050565b600067ffffffffffffffff821115620003df57620003de6200033d565b5b620003ea826200032c565b9050602081019050919050565b60005b8381101562000417578082015181840152602081019050620003fa565b8381111562000427576000848401525b50505050565b6000620004446200043e84620003c1565b620003a2565b90508281526020810184848401111562000463576200046262000327565b5b62000470848285620003f7565b509392505050565b600082601f83011262000490576200048f62000322565b5b8151620004a28482602086016200042d565b91505092915050565b600080600060608486031215620004c757620004c662000278565b5b6000620004d786828701620002d0565b9350506020620004ea868287016200030b565b925050604084015167ffffffffffffffff8111156200050e576200050d6200027d565b5b6200051c8682870162000478565b9150509250925092565b600081519050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b600060028204905060018216806200057957607f821691505b6020821081036200058f576200058e62000531565b5b50919050565b60008190508160005260206000209050919050565b60006020601f8301049050919050565b600082821b905092915050565b600060088302620005f97fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff82620005ba565b620006058683620005ba565b95508019841693508086168417925050509392505050565b6000819050919050565b600062000648620006426200063c84620002e7565b6200061d565b620002e7565b9050919050565b6000819050919050565b620006648362000627565b6200067c62000673826200064f565b848454620005c7565b825550505050565b600090565b6200069362000684565b620006a081848462000659565b505050565b5b81811015620006c857620006bc60008262000689565b600181019050620006a6565b5050565b601f8211156200071757620006e18162000595565b620006ec84620005aa565b81016020851015620006fc578190505b620007146200070b85620005aa565b830182620006a5565b50505b505050565b600082821c905092915050565b60006200073c600019846008026200071c565b1980831691505092915050565b600062000757838362000729565b9150826002028217905092915050565b620007728262000526565b67ffffffffffffffff8111156200078e576200078d6200033d565b5b6200079a825462000560565b620007a7828285620006cc565b600060209050601f831160018114620007df5760008415620007ca578287015190505b620007d6858262000749565b86555062000846565b601f198416620007ef8662000595565b60005b828110156200081957848901518255600182019150602085019450602081019050620007f2565b8683101562000839578489015162000835601f89168262000729565b8355505b6001600288020188555050505b505050505050565b615f2f806200085e6000396000f3fe608060405234801561001057600080fd5b50600436106102065760003560e01c80636352211e1161011a578063c2a8d57c116100ad578063d3158b021161007c578063d3158b021461065c578063e524b3071461067a578063e8354445146106aa578063e985e9c5146106c6578063ef7af2fc146106f657610206565b8063c2a8d57c146105c4578063c61a3c84146105e0578063c87b56dd146105fc578063c966409d1461062c57610206565b806395d89b41116100e957806395d89b411461053e578063a22cb4651461055c578063b88d4fde14610578578063be76bf8f1461059457610206565b80636352211e1461049257806369dbd7d7146104c25780636c5453a6146104f257806370a082311461050e57610206565b80632cc4837f1161019d57806340cb8af91161016c57806340cb8af9146103ca57806342842e0e146103fa5780634f6ccce714610416578063518a8802146104465780635e8c32111461046257610206565b80632cc4837f146103305780632f745c591461034e57806335faee731461037e5780633aedf55c146103ae57610206565b80631034fc0d116101d95780631034fc0d146102a557806313f36fc1146102c157806318160ddd146102f657806323b872dd1461031457610206565b806301ffc9a71461020b57806306fdde031461023b578063081812fc14610259578063095ea7b314610289575b600080fd5b61022560048036038101906102209190613a09565b610726565b6040516102329190613a51565b60405180910390f35b6102436107b8565b6040516102509190613b05565b60405180910390f35b610273600480360381019061026e9190613b5d565b610846565b6040516102809190613bcb565b60405180910390f35b6102a3600480360381019061029e9190613c12565b610879565b005b6102bf60048036038101906102ba9190613caa565b610a62565b005b6102db60048036038101906102d69190613b5d565b610baa565b6040516102ed96959493929190613d50565b60405180910390f35b6102fe610c34565b60405161030b9190613dc0565b60405180910390f35b61032e60048036038101906103299190613ddb565b610c41565b005b610338610c6e565b6040516103459190613dc0565b60405180910390f35b61036860048036038101906103639190613c12565b610c74565b6040516103759190613dc0565b60405180910390f35b61039860048036038101906103939190613e2e565b610d19565b6040516103a59190613a51565b60405180910390f35b6103c860048036038101906103c39190613ec0565b61119d565b005b6103e460048036038101906103df9190613b5d565b6113bd565b6040516103f19190613a51565b60405180910390f35b610414600480360381019061040f9190613ddb565b6113dd565b005b610430600480360381019061042b9190613b5d565b61140a565b60405161043d9190613dc0565b60405180910390f35b610460600480360381019061045b9190613f20565b61147b565b005b61047c60048036038101906104779190613fa5565b61154f565b6040516104899190613b05565b60405180910390f35b6104ac60048036038101906104a79190613b5d565b61158e565b6040516104b99190613bcb565b60405180910390f35b6104dc60048036038101906104d79190613fa5565b611639565b6040516104e99190613b05565b60405180910390f35b61050c60048036038101906105079190613ec0565b611697565b005b61052860048036038101906105239190613f20565b6118d9565b6040516105359190613dc0565b60405180910390f35b610546611990565b6040516105539190613b05565b60405180910390f35b6105766004803603810190610571919061400c565b611a1e565b005b610592600480360381019061058d919061404c565b611b1b565b005b6105ae60048036038101906105a99190613fa5565b611b4c565b6040516105bb9190614129565b60405180910390f35b6105de60048036038101906105d9919061414b565b611d49565b005b6105fa60048036038101906105f59190613b5d565b611fc9565b005b61061660048036038101906106119190613b5d565b6120ff565b6040516106239190613b05565b60405180910390f35b6106466004803603810190610641919061419e565b6121e9565b6040516106539190613b05565b60405180910390f35b610664612200565b6040516106719190613dc0565b60405180910390f35b610694600480360381019061068f91906141cb565b612206565b6040516106a1919061421e565b60405180910390f35b6106c460048036038101906106bf919061425b565b612265565b005b6106e060048036038101906106db9190614288565b612352565b6040516106ed9190613a51565b60405180910390f35b610710600480360381019061070b9190613b5d565b612381565b60405161071d9190614129565b60405180910390f35b60006301ffc9a760e01b827bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916148061078157506380ac58cd60e01b827bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916145b806107b15750635b5e139f60e01b827bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916145b9050919050565b600080546107c5906142f7565b80601f01602080910402602001604051908101604052809291908181526020018280546107f1906142f7565b801561083e5780601f106108135761010080835404028352916020019161083e565b820191906000526020600020905b81548152906001019060200180831161082157829003601f168201915b505050505081565b60046020528060005260406000206000915054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b60006002600083815260200190815260200160002060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1690508073ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614806109715750600560008273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff165b6109b0576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016109a790614374565b60405180910390fd5b826004600084815260200190815260200160002060006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550818373ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92560405160405180910390a4505050565b610a6b8261158e565b73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614610ad8576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610acf90614374565b60405180910390fd5b60011515610ae582610d19565b151514610b27576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610b1e906143e0565b60405180910390fd5b80600b600084815260200190815260200160002060000160056101000a8154816bffffffffffffffffffffffff021916908360a01c02179055508073ffffffffffffffffffffffffffffffffffffffff1916827fd1464fd210d58c95f829eeb54351bb3b0f9cf4277440f5627f822489a0548cd360405160405180910390a35050565b600b6020528060005260406000206000915090508060000160009054906101000a900460ff16908060000160019054906101000a900460ff16908060000160029054906101000a900460ff16908060000160039054906101000a900460ff16908060000160049054906101000a900460f81b908060000160059054906101000a900460a01b905086565b6000600880549050905090565b610c4a81612450565b610c5483826124ac565b610c5e8282612619565b610c69838383612698565b505050565b600f5481565b6000610c7f836118d9565b8210610cc0576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610cb790614472565b60405180910390fd5b600660008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600083815260200190815260200160002054905092915050565b60008060ff60f81b836000600c8110610d3557610d34614492565b5b1a60f81b7effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff19161480610da35750600060f81b836000600c8110610d7b57610d7a614492565b5b1a60f81b7effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916145b8015610e32575060ff60f81b836003600c8110610dc357610dc2614492565b5b1a60f81b7effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff19161480610e315750600060f81b836003600c8110610e0957610e08614492565b5b1a60f81b7effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916145b5b8015610ec1575060ff60f81b836006600c8110610e5257610e51614492565b5b1a60f81b7effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff19161480610ec05750600060f81b836006600c8110610e9857610e97614492565b5b1a60f81b7effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916145b5b8015610f50575060ff60f81b836009600c8110610ee157610ee0614492565b5b1a60f81b7effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff19161480610f4f5750600060f81b836009600c8110610f2757610f26614492565b5b1a60f81b7effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916145b5b90506000600160f81b846002600c8110610f6d57610f6c614492565b5b1a60f81b7effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff19161480610fdb5750600060f81b846002600c8110610fb357610fb2614492565b5b1a60f81b7effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916145b801561106a5750600160f81b846005600c8110610ffb57610ffa614492565b5b1a60f81b7effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff191614806110695750600060f81b846005600c811061104157611040614492565b5b1a60f81b7effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916145b5b80156110f95750600160f81b846008600c811061108a57611089614492565b5b1a60f81b7effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff191614806110f85750600060f81b846008600c81106110d0576110cf614492565b5b1a60f81b7effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916145b5b80156111885750600160f81b84600b600c811061111957611118614492565b5b1a60f81b7effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff191614806111875750600060f81b84600b600c811061115f5761115e614492565b5b1a60f81b7effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916145b5b90508080156111945750815b92505050919050565b600073ffffffffffffffffffffffffffffffffffffffff16601960009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff160361122e576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016112259061450d565b60405180910390fd5b60011515601b600085815260200190815260200160002060009054906101000a900460ff16151514611295576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161128c9061459f565b60405180910390fd5b601960009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614611325576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161131c9061460b565b60405180910390fd5b600080600061137785858080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f82011690508083019250505050505050612a97565b9250925092506113b58360ff168360ff1683600b60008b815260200190815260200160002060000160039054906101000a900460ff1660ff16612ab8565b505050505050565b601b6020528060005260406000206000915054906101000a900460ff1681565b6113e681612450565b6113f083826124ac565b6113fa8282612619565b611405838383612b1d565b505050565b6000611414610c34565b8210611455576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161144c9061469d565b60405180910390fd5b6008828154811061146957611468614492565b5b90600052602060002001549050919050565b601a60009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff161461150b576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161150290614374565b60405180910390fd5b80601960006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050565b606061156561156086868686611b4c565b612c55565b6040516020016115759190614745565b6040516020818303038152906040529050949350505050565b60008073ffffffffffffffffffffffffffffffffffffffff166002600084815260200190815260200160002060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1691508173ffffffffffffffffffffffffffffffffffffffff1603611634576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161162b906147b3565b60405180910390fd5b919050565b606061166e61164a8686868661154f565b60405160200161165a9190614a59565b604051602081830303815290604052612c55565b60405160200161167e9190614ad2565b6040516020818303038152906040529050949350505050565b600073ffffffffffffffffffffffffffffffffffffffff16601960009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1603611728576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161171f9061450d565b60405180910390fd5b60011515601b600085815260200190815260200160002060009054906101000a900460ff1615151461178f576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016117869061459f565b60405180910390fd5b601960009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff161461181f576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016118169061460b565b60405180910390fd5b60008061186f84848080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f82011690508083019250505050505050612a97565b509150915081600b600087815260200190815260200160002060000160006101000a81548160ff021916908360ff16021790555080600b600087815260200190815260200160002060000160016101000a81548160ff021916908360ff1602179055505050505050565b60008073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff1603611949576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161194090614b40565b60405180910390fd5b600360008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020549050919050565b6001805461199d906142f7565b80601f01602080910402602001604051908101604052809291908181526020018280546119c9906142f7565b8015611a165780601f106119eb57610100808354040283529160200191611a16565b820191906000526020600020905b8154815290600101906020018083116119f957829003601f168201915b505050505081565b80600560003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548160ff0219169083151502179055508173ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167f17307eab39ab6107e8899845ad3d59bd9653f200f220920489ca2b5937696c3183604051611b0f9190613a51565b60405180910390a35050565b611b2483612450565b611b2e85846124ac565b611b388484612619565b611b458585858585612dcd565b5050505050565b606060006040518061046001604052806104368152602001615ac461043691399050600061510067ffffffffffffffff811115611b8c57611b8b614b60565b5b6040519080825280601f01601f191660200182016040528015611bbe5781602001600182028036833780820191505090505b50905060005b6090811015611cb25760005b6090811015611ca0576000816090611be89190614bbe565b83611bf39190614c18565b9050600a60008b60ff1681526020019081526020016000206000018360908110611c2057611c1f614492565b5b600502018260908110611c3657611c35614492565b5b602091828204019190069054906101000a900460f81b848281518110611c5f57611c5e614492565b5b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a9053505080611c9990614c6e565b9050611bd0565b5080611cab90614c6e565b9050611bc4565b506001151584151503611d2a5760008560ff166090611cd19190614bbe565b8760ff16611cdf9190614c18565b905060a560f81b828281518110611cf957611cf8614492565b5b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a905350505b611d3d8183612f0b90919063ffffffff16565b92505050949350505050565b600f54600e5410611d8f576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401611d8690614d02565b60405180910390fd5b60011515611d9c83610d19565b151514611dde576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401611dd5906143e0565b60405180910390fd5b601860009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614611e6e576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401611e6590614d6e565b60405180910390fd5b60016004600e54611e7f9190614dbd565b611e899190614c18565b600d81905550600060106004600e54611ea29190614dee565b60048110611eb357611eb2614492565b5b01549050600060146004600e54611eca9190614dee565b60048110611edb57611eda614492565b5b01549050611eeb85600e54612619565b611ef6600e54612f95565b611f0285600e54612fde565b611f15600e5483836001600d54896131f0565b8373ffffffffffffffffffffffffffffffffffffffff1916600e547f66e34e24596794eb9fbba61ddf385dbbc89563ac1f0250e6587c2ac6c039481d600d54604051611f619190613dc0565b60405180910390a36000831115611f7f57611f7e600e5484613354565b5b6000601b6000600e54815260200190815260200160002060006101000a81548160ff0219169083151502179055506001600e54611fbc9190614c18565b600e819055505050505050565b600073ffffffffffffffffffffffffffffffffffffffff16601960009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff160361205a576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016120519061450d565b60405180910390fd5b6120638161158e565b73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16146120d0576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016120c790614374565b60405180910390fd5b6001601b600083815260200190815260200160002060006101000a81548160ff02191690831515021790555050565b60606121e282600b600085815260200190815260200160002060000160039054906101000a900460ff16600b600086815260200190815260200160002060000160059054906101000a900460a01b600b600087815260200190815260200160002060000160049054906101000a900460f81b600b600088815260200190815260200160002060000160009054906101000a900460ff16600b600089815260200190815260200160002060000160019054906101000a900460ff16600b60008a815260200190815260200160002060000160029054906101000a900460ff1661362c565b9050919050565b60606121f9826000806000611639565b9050919050565b600d5481565b6000600a600083815260200190815260200160002060000184609081106122305761222f614492565b5b60050201836090811061224657612245614492565b5b602091828204019190069054906101000a900460f81b90509392505050565b6122868160006002811061227c5761227b614492565b5b602002013561158e565b73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16146122f3576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016122ea90614374565b60405180910390fd5b60008160016002811061230957612308614492565b5b6020020135111561234f5761234e8160006002811061232b5761232a614492565b5b60200201358260016002811061234457612343614492565b5b6020020135613354565b5b50565b60056020528160005260406000206020528060005260406000206000915091509054906101000a900460ff1681565b6060600080600080602067ffffffffffffffff8111156123a4576123a3614b60565b5b6040519080825280601f01601f1916602001820160405280156123d65781602001600182028036833780820191505090505b5090506000600b600088815260200190815260200160002090508054945060ff8516935060ff8560081c169250600061242e8560ff168560ff16600160048c61241f9190614dbd565b6124299190614c18565b612206565b9050846020840153836021840153806022840152829650505050505050919050565b60011515601b600083815260200190815260200160002060009054906101000a900460ff161515036124a9576000601b600083815260200190815260200160002060006101000a81548160ff0219169083151502179055505b50565b600060016124b9846118d9565b6124c39190614e1f565b90506000600760008481526020019081526020016000205490508181146125a8576000600660008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600084815260200190815260200160002054905080600660008773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600084815260200190815260200160002081905550816007600083815260200190815260200160002081905550505b6007600084815260200190815260200160002060009055600660008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008381526020019081526020016000206000905550505050565b6000612624836118d9565b905081600660008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600083815260200190815260200160002081905550806007600084815260200190815260200160002081905550505050565b6002600082815260200190815260200160002060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff1614612739576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161273090614e9f565b60405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff16036127a8576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161279f90614f0b565b60405180910390fd5b8273ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614806128685750600560008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff165b806128d157506004600082815260200190815260200160002060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16145b612910576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161290790614374565b60405180910390fd5b600360008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000815480929190600190039190505550600360008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008154809291906001019190505550816002600083815260200190815260200160002060006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055506004600082815260200190815260200160002060006101000a81549073ffffffffffffffffffffffffffffffffffffffff0219169055808273ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef60405160405180910390a4505050565b60008060006001840151925060028401519150602284015190509193909250565b81600a60008381526020019081526020016000206000018560908110612ae157612ae0614492565b5b600502018460908110612af757612af6614492565b5b602091828204019190066101000a81548160ff021916908360f81c021790555050505050565b612b28838383610c41565b60008273ffffffffffffffffffffffffffffffffffffffff163b1480612c11575063150b7a0260e01b7bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19168273ffffffffffffffffffffffffffffffffffffffff1663150b7a023386856040518463ffffffff1660e01b8152600401612bad93929190614f51565b6020604051808303816000875af1158015612bcc573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612bf09190614fb0565b7bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916145b612c50576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401612c4790615029565b60405180910390fd5b505050565b60606000825103612c7757604051806020016040528060008152509050612dc8565b6000604051806060016040528060408152602001615a846040913990506000600360028551612ca69190614c18565b612cb09190614dbd565b6004612cbc9190614bbe565b90506000602082612ccd9190614c18565b67ffffffffffffffff811115612ce657612ce5614b60565b5b6040519080825280601f01601f191660200182016040528015612d185781602001600182028036833780820191505090505b509050818152600183018586518101602084015b81831015612d87576003830192508251603f8160121c168501518253600182019150603f81600c1c168501518253600182019150603f8160061c168501518253600182019150603f8116850151825360018201915050612d2c565b600389510660018114612da15760028114612db157612dbc565b613d3d60f01b6002830352612dbc565b603d60f81b60018303525b50505050508093505050505b919050565b612dd8858585610c41565b60008473ffffffffffffffffffffffffffffffffffffffff163b1480612ec5575063150b7a0260e01b7bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19168473ffffffffffffffffffffffffffffffffffffffff1663150b7a0233888787876040518663ffffffff1660e01b8152600401612e61959493929190615085565b6020604051808303816000875af1158015612e80573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612ea49190614fb0565b7bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916145b612f04576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401612efb90615029565b60405180910390fd5b5050505050565b6060806040519050835180825260208201818101602087015b81831015612f415780518352602083019250602081019050612f24565b50855192508351830184528091508282019050602086015b81831015612f765780518352602083019250602081019050612f59565b50601f19601f8851850115830101166040525050508091505092915050565b6008805490506009600083815260200190815260200160002081905550600881908060018154018082558091505060019003906000526020600020016000909190919091505550565b600073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff160361304d576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161304490614f0b565b60405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff166002600083815260200190815260200160002060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16146130ef576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016130e69061511f565b60405180910390fd5b600360008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008154809291906001019190505550816002600083815260200190815260200160002060006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550808273ffffffffffffffffffffffffffffffffffffffff16600073ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef60405160405180910390a45050565b60006040518060c001604052808760ff1681526020018660ff1681526020018560ff1681526020018460ff168152602001827effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff191681526020018373ffffffffffffffffffffffffffffffffffffffff1916815250600b600089815260200190815260200160002060008201518160000160006101000a81548160ff021916908360ff16021790555060208201518160000160016101000a81548160ff021916908360ff16021790555060408201518160000160026101000a81548160ff021916908360ff16021790555060608201518160000160036101000a81548160ff021916908360ff16021790555060808201518160000160046101000a81548160ff021916908360f81c021790555060a08201518160000160056101000a8154816bffffffffffffffffffffffff021916908360a01c021790555090505050505050505050565b600080600080600080600b600089815260200190815260200160002090508054915060005b878110156135ec5760ff8316955060ff8360081c1694508260181c935060006133ac8760ff168760ff168760ff16612206565b905070ffffffffffffffffffffffff0000000000841660ff8560101c16600080600064ff00000000891660201c1460008660f81c141615613400578360781b9b5060ff8460781c16905060ff8460701c1691505b600064ff00000000891660201c1460ff8660f81c141615613434578360901b9b5060ff8460601c16905060ff8460581c1691505b600164ff00000000891660201c1460008660f81c141615613468578360a81b9b5060ff8460481c16905060ff8460401c1691505b600164ff00000000891660201c1460ff8660f81c14161561349c578360c01b9b5060ff8460301c16905060ff8460281c1691505b600081600281146134bf57600881146134cc57600481146134ee578491506134f8565b60046001860891506134f8565b84600081146134e3576004600187030692506134e8565b600392505b506134f8565b6004600286010691505b50604051816000811461352257600281146135385760038114613569576001811461357f576135ac565b609060018f08601f8301538c601e8301536135ac565b8d600081146135555760018f03601f8401538d601e840153613563565b608f601f8401538d601e8401535b506135ac565b8d601f830153609060018e08601e8301536135ac565b8c6000811461359c578e601f84015360018e03601e8401536135aa565b8e601f840153608f601e8401535b505b5081601d8201538a601c82015383601b8201538581511799505050505050506135e08760ff168760ff168a8860ff16612ab8565b60018201915050613379565b50818155868360ff16897fbbe0aa2504840f3cabeaa7c222bc975ed888abf937945ff374653c8d57c55fbb60405160405180910390a45050505050505050565b606061367c61363b89896136a8565b6136488987876001611639565b6136568a8a8a8a8a8a6136e7565b60405160200161366893929190615353565b604051602081830303815290604052612c55565b60405160200161368c919061541d565b6040516020818303038152906040529050979650505050505050565b60606136b383613764565b6136bf8360ff16613764565b6040516020016136d09291906154d7565b604051602081830303815290604052905092915050565b60606136f58760ff16613764565b6136fe876137b4565b61370d8760f81c60ff16613764565b6137198760ff16613764565b6137258760ff16613764565b6137318760ff16613764565b600c60405160200161374997969594939291906158c7565b60405160208183030381529060405290509695505050505050565b606060a060405101806040526020810391506000825281835b60011561379f57600184039350600a81066030018453600a810490508061377d575b50828103602084039350808452505050919050565b6060600080601867ffffffffffffffff8111156137d4576137d3614b60565b5b6040519080825280601f01601f1916602001820160405280156138065781602001600182028036833780820191505090505b509050600091505b80518260ff161015613960576000600f60f81b8560028561382f91906159f2565b60ff16600c811061384357613842614492565b5b1a60f81b1660f81c9050600060048660028661385f91906159f2565b60ff16600c811061387357613872614492565b5b1a60f81b7effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916901c60f81c90506138a98161396a565b838560ff16815181106138bf576138be614492565b5b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a9053506001846138fb9190615a23565b93506139068261396a565b838560ff168151811061391c5761391b614492565b5b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a9053505050818061395890615a5a565b92505061380e565b8092505050919050565b6000600a8260ff16101561398f576030826139859190615a23565b60f81b90506139a2565b60578261399c9190615a23565b60f81b90505b919050565b600080fd5b600080fd5b60007fffffffff0000000000000000000000000000000000000000000000000000000082169050919050565b6139e6816139b1565b81146139f157600080fd5b50565b600081359050613a03816139dd565b92915050565b600060208284031215613a1f57613a1e6139a7565b5b6000613a2d848285016139f4565b91505092915050565b60008115159050919050565b613a4b81613a36565b82525050565b6000602082019050613a666000830184613a42565b92915050565b600081519050919050565b600082825260208201905092915050565b60005b83811015613aa6578082015181840152602081019050613a8b565b83811115613ab5576000848401525b50505050565b6000601f19601f8301169050919050565b6000613ad782613a6c565b613ae18185613a77565b9350613af1818560208601613a88565b613afa81613abb565b840191505092915050565b60006020820190508181036000830152613b1f8184613acc565b905092915050565b6000819050919050565b613b3a81613b27565b8114613b4557600080fd5b50565b600081359050613b5781613b31565b92915050565b600060208284031215613b7357613b726139a7565b5b6000613b8184828501613b48565b91505092915050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b6000613bb582613b8a565b9050919050565b613bc581613baa565b82525050565b6000602082019050613be06000830184613bbc565b92915050565b613bef81613baa565b8114613bfa57600080fd5b50565b600081359050613c0c81613be6565b92915050565b60008060408385031215613c2957613c286139a7565b5b6000613c3785828601613bfd565b9250506020613c4885828601613b48565b9150509250929050565b60007fffffffffffffffffffffffff000000000000000000000000000000000000000082169050919050565b613c8781613c52565b8114613c9257600080fd5b50565b600081359050613ca481613c7e565b92915050565b60008060408385031215613cc157613cc06139a7565b5b6000613ccf85828601613b48565b9250506020613ce085828601613c95565b9150509250929050565b600060ff82169050919050565b613d0081613cea565b82525050565b60007fff0000000000000000000000000000000000000000000000000000000000000082169050919050565b613d3b81613d06565b82525050565b613d4a81613c52565b82525050565b600060c082019050613d656000830189613cf7565b613d726020830188613cf7565b613d7f6040830187613cf7565b613d8c6060830186613cf7565b613d996080830185613d32565b613da660a0830184613d41565b979650505050505050565b613dba81613b27565b82525050565b6000602082019050613dd56000830184613db1565b92915050565b600080600060608486031215613df457613df36139a7565b5b6000613e0286828701613bfd565b9350506020613e1386828701613bfd565b9250506040613e2486828701613b48565b9150509250925092565b600060208284031215613e4457613e436139a7565b5b6000613e5284828501613c95565b91505092915050565b600080fd5b600080fd5b600080fd5b60008083601f840112613e8057613e7f613e5b565b5b8235905067ffffffffffffffff811115613e9d57613e9c613e60565b5b602083019150836001820283011115613eb957613eb8613e65565b5b9250929050565b600080600060408486031215613ed957613ed86139a7565b5b6000613ee786828701613b48565b935050602084013567ffffffffffffffff811115613f0857613f076139ac565b5b613f1486828701613e6a565b92509250509250925092565b600060208284031215613f3657613f356139a7565b5b6000613f4484828501613bfd565b91505092915050565b613f5681613cea565b8114613f6157600080fd5b50565b600081359050613f7381613f4d565b92915050565b613f8281613a36565b8114613f8d57600080fd5b50565b600081359050613f9f81613f79565b92915050565b60008060008060808587031215613fbf57613fbe6139a7565b5b6000613fcd87828801613f64565b9450506020613fde87828801613f64565b9350506040613fef87828801613f64565b925050606061400087828801613f90565b91505092959194509250565b60008060408385031215614023576140226139a7565b5b600061403185828601613bfd565b925050602061404285828601613f90565b9150509250929050565b600080600080600060808688031215614068576140676139a7565b5b600061407688828901613bfd565b955050602061408788828901613bfd565b945050604061409888828901613b48565b935050606086013567ffffffffffffffff8111156140b9576140b86139ac565b5b6140c588828901613e6a565b92509250509295509295909350565b600081519050919050565b600082825260208201905092915050565b60006140fb826140d4565b61410581856140df565b9350614115818560208601613a88565b61411e81613abb565b840191505092915050565b6000602082019050818103600083015261414381846140f0565b905092915050565b600080600060608486031215614164576141636139a7565b5b600061417286828701613bfd565b935050602061418386828701613c95565b925050604061419486828701613b48565b9150509250925092565b6000602082840312156141b4576141b36139a7565b5b60006141c284828501613f64565b91505092915050565b6000806000606084860312156141e4576141e36139a7565b5b60006141f286828701613b48565b935050602061420386828701613b48565b925050604061421486828701613b48565b9150509250925092565b60006020820190506142336000830184613d32565b92915050565b60008190508260206002028201111561425557614254613e65565b5b92915050565b600060408284031215614271576142706139a7565b5b600061427f84828501614239565b91505092915050565b6000806040838503121561429f5761429e6139a7565b5b60006142ad85828601613bfd565b92505060206142be85828601613bfd565b9150509250929050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b6000600282049050600182168061430f57607f821691505b602082108103614322576143216142c8565b5b50919050565b7f4e4f545f415554484f52495a4544000000000000000000000000000000000000600082015250565b600061435e600e83613a77565b915061436982614328565b602082019050919050565b6000602082019050818103600083015261438d81614351565b9050919050565b7f494e56414c49445f52554c450000000000000000000000000000000000000000600082015250565b60006143ca600c83613a77565b91506143d582614394565b602082019050919050565b600060208201905081810360008301526143f9816143bd565b9050919050565b7f455243373231456e756d657261626c653a206f776e657220696e646578206f7560008201527f74206f6620626f756e6473000000000000000000000000000000000000000000602082015250565b600061445c602b83613a77565b915061446782614400565b604082019050919050565b6000602082019050818103600083015261448b8161444f565b9050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b7f434f4e54524143545f49535f5a45524f41444452455353000000000000000000600082015250565b60006144f7601783613a77565b9150614502826144c1565b602082019050919050565b60006020820190508181036000830152614526816144ea565b9050919050565b7f434f4e54524143545f4e4f545f494e4954414c495a45445f42595f4e46545f4f60008201527f574e455200000000000000000000000000000000000000000000000000000000602082015250565b6000614589602483613a77565b91506145948261452d565b604082019050919050565b600060208201905081810360008301526145b88161457c565b9050919050565b7f43414c4c5f4f4e4c595f46524f4d5f4841434f4e545241435400000000000000600082015250565b60006145f5601983613a77565b9150614600826145bf565b602082019050919050565b60006020820190508181036000830152614624816145e8565b9050919050565b7f455243373231456e756d657261626c653a20676c6f62616c20696e646578206f60008201527f7574206f6620626f756e64730000000000000000000000000000000000000000602082015250565b6000614687602c83613a77565b91506146928261462b565b604082019050919050565b600060208201905081810360008301526146b68161467a565b9050919050565b600081905092915050565b7f646174613a696d6167652f626d703b6261736536342c00000000000000000000600082015250565b60006146fe6016836146bd565b9150614709826146c8565b601682019050919050565b600061471f82613a6c565b61472981856146bd565b9350614739818560208601613a88565b80840191505092915050565b6000614750826146f1565b915061475c8284614714565b915081905092915050565b7f4e4f545f4d494e54454400000000000000000000000000000000000000000000600082015250565b600061479d600a83613a77565b91506147a882614767565b602082019050919050565b600060208201905081810360008301526147cc81614790565b9050919050565b7f3c73766720636c6173733d227376674247472220786d6c6e733d22687474703a60008201527f2f2f7777772e77332e6f72672f323030302f737667222076657273696f6e3d2260208201527f312e31222077696474683d2235303022206865696768743d22353030223e3c6460408201527f6566732069643d22736f6d6544656673223e3c7374796c652069643d2273747960608201527f6c6531393939223e202e737667424747207b2077696474683a2035303070783b60808201527f6865696768743a2035303070783b6261636b67726f756e642d696d6167653a2060a08201527f75726c280000000000000000000000000000000000000000000000000000000060c082015250565b60006148ed60c4836146bd565b91506148f8826147d3565b60c482019050919050565b7f293b206261636b67726f756e642d7265706561743a206e6f2d7265706561743b60008201527f206261636b67726f756e642d73697a653a20313030253b20696d6167652d726560208201527f6e646572696e673a202d7765626b69742d6f7074696d697a652d636f6e74726160408201527f73743b202d6d732d696e746572706f6c6174696f6e2d6d6f64653a206e65617260608201527f6573742d6e65696768626f723b20696d6167652d72656e646572696e673a202d60808201527f6d6f7a2d63726973702d65646765733b20696d6167652d72656e646572696e6760a08201527f3a20706978656c617465643b7d3c2f7374796c653e3c2f646566733e3c2f737660c08201527f673e00000000000000000000000000000000000000000000000000000000000060e082015250565b6000614a4360e2836146bd565b9150614a4e82614903565b60e282019050919050565b6000614a64826148e0565b9150614a708284614714565b9150614a7b82614a36565b915081905092915050565b7f646174613a696d6167652f7376672b786d6c3b6261736536342c000000000000600082015250565b6000614abc601a836146bd565b9150614ac782614a86565b601a82019050919050565b6000614add82614aaf565b9150614ae98284614714565b915081905092915050565b7f5a45524f5f414444524553530000000000000000000000000000000000000000600082015250565b6000614b2a600c83613a77565b9150614b3582614af4565b602082019050919050565b60006020820190508181036000830152614b5981614b1d565b9050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b6000614bc982613b27565b9150614bd483613b27565b9250817fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0483118215151615614c0d57614c0c614b8f565b5b828202905092915050565b6000614c2382613b27565b9150614c2e83613b27565b9250827fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff03821115614c6357614c62614b8f565b5b828201905092915050565b6000614c7982613b27565b91507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8203614cab57614caa614b8f565b5b600182019050919050565b7f4d494e545f4f5645520000000000000000000000000000000000000000000000600082015250565b6000614cec600983613a77565b9150614cf782614cb6565b602082019050919050565b60006020820190508181036000830152614d1b81614cdf565b9050919050565b7f4f4e4c595f4d494e5441424c455f46524f4d5f4d494e545f434f4e5452414354600082015250565b6000614d58602083613a77565b9150614d6382614d22565b602082019050919050565b60006020820190508181036000830152614d8781614d4b565b9050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b6000614dc882613b27565b9150614dd383613b27565b925082614de357614de2614d8e565b5b828204905092915050565b6000614df982613b27565b9150614e0483613b27565b925082614e1457614e13614d8e565b5b828206905092915050565b6000614e2a82613b27565b9150614e3583613b27565b925082821015614e4857614e47614b8f565b5b828203905092915050565b7f57524f4e475f46524f4d00000000000000000000000000000000000000000000600082015250565b6000614e89600a83613a77565b9150614e9482614e53565b602082019050919050565b60006020820190508181036000830152614eb881614e7c565b9050919050565b7f494e56414c49445f524543495049454e54000000000000000000000000000000600082015250565b6000614ef5601183613a77565b9150614f0082614ebf565b602082019050919050565b60006020820190508181036000830152614f2481614ee8565b9050919050565b50565b6000614f3b6000836140df565b9150614f4682614f2b565b600082019050919050565b6000608082019050614f666000830186613bbc565b614f736020830185613bbc565b614f806040830184613db1565b8181036060830152614f9181614f2e565b9050949350505050565b600081519050614faa816139dd565b92915050565b600060208284031215614fc657614fc56139a7565b5b6000614fd484828501614f9b565b91505092915050565b7f554e534146455f524543495049454e5400000000000000000000000000000000600082015250565b6000615013601083613a77565b915061501e82614fdd565b602082019050919050565b6000602082019050818103600083015261504281615006565b9050919050565b82818337600083830152505050565b600061506483856140df565b9350615071838584615049565b61507a83613abb565b840190509392505050565b600060808201905061509a6000830188613bbc565b6150a76020830187613bbc565b6150b46040830186613db1565b81810360608301526150c7818486615058565b90509695505050505050565b7f414c52454144595f4d494e544544000000000000000000000000000000000000600082015250565b6000615109600e83613a77565b9150615114826150d3565b602082019050919050565b60006020820190508181036000830152615138816150fc565b9050919050565b7f7b226e616d65223a220000000000000000000000000000000000000000000000600082015250565b60006151756009836146bd565b91506151808261513f565b600982019050919050565b7f222c20226465736372697074696f6e223a220000000000000000000000000000600082015250565b60006151c16012836146bd565b91506151cc8261518b565b601282019050919050565b7f4f6e636861696e204d757469706c617965722041727400000000000000000000600082015250565b600061520d6016836146bd565b9150615218826151d7565b601682019050919050565b7f222c2022696d616765223a202200000000000000000000000000000000000000600082015250565b6000615259600d836146bd565b915061526482615223565b600d82019050919050565b7f222c000000000000000000000000000000000000000000000000000000000000600082015250565b60006152a56002836146bd565b91506152b08261526f565b600282019050919050565b7f2261747472696275746573223a20000000000000000000000000000000000000600082015250565b60006152f1600e836146bd565b91506152fc826152bb565b600e82019050919050565b7f7d00000000000000000000000000000000000000000000000000000000000000600082015250565b600061533d6001836146bd565b915061534882615307565b600182019050919050565b600061535e82615168565b915061536a8286614714565b9150615375826151b4565b915061538082615200565b915061538b8261524c565b91506153978285614714565b91506153a282615298565b91506153ad826152e4565b91506153b98284614714565b91506153c482615330565b9150819050949350505050565b7f646174613a6170706c69636174696f6e2f6a736f6e3b6261736536342c000000600082015250565b6000615407601d836146bd565b9150615412826153d1565b601d82019050919050565b6000615428826153fa565b91506154348284614714565b915081905092915050565b7f5475726d69746520000000000000000000000000000000000000000000000000600082015250565b60006154756008836146bd565b91506154808261543f565b600882019050919050565b7f20576f726c642000000000000000000000000000000000000000000000000000600082015250565b60006154c16007836146bd565b91506154cc8261548b565b600782019050919050565b60006154e282615468565b91506154ee8285614714565b91506154f9826154b4565b91506155058284614714565b91508190509392505050565b7f5b7b2274726169745f74797065223a22576f726c64222c2276616c7565223a22600082015250565b60006155476020836146bd565b915061555282615511565b602082019050919050565b7f227d2c0000000000000000000000000000000000000000000000000000000000600082015250565b60006155936003836146bd565b915061559e8261555d565b600382019050919050565b7f7b2274726169745f74797065223a2252756c65222c0000000000000000000000600082015250565b60006155df6015836146bd565b91506155ea826155a9565b601582019050919050565b7f2276616c7565223a220000000000000000000000000000000000000000000000600082015250565b600061562b6009836146bd565b9150615636826155f5565b600982019050919050565b7f7b2274726169745f74797065223a225374617465222c00000000000000000000600082015250565b60006156776016836146bd565b915061568282615641565b601682019050919050565b7f7b2274726169745f74797065223a22504f532058222c00000000000000000000600082015250565b60006156c36016836146bd565b91506156ce8261568d565b601682019050919050565b7f7b2274726169745f74797065223a22504f532059222c00000000000000000000600082015250565b600061570f6016836146bd565b915061571a826156d9565b601682019050919050565b7f7b2274726169745f74797065223a22446972656374696f6e222c000000000000600082015250565b600061575b601a836146bd565b915061576682615725565b601a82019050919050565b7f227d2c7b2274726169745f74797065223a224e6574776f726b222c2276616c7560008201527f65223a2200000000000000000000000000000000000000000000000000000000602082015250565b60006157cd6024836146bd565b91506157d882615771565b602482019050919050565b60008190508160005260206000209050919050565b60008154615805816142f7565b61580f81866146bd565b9450600182166000811461582a576001811461583f57615872565b60ff1983168652811515820286019350615872565b615848856157e3565b60005b8381101561586a5781548189015260018201915060208101905061584b565b838801955050505b50505092915050565b7f227d5d0000000000000000000000000000000000000000000000000000000000600082015250565b60006158b16003836146bd565b91506158bc8261587b565b600382019050919050565b60006158d28261553a565b91506158de828a614714565b91506158e982615586565b91506158f4826155d2565b91506158ff8261561e565b915061590b8289614714565b915061591682615586565b91506159218261566a565b915061592c8261561e565b91506159388288614714565b915061594382615586565b915061594e826156b6565b91506159598261561e565b91506159658287614714565b915061597082615586565b915061597b82615702565b91506159868261561e565b91506159928286614714565b915061599d82615586565b91506159a88261574e565b91506159b38261561e565b91506159bf8285614714565b91506159ca826157c0565b91506159d682846157f8565b91506159e1826158a4565b915081905098975050505050505050565b60006159fd82613cea565b9150615a0883613cea565b925082615a1857615a17614d8e565b5b828204905092915050565b6000615a2e82613cea565b9150615a3983613cea565b92508260ff03821115615a4f57615a4e614b8f565b5b828201905092915050565b6000615a6582613cea565b915060ff8203615a7857615a77614b8f565b5b60018201905091905056fe4142434445464748494a4b4c4d4e4f505152535455565758595a6162636465666768696a6b6c6d6e6f707172737475767778797a303132333435363738392b2f424d385500000000000036040000280000009000000090000000010008000000000002510000120b0000120b00000000000000000000000000000101010002020200030303000404040005050500060606000707070008080800090909000a0a0a000b0b0b000c0c0c000d0d0d000e0e0e000f0f0f00101010001111110012121200131313001414140015151500161616001717170018181800191919001a1a1a001b1b1b001c1c1c001d1d1d001e1e1e001f1f1f00202020002121210022222200232323002424240025252500262626002727270028282800292929002a2a2a002b2b2b002c2c2c002d2d2d002e2e2e002f2f2f00303030003131310032323200333333003434340035353500363636003737370038383800393939003a3a3a003b3b3b003c3c3c003d3d3d003e3e3e003f3f3f00404040004141410042424200434343004444440045454500464646004747470048484800494949004a4a4a004b4b4b004c4c4c004d4d4d004e4e4e004f4f4f00505050005151510052525200535353005454540055555500565656005757570058585800595959005a5a5a005b5b5b005c5c5c005d5d5d005e5e5e005f5f5f00606060006161610062626200636363006464640065656500666666006767670068686800696969006a6a6a006b6b6b006c6c6c006d6d6d006e6e6e006f6f6f00707070007171710072727200737373007474740075757500767676007777770078787800797979007a7a7a007b7b7b007c7c7c007d7d7d007e7e7e007f7f7f00808080008181810082828200838383008484840085858500868686008787870088888800898989008a8a8a008b8b8b008c8c8c008d8d8d008e8e8e008f8f8f00909090009191910092929200939393009494940095959500969696009797970098989800999999009a9a9a009b9b9b009c9c9c009d9d9d009e9e9e009f9f9f00a0a0a000a1a1a100a2a2a200a3a3a300a4a4a400a5a5a500a6a6a600a7a7a700a8a8a800a9a9a900aaaaaa00ababab00acacac00adadad00aeaeae00afafaf00b0b0b000b1b1b100b2b2b200b3b3b300b4b4b400b5b5b500b6b6b600b7b7b700b8b8b800b9b9b900bababa00bbbbbb00bcbcbc00bdbdbd00bebebe00bfbfbf00c0c0c000c1c1c100c2c2c200c3c3c300c4c4c400c5c5c500c6c6c600c7c7c700c8c8c800c9c9c900cacaca00cbcbcb00cccccc00cdcdcd00cecece00cfcfcf00d0d0d000d1d1d100d2d2d200d3d3d300d4d4d400d5d5d500d6d6d600d7d7d700d8d8d800d9d9d900dadada00dbdbdb00dcdcdc00dddddd00dedede00dfdfdf00e0e0e000e1e1e100e2e2e200e3e3e300e4e4e400e5e5e500e6e6e600e7e7e700e8e8e800e9e9e900eaeaea00ebebeb00ececec00ededed00eeeeee00efefef00f0f0f000f1f1f100f2f2f200f3f3f300f4f4f400f5f5f500f6f6f600f7f7f700f8f8f800f9f9f900fafafa00fbfbfb00fcfcfc00fdfdfd00fefefe00ffffff00a2646970667358221220792abd84e85ae7339fc7811867b48b0df27583811acd0908f337fe392af9356364736f6c634300080f0033000000000000000000000000b955c66ff031cea247ff22be1fdbeae23977d9d70000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000084f7074696d69736d000000000000000000000000000000000000000000000000

Deployed Bytecode

0x608060405234801561001057600080fd5b50600436106102065760003560e01c80636352211e1161011a578063c2a8d57c116100ad578063d3158b021161007c578063d3158b021461065c578063e524b3071461067a578063e8354445146106aa578063e985e9c5146106c6578063ef7af2fc146106f657610206565b8063c2a8d57c146105c4578063c61a3c84146105e0578063c87b56dd146105fc578063c966409d1461062c57610206565b806395d89b41116100e957806395d89b411461053e578063a22cb4651461055c578063b88d4fde14610578578063be76bf8f1461059457610206565b80636352211e1461049257806369dbd7d7146104c25780636c5453a6146104f257806370a082311461050e57610206565b80632cc4837f1161019d57806340cb8af91161016c57806340cb8af9146103ca57806342842e0e146103fa5780634f6ccce714610416578063518a8802146104465780635e8c32111461046257610206565b80632cc4837f146103305780632f745c591461034e57806335faee731461037e5780633aedf55c146103ae57610206565b80631034fc0d116101d95780631034fc0d146102a557806313f36fc1146102c157806318160ddd146102f657806323b872dd1461031457610206565b806301ffc9a71461020b57806306fdde031461023b578063081812fc14610259578063095ea7b314610289575b600080fd5b61022560048036038101906102209190613a09565b610726565b6040516102329190613a51565b60405180910390f35b6102436107b8565b6040516102509190613b05565b60405180910390f35b610273600480360381019061026e9190613b5d565b610846565b6040516102809190613bcb565b60405180910390f35b6102a3600480360381019061029e9190613c12565b610879565b005b6102bf60048036038101906102ba9190613caa565b610a62565b005b6102db60048036038101906102d69190613b5d565b610baa565b6040516102ed96959493929190613d50565b60405180910390f35b6102fe610c34565b60405161030b9190613dc0565b60405180910390f35b61032e60048036038101906103299190613ddb565b610c41565b005b610338610c6e565b6040516103459190613dc0565b60405180910390f35b61036860048036038101906103639190613c12565b610c74565b6040516103759190613dc0565b60405180910390f35b61039860048036038101906103939190613e2e565b610d19565b6040516103a59190613a51565b60405180910390f35b6103c860048036038101906103c39190613ec0565b61119d565b005b6103e460048036038101906103df9190613b5d565b6113bd565b6040516103f19190613a51565b60405180910390f35b610414600480360381019061040f9190613ddb565b6113dd565b005b610430600480360381019061042b9190613b5d565b61140a565b60405161043d9190613dc0565b60405180910390f35b610460600480360381019061045b9190613f20565b61147b565b005b61047c60048036038101906104779190613fa5565b61154f565b6040516104899190613b05565b60405180910390f35b6104ac60048036038101906104a79190613b5d565b61158e565b6040516104b99190613bcb565b60405180910390f35b6104dc60048036038101906104d79190613fa5565b611639565b6040516104e99190613b05565b60405180910390f35b61050c60048036038101906105079190613ec0565b611697565b005b61052860048036038101906105239190613f20565b6118d9565b6040516105359190613dc0565b60405180910390f35b610546611990565b6040516105539190613b05565b60405180910390f35b6105766004803603810190610571919061400c565b611a1e565b005b610592600480360381019061058d919061404c565b611b1b565b005b6105ae60048036038101906105a99190613fa5565b611b4c565b6040516105bb9190614129565b60405180910390f35b6105de60048036038101906105d9919061414b565b611d49565b005b6105fa60048036038101906105f59190613b5d565b611fc9565b005b61061660048036038101906106119190613b5d565b6120ff565b6040516106239190613b05565b60405180910390f35b6106466004803603810190610641919061419e565b6121e9565b6040516106539190613b05565b60405180910390f35b610664612200565b6040516106719190613dc0565b60405180910390f35b610694600480360381019061068f91906141cb565b612206565b6040516106a1919061421e565b60405180910390f35b6106c460048036038101906106bf919061425b565b612265565b005b6106e060048036038101906106db9190614288565b612352565b6040516106ed9190613a51565b60405180910390f35b610710600480360381019061070b9190613b5d565b612381565b60405161071d9190614129565b60405180910390f35b60006301ffc9a760e01b827bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916148061078157506380ac58cd60e01b827bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916145b806107b15750635b5e139f60e01b827bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916145b9050919050565b600080546107c5906142f7565b80601f01602080910402602001604051908101604052809291908181526020018280546107f1906142f7565b801561083e5780601f106108135761010080835404028352916020019161083e565b820191906000526020600020905b81548152906001019060200180831161082157829003601f168201915b505050505081565b60046020528060005260406000206000915054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b60006002600083815260200190815260200160002060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1690508073ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614806109715750600560008273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff165b6109b0576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016109a790614374565b60405180910390fd5b826004600084815260200190815260200160002060006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550818373ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92560405160405180910390a4505050565b610a6b8261158e565b73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614610ad8576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610acf90614374565b60405180910390fd5b60011515610ae582610d19565b151514610b27576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610b1e906143e0565b60405180910390fd5b80600b600084815260200190815260200160002060000160056101000a8154816bffffffffffffffffffffffff021916908360a01c02179055508073ffffffffffffffffffffffffffffffffffffffff1916827fd1464fd210d58c95f829eeb54351bb3b0f9cf4277440f5627f822489a0548cd360405160405180910390a35050565b600b6020528060005260406000206000915090508060000160009054906101000a900460ff16908060000160019054906101000a900460ff16908060000160029054906101000a900460ff16908060000160039054906101000a900460ff16908060000160049054906101000a900460f81b908060000160059054906101000a900460a01b905086565b6000600880549050905090565b610c4a81612450565b610c5483826124ac565b610c5e8282612619565b610c69838383612698565b505050565b600f5481565b6000610c7f836118d9565b8210610cc0576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610cb790614472565b60405180910390fd5b600660008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600083815260200190815260200160002054905092915050565b60008060ff60f81b836000600c8110610d3557610d34614492565b5b1a60f81b7effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff19161480610da35750600060f81b836000600c8110610d7b57610d7a614492565b5b1a60f81b7effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916145b8015610e32575060ff60f81b836003600c8110610dc357610dc2614492565b5b1a60f81b7effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff19161480610e315750600060f81b836003600c8110610e0957610e08614492565b5b1a60f81b7effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916145b5b8015610ec1575060ff60f81b836006600c8110610e5257610e51614492565b5b1a60f81b7effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff19161480610ec05750600060f81b836006600c8110610e9857610e97614492565b5b1a60f81b7effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916145b5b8015610f50575060ff60f81b836009600c8110610ee157610ee0614492565b5b1a60f81b7effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff19161480610f4f5750600060f81b836009600c8110610f2757610f26614492565b5b1a60f81b7effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916145b5b90506000600160f81b846002600c8110610f6d57610f6c614492565b5b1a60f81b7effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff19161480610fdb5750600060f81b846002600c8110610fb357610fb2614492565b5b1a60f81b7effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916145b801561106a5750600160f81b846005600c8110610ffb57610ffa614492565b5b1a60f81b7effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff191614806110695750600060f81b846005600c811061104157611040614492565b5b1a60f81b7effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916145b5b80156110f95750600160f81b846008600c811061108a57611089614492565b5b1a60f81b7effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff191614806110f85750600060f81b846008600c81106110d0576110cf614492565b5b1a60f81b7effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916145b5b80156111885750600160f81b84600b600c811061111957611118614492565b5b1a60f81b7effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff191614806111875750600060f81b84600b600c811061115f5761115e614492565b5b1a60f81b7effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916145b5b90508080156111945750815b92505050919050565b600073ffffffffffffffffffffffffffffffffffffffff16601960009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff160361122e576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016112259061450d565b60405180910390fd5b60011515601b600085815260200190815260200160002060009054906101000a900460ff16151514611295576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161128c9061459f565b60405180910390fd5b601960009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614611325576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161131c9061460b565b60405180910390fd5b600080600061137785858080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f82011690508083019250505050505050612a97565b9250925092506113b58360ff168360ff1683600b60008b815260200190815260200160002060000160039054906101000a900460ff1660ff16612ab8565b505050505050565b601b6020528060005260406000206000915054906101000a900460ff1681565b6113e681612450565b6113f083826124ac565b6113fa8282612619565b611405838383612b1d565b505050565b6000611414610c34565b8210611455576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161144c9061469d565b60405180910390fd5b6008828154811061146957611468614492565b5b90600052602060002001549050919050565b601a60009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff161461150b576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161150290614374565b60405180910390fd5b80601960006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050565b606061156561156086868686611b4c565b612c55565b6040516020016115759190614745565b6040516020818303038152906040529050949350505050565b60008073ffffffffffffffffffffffffffffffffffffffff166002600084815260200190815260200160002060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1691508173ffffffffffffffffffffffffffffffffffffffff1603611634576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161162b906147b3565b60405180910390fd5b919050565b606061166e61164a8686868661154f565b60405160200161165a9190614a59565b604051602081830303815290604052612c55565b60405160200161167e9190614ad2565b6040516020818303038152906040529050949350505050565b600073ffffffffffffffffffffffffffffffffffffffff16601960009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1603611728576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161171f9061450d565b60405180910390fd5b60011515601b600085815260200190815260200160002060009054906101000a900460ff1615151461178f576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016117869061459f565b60405180910390fd5b601960009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff161461181f576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016118169061460b565b60405180910390fd5b60008061186f84848080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f82011690508083019250505050505050612a97565b509150915081600b600087815260200190815260200160002060000160006101000a81548160ff021916908360ff16021790555080600b600087815260200190815260200160002060000160016101000a81548160ff021916908360ff1602179055505050505050565b60008073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff1603611949576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161194090614b40565b60405180910390fd5b600360008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020549050919050565b6001805461199d906142f7565b80601f01602080910402602001604051908101604052809291908181526020018280546119c9906142f7565b8015611a165780601f106119eb57610100808354040283529160200191611a16565b820191906000526020600020905b8154815290600101906020018083116119f957829003601f168201915b505050505081565b80600560003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548160ff0219169083151502179055508173ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167f17307eab39ab6107e8899845ad3d59bd9653f200f220920489ca2b5937696c3183604051611b0f9190613a51565b60405180910390a35050565b611b2483612450565b611b2e85846124ac565b611b388484612619565b611b458585858585612dcd565b5050505050565b606060006040518061046001604052806104368152602001615ac461043691399050600061510067ffffffffffffffff811115611b8c57611b8b614b60565b5b6040519080825280601f01601f191660200182016040528015611bbe5781602001600182028036833780820191505090505b50905060005b6090811015611cb25760005b6090811015611ca0576000816090611be89190614bbe565b83611bf39190614c18565b9050600a60008b60ff1681526020019081526020016000206000018360908110611c2057611c1f614492565b5b600502018260908110611c3657611c35614492565b5b602091828204019190069054906101000a900460f81b848281518110611c5f57611c5e614492565b5b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a9053505080611c9990614c6e565b9050611bd0565b5080611cab90614c6e565b9050611bc4565b506001151584151503611d2a5760008560ff166090611cd19190614bbe565b8760ff16611cdf9190614c18565b905060a560f81b828281518110611cf957611cf8614492565b5b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a905350505b611d3d8183612f0b90919063ffffffff16565b92505050949350505050565b600f54600e5410611d8f576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401611d8690614d02565b60405180910390fd5b60011515611d9c83610d19565b151514611dde576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401611dd5906143e0565b60405180910390fd5b601860009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614611e6e576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401611e6590614d6e565b60405180910390fd5b60016004600e54611e7f9190614dbd565b611e899190614c18565b600d81905550600060106004600e54611ea29190614dee565b60048110611eb357611eb2614492565b5b01549050600060146004600e54611eca9190614dee565b60048110611edb57611eda614492565b5b01549050611eeb85600e54612619565b611ef6600e54612f95565b611f0285600e54612fde565b611f15600e5483836001600d54896131f0565b8373ffffffffffffffffffffffffffffffffffffffff1916600e547f66e34e24596794eb9fbba61ddf385dbbc89563ac1f0250e6587c2ac6c039481d600d54604051611f619190613dc0565b60405180910390a36000831115611f7f57611f7e600e5484613354565b5b6000601b6000600e54815260200190815260200160002060006101000a81548160ff0219169083151502179055506001600e54611fbc9190614c18565b600e819055505050505050565b600073ffffffffffffffffffffffffffffffffffffffff16601960009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff160361205a576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016120519061450d565b60405180910390fd5b6120638161158e565b73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16146120d0576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016120c790614374565b60405180910390fd5b6001601b600083815260200190815260200160002060006101000a81548160ff02191690831515021790555050565b60606121e282600b600085815260200190815260200160002060000160039054906101000a900460ff16600b600086815260200190815260200160002060000160059054906101000a900460a01b600b600087815260200190815260200160002060000160049054906101000a900460f81b600b600088815260200190815260200160002060000160009054906101000a900460ff16600b600089815260200190815260200160002060000160019054906101000a900460ff16600b60008a815260200190815260200160002060000160029054906101000a900460ff1661362c565b9050919050565b60606121f9826000806000611639565b9050919050565b600d5481565b6000600a600083815260200190815260200160002060000184609081106122305761222f614492565b5b60050201836090811061224657612245614492565b5b602091828204019190069054906101000a900460f81b90509392505050565b6122868160006002811061227c5761227b614492565b5b602002013561158e565b73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16146122f3576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016122ea90614374565b60405180910390fd5b60008160016002811061230957612308614492565b5b6020020135111561234f5761234e8160006002811061232b5761232a614492565b5b60200201358260016002811061234457612343614492565b5b6020020135613354565b5b50565b60056020528160005260406000206020528060005260406000206000915091509054906101000a900460ff1681565b6060600080600080602067ffffffffffffffff8111156123a4576123a3614b60565b5b6040519080825280601f01601f1916602001820160405280156123d65781602001600182028036833780820191505090505b5090506000600b600088815260200190815260200160002090508054945060ff8516935060ff8560081c169250600061242e8560ff168560ff16600160048c61241f9190614dbd565b6124299190614c18565b612206565b9050846020840153836021840153806022840152829650505050505050919050565b60011515601b600083815260200190815260200160002060009054906101000a900460ff161515036124a9576000601b600083815260200190815260200160002060006101000a81548160ff0219169083151502179055505b50565b600060016124b9846118d9565b6124c39190614e1f565b90506000600760008481526020019081526020016000205490508181146125a8576000600660008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600084815260200190815260200160002054905080600660008773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600084815260200190815260200160002081905550816007600083815260200190815260200160002081905550505b6007600084815260200190815260200160002060009055600660008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008381526020019081526020016000206000905550505050565b6000612624836118d9565b905081600660008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600083815260200190815260200160002081905550806007600084815260200190815260200160002081905550505050565b6002600082815260200190815260200160002060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff1614612739576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161273090614e9f565b60405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff16036127a8576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161279f90614f0b565b60405180910390fd5b8273ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614806128685750600560008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff165b806128d157506004600082815260200190815260200160002060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16145b612910576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161290790614374565b60405180910390fd5b600360008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000815480929190600190039190505550600360008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008154809291906001019190505550816002600083815260200190815260200160002060006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055506004600082815260200190815260200160002060006101000a81549073ffffffffffffffffffffffffffffffffffffffff0219169055808273ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef60405160405180910390a4505050565b60008060006001840151925060028401519150602284015190509193909250565b81600a60008381526020019081526020016000206000018560908110612ae157612ae0614492565b5b600502018460908110612af757612af6614492565b5b602091828204019190066101000a81548160ff021916908360f81c021790555050505050565b612b28838383610c41565b60008273ffffffffffffffffffffffffffffffffffffffff163b1480612c11575063150b7a0260e01b7bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19168273ffffffffffffffffffffffffffffffffffffffff1663150b7a023386856040518463ffffffff1660e01b8152600401612bad93929190614f51565b6020604051808303816000875af1158015612bcc573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612bf09190614fb0565b7bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916145b612c50576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401612c4790615029565b60405180910390fd5b505050565b60606000825103612c7757604051806020016040528060008152509050612dc8565b6000604051806060016040528060408152602001615a846040913990506000600360028551612ca69190614c18565b612cb09190614dbd565b6004612cbc9190614bbe565b90506000602082612ccd9190614c18565b67ffffffffffffffff811115612ce657612ce5614b60565b5b6040519080825280601f01601f191660200182016040528015612d185781602001600182028036833780820191505090505b509050818152600183018586518101602084015b81831015612d87576003830192508251603f8160121c168501518253600182019150603f81600c1c168501518253600182019150603f8160061c168501518253600182019150603f8116850151825360018201915050612d2c565b600389510660018114612da15760028114612db157612dbc565b613d3d60f01b6002830352612dbc565b603d60f81b60018303525b50505050508093505050505b919050565b612dd8858585610c41565b60008473ffffffffffffffffffffffffffffffffffffffff163b1480612ec5575063150b7a0260e01b7bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19168473ffffffffffffffffffffffffffffffffffffffff1663150b7a0233888787876040518663ffffffff1660e01b8152600401612e61959493929190615085565b6020604051808303816000875af1158015612e80573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612ea49190614fb0565b7bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916145b612f04576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401612efb90615029565b60405180910390fd5b5050505050565b6060806040519050835180825260208201818101602087015b81831015612f415780518352602083019250602081019050612f24565b50855192508351830184528091508282019050602086015b81831015612f765780518352602083019250602081019050612f59565b50601f19601f8851850115830101166040525050508091505092915050565b6008805490506009600083815260200190815260200160002081905550600881908060018154018082558091505060019003906000526020600020016000909190919091505550565b600073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff160361304d576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161304490614f0b565b60405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff166002600083815260200190815260200160002060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16146130ef576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016130e69061511f565b60405180910390fd5b600360008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008154809291906001019190505550816002600083815260200190815260200160002060006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550808273ffffffffffffffffffffffffffffffffffffffff16600073ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef60405160405180910390a45050565b60006040518060c001604052808760ff1681526020018660ff1681526020018560ff1681526020018460ff168152602001827effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff191681526020018373ffffffffffffffffffffffffffffffffffffffff1916815250600b600089815260200190815260200160002060008201518160000160006101000a81548160ff021916908360ff16021790555060208201518160000160016101000a81548160ff021916908360ff16021790555060408201518160000160026101000a81548160ff021916908360ff16021790555060608201518160000160036101000a81548160ff021916908360ff16021790555060808201518160000160046101000a81548160ff021916908360f81c021790555060a08201518160000160056101000a8154816bffffffffffffffffffffffff021916908360a01c021790555090505050505050505050565b600080600080600080600b600089815260200190815260200160002090508054915060005b878110156135ec5760ff8316955060ff8360081c1694508260181c935060006133ac8760ff168760ff168760ff16612206565b905070ffffffffffffffffffffffff0000000000841660ff8560101c16600080600064ff00000000891660201c1460008660f81c141615613400578360781b9b5060ff8460781c16905060ff8460701c1691505b600064ff00000000891660201c1460ff8660f81c141615613434578360901b9b5060ff8460601c16905060ff8460581c1691505b600164ff00000000891660201c1460008660f81c141615613468578360a81b9b5060ff8460481c16905060ff8460401c1691505b600164ff00000000891660201c1460ff8660f81c14161561349c578360c01b9b5060ff8460301c16905060ff8460281c1691505b600081600281146134bf57600881146134cc57600481146134ee578491506134f8565b60046001860891506134f8565b84600081146134e3576004600187030692506134e8565b600392505b506134f8565b6004600286010691505b50604051816000811461352257600281146135385760038114613569576001811461357f576135ac565b609060018f08601f8301538c601e8301536135ac565b8d600081146135555760018f03601f8401538d601e840153613563565b608f601f8401538d601e8401535b506135ac565b8d601f830153609060018e08601e8301536135ac565b8c6000811461359c578e601f84015360018e03601e8401536135aa565b8e601f840153608f601e8401535b505b5081601d8201538a601c82015383601b8201538581511799505050505050506135e08760ff168760ff168a8860ff16612ab8565b60018201915050613379565b50818155868360ff16897fbbe0aa2504840f3cabeaa7c222bc975ed888abf937945ff374653c8d57c55fbb60405160405180910390a45050505050505050565b606061367c61363b89896136a8565b6136488987876001611639565b6136568a8a8a8a8a8a6136e7565b60405160200161366893929190615353565b604051602081830303815290604052612c55565b60405160200161368c919061541d565b6040516020818303038152906040529050979650505050505050565b60606136b383613764565b6136bf8360ff16613764565b6040516020016136d09291906154d7565b604051602081830303815290604052905092915050565b60606136f58760ff16613764565b6136fe876137b4565b61370d8760f81c60ff16613764565b6137198760ff16613764565b6137258760ff16613764565b6137318760ff16613764565b600c60405160200161374997969594939291906158c7565b60405160208183030381529060405290509695505050505050565b606060a060405101806040526020810391506000825281835b60011561379f57600184039350600a81066030018453600a810490508061377d575b50828103602084039350808452505050919050565b6060600080601867ffffffffffffffff8111156137d4576137d3614b60565b5b6040519080825280601f01601f1916602001820160405280156138065781602001600182028036833780820191505090505b509050600091505b80518260ff161015613960576000600f60f81b8560028561382f91906159f2565b60ff16600c811061384357613842614492565b5b1a60f81b1660f81c9050600060048660028661385f91906159f2565b60ff16600c811061387357613872614492565b5b1a60f81b7effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916901c60f81c90506138a98161396a565b838560ff16815181106138bf576138be614492565b5b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a9053506001846138fb9190615a23565b93506139068261396a565b838560ff168151811061391c5761391b614492565b5b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a9053505050818061395890615a5a565b92505061380e565b8092505050919050565b6000600a8260ff16101561398f576030826139859190615a23565b60f81b90506139a2565b60578261399c9190615a23565b60f81b90505b919050565b600080fd5b600080fd5b60007fffffffff0000000000000000000000000000000000000000000000000000000082169050919050565b6139e6816139b1565b81146139f157600080fd5b50565b600081359050613a03816139dd565b92915050565b600060208284031215613a1f57613a1e6139a7565b5b6000613a2d848285016139f4565b91505092915050565b60008115159050919050565b613a4b81613a36565b82525050565b6000602082019050613a666000830184613a42565b92915050565b600081519050919050565b600082825260208201905092915050565b60005b83811015613aa6578082015181840152602081019050613a8b565b83811115613ab5576000848401525b50505050565b6000601f19601f8301169050919050565b6000613ad782613a6c565b613ae18185613a77565b9350613af1818560208601613a88565b613afa81613abb565b840191505092915050565b60006020820190508181036000830152613b1f8184613acc565b905092915050565b6000819050919050565b613b3a81613b27565b8114613b4557600080fd5b50565b600081359050613b5781613b31565b92915050565b600060208284031215613b7357613b726139a7565b5b6000613b8184828501613b48565b91505092915050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b6000613bb582613b8a565b9050919050565b613bc581613baa565b82525050565b6000602082019050613be06000830184613bbc565b92915050565b613bef81613baa565b8114613bfa57600080fd5b50565b600081359050613c0c81613be6565b92915050565b60008060408385031215613c2957613c286139a7565b5b6000613c3785828601613bfd565b9250506020613c4885828601613b48565b9150509250929050565b60007fffffffffffffffffffffffff000000000000000000000000000000000000000082169050919050565b613c8781613c52565b8114613c9257600080fd5b50565b600081359050613ca481613c7e565b92915050565b60008060408385031215613cc157613cc06139a7565b5b6000613ccf85828601613b48565b9250506020613ce085828601613c95565b9150509250929050565b600060ff82169050919050565b613d0081613cea565b82525050565b60007fff0000000000000000000000000000000000000000000000000000000000000082169050919050565b613d3b81613d06565b82525050565b613d4a81613c52565b82525050565b600060c082019050613d656000830189613cf7565b613d726020830188613cf7565b613d7f6040830187613cf7565b613d8c6060830186613cf7565b613d996080830185613d32565b613da660a0830184613d41565b979650505050505050565b613dba81613b27565b82525050565b6000602082019050613dd56000830184613db1565b92915050565b600080600060608486031215613df457613df36139a7565b5b6000613e0286828701613bfd565b9350506020613e1386828701613bfd565b9250506040613e2486828701613b48565b9150509250925092565b600060208284031215613e4457613e436139a7565b5b6000613e5284828501613c95565b91505092915050565b600080fd5b600080fd5b600080fd5b60008083601f840112613e8057613e7f613e5b565b5b8235905067ffffffffffffffff811115613e9d57613e9c613e60565b5b602083019150836001820283011115613eb957613eb8613e65565b5b9250929050565b600080600060408486031215613ed957613ed86139a7565b5b6000613ee786828701613b48565b935050602084013567ffffffffffffffff811115613f0857613f076139ac565b5b613f1486828701613e6a565b92509250509250925092565b600060208284031215613f3657613f356139a7565b5b6000613f4484828501613bfd565b91505092915050565b613f5681613cea565b8114613f6157600080fd5b50565b600081359050613f7381613f4d565b92915050565b613f8281613a36565b8114613f8d57600080fd5b50565b600081359050613f9f81613f79565b92915050565b60008060008060808587031215613fbf57613fbe6139a7565b5b6000613fcd87828801613f64565b9450506020613fde87828801613f64565b9350506040613fef87828801613f64565b925050606061400087828801613f90565b91505092959194509250565b60008060408385031215614023576140226139a7565b5b600061403185828601613bfd565b925050602061404285828601613f90565b9150509250929050565b600080600080600060808688031215614068576140676139a7565b5b600061407688828901613bfd565b955050602061408788828901613bfd565b945050604061409888828901613b48565b935050606086013567ffffffffffffffff8111156140b9576140b86139ac565b5b6140c588828901613e6a565b92509250509295509295909350565b600081519050919050565b600082825260208201905092915050565b60006140fb826140d4565b61410581856140df565b9350614115818560208601613a88565b61411e81613abb565b840191505092915050565b6000602082019050818103600083015261414381846140f0565b905092915050565b600080600060608486031215614164576141636139a7565b5b600061417286828701613bfd565b935050602061418386828701613c95565b925050604061419486828701613b48565b9150509250925092565b6000602082840312156141b4576141b36139a7565b5b60006141c284828501613f64565b91505092915050565b6000806000606084860312156141e4576141e36139a7565b5b60006141f286828701613b48565b935050602061420386828701613b48565b925050604061421486828701613b48565b9150509250925092565b60006020820190506142336000830184613d32565b92915050565b60008190508260206002028201111561425557614254613e65565b5b92915050565b600060408284031215614271576142706139a7565b5b600061427f84828501614239565b91505092915050565b6000806040838503121561429f5761429e6139a7565b5b60006142ad85828601613bfd565b92505060206142be85828601613bfd565b9150509250929050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b6000600282049050600182168061430f57607f821691505b602082108103614322576143216142c8565b5b50919050565b7f4e4f545f415554484f52495a4544000000000000000000000000000000000000600082015250565b600061435e600e83613a77565b915061436982614328565b602082019050919050565b6000602082019050818103600083015261438d81614351565b9050919050565b7f494e56414c49445f52554c450000000000000000000000000000000000000000600082015250565b60006143ca600c83613a77565b91506143d582614394565b602082019050919050565b600060208201905081810360008301526143f9816143bd565b9050919050565b7f455243373231456e756d657261626c653a206f776e657220696e646578206f7560008201527f74206f6620626f756e6473000000000000000000000000000000000000000000602082015250565b600061445c602b83613a77565b915061446782614400565b604082019050919050565b6000602082019050818103600083015261448b8161444f565b9050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b7f434f4e54524143545f49535f5a45524f41444452455353000000000000000000600082015250565b60006144f7601783613a77565b9150614502826144c1565b602082019050919050565b60006020820190508181036000830152614526816144ea565b9050919050565b7f434f4e54524143545f4e4f545f494e4954414c495a45445f42595f4e46545f4f60008201527f574e455200000000000000000000000000000000000000000000000000000000602082015250565b6000614589602483613a77565b91506145948261452d565b604082019050919050565b600060208201905081810360008301526145b88161457c565b9050919050565b7f43414c4c5f4f4e4c595f46524f4d5f4841434f4e545241435400000000000000600082015250565b60006145f5601983613a77565b9150614600826145bf565b602082019050919050565b60006020820190508181036000830152614624816145e8565b9050919050565b7f455243373231456e756d657261626c653a20676c6f62616c20696e646578206f60008201527f7574206f6620626f756e64730000000000000000000000000000000000000000602082015250565b6000614687602c83613a77565b91506146928261462b565b604082019050919050565b600060208201905081810360008301526146b68161467a565b9050919050565b600081905092915050565b7f646174613a696d6167652f626d703b6261736536342c00000000000000000000600082015250565b60006146fe6016836146bd565b9150614709826146c8565b601682019050919050565b600061471f82613a6c565b61472981856146bd565b9350614739818560208601613a88565b80840191505092915050565b6000614750826146f1565b915061475c8284614714565b915081905092915050565b7f4e4f545f4d494e54454400000000000000000000000000000000000000000000600082015250565b600061479d600a83613a77565b91506147a882614767565b602082019050919050565b600060208201905081810360008301526147cc81614790565b9050919050565b7f3c73766720636c6173733d227376674247472220786d6c6e733d22687474703a60008201527f2f2f7777772e77332e6f72672f323030302f737667222076657273696f6e3d2260208201527f312e31222077696474683d2235303022206865696768743d22353030223e3c6460408201527f6566732069643d22736f6d6544656673223e3c7374796c652069643d2273747960608201527f6c6531393939223e202e737667424747207b2077696474683a2035303070783b60808201527f6865696768743a2035303070783b6261636b67726f756e642d696d6167653a2060a08201527f75726c280000000000000000000000000000000000000000000000000000000060c082015250565b60006148ed60c4836146bd565b91506148f8826147d3565b60c482019050919050565b7f293b206261636b67726f756e642d7265706561743a206e6f2d7265706561743b60008201527f206261636b67726f756e642d73697a653a20313030253b20696d6167652d726560208201527f6e646572696e673a202d7765626b69742d6f7074696d697a652d636f6e74726160408201527f73743b202d6d732d696e746572706f6c6174696f6e2d6d6f64653a206e65617260608201527f6573742d6e65696768626f723b20696d6167652d72656e646572696e673a202d60808201527f6d6f7a2d63726973702d65646765733b20696d6167652d72656e646572696e6760a08201527f3a20706978656c617465643b7d3c2f7374796c653e3c2f646566733e3c2f737660c08201527f673e00000000000000000000000000000000000000000000000000000000000060e082015250565b6000614a4360e2836146bd565b9150614a4e82614903565b60e282019050919050565b6000614a64826148e0565b9150614a708284614714565b9150614a7b82614a36565b915081905092915050565b7f646174613a696d6167652f7376672b786d6c3b6261736536342c000000000000600082015250565b6000614abc601a836146bd565b9150614ac782614a86565b601a82019050919050565b6000614add82614aaf565b9150614ae98284614714565b915081905092915050565b7f5a45524f5f414444524553530000000000000000000000000000000000000000600082015250565b6000614b2a600c83613a77565b9150614b3582614af4565b602082019050919050565b60006020820190508181036000830152614b5981614b1d565b9050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b6000614bc982613b27565b9150614bd483613b27565b9250817fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0483118215151615614c0d57614c0c614b8f565b5b828202905092915050565b6000614c2382613b27565b9150614c2e83613b27565b9250827fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff03821115614c6357614c62614b8f565b5b828201905092915050565b6000614c7982613b27565b91507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8203614cab57614caa614b8f565b5b600182019050919050565b7f4d494e545f4f5645520000000000000000000000000000000000000000000000600082015250565b6000614cec600983613a77565b9150614cf782614cb6565b602082019050919050565b60006020820190508181036000830152614d1b81614cdf565b9050919050565b7f4f4e4c595f4d494e5441424c455f46524f4d5f4d494e545f434f4e5452414354600082015250565b6000614d58602083613a77565b9150614d6382614d22565b602082019050919050565b60006020820190508181036000830152614d8781614d4b565b9050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b6000614dc882613b27565b9150614dd383613b27565b925082614de357614de2614d8e565b5b828204905092915050565b6000614df982613b27565b9150614e0483613b27565b925082614e1457614e13614d8e565b5b828206905092915050565b6000614e2a82613b27565b9150614e3583613b27565b925082821015614e4857614e47614b8f565b5b828203905092915050565b7f57524f4e475f46524f4d00000000000000000000000000000000000000000000600082015250565b6000614e89600a83613a77565b9150614e9482614e53565b602082019050919050565b60006020820190508181036000830152614eb881614e7c565b9050919050565b7f494e56414c49445f524543495049454e54000000000000000000000000000000600082015250565b6000614ef5601183613a77565b9150614f0082614ebf565b602082019050919050565b60006020820190508181036000830152614f2481614ee8565b9050919050565b50565b6000614f3b6000836140df565b9150614f4682614f2b565b600082019050919050565b6000608082019050614f666000830186613bbc565b614f736020830185613bbc565b614f806040830184613db1565b8181036060830152614f9181614f2e565b9050949350505050565b600081519050614faa816139dd565b92915050565b600060208284031215614fc657614fc56139a7565b5b6000614fd484828501614f9b565b91505092915050565b7f554e534146455f524543495049454e5400000000000000000000000000000000600082015250565b6000615013601083613a77565b915061501e82614fdd565b602082019050919050565b6000602082019050818103600083015261504281615006565b9050919050565b82818337600083830152505050565b600061506483856140df565b9350615071838584615049565b61507a83613abb565b840190509392505050565b600060808201905061509a6000830188613bbc565b6150a76020830187613bbc565b6150b46040830186613db1565b81810360608301526150c7818486615058565b90509695505050505050565b7f414c52454144595f4d494e544544000000000000000000000000000000000000600082015250565b6000615109600e83613a77565b9150615114826150d3565b602082019050919050565b60006020820190508181036000830152615138816150fc565b9050919050565b7f7b226e616d65223a220000000000000000000000000000000000000000000000600082015250565b60006151756009836146bd565b91506151808261513f565b600982019050919050565b7f222c20226465736372697074696f6e223a220000000000000000000000000000600082015250565b60006151c16012836146bd565b91506151cc8261518b565b601282019050919050565b7f4f6e636861696e204d757469706c617965722041727400000000000000000000600082015250565b600061520d6016836146bd565b9150615218826151d7565b601682019050919050565b7f222c2022696d616765223a202200000000000000000000000000000000000000600082015250565b6000615259600d836146bd565b915061526482615223565b600d82019050919050565b7f222c000000000000000000000000000000000000000000000000000000000000600082015250565b60006152a56002836146bd565b91506152b08261526f565b600282019050919050565b7f2261747472696275746573223a20000000000000000000000000000000000000600082015250565b60006152f1600e836146bd565b91506152fc826152bb565b600e82019050919050565b7f7d00000000000000000000000000000000000000000000000000000000000000600082015250565b600061533d6001836146bd565b915061534882615307565b600182019050919050565b600061535e82615168565b915061536a8286614714565b9150615375826151b4565b915061538082615200565b915061538b8261524c565b91506153978285614714565b91506153a282615298565b91506153ad826152e4565b91506153b98284614714565b91506153c482615330565b9150819050949350505050565b7f646174613a6170706c69636174696f6e2f6a736f6e3b6261736536342c000000600082015250565b6000615407601d836146bd565b9150615412826153d1565b601d82019050919050565b6000615428826153fa565b91506154348284614714565b915081905092915050565b7f5475726d69746520000000000000000000000000000000000000000000000000600082015250565b60006154756008836146bd565b91506154808261543f565b600882019050919050565b7f20576f726c642000000000000000000000000000000000000000000000000000600082015250565b60006154c16007836146bd565b91506154cc8261548b565b600782019050919050565b60006154e282615468565b91506154ee8285614714565b91506154f9826154b4565b91506155058284614714565b91508190509392505050565b7f5b7b2274726169745f74797065223a22576f726c64222c2276616c7565223a22600082015250565b60006155476020836146bd565b915061555282615511565b602082019050919050565b7f227d2c0000000000000000000000000000000000000000000000000000000000600082015250565b60006155936003836146bd565b915061559e8261555d565b600382019050919050565b7f7b2274726169745f74797065223a2252756c65222c0000000000000000000000600082015250565b60006155df6015836146bd565b91506155ea826155a9565b601582019050919050565b7f2276616c7565223a220000000000000000000000000000000000000000000000600082015250565b600061562b6009836146bd565b9150615636826155f5565b600982019050919050565b7f7b2274726169745f74797065223a225374617465222c00000000000000000000600082015250565b60006156776016836146bd565b915061568282615641565b601682019050919050565b7f7b2274726169745f74797065223a22504f532058222c00000000000000000000600082015250565b60006156c36016836146bd565b91506156ce8261568d565b601682019050919050565b7f7b2274726169745f74797065223a22504f532059222c00000000000000000000600082015250565b600061570f6016836146bd565b915061571a826156d9565b601682019050919050565b7f7b2274726169745f74797065223a22446972656374696f6e222c000000000000600082015250565b600061575b601a836146bd565b915061576682615725565b601a82019050919050565b7f227d2c7b2274726169745f74797065223a224e6574776f726b222c2276616c7560008201527f65223a2200000000000000000000000000000000000000000000000000000000602082015250565b60006157cd6024836146bd565b91506157d882615771565b602482019050919050565b60008190508160005260206000209050919050565b60008154615805816142f7565b61580f81866146bd565b9450600182166000811461582a576001811461583f57615872565b60ff1983168652811515820286019350615872565b615848856157e3565b60005b8381101561586a5781548189015260018201915060208101905061584b565b838801955050505b50505092915050565b7f227d5d0000000000000000000000000000000000000000000000000000000000600082015250565b60006158b16003836146bd565b91506158bc8261587b565b600382019050919050565b60006158d28261553a565b91506158de828a614714565b91506158e982615586565b91506158f4826155d2565b91506158ff8261561e565b915061590b8289614714565b915061591682615586565b91506159218261566a565b915061592c8261561e565b91506159388288614714565b915061594382615586565b915061594e826156b6565b91506159598261561e565b91506159658287614714565b915061597082615586565b915061597b82615702565b91506159868261561e565b91506159928286614714565b915061599d82615586565b91506159a88261574e565b91506159b38261561e565b91506159bf8285614714565b91506159ca826157c0565b91506159d682846157f8565b91506159e1826158a4565b915081905098975050505050505050565b60006159fd82613cea565b9150615a0883613cea565b925082615a1857615a17614d8e565b5b828204905092915050565b6000615a2e82613cea565b9150615a3983613cea565b92508260ff03821115615a4f57615a4e614b8f565b5b828201905092915050565b6000615a6582613cea565b915060ff8203615a7857615a77614b8f565b5b60018201905091905056fe4142434445464748494a4b4c4d4e4f505152535455565758595a6162636465666768696a6b6c6d6e6f707172737475767778797a303132333435363738392b2f424d385500000000000036040000280000009000000090000000010008000000000002510000120b0000120b00000000000000000000000000000101010002020200030303000404040005050500060606000707070008080800090909000a0a0a000b0b0b000c0c0c000d0d0d000e0e0e000f0f0f00101010001111110012121200131313001414140015151500161616001717170018181800191919001a1a1a001b1b1b001c1c1c001d1d1d001e1e1e001f1f1f00202020002121210022222200232323002424240025252500262626002727270028282800292929002a2a2a002b2b2b002c2c2c002d2d2d002e2e2e002f2f2f00303030003131310032323200333333003434340035353500363636003737370038383800393939003a3a3a003b3b3b003c3c3c003d3d3d003e3e3e003f3f3f00404040004141410042424200434343004444440045454500464646004747470048484800494949004a4a4a004b4b4b004c4c4c004d4d4d004e4e4e004f4f4f00505050005151510052525200535353005454540055555500565656005757570058585800595959005a5a5a005b5b5b005c5c5c005d5d5d005e5e5e005f5f5f00606060006161610062626200636363006464640065656500666666006767670068686800696969006a6a6a006b6b6b006c6c6c006d6d6d006e6e6e006f6f6f00707070007171710072727200737373007474740075757500767676007777770078787800797979007a7a7a007b7b7b007c7c7c007d7d7d007e7e7e007f7f7f00808080008181810082828200838383008484840085858500868686008787870088888800898989008a8a8a008b8b8b008c8c8c008d8d8d008e8e8e008f8f8f00909090009191910092929200939393009494940095959500969696009797970098989800999999009a9a9a009b9b9b009c9c9c009d9d9d009e9e9e009f9f9f00a0a0a000a1a1a100a2a2a200a3a3a300a4a4a400a5a5a500a6a6a600a7a7a700a8a8a800a9a9a900aaaaaa00ababab00acacac00adadad00aeaeae00afafaf00b0b0b000b1b1b100b2b2b200b3b3b300b4b4b400b5b5b500b6b6b600b7b7b700b8b8b800b9b9b900bababa00bbbbbb00bcbcbc00bdbdbd00bebebe00bfbfbf00c0c0c000c1c1c100c2c2c200c3c3c300c4c4c400c5c5c500c6c6c600c7c7c700c8c8c800c9c9c900cacaca00cbcbcb00cccccc00cdcdcd00cecece00cfcfcf00d0d0d000d1d1d100d2d2d200d3d3d300d4d4d400d5d5d500d6d6d600d7d7d700d8d8d800d9d9d900dadada00dbdbdb00dcdcdc00dddddd00dedede00dfdfdf00e0e0e000e1e1e100e2e2e200e3e3e300e4e4e400e5e5e500e6e6e600e7e7e700e8e8e800e9e9e900eaeaea00ebebeb00ececec00ededed00eeeeee00efefef00f0f0f000f1f1f100f2f2f200f3f3f300f4f4f400f5f5f500f6f6f600f7f7f700f8f8f800f9f9f900fafafa00fbfbfb00fcfcfc00fdfdfd00fefefe00ffffff00a2646970667358221220792abd84e85ae7339fc7811867b48b0df27583811acd0908f337fe392af9356364736f6c634300080f0033

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

000000000000000000000000b955c66ff031cea247ff22be1fdbeae23977d9d70000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000084f7074696d69736d000000000000000000000000000000000000000000000000

-----Decoded View---------------
Arg [0] : _minterContract (address): 0xb955c66FF031cEA247ff22bE1fDBeAE23977d9d7
Arg [1] : maxAmount (uint256): 1024
Arg [2] : network (string): Optimism

-----Encoded View---------------
5 Constructor Arguments found :
Arg [0] : 000000000000000000000000b955c66ff031cea247ff22be1fdbeae23977d9d7
Arg [1] : 0000000000000000000000000000000000000000000000000000000000000400
Arg [2] : 0000000000000000000000000000000000000000000000000000000000000060
Arg [3] : 0000000000000000000000000000000000000000000000000000000000000008
Arg [4] : 4f7074696d69736d000000000000000000000000000000000000000000000000


Loading...
Loading
[ 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.