ETH Price: $3,791.05 (-1.41%)

Contract

0xa769FA308Db9eAF53dB36EAf1fbcBa4A57E387b9
 

Overview

ETH Balance

0 ETH

Eth Value

$0.00

Multichain Info

No addresses found
Transaction Hash
Method
Block
From
To
Set Approval For...176085942023-07-02 20:06:47522 days ago1688328407IN
Check Turtz: CHECKTURTZ Token
0 ETH0.0007552412.66824298
Set Approval For...167776022023-03-07 16:26:23639 days ago1678206383IN
Check Turtz: CHECKTURTZ Token
0 ETH0.0023288339.06330723
Set Approval For...165995592023-02-10 16:30:11664 days ago1676046611IN
Check Turtz: CHECKTURTZ Token
0 ETH0.0020214533.8596381
Set Approval For...165992542023-02-10 15:28:23664 days ago1676042903IN
Check Turtz: CHECKTURTZ Token
0 ETH0.0017566529.46571398
Mint165984072023-02-10 12:38:11664 days ago1676032691IN
Check Turtz: CHECKTURTZ Token
0 ETH0.0032626524.28003083
Mint165983402023-02-10 12:24:47664 days ago1676031887IN
Check Turtz: CHECKTURTZ Token
0 ETH0.0032158523.93173868
Mint165979562023-02-10 11:07:47664 days ago1676027267IN
Check Turtz: CHECKTURTZ Token
0 ETH0.0032232618
Mint165977172023-02-10 10:19:35664 days ago1676024375IN
Check Turtz: CHECKTURTZ Token
0 ETH0.0029772322.15602948
Mint165977112023-02-10 10:18:23664 days ago1676024303IN
Check Turtz: CHECKTURTZ Token
0 ETH0.0038392721.44007045
Mint165975262023-02-10 9:41:23664 days ago1676022083IN
Check Turtz: CHECKTURTZ Token
0 ETH0.0029211221.73845003
Mint165975222023-02-10 9:40:35664 days ago1676022035IN
Check Turtz: CHECKTURTZ Token
0 ETH0.0040209422.45461955
Mint165973732023-02-10 9:10:23664 days ago1676020223IN
Check Turtz: CHECKTURTZ Token
0 ETH0.0042749123.87288526
Mint165973692023-02-10 9:09:35664 days ago1676020175IN
Check Turtz: CHECKTURTZ Token
0 ETH0.0031410423.37503387
Mint165972672023-02-10 8:49:11664 days ago1676018951IN
Check Turtz: CHECKTURTZ Token
0 ETH0.002774920.6503366
Mint165972612023-02-10 8:47:59664 days ago1676018879IN
Check Turtz: CHECKTURTZ Token
0 ETH0.0028517921.2224769
Mint165970562023-02-10 8:06:59664 days ago1676016419IN
Check Turtz: CHECKTURTZ Token
0 ETH0.0040940630.46720328
Mint165970012023-02-10 7:55:59664 days ago1676015759IN
Check Turtz: CHECKTURTZ Token
0 ETH0.0028138720.9403005
Set Approval For...165963892023-02-10 5:52:59664 days ago1676008379IN
Check Turtz: CHECKTURTZ Token
0 ETH0.0011455519.21529601
Mint165962832023-02-10 5:31:35664 days ago1676007095IN
Check Turtz: CHECKTURTZ Token
0 ETH0.0028808221.43851276
Mint165962742023-02-10 5:29:47664 days ago1676006987IN
Check Turtz: CHECKTURTZ Token
0 ETH0.0027681320.59991982
Mint165959622023-02-10 4:26:47664 days ago1676003207IN
Check Turtz: CHECKTURTZ Token
0 ETH0.0024964818.57838631
Mint165959142023-02-10 4:17:11664 days ago1676002631IN
Check Turtz: CHECKTURTZ Token
0 ETH0.0025569219.02812021
Mint165959032023-02-10 4:14:59664 days ago1676002499IN
Check Turtz: CHECKTURTZ Token
0 ETH0.0029437321.90670653
Mint165958952023-02-10 4:13:23664 days ago1676002403IN
Check Turtz: CHECKTURTZ Token
0 ETH0.0031117923.15740557
Mint165958042023-02-10 3:55:11664 days ago1676001311IN
Check Turtz: CHECKTURTZ Token
0 ETH0.0035995720.10149889
View all transactions

View more zero value Internal Transactions in Advanced View mode

Advanced mode:
Loading...
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
CheckTurtz

Compiler Version
v0.8.17+commit.8df45f5f

Optimization Enabled:
Yes with 200 runs

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

/*

((((((((((((((((((((((((((((((((((((((((((          
((((((((((((((((((((((((((((((((((((((((((             
((((((((((((((((((((((((((((((((((((((((((             
((((((((((((((((((((((((((((((((((((((((((             
((((((((((((((((((((((((((((((((((((((((((             
((((((((((((((((((((((((((((((((((((((((((             
((((((((((((((                            ((((((((((((((
((((((((((((((                            ((((((((((((((
((((((((((((((                            ((((((((((((((
((((((((((((((                            ((((((((((((((
((((((((((((((                            ((((((((((((((
((((((((((((((                            ((((((((((((((
((((((((((((((              @@@@@@@@@@@@@@((((((((((((((
((((((((((((((              @@@@@@@@@@@@@@((((((((((((((
((((((((((((((              @@@@@@@@@@@@@@((((((((((((((
((((((((((((((              @@@@@@@@@@@@@@((((((((((((((
((((((((((((((              @@@@@@@@@@@@@@((((((((((((((
((((((((((((((              @@@@@@@@@@@@@@((((((((((((((
((((((((((((((((((((((((((((((((((((((((((((((((((((((((
((((((((((((((((((((((((((((((((((((((((((((((((((((((((
((((((((((((((((((((((((((((((((((((((((((((((((((((((((
((((((((((((((((((((((((((((((((((((((((((((((((((((((((
((((((((((((((((((((((((((((((((((((((((((((((((((((((((
((((((((((((((((((((((((((((((((((((((((((((((((((((((((
((((((((((((((((((((((((((((((((((((((((((
((((((((((((((((((((((((((((((((((((((((((
((((((((((((((((((((((((((((((((((((((((((
((((((((((((((((((((((((((((((((((((((((((
((((((((((((((((((((((((((((((((((((((((((
((((((((((((((((((((((((((((((((((((((((((

*/

// @title Check Turtz
// @author @tom_hirst
// @notice A fully on-chain, gamified Tiny Winged Turtlez x Checks derivative

/*

How it works:

- Anyone can mint a Check Turtz NFT for free + gas
- There is a mint limit of 1 Check Turtz NFT per wallet
- Minting will last for 72 hours as an open edition with no max supply
- Holding Tiny Winged Turtlez and/or Checks NFTs in the wallet you mint from unlocks differentiated art
- Your minted Check Turtz NFT will receive a Colors trait based on the number of Tiny Winged Turtlez and/or Checks NFTs you hold
- Example 1: If you hold 5 Tiny Winged Turtlez in your wallet when you mint, your Check Turtz NFT will have a Colors trait of 5
- Example 2: If you hold 10 Checks NFTs in your wallet when you mint, your Check Turtz NFT will have a Colors trait of 10
- Example 3: If you hold 5 Tiny Winged Turtlez and 10 Checks NFTs in your wallet when you mint, your Check Turtz NFT will have a Colors trait of 15
- The maximum Colors trait value is 80. This is the number of elements in the original Checks piece by @jackbutcher
- Check Turtz NFTs with a Colors trait feature pseudo-random elements in their art
- For example, if your Check Turtz NFT has a Colors trait value of 5, its artwork will have 5 pseudo-randomly coloured and positioned elements
- Colours and positions used are distinct per Check Turtz NFT with differentiated art
- The colour palette is inspired by the original Checks piece designed by @jackbutcher
- Color traits are assigned at mint time and won't change thereafter
- For example, your Check Turtz NFT will still have a Colors trait of 5, even if you no longer hold the 5 Tiny Winged Turtlez and/or Checks NFTs used to unlock it
- Tiny Winged Turtlez and Checks NFTs are NOT burned
- Check Turtz NFTs are burnable

*/

pragma solidity ^0.8.17;

import "@openzeppelin/contracts/token/ERC721/ERC721.sol";
import "@openzeppelin/contracts/interfaces/IERC2981.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts/utils/Base64.sol";
import "@openzeppelin/contracts/utils/Strings.sol";
import "operator-filter-registry/src/DefaultOperatorFilterer.sol";
import "./lib/CheckTurtzDelegateBalance.sol";
import "./interfaces/IERC4906.sol";

contract CheckTurtz is
    ERC721,
    IERC2981,
    IERC4906,
    Ownable,
    DefaultOperatorFilterer,
    CheckTurtzDelegateBalance
{
    address public immutable TINY_WINGED_TURTLEZ_ADDRESS;
    address public immutable CHECKS_ADDRESS;

    uint256 public mintEnds;

    // @dev Just in case
    address public checksV2Address;

    uint256 private _totalSupply;
    uint256 private nextTokenId;

    struct CheckTurt {
        uint16 colors;
        uint256 psuedoRandomNumber;
        bool darkMode;
    }

    mapping(uint256 => CheckTurt) public checkTurtz;

    mapping(address => bool) public hasMinted;

    string[80] private colorPalette = [
        "#DB395E",
        "#602263",
        "#5C83CB",
        "#B1EFC9",
        "#25438C",
        "#7A2520",
        "#85C33C",
        "#C23532",
        "#2E668B",
        "#F6CBA6",
        "#DA3321",
        "#2D5352",
        "#5FCD8C",
        "#4291A8",
        "#EF8C37",
        "#535687",
        "#F2A43A",
        "#5ABAD3",
        "#D6F4E1",
        "#D1DF4F",
        "#A4C8EE",
        "#EF8933",
        "#A7DDF9",
        "#9DEFBF",
        "#525EAA",
        "#EC7368",
        "#FBEA5B",
        "#93CF98",
        "#EB5A2A",
        "#B82C36",
        "#EA3A2D",
        "#F6CB45",
        "#33758D",
        "#F09837",
        "#9AD9FB",
        "#F7DD9B",
        "#FAE663",
        "#F4BDBE",
        "#F7CA57",
        "#EE837D",
        "#ED7C30",
        "#3E8BA3",
        "#F0A0CA",
        "#4D3658",
        "#81D1EC",
        "#3B2F39",
        "#F2A93B",
        "#60B1F4",
        "#977A31",
        "#D5332F",
        "#E73E53",
        "#2F2243",
        "#DB4D58",
        "#F2A93C",
        "#5A9F3E",
        "#6D2F22",
        "#4068C1",
        "#F9DA4D",
        "#77D3DE",
        "#6A552A",
        "#8A2235",
        "#FAE272",
        "#EB4429",
        "#E0C963",
        "#F9DA4A",
        "#C7EDF2",
        "#F2B341",
        "#EA5B33",
        "#D97D2E",
        "#ABDD45",
        "#F2A840",
        "#EE828F",
        "#322F92",
        "#E8424E",
        "#2E4985",
        "#5FC9BF",
        "#F9DB49",
        "#4AA392",
        "#DE3237",
        "#7A5AB4"
    ];

    uint8[80] private positions = [
        0,
        1,
        2,
        3,
        4,
        5,
        6,
        7,
        8,
        9,
        10,
        11,
        12,
        13,
        14,
        15,
        16,
        17,
        18,
        19,
        20,
        21,
        22,
        23,
        24,
        25,
        26,
        27,
        28,
        29,
        30,
        31,
        32,
        33,
        34,
        35,
        36,
        37,
        38,
        39,
        40,
        41,
        42,
        43,
        44,
        45,
        46,
        47,
        48,
        49,
        50,
        51,
        52,
        53,
        54,
        55,
        56,
        57,
        58,
        59,
        60,
        61,
        62,
        63,
        64,
        65,
        66,
        67,
        68,
        69,
        70,
        71,
        72,
        73,
        74,
        75,
        76,
        77,
        78,
        79
    ];

    uint16[2][80] private coordinates = [
        [600, 498],
        [708, 498],
        [816, 498],
        [924, 498],
        [1032, 498],
        [1140, 498],
        [1248, 498],
        [1356, 498],
        [600, 603],
        [708, 603],
        [816, 603],
        [924, 603],
        [1032, 603],
        [1140, 603],
        [1248, 603],
        [1356, 603],
        [600, 708],
        [708, 708],
        [816, 708],
        [924, 708],
        [1032, 708],
        [1140, 708],
        [1248, 708],
        [1356, 708],
        [600, 813],
        [708, 813],
        [816, 813],
        [924, 813],
        [1032, 813],
        [1140, 813],
        [1248, 813],
        [1356, 813],
        [600, 918],
        [708, 918],
        [816, 918],
        [924, 918],
        [1032, 918],
        [1140, 918],
        [1248, 918],
        [1356, 918],
        [600, 1023],
        [708, 1023],
        [816, 1023],
        [924, 1023],
        [1032, 1023],
        [1140, 1023],
        [1248, 1023],
        [1356, 1023],
        [600, 1128],
        [708, 1128],
        [816, 1128],
        [924, 1128],
        [1032, 1128],
        [1140, 1128],
        [1248, 1128],
        [1356, 1128],
        [600, 1233],
        [708, 1233],
        [816, 1233],
        [924, 1233],
        [1032, 1233],
        [1140, 1233],
        [1248, 1233],
        [1356, 1233],
        [600, 1338],
        [708, 1338],
        [816, 1338],
        [924, 1338],
        [1032, 1338],
        [1140, 1338],
        [1248, 1338],
        [1356, 1338],
        [600, 1443],
        [708, 1443],
        [816, 1443],
        [924, 1443],
        [1032, 1443],
        [1140, 1443],
        [1248, 1443],
        [1356, 1443]
    ];

    error MintEndsInPast();
    error EmptyTWTContract();
    error EmptyChecksContract();
    error ChecksV2AlreadySet();
    error EmptyChecksV2Contract();
    error MintingEnded();
    error AlreadyMinted();
    error TokenDoesNotExist();
    error NotApprovedOrOwner();

    constructor(
        uint256 _mintEnds,
        address _twtAddress,
        address _checksAddress,
        address _warmWalletAddress
    )
        ERC721("Check Turtz", "CHECKTURTZ")
        CheckTurtzDelegateBalance(_warmWalletAddress)
    {
        if (block.timestamp > _mintEnds) {
            revert MintEndsInPast();
        }

        if (_twtAddress == address(0)) {
            revert EmptyTWTContract();
        }

        if (_checksAddress == address(0)) {
            revert EmptyChecksContract();
        }

        mintEnds = _mintEnds;
        TINY_WINGED_TURTLEZ_ADDRESS = _twtAddress;
        CHECKS_ADDRESS = _checksAddress;
    }

    function totalSupply() external view returns (uint256) {
        return _totalSupply;
    }

    function addChecksV2Contract(address _checksV2Address) external onlyOwner {
        if (checksV2Address != address(0)) {
            revert ChecksV2AlreadySet();
        }

        if (_checksV2Address == address(0)) {
            revert EmptyChecksV2Contract();
        }

        checksV2Address = _checksV2Address;
    }

    function mint() external {
        if (block.timestamp > mintEnds) {
            revert MintingEnded();
        }

        if (hasMinted[msg.sender]) {
            revert AlreadyMinted();
        }

        uint256 tokenId = ++nextTokenId;
        // @dev Max supply 5,000 + 16,030 = 24,030
        uint16 colors = 0;

        colors = uint16(
            delegateBalance(TINY_WINGED_TURTLEZ_ADDRESS) +
                delegateBalance(CHECKS_ADDRESS)
        );

        if (checksV2Address != address(0)) {
            colors += uint16(delegateBalance(checksV2Address));
        }

        if (colors > 0) {
            checkTurtz[tokenId].colors = colors < 81 ? colors : 80;

            checkTurtz[tokenId].psuedoRandomNumber = uint256(
                keccak256(
                    abi.encodePacked(msg.sender, block.coinbase, _totalSupply)
                )
            );
        }

        hasMinted[msg.sender] = true;
        ++_totalSupply;
        _mint(msg.sender, tokenId);
    }

    function toggleTokenMode(uint256 tokenId) external {
        if (!_isApprovedOrOwner(msg.sender, tokenId)) {
            revert NotApprovedOrOwner();
        }

        checkTurtz[tokenId].darkMode = !checkTurtz[tokenId].darkMode;
        emit MetadataUpdate(tokenId);
    }

    function getFrame(uint256 tokenId) internal view returns (string memory) {
        if (checkTurtz[tokenId].darkMode) {
            return
                "<path d='M2000 0H0V2000H2000V0Z' fill='#000000'/><path d='M1448.89 447.852H551.107V1552.15H1448.89V447.852Z' fill='#111111' />";
        }

        return
            "<path d='M2000 0H0V2000H2000V0Z' fill='#efefef'/><path d='M1448.89 447.852H551.107V1552.15H1448.89V447.852Z' fill='#ffffff' />";
    }

    function getDefaultTurt() internal pure returns (string memory) {
        return
            "<path d='M0.893005 0.897949H35.093L35.093 12.298H46.493V46.498H35.093L35.093 57.8979H0.893005V0.897949Z' fill='#65bc48' /><path d='M35.093 12.298H12.293V35.098H35.093V12.298Z' fill='#ffffff' /><path d='M35.1 23.698H23.7V35.098H35.1V23.698Z' fill='#000000' />";
    }

    function getCheckTurt(
        uint256 tokenId,
        uint8 i
    ) internal view returns (string memory) {
        uint8 colorPaletteIndex = uint8(
            uint8(checkTurtz[tokenId].psuedoRandomNumber >> i) %
                colorPalette.length
        );

        return
            string(
                abi.encodePacked(
                    "<path d='M0.893005 0.897949H35.093L35.093 12.298H46.493V46.498H35.093L35.093 57.8979H0.893005V0.897949Z' fill='",
                    colorPalette[colorPaletteIndex],
                    "' /><path d='M35.093 12.298H12.293V35.098H35.093V12.298Z' fill='#ffffff' /><path d='M35.1 23.698H23.7V35.098H35.1V23.698Z' fill='#000000' />"
                )
            );
    }

    function shufflePositions(
        uint256 tokenId
    ) internal view returns (uint8[80] memory) {
        uint8[80] memory shuffledPositions = positions;

        for (uint i = 0; i < shuffledPositions.length; ) {
            uint j = checkTurtz[tokenId].psuedoRandomNumber % (i + 1);
            uint8 temp = shuffledPositions[i];

            shuffledPositions[i] = shuffledPositions[j];
            shuffledPositions[j] = temp;

            unchecked {
                ++i;
            }
        }

        return shuffledPositions;
    }

    function getTurtGrid(
        uint256 tokenId
    ) internal view returns (string memory grid) {
        for (uint8 i = 0; i < 80; ) {
            uint8[80] memory shuffledPositions = shufflePositions(tokenId);

            uint8 positionIndex = checkTurtz[tokenId].colors > 0
                ? shuffledPositions[i]
                : i;

            if (i < checkTurtz[tokenId].colors) {
                grid = string(
                    abi.encodePacked(
                        grid,
                        "<g transform='translate(",
                        Strings.toString(coordinates[positionIndex][0]),
                        " ",
                        Strings.toString(coordinates[positionIndex][1]),
                        ")'>",
                        getCheckTurt(tokenId, i),
                        "</g>"
                    )
                );
            } else {
                grid = string(
                    abi.encodePacked(
                        grid,
                        "<g transform='translate(",
                        Strings.toString(coordinates[positionIndex][0]),
                        " ",
                        Strings.toString(coordinates[positionIndex][1]),
                        ")'>",
                        getDefaultTurt(),
                        "</g>"
                    )
                );
            }

            unchecked {
                ++i;
            }
        }

        return grid;
    }

    function getTokenIdSvg(
        uint256 tokenId
    ) internal view returns (string memory) {
        return
            string(
                abi.encodePacked(
                    "<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 2000 2000'>",
                    getFrame(tokenId),
                    getTurtGrid(tokenId),
                    "</svg>"
                )
            );
    }

    function getTokenIdMetadata(
        uint256 tokenId
    ) internal view returns (string memory metadata) {
        metadata = string(
            abi.encodePacked(
                '{"trait_type": "Mode", "value": "',
                checkTurtz[tokenId].darkMode ? "Dark" : "Light",
                '"}'
            )
        );

        if (checkTurtz[tokenId].colors > 0) {
            metadata = string(
                abi.encodePacked(
                    metadata,
                    ',{"trait_type": "Colors", "value": "',
                    Strings.toString(checkTurtz[tokenId].colors),
                    '"}'
                )
            );
        }

        return metadata;
    }

    function tokenURI(
        uint256 tokenId
    ) public view override returns (string memory) {
        if (!_exists(tokenId)) {
            revert TokenDoesNotExist();
        }

        return
            string(
                abi.encodePacked(
                    "data:application/json;base64,",
                    Base64.encode(
                        bytes(
                            string(
                                abi.encodePacked(
                                    '{"name": "Check Turtz #',
                                    Strings.toString(tokenId),
                                    '", "description": "Check Turtz is a fully on-chain, gamified Tiny Winged Turtlez x Checks derivative.", "image": "data:image/svg+xml;base64,',
                                    Base64.encode(
                                        bytes(getTokenIdSvg(tokenId))
                                    ),
                                    '","attributes":[',
                                    getTokenIdMetadata(tokenId),
                                    "]}"
                                )
                            )
                        )
                    )
                )
            );
    }

    function burn(uint256 tokenId) external {
        if (!_isApprovedOrOwner(msg.sender, tokenId)) {
            revert NotApprovedOrOwner();
        }

        --_totalSupply;
        _burn(tokenId);
    }

    function supportsInterface(
        bytes4 interfaceId
    ) public view virtual override(IERC165, ERC721) returns (bool) {
        return
            interfaceId == type(IERC2981).interfaceId ||
            interfaceId == bytes4(0x49064906) ||
            super.supportsInterface(interfaceId);
    }

    function setApprovalForAll(
        address operator,
        bool approved
    ) public override(IERC721, ERC721) onlyAllowedOperatorApproval(operator) {
        super.setApprovalForAll(operator, approved);
    }

    function approve(
        address operator,
        uint256 tokenId
    ) public override(IERC721, ERC721) onlyAllowedOperatorApproval(operator) {
        super.approve(operator, tokenId);
    }

    function transferFrom(
        address from,
        address to,
        uint256 tokenId
    ) public override(IERC721, ERC721) onlyAllowedOperator(from) {
        super.transferFrom(from, to, tokenId);
    }

    function safeTransferFrom(
        address from,
        address to,
        uint256 tokenId
    ) public override(IERC721, ERC721) onlyAllowedOperator(from) {
        super.safeTransferFrom(from, to, tokenId);
    }

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

    function royaltyInfo(
        uint256 tokenId,
        uint256 salePrice
    ) public view returns (address, uint256) {
        if (!_exists(tokenId)) {
            revert TokenDoesNotExist();
        }

        return (owner(), (salePrice * 25) / 1000);
    }
}

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

pragma solidity ^0.8.0;

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

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

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

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

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

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

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

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

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

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

File 3 of 22 : IERC165.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (interfaces/IERC165.sol)

pragma solidity ^0.8.0;

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

File 4 of 22 : IERC2981.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.6.0) (interfaces/IERC2981.sol)

pragma solidity ^0.8.0;

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

/**
 * @dev Interface for the NFT Royalty Standard.
 *
 * A standardized way to retrieve royalty payment information for non-fungible tokens (NFTs) to enable universal
 * support for royalty payments across all NFT marketplaces and ecosystem participants.
 *
 * _Available since v4.5._
 */
interface IERC2981 is IERC165 {
    /**
     * @dev Returns how much royalty is owed and to whom, based on a sale price that may be denominated in any unit of
     * exchange. The royalty amount is denominated and should be paid in that same unit of exchange.
     */
    function royaltyInfo(uint256 tokenId, uint256 salePrice)
        external
        view
        returns (address receiver, uint256 royaltyAmount);
}

File 5 of 22 : IERC721.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (interfaces/IERC721.sol)

pragma solidity ^0.8.0;

import "../token/ERC721/IERC721.sol";

File 6 of 22 : ERC721.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.0) (token/ERC721/ERC721.sol)

pragma solidity ^0.8.0;

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

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

    // Token name
    string private _name;

    // Token symbol
    string private _symbol;

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

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

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

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

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

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

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

    /**
     * @dev See {IERC721-ownerOf}.
     */
    function ownerOf(uint256 tokenId) public view virtual override returns (address) {
        address owner = _ownerOf(tokenId);
        require(owner != address(0), "ERC721: invalid token ID");
        return owner;
    }

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

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

    /**
     * @dev See {IERC721Metadata-tokenURI}.
     */
    function tokenURI(uint256 tokenId) public view virtual override returns (string memory) {
        _requireMinted(tokenId);

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

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

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

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

        _approve(to, tokenId);
    }

    /**
     * @dev See {IERC721-getApproved}.
     */
    function getApproved(uint256 tokenId) public view virtual override returns (address) {
        _requireMinted(tokenId);

        return _tokenApprovals[tokenId];
    }

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

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

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

        _transfer(from, to, tokenId);
    }

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

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

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

    /**
     * @dev Returns the owner of the `tokenId`. Does NOT revert if token doesn't exist
     */
    function _ownerOf(uint256 tokenId) internal view virtual returns (address) {
        return _owners[tokenId];
    }

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

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

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

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

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

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

        // Check that tokenId was not minted by `_beforeTokenTransfer` hook
        require(!_exists(tokenId), "ERC721: token already minted");

        unchecked {
            // Will not overflow unless all 2**256 token ids are minted to the same owner.
            // Given that tokens are minted one by one, it is impossible in practice that
            // this ever happens. Might change if we allow batch minting.
            // The ERC fails to describe this case.
            _balances[to] += 1;
        }

        _owners[tokenId] = to;

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

        _afterTokenTransfer(address(0), to, tokenId, 1);
    }

    /**
     * @dev Destroys `tokenId`.
     * The approval is cleared when the token is burned.
     * This is an internal function that does not check if the sender is authorized to operate on the token.
     *
     * Requirements:
     *
     * - `tokenId` must exist.
     *
     * Emits a {Transfer} event.
     */
    function _burn(uint256 tokenId) internal virtual {
        address owner = ERC721.ownerOf(tokenId);

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

        // Update ownership in case tokenId was transferred by `_beforeTokenTransfer` hook
        owner = ERC721.ownerOf(tokenId);

        // Clear approvals
        delete _tokenApprovals[tokenId];

        unchecked {
            // Cannot overflow, as that would require more tokens to be burned/transferred
            // out than the owner initially received through minting and transferring in.
            _balances[owner] -= 1;
        }
        delete _owners[tokenId];

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

        _afterTokenTransfer(owner, address(0), tokenId, 1);
    }

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

        _beforeTokenTransfer(from, to, tokenId, 1);

        // Check that tokenId was not transferred by `_beforeTokenTransfer` hook
        require(ERC721.ownerOf(tokenId) == from, "ERC721: transfer from incorrect owner");

        // Clear approvals from the previous owner
        delete _tokenApprovals[tokenId];

        unchecked {
            // `_balances[from]` cannot overflow for the same reason as described in `_burn`:
            // `from`'s balance is the number of token held, which is at least one before the current
            // transfer.
            // `_balances[to]` could overflow in the conditions described in `_mint`. That would require
            // all 2**256 token ids to be minted, which in practice is impossible.
            _balances[from] -= 1;
            _balances[to] += 1;
        }
        _owners[tokenId] = to;

        emit Transfer(from, to, tokenId);

        _afterTokenTransfer(from, to, tokenId, 1);
    }

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

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

    /**
     * @dev Reverts if the `tokenId` has not been minted yet.
     */
    function _requireMinted(uint256 tokenId) internal view virtual {
        require(_exists(tokenId), "ERC721: invalid token ID");
    }

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

    /**
     * @dev Hook that is called before any token transfer. This includes minting and burning. If {ERC721Consecutive} is
     * used, the hook may be called as part of a consecutive (batch) mint, as indicated by `batchSize` greater than 1.
     *
     * Calling conditions:
     *
     * - When `from` and `to` are both non-zero, ``from``'s tokens will be transferred to `to`.
     * - When `from` is zero, the tokens will be minted for `to`.
     * - When `to` is zero, ``from``'s tokens will be burned.
     * - `from` and `to` are never both zero.
     * - `batchSize` is non-zero.
     *
     * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
     */
    function _beforeTokenTransfer(
        address from,
        address to,
        uint256, /* firstTokenId */
        uint256 batchSize
    ) internal virtual {
        if (batchSize > 1) {
            if (from != address(0)) {
                _balances[from] -= batchSize;
            }
            if (to != address(0)) {
                _balances[to] += batchSize;
            }
        }
    }

    /**
     * @dev Hook that is called after any token transfer. This includes minting and burning. If {ERC721Consecutive} is
     * used, the hook may be called as part of a consecutive (batch) mint, as indicated by `batchSize` greater than 1.
     *
     * Calling conditions:
     *
     * - When `from` and `to` are both non-zero, ``from``'s tokens were transferred to `to`.
     * - When `from` is zero, the tokens were minted for `to`.
     * - When `to` is zero, ``from``'s tokens were burned.
     * - `from` and `to` are never both zero.
     * - `batchSize` is non-zero.
     *
     * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
     */
    function _afterTokenTransfer(
        address from,
        address to,
        uint256 firstTokenId,
        uint256 batchSize
    ) internal virtual {}
}

File 7 of 22 : IERC721Metadata.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (token/ERC721/extensions/IERC721Metadata.sol)

pragma solidity ^0.8.0;

import "../IERC721.sol";

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

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

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

File 8 of 22 : IERC721.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.0) (token/ERC721/IERC721.sol)

pragma solidity ^0.8.0;

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

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

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

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

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

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

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

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

    /**
     * @dev Transfers `tokenId` token from `from` to `to`.
     *
     * WARNING: Note that the caller is responsible to confirm that the recipient is capable of receiving ERC721
     * or else they may be permanently lost. Usage of {safeTransferFrom} prevents loss, though the caller must
     * understand this adds an external call which potentially creates a reentrancy vulnerability.
     *
     * Requirements:
     *
     * - `from` cannot be the zero address.
     * - `to` cannot be the zero address.
     * - `tokenId` token must be owned by `from`.
     * - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}.
     *
     * Emits a {Transfer} event.
     */
    function transferFrom(
        address from,
        address to,
        uint256 tokenId
    ) external;

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

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

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

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

File 9 of 22 : IERC721Receiver.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.6.0) (token/ERC721/IERC721Receiver.sol)

pragma solidity ^0.8.0;

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

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

pragma solidity ^0.8.1;

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

        return account.code.length > 0;
    }

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

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

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

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

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

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

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

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

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

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

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

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

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

File 11 of 22 : Base64.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.0) (utils/Base64.sol)

pragma solidity ^0.8.0;

/**
 * @dev Provides a set of functions to operate with Base64 strings.
 *
 * _Available since v4.5._
 */
library Base64 {
    /**
     * @dev Base64 Encoding/Decoding Table
     */
    string internal constant _TABLE = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";

    /**
     * @dev Converts a `bytes` to its Bytes64 `string` representation.
     */
    function encode(bytes memory data) internal pure returns (string memory) {
        /**
         * Inspired by Brecht Devos (Brechtpd) implementation - MIT licence
         * https://github.com/Brechtpd/base64/blob/e78d9fd951e7b0977ddca77d92dc85183770daf4/base64.sol
         */
        if (data.length == 0) return "";

        // Loads the table into memory
        string memory table = _TABLE;

        // Encoding takes 3 bytes chunks of binary data from `bytes` data parameter
        // and split into 4 numbers of 6 bits.
        // The final Base64 length should be `bytes` data length multiplied by 4/3 rounded up
        // - `data.length + 2`  -> Round up
        // - `/ 3`              -> Number of 3-bytes chunks
        // - `4 *`              -> 4 characters for each chunk
        string memory result = new string(4 * ((data.length + 2) / 3));

        /// @solidity memory-safe-assembly
        assembly {
            // Prepare the lookup table (skip the first "length" byte)
            let tablePtr := add(table, 1)

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

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

            } {
                // Advance 3 bytes
                dataPtr := add(dataPtr, 3)
                let input := mload(dataPtr)

                // To write each character, shift the 3 bytes (18 bits) chunk
                // 4 times in blocks of 6 bits for each character (18, 12, 6, 0)
                // and apply logical AND with 0x3F which is the number of
                // the previous character in the ASCII table prior to the Base64 Table
                // The result is then added to the table to get the character to write,
                // and finally write it in the result pointer but with a left shift
                // of 256 (1 byte) - 8 (1 ASCII char) = 248 bits

                mstore8(resultPtr, mload(add(tablePtr, and(shr(18, input), 0x3F))))
                resultPtr := add(resultPtr, 1) // Advance

                mstore8(resultPtr, mload(add(tablePtr, and(shr(12, input), 0x3F))))
                resultPtr := add(resultPtr, 1) // Advance

                mstore8(resultPtr, mload(add(tablePtr, and(shr(6, input), 0x3F))))
                resultPtr := add(resultPtr, 1) // Advance

                mstore8(resultPtr, mload(add(tablePtr, and(input, 0x3F))))
                resultPtr := add(resultPtr, 1) // Advance
            }

            // When data `bytes` is not exactly 3 bytes long
            // it is padded with `=` characters at the end
            switch mod(mload(data), 3)
            case 1 {
                mstore8(sub(resultPtr, 1), 0x3d)
                mstore8(sub(resultPtr, 2), 0x3d)
            }
            case 2 {
                mstore8(sub(resultPtr, 1), 0x3d)
            }
        }

        return result;
    }
}

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

pragma solidity ^0.8.0;

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

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

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

pragma solidity ^0.8.0;

import "./IERC165.sol";

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

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

pragma solidity ^0.8.0;

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

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

pragma solidity ^0.8.0;

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

pragma solidity ^0.8.0;

import "./math/Math.sol";

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

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

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

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

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

File 17 of 22 : IERC4906.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.17;

import "@openzeppelin/contracts/interfaces/IERC165.sol";
import "@openzeppelin/contracts/interfaces/IERC721.sol";

/// @title EIP-721 Metadata Update Extension
interface IERC4906 is IERC165, IERC721 {
    /// @dev This event emits when the metadata of a token is changed.
    /// So that the third-party platforms such as NFT market could
    /// timely update the images and related attributes of the NFT.
    event MetadataUpdate(uint256 _tokenId);

    /// @dev This event emits when the metadata of a range of tokens is changed.
    /// So that the third-party platforms such as NFT market could
    /// timely update the images and related attributes of the NFTs.
    event BatchMetadataUpdate(uint256 _fromTokenId, uint256 _toTokenId);
}

File 18 of 22 : CheckTurtzDelegateBalance.sol
// SPDX-License-Identifier: MIT

/*

((((((((((((((((((((((((((((((((((((((((((          
((((((((((((((((((((((((((((((((((((((((((             
((((((((((((((((((((((((((((((((((((((((((             
((((((((((((((((((((((((((((((((((((((((((             
((((((((((((((((((((((((((((((((((((((((((             
((((((((((((((((((((((((((((((((((((((((((             
((((((((((((((                            ((((((((((((((
((((((((((((((                            ((((((((((((((
((((((((((((((                            ((((((((((((((
((((((((((((((                            ((((((((((((((
((((((((((((((                            ((((((((((((((
((((((((((((((                            ((((((((((((((
((((((((((((((              @@@@@@@@@@@@@@((((((((((((((
((((((((((((((              @@@@@@@@@@@@@@((((((((((((((
((((((((((((((              @@@@@@@@@@@@@@((((((((((((((
((((((((((((((              @@@@@@@@@@@@@@((((((((((((((
((((((((((((((              @@@@@@@@@@@@@@((((((((((((((
((((((((((((((              @@@@@@@@@@@@@@((((((((((((((
((((((((((((((((((((((((((((((((((((((((((((((((((((((((
((((((((((((((((((((((((((((((((((((((((((((((((((((((((
((((((((((((((((((((((((((((((((((((((((((((((((((((((((
((((((((((((((((((((((((((((((((((((((((((((((((((((((((
((((((((((((((((((((((((((((((((((((((((((((((((((((((((
((((((((((((((((((((((((((((((((((((((((((((((((((((((((
((((((((((((((((((((((((((((((((((((((((((
((((((((((((((((((((((((((((((((((((((((((
((((((((((((((((((((((((((((((((((((((((((
((((((((((((((((((((((((((((((((((((((((((
((((((((((((((((((((((((((((((((((((((((((
((((((((((((((((((((((((((((((((((((((((((

*/

// @title Check Turtz Delegate Balance
// @author @tom_hirst
// @notice Checks the token balance of a delegate wallet
// @dev Uses Warm Wallet (warm.xyz)

pragma solidity ^0.8.17;

interface IWarmWallet {
    function balanceOf(
        address contractAddress,
        address owner
    ) external view returns (uint256);
}

error EmptyWarmWalletContract();

contract CheckTurtzDelegateBalance {
    address public immutable WARM_WALLET_ADDRESS;

    constructor(address _warmWalletAddress) {
        if (_warmWalletAddress == address(0)) {
            revert EmptyWarmWalletContract();
        }

        WARM_WALLET_ADDRESS = _warmWalletAddress;
    }

    function delegateBalance(
        address contractAddress
    ) internal view returns (uint256) {
        return
            IWarmWallet(WARM_WALLET_ADDRESS).balanceOf(
                contractAddress,
                msg.sender
            );
    }
}

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

import {OperatorFilterer} from "./OperatorFilterer.sol";
import {CANONICAL_CORI_SUBSCRIPTION} from "./lib/Constants.sol";
/**
 * @title  DefaultOperatorFilterer
 * @notice Inherits from OperatorFilterer and automatically subscribes to the default OpenSea subscription.
 * @dev    Please note that if your token contract does not provide an owner with EIP-173, it must provide
 *         administration methods on the contract itself to interact with the registry otherwise the subscription
 *         will be locked to the options set during construction.
 */

abstract contract DefaultOperatorFilterer is OperatorFilterer {
    /// @dev The constructor that is called when the contract is being deployed.
    constructor() OperatorFilterer(CANONICAL_CORI_SUBSCRIPTION, true) {}
}

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

interface IOperatorFilterRegistry {
    /**
     * @notice Returns true if operator is not filtered for a given token, either by address or codeHash. Also returns
     *         true if supplied registrant address is not registered.
     */
    function isOperatorAllowed(address registrant, address operator) external view returns (bool);

    /**
     * @notice Registers an address with the registry. May be called by address itself or by EIP-173 owner.
     */
    function register(address registrant) external;

    /**
     * @notice Registers an address with the registry and "subscribes" to another address's filtered operators and codeHashes.
     */
    function registerAndSubscribe(address registrant, address subscription) external;

    /**
     * @notice Registers an address with the registry and copies the filtered operators and codeHashes from another
     *         address without subscribing.
     */
    function registerAndCopyEntries(address registrant, address registrantToCopy) external;

    /**
     * @notice Unregisters an address with the registry and removes its subscription. May be called by address itself or by EIP-173 owner.
     *         Note that this does not remove any filtered addresses or codeHashes.
     *         Also note that any subscriptions to this registrant will still be active and follow the existing filtered addresses and codehashes.
     */
    function unregister(address addr) external;

    /**
     * @notice Update an operator address for a registered address - when filtered is true, the operator is filtered.
     */
    function updateOperator(address registrant, address operator, bool filtered) external;

    /**
     * @notice Update multiple operators for a registered address - when filtered is true, the operators will be filtered. Reverts on duplicates.
     */
    function updateOperators(address registrant, address[] calldata operators, bool filtered) external;

    /**
     * @notice Update a codeHash for a registered address - when filtered is true, the codeHash is filtered.
     */
    function updateCodeHash(address registrant, bytes32 codehash, bool filtered) external;

    /**
     * @notice Update multiple codeHashes for a registered address - when filtered is true, the codeHashes will be filtered. Reverts on duplicates.
     */
    function updateCodeHashes(address registrant, bytes32[] calldata codeHashes, bool filtered) external;

    /**
     * @notice Subscribe an address to another registrant's filtered operators and codeHashes. Will remove previous
     *         subscription if present.
     *         Note that accounts with subscriptions may go on to subscribe to other accounts - in this case,
     *         subscriptions will not be forwarded. Instead the former subscription's existing entries will still be
     *         used.
     */
    function subscribe(address registrant, address registrantToSubscribe) external;

    /**
     * @notice Unsubscribe an address from its current subscribed registrant, and optionally copy its filtered operators and codeHashes.
     */
    function unsubscribe(address registrant, bool copyExistingEntries) external;

    /**
     * @notice Get the subscription address of a given registrant, if any.
     */
    function subscriptionOf(address addr) external returns (address registrant);

    /**
     * @notice Get the set of addresses subscribed to a given registrant.
     *         Note that order is not guaranteed as updates are made.
     */
    function subscribers(address registrant) external returns (address[] memory);

    /**
     * @notice Get the subscriber at a given index in the set of addresses subscribed to a given registrant.
     *         Note that order is not guaranteed as updates are made.
     */
    function subscriberAt(address registrant, uint256 index) external returns (address);

    /**
     * @notice Copy filtered operators and codeHashes from a different registrantToCopy to addr.
     */
    function copyEntriesOf(address registrant, address registrantToCopy) external;

    /**
     * @notice Returns true if operator is filtered by a given address or its subscription.
     */
    function isOperatorFiltered(address registrant, address operator) external returns (bool);

    /**
     * @notice Returns true if the hash of an address's code is filtered by a given address or its subscription.
     */
    function isCodeHashOfFiltered(address registrant, address operatorWithCode) external returns (bool);

    /**
     * @notice Returns true if a codeHash is filtered by a given address or its subscription.
     */
    function isCodeHashFiltered(address registrant, bytes32 codeHash) external returns (bool);

    /**
     * @notice Returns a list of filtered operators for a given address or its subscription.
     */
    function filteredOperators(address addr) external returns (address[] memory);

    /**
     * @notice Returns the set of filtered codeHashes for a given address or its subscription.
     *         Note that order is not guaranteed as updates are made.
     */
    function filteredCodeHashes(address addr) external returns (bytes32[] memory);

    /**
     * @notice Returns the filtered operator at the given index of the set of filtered operators for a given address or
     *         its subscription.
     *         Note that order is not guaranteed as updates are made.
     */
    function filteredOperatorAt(address registrant, uint256 index) external returns (address);

    /**
     * @notice Returns the filtered codeHash at the given index of the list of filtered codeHashes for a given address or
     *         its subscription.
     *         Note that order is not guaranteed as updates are made.
     */
    function filteredCodeHashAt(address registrant, uint256 index) external returns (bytes32);

    /**
     * @notice Returns true if an address has registered
     */
    function isRegistered(address addr) external returns (bool);

    /**
     * @dev Convenience method to compute the code hash of an arbitrary contract
     */
    function codeHashOf(address addr) external returns (bytes32);
}

File 21 of 22 : Constants.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;

address constant CANONICAL_OPERATOR_FILTER_REGISTRY_ADDRESS = 0x000000000000AAeB6D7670E522A718067333cd4E;
address constant CANONICAL_CORI_SUBSCRIPTION = 0x3cc6CddA760b79bAfa08dF41ECFA224f810dCeB6;

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

import {IOperatorFilterRegistry} from "./IOperatorFilterRegistry.sol";
import {CANONICAL_OPERATOR_FILTER_REGISTRY_ADDRESS} from "./lib/Constants.sol";
/**
 * @title  OperatorFilterer
 * @notice Abstract contract whose constructor automatically registers and optionally subscribes to or copies another
 *         registrant's entries in the OperatorFilterRegistry.
 * @dev    This smart contract is meant to be inherited by token contracts so they can use the following:
 *         - `onlyAllowedOperator` modifier for `transferFrom` and `safeTransferFrom` methods.
 *         - `onlyAllowedOperatorApproval` modifier for `approve` and `setApprovalForAll` methods.
 *         Please note that if your token contract does not provide an owner with EIP-173, it must provide
 *         administration methods on the contract itself to interact with the registry otherwise the subscription
 *         will be locked to the options set during construction.
 */

abstract contract OperatorFilterer {
    /// @dev Emitted when an operator is not allowed.
    error OperatorNotAllowed(address operator);

    IOperatorFilterRegistry public constant OPERATOR_FILTER_REGISTRY =
        IOperatorFilterRegistry(CANONICAL_OPERATOR_FILTER_REGISTRY_ADDRESS);

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

    /**
     * @dev A helper function to check if an operator is allowed.
     */
    modifier onlyAllowedOperator(address from) virtual {
        // Allow spending tokens from addresses with balance
        // Note that this still allows listings and marketplaces with escrow to transfer tokens if transferred
        // from an EOA.
        if (from != msg.sender) {
            _checkFilterOperator(msg.sender);
        }
        _;
    }

    /**
     * @dev A helper function to check if an operator approval is allowed.
     */
    modifier onlyAllowedOperatorApproval(address operator) virtual {
        _checkFilterOperator(operator);
        _;
    }

    /**
     * @dev A helper function to check if an operator is allowed.
     */
    function _checkFilterOperator(address operator) internal view virtual {
        // Check registry code length to facilitate testing in environments without a deployed registry.
        if (address(OPERATOR_FILTER_REGISTRY).code.length > 0) {
            // under normal circumstances, this function will revert rather than return false, but inheriting contracts
            // may specify their own OperatorFilterRegistry implementations, which may behave differently
            if (!OPERATOR_FILTER_REGISTRY.isOperatorAllowed(address(this), operator)) {
                revert OperatorNotAllowed(operator);
            }
        }
    }
}

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

Contract Security Audit

Contract ABI

[{"inputs":[{"internalType":"uint256","name":"_mintEnds","type":"uint256"},{"internalType":"address","name":"_twtAddress","type":"address"},{"internalType":"address","name":"_checksAddress","type":"address"},{"internalType":"address","name":"_warmWalletAddress","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"AlreadyMinted","type":"error"},{"inputs":[],"name":"ChecksV2AlreadySet","type":"error"},{"inputs":[],"name":"EmptyChecksContract","type":"error"},{"inputs":[],"name":"EmptyChecksV2Contract","type":"error"},{"inputs":[],"name":"EmptyTWTContract","type":"error"},{"inputs":[],"name":"EmptyWarmWalletContract","type":"error"},{"inputs":[],"name":"MintEndsInPast","type":"error"},{"inputs":[],"name":"MintingEnded","type":"error"},{"inputs":[],"name":"NotApprovedOrOwner","type":"error"},{"inputs":[{"internalType":"address","name":"operator","type":"address"}],"name":"OperatorNotAllowed","type":"error"},{"inputs":[],"name":"TokenDoesNotExist","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"approved","type":"address"},{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"operator","type":"address"},{"indexed":false,"internalType":"bool","name":"approved","type":"bool"}],"name":"ApprovalForAll","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"_fromTokenId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"_toTokenId","type":"uint256"}],"name":"BatchMetadataUpdate","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"_tokenId","type":"uint256"}],"name":"MetadataUpdate","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"Transfer","type":"event"},{"inputs":[],"name":"CHECKS_ADDRESS","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"OPERATOR_FILTER_REGISTRY","outputs":[{"internalType":"contract IOperatorFilterRegistry","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"TINY_WINGED_TURTLEZ_ADDRESS","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"WARM_WALLET_ADDRESS","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_checksV2Address","type":"address"}],"name":"addChecksV2Contract","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"operator","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"approve","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"burn","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"checkTurtz","outputs":[{"internalType":"uint16","name":"colors","type":"uint16"},{"internalType":"uint256","name":"psuedoRandomNumber","type":"uint256"},{"internalType":"bool","name":"darkMode","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"checksV2Address","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"getApproved","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"hasMinted","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"operator","type":"address"}],"name":"isApprovedForAll","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"mint","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"mintEnds","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"ownerOf","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"uint256","name":"salePrice","type":"uint256"}],"name":"royaltyInfo","outputs":[{"internalType":"address","name":"","type":"address"},{"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":"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":"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":"tokenId","type":"uint256"}],"name":"toggleTokenMode","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"tokenURI","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"transferFrom","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"}]

60e0346200206857600062004b613881900390601f8201601f191684016001600160401b03811185821017620020515791808592608094604052833981010312620020655781516200005460208401620020bc565b916200007160606200006960408701620020bc565b9501620020bc565b906040516200008081620020a0565b600b81526a21b432b1b5902a3ab93a3d60a91b602082015260405190620000a782620020a0565b600a82526921a422a1a5aa2aa92a2d60b11b60208301528051906001600160401b03821162002051578190620000de8554620020d1565b601f81116200201d575b50602090601f831160011462001fb457859262001fa8575b50508160011b916000199060031b1c19161782555b8051906001600160401b03821162001f9457819062000136600154620020d1565b601f811162001f47575b50602090601f831160011462001ed057849262001ec4575b50508160011b916000199060031b1c1916176001555b60068054336001600160a01b0319821681179092556001600160a01b03167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e08380a36daaeb6d7670e522a718067333cd4e803b62001e3b575b50906001600160a01b0381161562001e2957608052604051620001ea816200206d565b604051620001f881620020a0565b60078152662344423339354560c81b602082015281526040516200021c81620020a0565b60078152662336303232363360c81b602082015260208201526040516200024381620020a0565b6007815266119aa19c19a1a160c91b602082015260408201526040516200026a81620020a0565b60078152662342314546433960c81b602082015260608201526040516200029181620020a0565b60078152662332353433384360c81b60208201526080820152604051620002b881620020a0565b60078152660233741323532360cc1b602082015260a0820152604051620002df81620020a0565b60078152662338354333334360c81b602082015260c08201526040516200030681620020a0565b600781526611a199199a999960c91b602082015260e08201526040516200032d81620020a0565b60078152661199229b1b1c2160c91b60208201526101008201526040516200035581620020a0565b600781526611a31b21a1209b60c91b60208201526101208201526040516200037d81620020a0565b60078152662344413333323160c81b6020820152610140820152604051620003a581620020a0565b60078152661199221a999a9960c91b6020820152610160820152604051620003cd81620020a0565b60078152662335464344384360c81b6020820152610180820152604051620003f581620020a0565b60078152660466864726282760cb1b60208201526101a08201526040516200041d81620020a0565b60078152662345463843333760c81b60208201526101c08201526040516200044581620020a0565b60078152662335333536383760c81b60208201526101e08201526040516200046d81620020a0565b60078152662346324134334160c81b60208201526102008201526040516200049581620020a0565b60078152662335414241443360c81b6020820152610220820152604051620004bd81620020a0565b60078152662344364634453160c81b6020820152610240820152604051620004e581620020a0565b600781526611a218a2231a2360c91b60208201526102608201526040516200050d81620020a0565b60078152662341344338454560c81b60208201526102808201526040516200053581620020a0565b60078152662345463839333360c81b60208201526102a08201526040516200055d81620020a0565b60078152662341374444463960c81b60208201526102c08201526040516200058581620020a0565b6007815266119ca222a3212360c91b60208201526102e0820152604051620005ad81620020a0565b60078152662335323545414160c81b6020820152610300820152604051620005d581620020a0565b60078152660468a866e666c760cb1b6020820152610320820152604051620005fd81620020a0565b600781526611a32122a09aa160c91b60208201526103408201526040516200062581620020a0565b60078152660467266868c72760cb1b60208201526103608201526040516200064d81620020a0565b60078152662345423541324160c81b60208201526103808201526040516200067581620020a0565b600781526611a11c1921999b60c91b60208201526103a08201526040516200069d81620020a0565b600781526608d1504cd04c9160ca1b60208201526103c0820152604051620006c581620020a0565b60078152662346364342343560c81b60208201526103e0820152604051620006ed81620020a0565b600781526608cccccdcd4e1160ca1b60208201526104008201526040516200071581620020a0565b60078152662346303938333760c81b60208201526104208201526040516200073d81620020a0565b6007815266119ca0a21ca32160c91b60208201526104408201526040516200076581620020a0565b600781526611a31ba2221ca160c91b60208201526104608201526040516200078d81620020a0565b60078152662346414536363360c81b6020820152610480820152604051620007b581620020a0565b60078152662346344244424560c81b60208201526104a0820152604051620007dd81620020a0565b60078152662346374341353760c81b60208201526104c08201526040516200080581620020a0565b600781526608d1514e0ccdd160ca1b60208201526104e08201526040516200082d81620020a0565b60078152660234544374333360cc1b60208201526105008201526040516200085581620020a0565b60078152662333453842413360c81b60208201526105208201526040516200087d81620020a0565b60078152662346304130434160c81b6020820152610540820152604051620008a581620020a0565b60078152660466888666c6a760cb1b6020820152610560820152604051620008cd81620020a0565b60078152662338314431454360c81b6020820152610580820152604051620008f581620020a0565b60078152662333423246333960c81b60208201526105a08201526040516200091d81620020a0565b600781526611a319209c99a160c91b60208201526105c08201526040516200094581620020a0565b600781526608cd8c108c518d60ca1b60208201526105e08201526040516200096d81620020a0565b60078152662339373741333160c81b60208201526106008201526040516200099581620020a0565b600781526611a21a9999992360c91b6020820152610620820152604051620009bd81620020a0565b60078152662345373345353360c81b6020820152610640820152604051620009e581620020a0565b60078152662332463232343360c81b602082015261066082015260405162000a0d81620020a0565b6007815266046888468886a760cb1b602082015261068082015260405162000a3581620020a0565b60078152662346324139334360c81b60208201526106a082015260405162000a5d81620020a0565b60078152662335413946334560c81b60208201526106c082015260405162000a8581620020a0565b6007815266119b221923191960c91b60208201526106e082015260405162000aad81620020a0565b60078152662334303638433160c81b602082015261070082015260405162000ad581620020a0565b600781526608d18e51104d1160ca1b602082015261072082015260405162000afd81620020a0565b60078152662337374433444560c81b602082015261074082015260405162000b2581620020a0565b60078152662336413535324160c81b602082015261076082015260405162000b4d81620020a0565b60078152662338413232333560c81b602082015261078082015260405162000b7581620020a0565b600781526611a320a2991b9960c91b60208201526107a082015260405162000b9d81620020a0565b60078152662345423434323960c81b60208201526107c082015260405162000bc581620020a0565b60078152662345304339363360c81b60208201526107e082015260405162000bed81620020a0565b60078152662346394441344160c81b602082015261080082015260405162000c1581620020a0565b600781526611a19ba2a2231960c91b602082015261082082015260405162000c3d81620020a0565b60078152662346324233343160c81b602082015261084082015260405162000c6581620020a0565b60078152662345413542333360c81b602082015261086082015260405162000c8d81620020a0565b60078152662344393744324560c81b602082015261088082015260405162000cb581620020a0565b60078152662341424444343560c81b60208201526108a082015260405162000cdd81620020a0565b60078152660234632413834360cc1b60208201526108c082015260405162000d0581620020a0565b600781526611a2a29c191c2360c91b60208201526108e082015260405162000d2d81620020a0565b600781526611999919231c9960c91b602082015261090082015260405162000d5581620020a0565b60078152662345383432344560c81b602082015261092082015260405162000d7d81620020a0565b60078152662332453439383560c81b602082015261094082015260405162000da581620020a0565b6007815266119aa3219ca12360c91b602082015261096082015260405162000dcd81620020a0565b60078152662346394442343960c81b602082015261098082015260405162000df581620020a0565b6007815266119a20a0999c9960c91b60208201526109a082015260405162000e1d81620020a0565b60078152662344453332333760c81b60208201526109c082015260405162000e4581620020a0565b600781526608cdd04d50508d60ca1b60208201526109e0820152600d9082905b6050821062001ce95750505060405162000e7f816200206d565b81815260016020820152600260408201526003606082015260046080820152600560a0820152600660c0820152600760e082015260086101008201526009610120820152600a610140820152600b610160820152600c610180820152600d6101a0820152600e6101c0820152600f6101e08201526010610200820152601161022082015260126102408201526013610260820152601461028082015260156102a082015260166102c082015260176102e082015260186103008201526019610320820152601a610340820152601b610360820152601c610380820152601d6103a0820152601e6103c0820152601f6103e08201526020610400820152602161042082015260226104408201526023610460820152602461048082015260256104a082015260266104c082015260276104e082015260286105008201526029610520820152602a610540820152602b610560820152602c610580820152602d6105a0820152602e6105c0820152602f6105e08201526030610600820152603161062082015260326106408201526033610660820152603461068082015260356106a082015260366106c082015260376106e082015260386107008201526039610720820152603a610740820152603b610760820152603c610780820152603d6107a0820152603e6107c0820152603f6107e08201526040610800820152604161082082015260426108408201526043610860820152604461088082015260456108a082015260466108c082015260476108e082015260486109008201526049610920820152604a610940820152604b610960820152604c610980820152604d6109a0820152604e6109c0820152604f6109e0820152815b6002811062001c9e57508190825b6010811062001c6e575050605f556040516200111f816200206d565b6040516200112d81620020a0565b61025881526101f2602082015281526040516200114a81620020a0565b6102c481526101f2602082015260208201526040516200116a81620020a0565b61033081526101f2602082015260408201526040516200118a81620020a0565b61039c81526101f260208201526060820152604051620011aa81620020a0565b61040881526101f260208201526080820152604051620011ca81620020a0565b61047481526101f2602082015260a0820152604051620011ea81620020a0565b6104e081526101f2602082015260c08201526040516200120a81620020a0565b61054c81526101f2602082015260e08201526040516200122a81620020a0565b610258815261025b60208201526101008201526040516200124b81620020a0565b6102c4815261025b60208201526101208201526040516200126c81620020a0565b610330815261025b60208201526101408201526040516200128d81620020a0565b61039c815261025b6020820152610160820152604051620012ae81620020a0565b610408815261025b6020820152610180820152604051620012cf81620020a0565b610474815261025b60208201526101a0820152604051620012f081620020a0565b6104e0815261025b60208201526101c08201526040516200131181620020a0565b61054c815261025b60208201526101e08201526040516200133281620020a0565b61025881526102c460208201526102008201526040516200135381620020a0565b6102c481526102c460208201526102208201526040516200137481620020a0565b61033081526102c460208201526102408201526040516200139581620020a0565b61039c81526102c46020820152610260820152604051620013b681620020a0565b61040881526102c46020820152610280820152604051620013d781620020a0565b61047481526102c460208201526102a0820152604051620013f881620020a0565b6104e081526102c460208201526102c08201526040516200141981620020a0565b61054c81526102c460208201526102e08201526040516200143a81620020a0565b610258815261032d60208201526103008201526040516200145b81620020a0565b6102c4815261032d60208201526103208201526040516200147c81620020a0565b610330815261032d60208201526103408201526040516200149d81620020a0565b61039c815261032d6020820152610360820152604051620014be81620020a0565b610408815261032d6020820152610380820152604051620014df81620020a0565b610474815261032d60208201526103a08201526040516200150081620020a0565b6104e0815261032d60208201526103c08201526040516200152181620020a0565b61054c815261032d60208201526103e08201526040516200154281620020a0565b610258815261039660208201526104008201526040516200156381620020a0565b6102c4815261039660208201526104208201526040516200158481620020a0565b61033081526103966020820152610440820152604051620015a581620020a0565b61039c81526103966020820152610460820152604051620015c681620020a0565b61040881526103966020820152610480820152604051620015e781620020a0565b610474815261039660208201526104a08201526040516200160881620020a0565b6104e0815261039660208201526104c08201526040516200162981620020a0565b61054c815261039660208201526104e08201526040516200164a81620020a0565b61025881526103ff60208201526105008201526040516200166b81620020a0565b6102c481526103ff60208201526105208201526040516200168c81620020a0565b61033081526103ff6020820152610540820152604051620016ad81620020a0565b61039c81526103ff6020820152610560820152604051620016ce81620020a0565b61040881526103ff6020820152610580820152604051620016ef81620020a0565b61047481526103ff60208201526105a08201526040516200171081620020a0565b6104e081526103ff60208201526105c08201526040516200173181620020a0565b61054c81526103ff60208201526105e08201526040516200175281620020a0565b610258815261046860208201526106008201526040516200177381620020a0565b6102c4815261046860208201526106208201526040516200179481620020a0565b61033081526104686020820152610640820152604051620017b581620020a0565b61039c81526104686020820152610660820152604051620017d681620020a0565b61040881526104686020820152610680820152604051620017f781620020a0565b610474815261046860208201526106a08201526040516200181881620020a0565b6104e0815261046860208201526106c08201526040516200183981620020a0565b61054c815261046860208201526106e08201526040516200185a81620020a0565b61025881526104d160208201526107008201526040516200187b81620020a0565b6102c481526104d160208201526107208201526040516200189c81620020a0565b61033081526104d16020820152610740820152604051620018bd81620020a0565b61039c81526104d16020820152610760820152604051620018de81620020a0565b61040881526104d16020820152610780820152604051620018ff81620020a0565b61047481526104d160208201526107a08201526040516200192081620020a0565b6104e081526104d160208201526107c08201526040516200194181620020a0565b61054c81526104d160208201526107e08201526040516200196281620020a0565b610258815261053a60208201526108008201526040516200198381620020a0565b6102c4815261053a6020820152610820820152604051620019a481620020a0565b610330815261053a6020820152610840820152604051620019c581620020a0565b61039c815261053a6020820152610860820152604051620019e681620020a0565b610408815261053a602082015261088082015260405162001a0781620020a0565b610474815261053a60208201526108a082015260405162001a2881620020a0565b6104e0815261053a60208201526108c082015260405162001a4981620020a0565b61054c815261053a60208201526108e082015260405162001a6a81620020a0565b61025881526105a3602082015261090082015260405162001a8b81620020a0565b6102c481526105a3602082015261092082015260405162001aac81620020a0565b61033081526105a3602082015261094082015260405162001acd81620020a0565b61039c81526105a3602082015261096082015260405162001aee81620020a0565b61040881526105a3602082015261098082015260405162001b0f81620020a0565b61047481526105a360208201526109a082015260405162001b3081620020a0565b6104e081526105a360208201526109c082015260405162001b5181620020a0565b61054c81526105a360208201526109e0820152606082915b6050831062001c1d575050505080421162001c0b576001600160a01b0382161562001bf9576001600160a01b0383161562001be75760075560a05260c052604051612a19908162002128823960805181818161118a01526128aa015260a0518181816117c90152611a33015260c0518181816117f20152611bfb0152f35b60405163c6eb2ce760e01b8152600490fd5b60405163205cb9a960e21b8152600490fd5b604051633d24da4f60e01b8152600490fd5b80518490855b6002811062001c445750506020600192839285550192019201919062001b69565b9091602060019161ffff9081865116918560041b90811b9283911b16911916179301910162001c23565b9091602062001c946001928460ff875116919060031b60ff811b9283911b169119161790565b9301910162001103565b82835b6020811062001cb95750605d820155600101620010f5565b9290602062001cdf6001928660ff865116919060031b60ff811b9283911b169119161790565b9201930162001ca1565b80518051906001600160401b03821162001e155762001d098554620020d1565b601f811162001dd5575b50602090601f831160011462001d635792826001949360209386958a9262001d57575b5050600019600383901b1c191690841b1786555b0193019101909162000e65565b01519050388062001d36565b908587526020872091875b601f198516811062001dbc575083602093600196938796938794601f1981161062001da2575b505050811b01865562001d4a565b015160001960f88460031b161c1916905538808062001d94565b9192602060018192868501518155019401920162001d6e565b62001e0390868852602088206005601f8601811c8201926020871062001e0a575b601f01901c01906200210e565b3862001d13565b919250829162001df6565b634e487b7160e01b86526041600452602486fd5b6040516350971b6960e01b8152600490fd5b803b1562001ec057818091604460405180948193633e9f1edf60e11b8352306004840152733cc6cdda760b79bafa08df41ecfa224f810dceb660248401525af1801562001eb55715620001c7576001600160401b03811162001ea15760405238620001c7565b634e487b7160e01b82526041600452602482fd5b6040513d84823e3d90fd5b5080fd5b01519050388062000158565b6001855260008051602062004b418339815191529250601f198416855b81811062001f2e575090846001959493921062001f14575b505050811b016001556200016e565b015160001960f88460031b161c1916905538808062001f05565b9293602060018192878601518155019501930162001eed565b6001855262001f829060008051602062004b41833981519152601f850160051c8101916020861062001f89575b601f0160051c01906200210e565b3862000140565b909150819062001f74565b634e487b7160e01b83526041600452602483fd5b01519050388062000100565b858052602086209250601f198416865b81811062002004575090846001959493921062001fea575b505050811b01825562000115565b015160001960f88460031b161c1916905538808062001fdc565b9293602060018192878601518155019501930162001fc4565b6200204a9086805260208720601f850160051c8101916020861062001f8957601f0160051c01906200210e565b38620000e8565b634e487b7160e01b84526041600452602484fd5b80fd5b600080fd5b610a0081019081106001600160401b038211176200208a57604052565b634e487b7160e01b600052604160045260246000fd5b604081019081106001600160401b038211176200208a57604052565b51906001600160a01b03821682036200206857565b90600182811c9216801562002103575b6020831014620020ed57565b634e487b7160e01b600052602260045260246000fd5b91607f1691620020e1565b8181106200211a575050565b600081556001016200210e56fe608080604052600436101561001357600080fd5b60003560e01c90816301ffc9a714611ceb5750806306fdde0314611c48578063081812fc14611c2a5780630869806e14611be5578063095ea7b314611a6257806310fef2da14611a1d5780631249c58b1461178357806318160ddd1461176557806323b872dd146117205780632a55205a146116ba57806338e21cce1461167b5780633d317ed01461160757806340de28291461159657806341f434341461156d57806342842e0e1461151557806342966c68146114465780636352211e14611416578063685eca95146113ed57806370a0823114611356578063715018a6146112f95780638d899ea4146112ad5780638da5cb5b1461128457806395d89b41146111b9578063a085048714611174578063a22cb46514611090578063b88d4fde14610fa7578063c87b56dd146102a6578063e985e9c514610250578063f2fde38b1461018c5763fcfae6081461016957600080fd5b34610187576000366003190112610187576020600754604051908152f35b600080fd5b34610187576020366003190112610187576101a5611dd0565b6101ad611ee0565b6001600160a01b039081169081156101fc57600654826001600160601b0360a01b821617600655167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0600080a3005b60405162461bcd60e51b815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201526564647265737360d01b6064820152608490fd5b3461018757604036600319011261018757610269611dd0565b610271611de6565b9060018060a01b03809116600052600560205260406000209116600052602052602060ff604060002054166040519015158152f35b34610187576020366003190112610187576004356000908152600260205260409020546001600160a01b031615610f95576102e2600435612274565b6102ed6004356123b7565b60009160605b605060ff85161015610ba557610a0060405161030e81611e4d565b36903760405180605d60005b6050601f820110610a19575060ff6101e091548181168452818160081c166020850152818160101c166040850152818160181c166060850152818160201c166080850152818160281c1660a0850152818160301c1660c0850152818160381c1660e0850152818160401c16610100850152818160481c16610120850152818160501c16610140850152818160581c16610160850152818160601c16610180850152818160681c166101a0850152818160701c166101c085015260781c169101526103e381611e4d565b60005b6050811061098c57506004356000908152600b602052604090205461ffff16156109855761041960ff9182871690612525565b51165b600435600052600b60205261ffff6040600020541660ff86161060001461073f57605081101561072957606001549061046661ffff61045c818516612274565b9360101c16612274565b600435600052600b60205260ff605081600160406000200154818a161c160616605081101561072957600d0190604051917f3c7061746820643d274d302e38393330303520302e3839373934394833352e3060208401527f39334c33352e3039332031322e3239384834362e3439335634362e343938483360408401527f352e3039334c33352e3039332035372e3839373948302e38393330303556302e60608401526e3839373934395a272066696c6c3d2760881b608084015260009080549061053082611faa565b916001811690811561070457506001146106c9575b505060019383631e17b39f60e11b6106bf9461061a608c60ff9a98877f27202f3e3c7061746820643d274d33352e3039332031322e3239384831322e32604099527f39335633352e3039384833352e3039335631322e3239385a272066696c6c3d2760208201527f2366666666666627202f3e3c7061746820643d274d33352e312032332e363938898201527f4832332e375633352e3039384833352e315632332e3639385a272066696c6c3d60608201526b13919818181818181390179f60a11b608082015203606c810186520184611ea2565b6214939f60e91b855197856106398a975180926020808b019101611d88565b860177078ce40e8e4c2dce6ccdee4da7a4ee8e4c2dce6d8c2e8ca560431b6020820152610670825180936020603885019101611d88565b01600160fd1b60388201528251906106918260399560208785019101611d88565b01918201528251906106ac82603c9560208785019101611d88565b0191820152036020810184520182611ea2565b945b0116926102f3565b90915060005260206000206000905b8282106106ed5750508201608f018383610545565b60018160209254608f8589010152019101906106d8565b60ff1916608f8088019190915283151590930286019092019250859150849050610545565b634e487b7160e01b600052603260045260246000fd5b605081101561072957606001549061075e61ffff61045c818516612274565b916040518061014081011067ffffffffffffffff6101408301111761096f57604060019382631e17b39f60e11b60ff9761014061096996018552610102835260208301927f3c7061746820643d274d302e38393330303520302e3839373934394833352e3084527f39334c33352e3039332031322e3239384834362e3439335634362e3439384833868201527f352e3039334c33352e3039332035372e3839373948302e38393330303556302e60608201527f3839373934395a272066696c6c3d272336356263343827202f3e3c706174682060808201527f643d274d33352e3039332031322e3239384831322e3239335633352e3039384860a08201527f33352e3039335631322e3239385a272066696c6c3d272366666666666627202f60c08201527f3e3c7061746820643d274d33352e312032332e3639384832332e375633352e3060e08201527f39384833352e315632332e3639385a272066696c6c3d2723303030303030272061010082015261179f60f11b6101208201526214939f60e91b865198866108fa8b985180926020808c019101611d88565b870177078ce40e8e4c2dce6ccdee4da7a4ee8e4c2dce6d8c2e8ca560431b6020820152610931825180936020603885019101611d88565b01600160fd1b60388201528351906109528260399660208885019101611d88565b019283015251906106ac82603c9586840190611d88565b946106c1565b634e487b7160e01b600052604160045260246000fd5b508361041c565b600435600052600b602052600190816040600020015491810191828211610a035782156109ed57826109e7910660ff6109c58487612525565b5116926109e060ff6109d78489612525565b51169187612525565b5284612525565b526103e6565b634e487b7160e01b600052601260045260246000fd5b634e487b7160e01b600052601160045260246000fd5b90916001610400602092855460ff8116825260ff8160081c168583015260ff8160101c16604083015260ff8160181c16606083015260ff8160808282891c1681860152828260a0828260281c1681890152828260c095828260301c16878c0152828260381c1660e08c0152828260401c166101008c0152828260481c166101208c0152828260501c166101408c0152828260581c166101608c0152828260601c166101808c0152828260681c166101a08c0152828260701c166101c08c0152828260781c166101e08c01521c16610200890152828260881c16610220890152828260901c16610240890152828260981c166102608901521c16610280860152828260a81c166102a0860152828260b01c166102c0860152828260b81c166102e08601521c1661030083015260ff8160c81c1661032083015260ff8160d01c1661034083015260ff8160d81c1661036083015260ff8160e01c1661038083015260ff8160e81c166103a083015260ff8160f01c166103c083015260f81c6103e0820152019301910161031a565b90610c5560d5610c5a610e6f94610c556066610ed6986040519384917f3c73766720786d6c6e733d27687474703a2f2f7777772e77332e6f72672f323060208401527f30302f737667272076696577426f783d2730203020323030302032303030273e6040840152610c21815180926020606087019101611d88565b8201610c37825180936020606085019101611d88565b01651e17b9bb339f60d11b6060820152036046810184520182611ea2565b61211d565b92600435600052600b60205260ff60026040600020015416600014610f7257604051610c8581611e6a565b60048152634461726b60e01b60208201525b60405160208101917f7b2274726169745f74797065223a20224d6f6465222c202276616c7565223a208352601160f91b6040830152610ce0815180926020604186019101611d88565b8101610d0360438361227d60f01b93846041820152036023810185520183611ea2565b8192600435600052600b60205261ffff8060406000205416610eda575b505050506040519485927f7b226e616d65223a2022436865636b20547572747a20230000000000000000006020850152610d64815180926020603788019101611d88565b83017f222c20226465736372697074696f6e223a2022436865636b20547572747a206960378201527f7320612066756c6c79206f6e2d636861696e2c2067616d69666965642054696e60578201527f792057696e67656420547572746c657a207820436865636b732064657269766160778201527f746976652e222c2022696d616765223a2022646174613a696d6167652f73766760978201526b0ade1b5b0ed8985cd94d8d0b60a21b60b7820152610e2782518093602060c385019101611d88565b016f222c2261747472696275746573223a5b60801b60c3820152610e5582518093602060d385019101611d88565b01615d7d60f01b60d38201520360b5810184520182611ea2565b610ec2603d60405180937f646174613a6170706c69636174696f6e2f6a736f6e3b6261736536342c0000006020830152610eb28151809260208686019101611d88565b810103601d810184520182611ea2565b604051918291602083526020830190611dab565b0390f35b610f0b945091610f699391610ef760469460406000205416612274565b906040519687945180926020870190611d88565b83017f2c7b2274726169745f74797065223a2022436f6c6f7273222c202276616c7565602082015263111d101160e11b6040820152610f54825180936020604485019101611d88565b01906044820152036026810184520182611ea2565b87808080610d20565b604051610f7e81611e6a565b6005815264131a59da1d60da1b6020820152610c97565b60405163677510db60e11b8152600490fd5b3461018757608036600319011261018757610fc0611dd0565b610fc8611de6565b906044356064359267ffffffffffffffff8411610187573660238501121561018757836004013592610ff984611ec4565b936110076040519586611ea2565b808552366024828801011161018757602081600092602461105e99018389013786010152336001600160a01b03821603611082575b61104e611049843361207d565b612536565b6110598383836125f2565b612756565b1561106557005b60405162461bcd60e51b81528061107e60048201612703565b0390fd5b61108b33612923565b61103c565b34610187576040366003190112610187576110a9611dd0565b6024359081151590818303610187576110c181612923565b6001600160a01b03169133831461112f576111019033600052600560205260406000208460005260205260406000209060ff801983541691151516179055565b6040519081527f17307eab39ab6107e8899845ad3d59bd9653f200f220920489ca2b5937696c3160203392a3005b60405162461bcd60e51b815260206004820152601960248201527f4552433732313a20617070726f766520746f2063616c6c6572000000000000006044820152606490fd5b34610187576000366003190112610187576040517f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03168152602090f35b3461018757600036600319011261018757604051600060018054906111dd82611faa565b8085529181811690811561125d5750600114611204575b610ed684610ec281860382611ea2565b600081815292507fb10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf65b828410611245575050508101602001610ec2826111f4565b8054602085870181019190915290930192810161122d565b60ff191660208087019190915292151560051b85019092019250610ec291508390506111f4565b34610187576000366003190112610187576006546040516001600160a01b039091168152602090f35b3461018757602036600319011261018757600435600052600b6020526060604060002061ffff8154169060ff600260018301549201541690604051928352602083015215156040820152f35b3461018757600036600319011261018757611312611ee0565b600680546001600160a01b031981169091556000906001600160a01b03167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e08280a3005b34610187576020366003190112610187576001600160a01b03611377611dd0565b1680156113965760005260036020526020604060002054604051908152f35b60405162461bcd60e51b815260206004820152602960248201527f4552433732313a2061646472657373207a65726f206973206e6f7420612076616044820152683634b21037bbb732b960b91b6064820152608490fd5b34610187576000366003190112610187576008546040516001600160a01b039091168152602090f35b34610187576020366003190112610187576020611434600435611f84565b6040516001600160a01b039091168152f35b3461018757602036600319011261018757600435611464813361207d565b15611503576009548015610a035760001990810160095560009061148783611f84565b5061149183611f84565b838352600460205260408320906001600160601b0360a01b9182815416905560018060a01b0316918284526003602052604084209081540190558383526002602052604083209081541690557fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef8280a4005b60405163390cdd9b60e21b8152600490fd5b346101875761105e61152636611dfc565b6001600160a01b038316331415929091908361155f575b6040519361154a85611e31565b600085526110825761104e611049843361207d565b61156833612923565b61153d565b346101875760003660031901126101875760206040516daaeb6d7670e522a718067333cd4e8152f35b34610187576020366003190112610187576115af611dd0565b6115b7611ee0565b600854906001600160a01b03908183166115f557169081156115e3576001600160a01b03191617600855005b60405163022750fb60e31b8152600490fd5b604051631b7d42d760e11b8152600490fd5b3461018757602036600319011261018757600435611625813361207d565b15611503576000818152600b60209081526040909120600201805460ff818116151660ff199091161790557ff8e1a15aba9398e019f0b49df1a4fde98ee17ae345cb5f6b5e2c27f5033e8ce791604051908152a1005b34610187576020366003190112610187576001600160a01b0361169c611dd0565b16600052600c602052602060ff604060002054166040519015158152f35b3461018757604036600319011261018757600435600090815260026020526040902054602435906001600160a01b031615610f955760018060a01b0360065416601982029180830460191490151715610a03576103e86040928351928352046020820152f35b346101875761175561173136611dfc565b91336001600160a01b03821603611757575b611750611049843361207d565b6125f2565b005b61176033612923565b611743565b34610187576000366003190112610187576020600954604051908152f35b34610187576000366003190112610187576007544211611a0b57336000526020600c815260ff604060002054166119f9576117bf600a54612022565b9081600a556117ed7f000000000000000000000000000000000000000000000000000000000000000061287b565b6118167f000000000000000000000000000000000000000000000000000000000000000061287b565b8101809111610a035760085461ffff9182169082906001600160a01b0316806119e0575b5081168061195c575b50505033600052600c81526040600020600160ff19825416179055611869600954612022565b60095533156119195760029061189d61189784600052600260205260018060a01b0360406000205416151590565b15612031565b6000838152600260205260409020546118c0906001600160a01b03161515611897565b336000526003815260406000206001815401905582600052526040600020336001600160601b0360a01b8254161790553360007fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef8180a4005b6064906040519062461bcd60e51b825280600483015260248201527f4552433732313a206d696e7420746f20746865207a65726f20616464726573736044820152fd5b605111156119d757905b83600052600b83526040600020911661ffff19825416179055600954604051828101913360601b83524160601b60348301526048820152604881526080810181811067ffffffffffffffff82111761096f5760405251902082600052600b8252600160406000200155828080611843565b50605090611966565b6119e99061287b565b1601818111610a0357818561183a565b604051631bbdf5c560e31b8152600490fd5b604051633d20ce7960e21b8152600490fd5b34610187576000366003190112610187576040517f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03168152602090f35b3461018757604036600319011261018757611a7b611dd0565b60243590611a8881612923565b6001600160a01b038080611a9b85611f84565b16921691808314611b9657803314908115611b71575b5015611b0657600083815260046020526040902080546001600160a01b03191683179055611ade83611f84565b167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925600080a4005b60405162461bcd60e51b815260206004820152603d60248201527f4552433732313a20617070726f76652063616c6c6572206973206e6f7420746f60448201527f6b656e206f776e6572206f7220617070726f76656420666f7220616c6c0000006064820152608490fd5b9050600052600560205260406000203360005260205260ff6040600020541684611ab1565b60405162461bcd60e51b815260206004820152602160248201527f4552433732313a20617070726f76616c20746f2063757272656e74206f776e656044820152603960f91b6064820152608490fd5b34610187576000366003190112610187576040517f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03168152602090f35b34610187576020366003190112610187576020611434600435611fe4565b346101875760003660031901126101875760405160008054611c6981611faa565b8084529060019081811690811561125d5750600114611c9257610ed684610ec281860382611ea2565b600080805292507f290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e5635b828410611cd3575050508101602001610ec2826111f4565b80546020858701810191909152909301928101611cbb565b34610187576020366003190112610187576004359063ffffffff60e01b82168092036101875760209163152a902d60e11b8114908115611d77575b8115611d34575b5015158152f35b6380ac58cd60e01b811491508115611d66575b8115611d55575b5083611d2d565b6301ffc9a760e01b14905083611d4e565b635b5e139f60e01b81149150611d47565b632483248360e11b81149150611d26565b60005b838110611d9b5750506000910152565b8181015183820152602001611d8b565b90602091611dc481518092818552858086019101611d88565b601f01601f1916010190565b600435906001600160a01b038216820361018757565b602435906001600160a01b038216820361018757565b6060906003190112610187576001600160a01b0390600435828116810361018757916024359081168103610187579060443590565b6020810190811067ffffffffffffffff82111761096f57604052565b610a00810190811067ffffffffffffffff82111761096f57604052565b6040810190811067ffffffffffffffff82111761096f57604052565b60a0810190811067ffffffffffffffff82111761096f57604052565b90601f8019910116810190811067ffffffffffffffff82111761096f57604052565b67ffffffffffffffff811161096f57601f01601f191660200190565b6006546001600160a01b03163303611ef457565b606460405162461bcd60e51b815260206004820152602060248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e65726044820152fd5b15611f3f57565b60405162461bcd60e51b815260206004820152601860248201527f4552433732313a20696e76616c696420746f6b656e20494400000000000000006044820152606490fd5b6000908152600260205260409020546001600160a01b0316611fa7811515611f38565b90565b90600182811c92168015611fda575b6020831014611fc457565b634e487b7160e01b600052602260045260246000fd5b91607f1691611fb9565b600081815260026020526040902054612007906001600160a01b03161515611f38565b6000908152600460205260409020546001600160a01b031690565b6000198114610a035760010190565b1561203857565b60405162461bcd60e51b815260206004820152601c60248201527f4552433732313a20746f6b656e20616c7265616479206d696e746564000000006044820152606490fd5b906001600160a01b03808061209184611f84565b169316918383149384156120c4575b5083156120ae575b50505090565b6120ba91929350611fe4565b16143880806120a8565b909350600052600560205260406000208260005260205260ff6040600020541692386120a0565b906120f582611ec4565b6121026040519182611ea2565b8281528092612113601f1991611ec4565b0190602036910137565b805115612260576040516060810181811067ffffffffffffffff82111761096f57604052604081527f4142434445464748494a4b4c4d4e4f505152535455565758595a61626364656660208201527f6768696a6b6c6d6e6f707172737475767778797a303132333435363738392b2f60408201528151600292838201809211610a03576003918290046001600160fe1b0381168103610a03576121c3908594951b6120eb565b936020850193829183518401925b83811061220f57505050505106806001146121fc576002146121f1575090565b603d90600019015390565b50603d9081600019820153600119015390565b85600491979293949701918251600190603f9082828260121c16880101518453828282600c1c16880101518385015382828260061c16880101518885015316850101518782015301959291906121d1565b5060405161226d81611e31565b6000815290565b806000917a184f03e93ff9f4daa797ed6e38ed64bf6a1f010000000000000000808210156123a9575b506d04ee2d6d415b85acef81000000008083101561239a575b50662386f26fc100008083101561238b575b506305f5e1008083101561237c575b506127108083101561236d575b50606482101561235d575b600a80921015612353575b60019081602161230b8287016120eb565b95860101905b61231d575b5050505090565b600019019083906f181899199a1a9b1b9c1cb0b131b232b360811b8282061a83530491821561234e57919082612311565b612316565b91600101916122fa565b91906064600291049101916122ef565b600491939204910191386122e4565b600891939204910191386122d7565b601091939204910191386122c8565b602091939204910191386122b6565b60409350810491503861229d565b600052600b60205260ff6002604060002001541661247a576040516123db81611e86565b607e81527f3c7061746820643d274d32303030203048305632303030483230303056305a2760208201527f2066696c6c3d2723656665666566272f3e3c7061746820643d274d313434382e60408201527f3839203434372e383532483535312e31303756313535322e313548313434382e60608201527f3839563434372e3835325a272066696c6c3d272366666666666627202f3e0000608082015290565b60405161248681611e86565b607e81527f3c7061746820643d274d32303030203048305632303030483230303056305a2760208201527f2066696c6c3d2723303030303030272f3e3c7061746820643d274d313434382e60408201527f3839203434372e383532483535312e31303756313535322e313548313434382e60608201527f3839563434372e3835325a272066696c6c3d272331313131313127202f3e0000608082015290565b9060508110156107295760051b0190565b1561253d57565b60405162461bcd60e51b815260206004820152602d60248201527f4552433732313a2063616c6c6572206973206e6f7420746f6b656e206f776e6560448201526c1c881bdc88185c1c1c9bdd9959609a1b6064820152608490fd5b1561259f57565b60405162461bcd60e51b815260206004820152602560248201527f4552433732313a207472616e736665722066726f6d20696e636f72726563742060448201526437bbb732b960d91b6064820152608490fd5b9061261a9161260084611f84565b6001600160a01b0393918416928492909183168414612598565b169182156126b257816126379161263086611f84565b1614612598565b7fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef60008481526004602052604081206001600160601b0360a01b9081815416905583825260036020526040822060001981540190558482526040822060018154019055858252600260205284604083209182541617905580a4565b60405162461bcd60e51b8152602060048201526024808201527f4552433732313a207472616e7366657220746f20746865207a65726f206164646044820152637265737360e01b6064820152608490fd5b60809060208152603260208201527f4552433732313a207472616e7366657220746f206e6f6e20455243373231526560408201527131b2b4bb32b91034b6b83632b6b2b73a32b960711b60608201520190565b9293600093909291803b15612870579484916127b09660405180948193630a85bd0160e11b9788845233600485015260018060a01b0380921660248501526044840152608060648401528260209b8c976084830190611dab565b0393165af184918161282c575b5061281b575050503d600014612813573d6127d781611ec4565b906127e56040519283611ea2565b81528091833d92013e5b805191826128105760405162461bcd60e51b81528061107e60048201612703565b01fd5b5060606127ef565b6001600160e01b0319161492509050565b9091508581813d8311612869575b6128448183611ea2565b8101031261286557516001600160e01b0319811681036128655790386127bd565b8480fd5b503d61283a565b505050915050600190565b604051633de222bb60e21b81526001600160a01b039091166004820152336024820152602081806044810103817f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03165afa908115612917576000916128e6575090565b906020823d821161290f575b816128ff60209383611ea2565b8101031261290c57505190565b80fd5b3d91506128f2565b6040513d6000823e3d90fd5b6daaeb6d7670e522a718067333cd4e803b61293c575050565b604051633185c44d60e21b81523060048201526001600160a01b038316602482015290602090829060449082905afa908115612917576000916129a5575b50156129835750565b604051633b79c77360e21b81526001600160a01b039091166004820152602490fd5b6020813d82116129db575b816129bd60209383611ea2565b810103126129d7575190811515820361290c57503861297a565b5080fd5b3d91506129b056fea2646970667358221220b79c97330278fc89a1df5625b96a51114b36b194edf3eba9f4a336a741fb9d5364736f6c63430008110033b10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf60000000000000000000000000000000000000000000000000000000063e647420000000000000000000000006fe0b5c1ba36c79d9d7dd43f60a3e53e3699c8c000000000000000000000000034eebee6942d8def3c125458d1a86e0a897fd6f9000000000000000000000000c3aa9bc72bd623168860a1e5c6a4530d3d80456c

Deployed Bytecode

0x608080604052600436101561001357600080fd5b60003560e01c90816301ffc9a714611ceb5750806306fdde0314611c48578063081812fc14611c2a5780630869806e14611be5578063095ea7b314611a6257806310fef2da14611a1d5780631249c58b1461178357806318160ddd1461176557806323b872dd146117205780632a55205a146116ba57806338e21cce1461167b5780633d317ed01461160757806340de28291461159657806341f434341461156d57806342842e0e1461151557806342966c68146114465780636352211e14611416578063685eca95146113ed57806370a0823114611356578063715018a6146112f95780638d899ea4146112ad5780638da5cb5b1461128457806395d89b41146111b9578063a085048714611174578063a22cb46514611090578063b88d4fde14610fa7578063c87b56dd146102a6578063e985e9c514610250578063f2fde38b1461018c5763fcfae6081461016957600080fd5b34610187576000366003190112610187576020600754604051908152f35b600080fd5b34610187576020366003190112610187576101a5611dd0565b6101ad611ee0565b6001600160a01b039081169081156101fc57600654826001600160601b0360a01b821617600655167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0600080a3005b60405162461bcd60e51b815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201526564647265737360d01b6064820152608490fd5b3461018757604036600319011261018757610269611dd0565b610271611de6565b9060018060a01b03809116600052600560205260406000209116600052602052602060ff604060002054166040519015158152f35b34610187576020366003190112610187576004356000908152600260205260409020546001600160a01b031615610f95576102e2600435612274565b6102ed6004356123b7565b60009160605b605060ff85161015610ba557610a0060405161030e81611e4d565b36903760405180605d60005b6050601f820110610a19575060ff6101e091548181168452818160081c166020850152818160101c166040850152818160181c166060850152818160201c166080850152818160281c1660a0850152818160301c1660c0850152818160381c1660e0850152818160401c16610100850152818160481c16610120850152818160501c16610140850152818160581c16610160850152818160601c16610180850152818160681c166101a0850152818160701c166101c085015260781c169101526103e381611e4d565b60005b6050811061098c57506004356000908152600b602052604090205461ffff16156109855761041960ff9182871690612525565b51165b600435600052600b60205261ffff6040600020541660ff86161060001461073f57605081101561072957606001549061046661ffff61045c818516612274565b9360101c16612274565b600435600052600b60205260ff605081600160406000200154818a161c160616605081101561072957600d0190604051917f3c7061746820643d274d302e38393330303520302e3839373934394833352e3060208401527f39334c33352e3039332031322e3239384834362e3439335634362e343938483360408401527f352e3039334c33352e3039332035372e3839373948302e38393330303556302e60608401526e3839373934395a272066696c6c3d2760881b608084015260009080549061053082611faa565b916001811690811561070457506001146106c9575b505060019383631e17b39f60e11b6106bf9461061a608c60ff9a98877f27202f3e3c7061746820643d274d33352e3039332031322e3239384831322e32604099527f39335633352e3039384833352e3039335631322e3239385a272066696c6c3d2760208201527f2366666666666627202f3e3c7061746820643d274d33352e312032332e363938898201527f4832332e375633352e3039384833352e315632332e3639385a272066696c6c3d60608201526b13919818181818181390179f60a11b608082015203606c810186520184611ea2565b6214939f60e91b855197856106398a975180926020808b019101611d88565b860177078ce40e8e4c2dce6ccdee4da7a4ee8e4c2dce6d8c2e8ca560431b6020820152610670825180936020603885019101611d88565b01600160fd1b60388201528251906106918260399560208785019101611d88565b01918201528251906106ac82603c9560208785019101611d88565b0191820152036020810184520182611ea2565b945b0116926102f3565b90915060005260206000206000905b8282106106ed5750508201608f018383610545565b60018160209254608f8589010152019101906106d8565b60ff1916608f8088019190915283151590930286019092019250859150849050610545565b634e487b7160e01b600052603260045260246000fd5b605081101561072957606001549061075e61ffff61045c818516612274565b916040518061014081011067ffffffffffffffff6101408301111761096f57604060019382631e17b39f60e11b60ff9761014061096996018552610102835260208301927f3c7061746820643d274d302e38393330303520302e3839373934394833352e3084527f39334c33352e3039332031322e3239384834362e3439335634362e3439384833868201527f352e3039334c33352e3039332035372e3839373948302e38393330303556302e60608201527f3839373934395a272066696c6c3d272336356263343827202f3e3c706174682060808201527f643d274d33352e3039332031322e3239384831322e3239335633352e3039384860a08201527f33352e3039335631322e3239385a272066696c6c3d272366666666666627202f60c08201527f3e3c7061746820643d274d33352e312032332e3639384832332e375633352e3060e08201527f39384833352e315632332e3639385a272066696c6c3d2723303030303030272061010082015261179f60f11b6101208201526214939f60e91b865198866108fa8b985180926020808c019101611d88565b870177078ce40e8e4c2dce6ccdee4da7a4ee8e4c2dce6d8c2e8ca560431b6020820152610931825180936020603885019101611d88565b01600160fd1b60388201528351906109528260399660208885019101611d88565b019283015251906106ac82603c9586840190611d88565b946106c1565b634e487b7160e01b600052604160045260246000fd5b508361041c565b600435600052600b602052600190816040600020015491810191828211610a035782156109ed57826109e7910660ff6109c58487612525565b5116926109e060ff6109d78489612525565b51169187612525565b5284612525565b526103e6565b634e487b7160e01b600052601260045260246000fd5b634e487b7160e01b600052601160045260246000fd5b90916001610400602092855460ff8116825260ff8160081c168583015260ff8160101c16604083015260ff8160181c16606083015260ff8160808282891c1681860152828260a0828260281c1681890152828260c095828260301c16878c0152828260381c1660e08c0152828260401c166101008c0152828260481c166101208c0152828260501c166101408c0152828260581c166101608c0152828260601c166101808c0152828260681c166101a08c0152828260701c166101c08c0152828260781c166101e08c01521c16610200890152828260881c16610220890152828260901c16610240890152828260981c166102608901521c16610280860152828260a81c166102a0860152828260b01c166102c0860152828260b81c166102e08601521c1661030083015260ff8160c81c1661032083015260ff8160d01c1661034083015260ff8160d81c1661036083015260ff8160e01c1661038083015260ff8160e81c166103a083015260ff8160f01c166103c083015260f81c6103e0820152019301910161031a565b90610c5560d5610c5a610e6f94610c556066610ed6986040519384917f3c73766720786d6c6e733d27687474703a2f2f7777772e77332e6f72672f323060208401527f30302f737667272076696577426f783d2730203020323030302032303030273e6040840152610c21815180926020606087019101611d88565b8201610c37825180936020606085019101611d88565b01651e17b9bb339f60d11b6060820152036046810184520182611ea2565b61211d565b92600435600052600b60205260ff60026040600020015416600014610f7257604051610c8581611e6a565b60048152634461726b60e01b60208201525b60405160208101917f7b2274726169745f74797065223a20224d6f6465222c202276616c7565223a208352601160f91b6040830152610ce0815180926020604186019101611d88565b8101610d0360438361227d60f01b93846041820152036023810185520183611ea2565b8192600435600052600b60205261ffff8060406000205416610eda575b505050506040519485927f7b226e616d65223a2022436865636b20547572747a20230000000000000000006020850152610d64815180926020603788019101611d88565b83017f222c20226465736372697074696f6e223a2022436865636b20547572747a206960378201527f7320612066756c6c79206f6e2d636861696e2c2067616d69666965642054696e60578201527f792057696e67656420547572746c657a207820436865636b732064657269766160778201527f746976652e222c2022696d616765223a2022646174613a696d6167652f73766760978201526b0ade1b5b0ed8985cd94d8d0b60a21b60b7820152610e2782518093602060c385019101611d88565b016f222c2261747472696275746573223a5b60801b60c3820152610e5582518093602060d385019101611d88565b01615d7d60f01b60d38201520360b5810184520182611ea2565b610ec2603d60405180937f646174613a6170706c69636174696f6e2f6a736f6e3b6261736536342c0000006020830152610eb28151809260208686019101611d88565b810103601d810184520182611ea2565b604051918291602083526020830190611dab565b0390f35b610f0b945091610f699391610ef760469460406000205416612274565b906040519687945180926020870190611d88565b83017f2c7b2274726169745f74797065223a2022436f6c6f7273222c202276616c7565602082015263111d101160e11b6040820152610f54825180936020604485019101611d88565b01906044820152036026810184520182611ea2565b87808080610d20565b604051610f7e81611e6a565b6005815264131a59da1d60da1b6020820152610c97565b60405163677510db60e11b8152600490fd5b3461018757608036600319011261018757610fc0611dd0565b610fc8611de6565b906044356064359267ffffffffffffffff8411610187573660238501121561018757836004013592610ff984611ec4565b936110076040519586611ea2565b808552366024828801011161018757602081600092602461105e99018389013786010152336001600160a01b03821603611082575b61104e611049843361207d565b612536565b6110598383836125f2565b612756565b1561106557005b60405162461bcd60e51b81528061107e60048201612703565b0390fd5b61108b33612923565b61103c565b34610187576040366003190112610187576110a9611dd0565b6024359081151590818303610187576110c181612923565b6001600160a01b03169133831461112f576111019033600052600560205260406000208460005260205260406000209060ff801983541691151516179055565b6040519081527f17307eab39ab6107e8899845ad3d59bd9653f200f220920489ca2b5937696c3160203392a3005b60405162461bcd60e51b815260206004820152601960248201527f4552433732313a20617070726f766520746f2063616c6c6572000000000000006044820152606490fd5b34610187576000366003190112610187576040517f000000000000000000000000c3aa9bc72bd623168860a1e5c6a4530d3d80456c6001600160a01b03168152602090f35b3461018757600036600319011261018757604051600060018054906111dd82611faa565b8085529181811690811561125d5750600114611204575b610ed684610ec281860382611ea2565b600081815292507fb10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf65b828410611245575050508101602001610ec2826111f4565b8054602085870181019190915290930192810161122d565b60ff191660208087019190915292151560051b85019092019250610ec291508390506111f4565b34610187576000366003190112610187576006546040516001600160a01b039091168152602090f35b3461018757602036600319011261018757600435600052600b6020526060604060002061ffff8154169060ff600260018301549201541690604051928352602083015215156040820152f35b3461018757600036600319011261018757611312611ee0565b600680546001600160a01b031981169091556000906001600160a01b03167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e08280a3005b34610187576020366003190112610187576001600160a01b03611377611dd0565b1680156113965760005260036020526020604060002054604051908152f35b60405162461bcd60e51b815260206004820152602960248201527f4552433732313a2061646472657373207a65726f206973206e6f7420612076616044820152683634b21037bbb732b960b91b6064820152608490fd5b34610187576000366003190112610187576008546040516001600160a01b039091168152602090f35b34610187576020366003190112610187576020611434600435611f84565b6040516001600160a01b039091168152f35b3461018757602036600319011261018757600435611464813361207d565b15611503576009548015610a035760001990810160095560009061148783611f84565b5061149183611f84565b838352600460205260408320906001600160601b0360a01b9182815416905560018060a01b0316918284526003602052604084209081540190558383526002602052604083209081541690557fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef8280a4005b60405163390cdd9b60e21b8152600490fd5b346101875761105e61152636611dfc565b6001600160a01b038316331415929091908361155f575b6040519361154a85611e31565b600085526110825761104e611049843361207d565b61156833612923565b61153d565b346101875760003660031901126101875760206040516daaeb6d7670e522a718067333cd4e8152f35b34610187576020366003190112610187576115af611dd0565b6115b7611ee0565b600854906001600160a01b03908183166115f557169081156115e3576001600160a01b03191617600855005b60405163022750fb60e31b8152600490fd5b604051631b7d42d760e11b8152600490fd5b3461018757602036600319011261018757600435611625813361207d565b15611503576000818152600b60209081526040909120600201805460ff818116151660ff199091161790557ff8e1a15aba9398e019f0b49df1a4fde98ee17ae345cb5f6b5e2c27f5033e8ce791604051908152a1005b34610187576020366003190112610187576001600160a01b0361169c611dd0565b16600052600c602052602060ff604060002054166040519015158152f35b3461018757604036600319011261018757600435600090815260026020526040902054602435906001600160a01b031615610f955760018060a01b0360065416601982029180830460191490151715610a03576103e86040928351928352046020820152f35b346101875761175561173136611dfc565b91336001600160a01b03821603611757575b611750611049843361207d565b6125f2565b005b61176033612923565b611743565b34610187576000366003190112610187576020600954604051908152f35b34610187576000366003190112610187576007544211611a0b57336000526020600c815260ff604060002054166119f9576117bf600a54612022565b9081600a556117ed7f0000000000000000000000006fe0b5c1ba36c79d9d7dd43f60a3e53e3699c8c061287b565b6118167f00000000000000000000000034eebee6942d8def3c125458d1a86e0a897fd6f961287b565b8101809111610a035760085461ffff9182169082906001600160a01b0316806119e0575b5081168061195c575b50505033600052600c81526040600020600160ff19825416179055611869600954612022565b60095533156119195760029061189d61189784600052600260205260018060a01b0360406000205416151590565b15612031565b6000838152600260205260409020546118c0906001600160a01b03161515611897565b336000526003815260406000206001815401905582600052526040600020336001600160601b0360a01b8254161790553360007fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef8180a4005b6064906040519062461bcd60e51b825280600483015260248201527f4552433732313a206d696e7420746f20746865207a65726f20616464726573736044820152fd5b605111156119d757905b83600052600b83526040600020911661ffff19825416179055600954604051828101913360601b83524160601b60348301526048820152604881526080810181811067ffffffffffffffff82111761096f5760405251902082600052600b8252600160406000200155828080611843565b50605090611966565b6119e99061287b565b1601818111610a0357818561183a565b604051631bbdf5c560e31b8152600490fd5b604051633d20ce7960e21b8152600490fd5b34610187576000366003190112610187576040517f0000000000000000000000006fe0b5c1ba36c79d9d7dd43f60a3e53e3699c8c06001600160a01b03168152602090f35b3461018757604036600319011261018757611a7b611dd0565b60243590611a8881612923565b6001600160a01b038080611a9b85611f84565b16921691808314611b9657803314908115611b71575b5015611b0657600083815260046020526040902080546001600160a01b03191683179055611ade83611f84565b167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925600080a4005b60405162461bcd60e51b815260206004820152603d60248201527f4552433732313a20617070726f76652063616c6c6572206973206e6f7420746f60448201527f6b656e206f776e6572206f7220617070726f76656420666f7220616c6c0000006064820152608490fd5b9050600052600560205260406000203360005260205260ff6040600020541684611ab1565b60405162461bcd60e51b815260206004820152602160248201527f4552433732313a20617070726f76616c20746f2063757272656e74206f776e656044820152603960f91b6064820152608490fd5b34610187576000366003190112610187576040517f00000000000000000000000034eebee6942d8def3c125458d1a86e0a897fd6f96001600160a01b03168152602090f35b34610187576020366003190112610187576020611434600435611fe4565b346101875760003660031901126101875760405160008054611c6981611faa565b8084529060019081811690811561125d5750600114611c9257610ed684610ec281860382611ea2565b600080805292507f290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e5635b828410611cd3575050508101602001610ec2826111f4565b80546020858701810191909152909301928101611cbb565b34610187576020366003190112610187576004359063ffffffff60e01b82168092036101875760209163152a902d60e11b8114908115611d77575b8115611d34575b5015158152f35b6380ac58cd60e01b811491508115611d66575b8115611d55575b5083611d2d565b6301ffc9a760e01b14905083611d4e565b635b5e139f60e01b81149150611d47565b632483248360e11b81149150611d26565b60005b838110611d9b5750506000910152565b8181015183820152602001611d8b565b90602091611dc481518092818552858086019101611d88565b601f01601f1916010190565b600435906001600160a01b038216820361018757565b602435906001600160a01b038216820361018757565b6060906003190112610187576001600160a01b0390600435828116810361018757916024359081168103610187579060443590565b6020810190811067ffffffffffffffff82111761096f57604052565b610a00810190811067ffffffffffffffff82111761096f57604052565b6040810190811067ffffffffffffffff82111761096f57604052565b60a0810190811067ffffffffffffffff82111761096f57604052565b90601f8019910116810190811067ffffffffffffffff82111761096f57604052565b67ffffffffffffffff811161096f57601f01601f191660200190565b6006546001600160a01b03163303611ef457565b606460405162461bcd60e51b815260206004820152602060248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e65726044820152fd5b15611f3f57565b60405162461bcd60e51b815260206004820152601860248201527f4552433732313a20696e76616c696420746f6b656e20494400000000000000006044820152606490fd5b6000908152600260205260409020546001600160a01b0316611fa7811515611f38565b90565b90600182811c92168015611fda575b6020831014611fc457565b634e487b7160e01b600052602260045260246000fd5b91607f1691611fb9565b600081815260026020526040902054612007906001600160a01b03161515611f38565b6000908152600460205260409020546001600160a01b031690565b6000198114610a035760010190565b1561203857565b60405162461bcd60e51b815260206004820152601c60248201527f4552433732313a20746f6b656e20616c7265616479206d696e746564000000006044820152606490fd5b906001600160a01b03808061209184611f84565b169316918383149384156120c4575b5083156120ae575b50505090565b6120ba91929350611fe4565b16143880806120a8565b909350600052600560205260406000208260005260205260ff6040600020541692386120a0565b906120f582611ec4565b6121026040519182611ea2565b8281528092612113601f1991611ec4565b0190602036910137565b805115612260576040516060810181811067ffffffffffffffff82111761096f57604052604081527f4142434445464748494a4b4c4d4e4f505152535455565758595a61626364656660208201527f6768696a6b6c6d6e6f707172737475767778797a303132333435363738392b2f60408201528151600292838201809211610a03576003918290046001600160fe1b0381168103610a03576121c3908594951b6120eb565b936020850193829183518401925b83811061220f57505050505106806001146121fc576002146121f1575090565b603d90600019015390565b50603d9081600019820153600119015390565b85600491979293949701918251600190603f9082828260121c16880101518453828282600c1c16880101518385015382828260061c16880101518885015316850101518782015301959291906121d1565b5060405161226d81611e31565b6000815290565b806000917a184f03e93ff9f4daa797ed6e38ed64bf6a1f010000000000000000808210156123a9575b506d04ee2d6d415b85acef81000000008083101561239a575b50662386f26fc100008083101561238b575b506305f5e1008083101561237c575b506127108083101561236d575b50606482101561235d575b600a80921015612353575b60019081602161230b8287016120eb565b95860101905b61231d575b5050505090565b600019019083906f181899199a1a9b1b9c1cb0b131b232b360811b8282061a83530491821561234e57919082612311565b612316565b91600101916122fa565b91906064600291049101916122ef565b600491939204910191386122e4565b600891939204910191386122d7565b601091939204910191386122c8565b602091939204910191386122b6565b60409350810491503861229d565b600052600b60205260ff6002604060002001541661247a576040516123db81611e86565b607e81527f3c7061746820643d274d32303030203048305632303030483230303056305a2760208201527f2066696c6c3d2723656665666566272f3e3c7061746820643d274d313434382e60408201527f3839203434372e383532483535312e31303756313535322e313548313434382e60608201527f3839563434372e3835325a272066696c6c3d272366666666666627202f3e0000608082015290565b60405161248681611e86565b607e81527f3c7061746820643d274d32303030203048305632303030483230303056305a2760208201527f2066696c6c3d2723303030303030272f3e3c7061746820643d274d313434382e60408201527f3839203434372e383532483535312e31303756313535322e313548313434382e60608201527f3839563434372e3835325a272066696c6c3d272331313131313127202f3e0000608082015290565b9060508110156107295760051b0190565b1561253d57565b60405162461bcd60e51b815260206004820152602d60248201527f4552433732313a2063616c6c6572206973206e6f7420746f6b656e206f776e6560448201526c1c881bdc88185c1c1c9bdd9959609a1b6064820152608490fd5b1561259f57565b60405162461bcd60e51b815260206004820152602560248201527f4552433732313a207472616e736665722066726f6d20696e636f72726563742060448201526437bbb732b960d91b6064820152608490fd5b9061261a9161260084611f84565b6001600160a01b0393918416928492909183168414612598565b169182156126b257816126379161263086611f84565b1614612598565b7fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef60008481526004602052604081206001600160601b0360a01b9081815416905583825260036020526040822060001981540190558482526040822060018154019055858252600260205284604083209182541617905580a4565b60405162461bcd60e51b8152602060048201526024808201527f4552433732313a207472616e7366657220746f20746865207a65726f206164646044820152637265737360e01b6064820152608490fd5b60809060208152603260208201527f4552433732313a207472616e7366657220746f206e6f6e20455243373231526560408201527131b2b4bb32b91034b6b83632b6b2b73a32b960711b60608201520190565b9293600093909291803b15612870579484916127b09660405180948193630a85bd0160e11b9788845233600485015260018060a01b0380921660248501526044840152608060648401528260209b8c976084830190611dab565b0393165af184918161282c575b5061281b575050503d600014612813573d6127d781611ec4565b906127e56040519283611ea2565b81528091833d92013e5b805191826128105760405162461bcd60e51b81528061107e60048201612703565b01fd5b5060606127ef565b6001600160e01b0319161492509050565b9091508581813d8311612869575b6128448183611ea2565b8101031261286557516001600160e01b0319811681036128655790386127bd565b8480fd5b503d61283a565b505050915050600190565b604051633de222bb60e21b81526001600160a01b039091166004820152336024820152602081806044810103817f000000000000000000000000c3aa9bc72bd623168860a1e5c6a4530d3d80456c6001600160a01b03165afa908115612917576000916128e6575090565b906020823d821161290f575b816128ff60209383611ea2565b8101031261290c57505190565b80fd5b3d91506128f2565b6040513d6000823e3d90fd5b6daaeb6d7670e522a718067333cd4e803b61293c575050565b604051633185c44d60e21b81523060048201526001600160a01b038316602482015290602090829060449082905afa908115612917576000916129a5575b50156129835750565b604051633b79c77360e21b81526001600160a01b039091166004820152602490fd5b6020813d82116129db575b816129bd60209383611ea2565b810103126129d7575190811515820361290c57503861297a565b5080fd5b3d91506129b056fea2646970667358221220b79c97330278fc89a1df5625b96a51114b36b194edf3eba9f4a336a741fb9d5364736f6c63430008110033

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

0000000000000000000000000000000000000000000000000000000063e647420000000000000000000000006fe0b5c1ba36c79d9d7dd43f60a3e53e3699c8c000000000000000000000000034eebee6942d8def3c125458d1a86e0a897fd6f9000000000000000000000000c3aa9bc72bd623168860a1e5c6a4530d3d80456c

-----Decoded View---------------
Arg [0] : _mintEnds (uint256): 1676035906
Arg [1] : _twtAddress (address): 0x6fE0b5c1ba36C79D9D7dD43f60a3e53E3699C8C0
Arg [2] : _checksAddress (address): 0x34eEBEE6942d8Def3c125458D1a86e0A897fd6f9
Arg [3] : _warmWalletAddress (address): 0xC3AA9bc72Bd623168860a1e5c6a4530d3D80456c

-----Encoded View---------------
4 Constructor Arguments found :
Arg [0] : 0000000000000000000000000000000000000000000000000000000063e64742
Arg [1] : 0000000000000000000000006fe0b5c1ba36c79d9d7dd43f60a3e53e3699c8c0
Arg [2] : 00000000000000000000000034eebee6942d8def3c125458d1a86e0a897fd6f9
Arg [3] : 000000000000000000000000c3aa9bc72bd623168860a1e5c6a4530d3d80456c


Block Transaction Difficulty Gas Used Reward
View All Blocks Produced

Block Uncle Number Difficulty Gas Used Reward
View All Uncles
Loading...
Loading
Loading...
Loading

OVERVIEW

Check Turtz is a fully on-chain, gamified Tiny Winged Turtlez x Checks derivative.

Validator Index Block Amount
View All Withdrawals

Transaction Hash Block Value Eth2 PubKey Valid
View All Deposits
Loading...
Loading
[ Download: CSV Export  ]

A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.