ETH Price: $3,855.58 (-1.60%)

Contract

0xa518286D66f3699BA4F546F3AddB75F2B838C307
 

Overview

ETH Balance

0 ETH

Eth Value

$0.00

Token Holdings

Multichain Info

No addresses found
Transaction Hash
Method
Block
From
To
Set Approval For...212488122024-11-23 7:01:3521 days ago1732345295IN
0xa518286D...2B838C307
0 ETH0.0006165510.38885892
Set Approval For...212432912024-11-22 12:32:1122 days ago1732278731IN
0xa518286D...2B838C307
0 ETH0.0007390112.45218302
Set Approval For...210227062024-10-22 17:46:5953 days ago1729619219IN
0xa518286D...2B838C307
0 ETH0.0007717213.00342307
Set Approval For...197300962024-04-25 4:48:35233 days ago1714020515IN
0xa518286D...2B838C307
0 ETH0.000393066.61376303
Set Approval For...196851822024-04-18 22:00:23239 days ago1713477623IN
0xa518286D...2B838C307
0 ETH0.000330198.82033041
Withdraw196013292024-04-07 4:06:23251 days ago1712462783IN
0xa518286D...2B838C307
0 ETH0.0003309310.79129559
Safe Transfer Fr...190798792024-01-24 23:49:47324 days ago1706140187IN
0xa518286D...2B838C307
0 ETH0.0005976312.40622555
Safe Transfer Fr...190798062024-01-24 23:34:59324 days ago1706139299IN
0xa518286D...2B838C307
0 ETH0.000478929.94193334
Safe Transfer Fr...190797322024-01-24 23:20:11324 days ago1706138411IN
0xa518286D...2B838C307
0 ETH0.0005198910.79254387
Safe Transfer Fr...190796992024-01-24 23:13:35324 days ago1706138015IN
0xa518286D...2B838C307
0 ETH0.0005226810.85035241
Safe Transfer Fr...190796842024-01-24 23:10:35324 days ago1706137835IN
0xa518286D...2B838C307
0 ETH0.000499310.36503918
Set Approval For...189687062024-01-09 10:07:11340 days ago1704794831IN
0xa518286D...2B838C307
0 ETH0.0008601614.49353156
Transfer From189685862024-01-09 9:42:59340 days ago1704793379IN
0xa518286D...2B838C307
0 ETH0.0010460316.87643457
Set Approval For...189660652024-01-09 1:11:23340 days ago1704762683IN
0xa518286D...2B838C307
0 ETH0.0009304215.67746126
Set Approval For...189573152024-01-07 19:35:35342 days ago1704656135IN
0xa518286D...2B838C307
0 ETH0.0017658529.71211112
Set Approval For...189389902024-01-05 5:26:35344 days ago1704432395IN
0xa518286D...2B838C307
0 ETH0.0009166815.4459094
Set Approval For...189216302024-01-02 18:58:59347 days ago1704221939IN
0xa518286D...2B838C307
0 ETH0.0007975521.30443469
Set Approval For...189020822023-12-31 1:05:47349 days ago1703984747IN
0xa518286D...2B838C307
0 ETH0.0009390415.8003342
Set Approval For...189020812023-12-31 1:05:35349 days ago1703984735IN
0xa518286D...2B838C307
0 ETH0.0009314215.69437098
Set Approval For...189019852023-12-31 0:45:47349 days ago1703983547IN
0xa518286D...2B838C307
0 ETH0.000920715.47512781
Set Approval For...188938882023-12-29 21:25:35350 days ago1703885135IN
0xa518286D...2B838C307
0 ETH0.0010513117.71444619
Set Approval For...188667892023-12-26 2:06:59354 days ago1703556419IN
0xa518286D...2B838C307
0 ETH0.0004207211.2386105
Set Approval For...188517142023-12-23 23:15:11356 days ago1703373311IN
0xa518286D...2B838C307
0 ETH0.0010308117.36904639
Set Approval For...188488592023-12-23 13:37:59357 days ago1703338679IN
0xa518286D...2B838C307
0 ETH0.0014434924.32255905
Set Approval For...188451422023-12-23 1:07:23357 days ago1703293643IN
0xa518286D...2B838C307
0 ETH0.0013438222.64307637
View all transactions

Latest 25 internal transactions (View All)

Advanced mode:
Parent Transaction Hash Block
From
To
196013292024-04-07 4:06:23251 days ago1712462783
0xa518286D...2B838C307
19.63 ETH
161007482022-12-03 0:36:59742 days ago1670027819
0xa518286D...2B838C307
0.006574 ETH
161007482022-12-03 0:36:59742 days ago1670027819
0xa518286D...2B838C307
0.013426 ETH
161007472022-12-03 0:36:47742 days ago1670027807
0xa518286D...2B838C307
0.009231 ETH
161007472022-12-03 0:36:47742 days ago1670027807
0xa518286D...2B838C307
0.000769 ETH
161007472022-12-03 0:36:47742 days ago1670027807
0xa518286D...2B838C307
0.008935 ETH
161007472022-12-03 0:36:47742 days ago1670027807
0xa518286D...2B838C307
0.001065 ETH
161007472022-12-03 0:36:47742 days ago1670027807
0xa518286D...2B838C307
0.016434 ETH
161007472022-12-03 0:36:47742 days ago1670027807
0xa518286D...2B838C307
0.003566 ETH
161007462022-12-03 0:36:35742 days ago1670027795
0xa518286D...2B838C307
0.001825 ETH
161007462022-12-03 0:36:35742 days ago1670027795
0xa518286D...2B838C307
0.008175 ETH
161007452022-12-03 0:36:23742 days ago1670027783
0xa518286D...2B838C307
0.005637 ETH
161007452022-12-03 0:36:23742 days ago1670027783
0xa518286D...2B838C307
0.004363 ETH
161007452022-12-03 0:36:23742 days ago1670027783
0xa518286D...2B838C307
0.09079 ETH
161007452022-12-03 0:36:23742 days ago1670027783
0xa518286D...2B838C307
0.00921 ETH
161007452022-12-03 0:36:23742 days ago1670027783
0xa518286D...2B838C307
0.010734 ETH
161007452022-12-03 0:36:23742 days ago1670027783
0xa518286D...2B838C307
0.009266 ETH
161007452022-12-03 0:36:23742 days ago1670027783
0xa518286D...2B838C307
0.07 ETH
161007452022-12-03 0:36:23742 days ago1670027783
0xa518286D...2B838C307
0.03 ETH
161007432022-12-03 0:35:59742 days ago1670027759
0xa518286D...2B838C307
0.006596 ETH
161007432022-12-03 0:35:59742 days ago1670027759
0xa518286D...2B838C307
0.003404 ETH
161007432022-12-03 0:35:59742 days ago1670027759
0xa518286D...2B838C307
0.001001 ETH
161007432022-12-03 0:35:59742 days ago1670027759
0xa518286D...2B838C307
0.008999 ETH
161007432022-12-03 0:35:59742 days ago1670027759
0xa518286D...2B838C307
0.008769 ETH
161007432022-12-03 0:35:59742 days ago1670027759
0xa518286D...2B838C307
0.001231 ETH
View All Internal Transactions
Loading...
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
NonFungibleMoons

Compiler Version
v0.8.17+commit.8df45f5f

Optimization Enabled:
Yes with 200 runs

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

import {ERC721A} from "@erc721a/contracts/ERC721A.sol";
import {Strings} from "@openzeppelin/contracts/utils/Strings.sol";

import {LibPRNG} from "../utils/LibPRNG.sol";
import {Utils} from "../utils/Utils.sol";
import {MoonCalculations} from "../moon/MoonCalculations.sol";
import {MoonRenderer} from "../moon/MoonRenderer.sol";
import {MoonSvg} from "../moon/MoonSvg.sol";
import {MoonConfig} from "../moon/MoonConfig.sol";
import {DynamicNftRegistryInterface} from "../interfaces/dynamicNftRegistry/DynamicNftRegistryInterface.sol";
import {AlienArtBase, MoonImageConfig} from "../interfaces/alienArt/AlienArtBase.sol";
import {AlienArtConstellation} from "../alienArt/constellation/AlienArtConstellation.sol";
import {ERC1155TokenReceiver} from "../ext/ERC1155.sol";
import {MoonNFTEventsAndErrors} from "./MoonNFTEventsAndErrors.sol";
import {Ownable} from "../ext/Ownable.sol";
import {IERC2981} from "../interfaces/ext/IERC2981.sol";
import {IERC165} from "../interfaces/ext/IERC165.sol";
import {DefaultOperatorFilterer} from "../ext/DefaultOperatorFilterer.sol";

/*
███╗░░██╗░█████╗░███╗░░██╗
████╗░██║██╔══██╗████╗░██║
██╔██╗██║██║░░██║██╔██╗██║
██║╚████║██║░░██║██║╚████║
██║░╚███║╚█████╔╝██║░╚███║
╚═╝░░╚══╝░╚════╝░╚═╝░░╚══╝

███████╗██╗░░░██╗███╗░░██╗░██████╗░██╗██████╗░██╗░░░░░███████╗
██╔════╝██║░░░██║████╗░██║██╔════╝░██║██╔══██╗██║░░░░░██╔════╝
█████╗░░██║░░░██║██╔██╗██║██║░░██╗░██║██████╦╝██║░░░░░█████╗░░
██╔══╝░░██║░░░██║██║╚████║██║░░╚██╗██║██╔══██╗██║░░░░░██╔══╝░░
██║░░░░░╚██████╔╝██║░╚███║╚██████╔╝██║██████╦╝███████╗███████╗
╚═╝░░░░░░╚═════╝░╚═╝░░╚══╝░╚═════╝░╚═╝╚═════╝░╚══════╝╚══════╝

███╗░░░███╗░█████╗░░█████╗░███╗░░██╗░██████╗
████╗░████║██╔══██╗██╔══██╗████╗░██║██╔════╝
██╔████╔██║██║░░██║██║░░██║██╔██╗██║╚█████╗░
██║╚██╔╝██║██║░░██║██║░░██║██║╚████║░╚═══██╗
██║░╚═╝░██║╚█████╔╝╚█████╔╝██║░╚███║██████╔╝
*/

/// @title NonFungibleMoons
/// @author Aspyn Palatnick (aspyn.eth, stuckinaboot.eth)
/// @notice Interactive on-chain generative moon NFTs with art that closely mirrors the phase of
/// the real world moon. These NFTs support on-chain art composition, art regeneration, and mint referrals.
contract NonFungibleMoons is
    DefaultOperatorFilterer,
    ERC721A,
    IERC2981,
    ERC1155TokenReceiver,
    Ownable,
    MoonNFTEventsAndErrors
{
    using LibPRNG for LibPRNG.PRNG;

    uint256 public constant MAX_SUPPLY = 513;
    uint256 public constant PRICE = 0.04 ether;

    address payable internal constant VAULT_ADDRESS =
        payable(0x39Ab90066cec746A032D67e4fe3378f16294CF6b);

    // On mint, PRICE / FRACTION_OF_PRICE_FOR_REFERRAL will go to referrals
    uint256 internal constant FRACTION_OF_PRICE_FOR_REFERRAL = 4;

    // Maps moon token id to randomness seed
    mapping(uint256 => bytes32) public moonSeeds;
    // Maps moon token id to number of regenerates used by current owner
    mapping(uint256 => uint8) public regeneratesUsedByCurrentOwner;
    uint8 internal constant MAX_REGENERATES_PER_OWNER = 3;
    uint64 internal constant COOLDOWN_PERIOD = 120;

    address public dynamicNftRegistryAddress;
    address public defaultAlienArtAddress;

    // Mapping from token ID to alien art
    mapping(uint256 => address) public alienArtAddressMap;

    uint256 internal constant INTERVAL_BETWEEN_ANIMATION_SAMPLES =
        MoonCalculations.LUNAR_MONTH_LENGTH_IN_MS / 120;

    /***********************************
     ** Welcome to Non-Fungible Moons **
     ***********************************/

    constructor(
        string memory _name,
        string memory _symbol,
        address _defaultAlienArtAddress
    ) ERC721A(_name, _symbol) {
        // Set default alien art contract, which should be the constellations address
        defaultAlienArtAddress = _defaultAlienArtAddress;
    }

    /*************************************************************
     ** Collect moons and explore the potential of on-chain art **
     *************************************************************/

    /// @notice Mint NFT.
    /// @param amount amount of token that the sender wants to mint.
    function mint(uint256 amount) external payable {
        _mintCore(amount);
    }

    function _mintCore(uint256 amount) internal returns (uint256) {
        // Checks

        // Enforce basic mint checks
        if (MAX_SUPPLY < _nextTokenId() + amount) {
            revert MaxSupplyReached();
        }
        if (msg.value != PRICE * amount) {
            revert WrongEtherAmount();
        }

        // Effects
        uint256 nextMoonTokenIdToBeMinted = _nextTokenId();

        // Store moon seeds
        // NOTE: we do not need to set regenerates used for these tokens (regeneratesUsedByCurrentOwner) since the
        // regenerates used for newly minted token ids will default to 0
        for (
            uint256 tokenId = nextMoonTokenIdToBeMinted;
            tokenId < nextMoonTokenIdToBeMinted + amount;
            ++tokenId
        ) {
            moonSeeds[tokenId] = MoonConfig.getMoonSeed(tokenId);
        }

        // Mint moons
        _mint(msg.sender, amount);

        // Interactions

        // Mint constellations
        AlienArtConstellation(defaultAlienArtAddress).mint(
            nextMoonTokenIdToBeMinted,
            amount
        );

        return nextMoonTokenIdToBeMinted;
    }

    /**************************************************************
     ** Once you own a moon, earn on-chain mint referral rewards **
     **************************************************************/

    /// @notice Mint NFT with referrer.
    /// @param amount amount of token that the sender wants to mint.
    /// @param referrer referrer who will receive part of the payment.
    /// @param referrerTokenId token that referrer owns.
    function mintWithReferrer(
        uint256 amount,
        address payable referrer,
        uint256 referrerTokenId
    ) public payable {
        uint256 nextMoonTokenIdToBeMinted = _mintCore(amount);

        // Pay out referral funds if the following conditions are met
        if (
            // 1. Referrer is not 0 address
            referrer != address(0) &&
            // 2. Referrer is not self
            referrer != msg.sender &&
            // 3. Referrer owns the input token
            referrer == ownerOf(referrerTokenId)
        ) {
            // Get referral amounts
            (uint256 referrerValue, uint256 referredValue) = getReferralAmounts(
                referrer,
                msg.sender,
                msg.value
            );

            // Emit minted with referrer event
            emit MintedWithReferrer(
                referrer,
                referrerTokenId,
                msg.sender,
                nextMoonTokenIdToBeMinted,
                amount,
                referrerValue,
                referredValue
            );

            // Transfer ETH to referrer and referred
            referrer.transfer(referrerValue);
            payable(msg.sender).transfer(referredValue);
        }
    }

    /// @notice Get amounts that should be paid out to referrer and referred.
    /// @param referrer referrer who will receive part of the payment.
    /// @param referred referred who will receive part of the payment.
    /// @param value value of the mint.
    /// @return referrerValue value to be paid to referrer, referredValue value to be paid to referred.
    function getReferralAmounts(
        address referrer,
        address referred,
        uint256 value
    ) public view returns (uint256 referrerValue, uint256 referredValue) {
        // Amount from the value that will be distributed between the referrer and referred
        uint256 amtWithheldForReferrals = value /
            FRACTION_OF_PRICE_FOR_REFERRAL;

        LibPRNG.PRNG memory prng;
        prng.seed(
            keccak256(abi.encodePacked(block.difficulty, referrer, referred))
        );
        // Note: integer division will imply the result is truncated (e.g. 5 / 2 = 2).
        // This is the expected behavior.
        referredValue =
            // Random value ranging from 0 to 10000
            (amtWithheldForReferrals * prng.uniform(10001)) /
            10000;
        referrerValue = amtWithheldForReferrals - referredValue;
    }

    /****************************************************
     ** Alter the Alien Art for your moons at any time **
     ****************************************************/

    /// @notice Set alien art address for particular tokens.
    /// @param tokenIds token ids.
    /// @param alienArtAddress alien art contract.
    function setAlienArtAddresses(
        uint256[] calldata tokenIds,
        address alienArtAddress
    ) external {
        if (tokenIds.length > MAX_SUPPLY) {
            revert MaxSupplyReached();
        }

        // If alien art address is not null address, validate that alien
        // art address is pointing to a valid alien art contract
        if (
            alienArtAddress != address(0) &&
            !AlienArtBase(alienArtAddress).supportsInterface(
                type(AlienArtBase).interfaceId
            )
        ) {
            revert AlienArtContractFailedValidation();
        }

        for (uint256 i = 0; i < tokenIds.length; ++i) {
            uint256 tokenId = tokenIds[i];
            if (ownerOf(tokenId) != msg.sender) {
                revert OwnerNotMsgSender();
            }

            alienArtAddressMap[tokenId] = alienArtAddress;
            emit AlienArtAddressUpdated(tokenId, alienArtAddress);
        }
    }

    /// @notice Get alien art address for a particular token.
    /// @param tokenId token id.
    /// @return tuple containing (True if default alien art contract is used; false otherwise, alien art contract).
    function getAlienArtContractForToken(uint256 tokenId)
        external
        view
        returns (bool, AlienArtBase)
    {
        AlienArtBase alienArtContract;
        if (alienArtAddressMap[tokenId] != address(0)) {
            // Use defined alien art contract if alien art address for token is not 0
            alienArtContract = AlienArtBase(alienArtAddressMap[tokenId]);
        } else {
            // Use default alien art contract if alien art address for token is 0
            alienArtContract = AlienArtBase(defaultAlienArtAddress);
        }

        // Default alien art is used if the alien art address is
        // the default alien art address or if alien art address is 0 address
        return (
            alienArtAddressMap[tokenId] == defaultAlienArtAddress ||
                alienArtAddressMap[tokenId] == address(0),
            alienArtContract
        );
    }

    /// @notice Get alien art values.
    /// @param alienArtContract alien art contract to get values from.
    /// @param tokenId token id.
    /// @param rotationInDegrees rotation in degrees.
    /// @return alien art image, alien art moon filter, alien art trait.
    function getAlienArtValues(
        AlienArtBase alienArtContract,
        uint256 tokenId,
        uint256 rotationInDegrees
    )
        internal
        view
        returns (
            string memory,
            string memory,
            string memory
        )
    {
        bytes32 seed = moonSeeds[tokenId];
        MoonImageConfig memory config = MoonConfig.getMoonConfig(seed);
        return (
            alienArtContract.getArt(tokenId, seed, config, rotationInDegrees),
            alienArtContract.getMoonFilter(
                tokenId,
                seed,
                config,
                rotationInDegrees
            ),
            alienArtContract.getTraits(tokenId, seed, config, rotationInDegrees)
        );
    }

    /**************************
     ** Regenerate your moon **
     **************************/

    /// @notice Regenerate a moon's seed, which will permanently regenerate the moon's art and traits.
    /// @param tokenId moon token id.
    function regenerateMoon(uint256 tokenId) external payable {
        // Checks
        if (
            regeneratesUsedByCurrentOwner[tokenId] == MAX_REGENERATES_PER_OWNER
        ) {
            revert NoRegenerationsRemaining();
        }
        if (msg.value != PRICE) {
            revert WrongEtherAmount();
        }
        if (ownerOf(tokenId) != msg.sender) {
            revert OwnerNotMsgSender();
        }

        // Effects

        // Update moon seed
        bytes32 originalMoonSeed = moonSeeds[tokenId];
        moonSeeds[tokenId] = MoonConfig.getMoonSeed(tokenId);
        // Increment regenerates used
        ++regeneratesUsedByCurrentOwner[tokenId];

        // Emit regeneration event
        emit MoonRegenerated(
            msg.sender,
            tokenId,
            moonSeeds[tokenId],
            originalMoonSeed,
            regeneratesUsedByCurrentOwner[tokenId]
        );

        // Interactions

        // Burn existing constellation and mint new one
        AlienArtConstellation(defaultAlienArtAddress).burnAndMint(tokenId);

        // Update dynamic NFT registry if present
        if (dynamicNftRegistryAddress != address(0)) {
            DynamicNftRegistryInterface(dynamicNftRegistryAddress).updateToken(
                address(this),
                tokenId,
                COOLDOWN_PERIOD,
                false
            );
        }
    }

    function _afterTokenTransfers(
        address,
        address,
        uint256 startTokenId,
        uint256 quantity
    ) internal override {
        // After token transfer, reset regenerates for the new owner
        for (
            uint256 tokenId = startTokenId;
            tokenId < startTokenId + quantity;
            ++tokenId
        ) {
            regeneratesUsedByCurrentOwner[tokenId] = 0;
        }
    }

    /*********************************
     ** Withdraw funds to the vault **
     *********************************/

    /// @notice Withdraw all ETH from the contract to the vault.
    function withdraw() external {
        VAULT_ADDRESS.transfer(address(this).balance);
    }

    /***************************************************************
     ** Generate on-chain SVG and interactive HTML token metadata **
     ***************************************************************/

    /// @notice Get token URI for a particular token.
    /// @param tokenId token id.
    /// @return token uri.
    function tokenURI(uint256 tokenId)
        public
        view
        override
        returns (string memory)
    {
        ownerOf(tokenId);

        (bool defaultAlienArt, AlienArtBase alienArtContract) = this
            .getAlienArtContractForToken(tokenId);

        uint256 timestamp = block.timestamp * 1e3;
        (, , string memory alienArtTrait) = getAlienArtValues(
            alienArtContract,
            tokenId,
            MoonRenderer.getLunarCycleDistanceFromDateAsRotationInDegrees(
                timestamp
            )
        );

        bytes32 moonSeed = moonSeeds[tokenId];
        string memory traits = MoonConfig.getMoonTraits(
            moonSeed,
            alienArtTrait,
            alienArtContract.getArtName(),
            Strings.toHexString(address(alienArtContract)),
            defaultAlienArt
        );

        string memory moonName = string.concat(
            "Non-Fungible Moon #",
            Utils.uint2str(tokenId)
        );

        (
            string memory moonSvg,
            string memory moonAnimation
        ) = generateOnChainMoon(tokenId, timestamp, alienArtContract);

        return
            Utils.formatTokenURI(
                Utils.svgToImageURI(moonSvg),
                Utils.htmlToURI(moonAnimation),
                moonName,
                "Non-Fungible Moons are on-chain generative moon NFTs. All moon art is generated on-chain and updates in real-time, based on current block time and using an on-chain SVG library, to closely mirror the phase of the moon in the real world.",
                traits
            );
    }

    // Generate moon svg image and interactive moon animation html based on initial timestamp
    function generateOnChainMoon(
        uint256 tokenId,
        uint256 initialTimestamp,
        AlienArtBase alienArtContract
    ) internal view returns (string memory, string memory) {
        bytes32 moonSeed = moonSeeds[tokenId];

        string memory moonSvgText;
        string memory firstSvg;

        for (
            uint256 timestamp = initialTimestamp;
            timestamp <
            initialTimestamp + MoonCalculations.LUNAR_MONTH_LENGTH_IN_MS;
            timestamp += INTERVAL_BETWEEN_ANIMATION_SAMPLES
        ) {
            (
                string memory alienArt,
                string memory alienArtMoonFilter,

            ) = getAlienArtValues(
                    alienArtContract,
                    tokenId,
                    MoonRenderer
                        .getLunarCycleDistanceFromDateAsRotationInDegrees(
                            timestamp
                        )
                );

            string memory moonSvg = MoonRenderer.renderWithTimestamp(
                moonSeed,
                timestamp,
                alienArt,
                alienArtMoonFilter
            );

            if (timestamp == initialTimestamp) {
                firstSvg = moonSvg;
                moonSvgText = string.concat(
                    '<!DOCTYPE html><html><head><style type="text/css">html{overflow:hidden}body{margin:0}#moon{display:block;margin:auto}</style></head><body><div id="moonDiv"></div><script>let gs=[`',
                    moonSvg,
                    "`"
                );
            } else {
                moonSvgText = string.concat(moonSvgText, ",`", moonSvg, "`");
            }
        }

        return (
            firstSvg,
            string.concat(
                moonSvgText,
                '];let $=document.getElementById.bind(document);$("moonDiv").innerHTML=gs[0];let mo=$("moonDiv");let u=e=>{let t=$("moon").getBoundingClientRect();$("moonDiv").innerHTML=gs[Math.max(0,Math.min(Math.floor(((e-t.left)/t.width)*gs.length),gs.length-1))];};mo.onmousemove=e=>u(e.clientX);mo.addEventListener("touchstart",e=>{let t=e=>u(e.touches[0].clientX);n=()=>{e.target.removeEventListener("touchmove",t),e.target.removeEventListener("touchend",n);};e.target.addEventListener("touchmove",t);e.target.addEventListener("touchend",n);});</script></body></html>'
            )
        );
    }

    /**************************
     ** Dynamic NFT registry **
     **************************/

    /// @notice Set up dynamic NFT registry and add default alien art as an allowed updater of this token.
    /// @param _dynamicNftRegistryAddress dynamic NFT registry address.
    function setupDynamicNftRegistry(address _dynamicNftRegistryAddress)
        external
        onlyOwner
    {
        dynamicNftRegistryAddress = _dynamicNftRegistryAddress;
        DynamicNftRegistryInterface registry = DynamicNftRegistryInterface(
            dynamicNftRegistryAddress
        );
        // Register this token with dynamic nft registry
        registry.registerToken(address(this));
        // Add default alien art as an allowed updater of this token
        registry.addAllowedUpdater(address(this), defaultAlienArtAddress);
        // Add this as an allowed updater of this token
        registry.addAllowedUpdater(address(this), address(this));
    }

    /*********************
     ** Operator filter **
     *********************/

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

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

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

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

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

    /*************************
     ** Royalty definitions **
     *************************/

    function royaltyInfo(uint256, uint256 salePrice)
        external
        pure
        returns (address receiver, uint256 royaltyAmount)
    {
        return (VAULT_ADDRESS, (salePrice * 250) / 10000);
    }

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

    /*************
     ** Tip jar **
     *************/

    receive() external payable {}
}

File 2 of 31 : ERC721A.sol
// SPDX-License-Identifier: MIT
// ERC721A Contracts v4.2.3
// Creator: Chiru Labs

pragma solidity ^0.8.4;

import './IERC721A.sol';

/**
 * @dev Interface of ERC721 token receiver.
 */
interface ERC721A__IERC721Receiver {
    function onERC721Received(
        address operator,
        address from,
        uint256 tokenId,
        bytes calldata data
    ) external returns (bytes4);
}

/**
 * @title ERC721A
 *
 * @dev Implementation of the [ERC721](https://eips.ethereum.org/EIPS/eip-721)
 * Non-Fungible Token Standard, including the Metadata extension.
 * Optimized for lower gas during batch mints.
 *
 * Token IDs are minted in sequential order (e.g. 0, 1, 2, 3, ...)
 * starting from `_startTokenId()`.
 *
 * Assumptions:
 *
 * - An owner cannot have more than 2**64 - 1 (max value of uint64) of supply.
 * - The maximum token ID cannot exceed 2**256 - 1 (max value of uint256).
 */
contract ERC721A is IERC721A {
    // Bypass for a `--via-ir` bug (https://github.com/chiru-labs/ERC721A/pull/364).
    struct TokenApprovalRef {
        address value;
    }

    // =============================================================
    //                           CONSTANTS
    // =============================================================

    // Mask of an entry in packed address data.
    uint256 private constant _BITMASK_ADDRESS_DATA_ENTRY = (1 << 64) - 1;

    // The bit position of `numberMinted` in packed address data.
    uint256 private constant _BITPOS_NUMBER_MINTED = 64;

    // The bit position of `numberBurned` in packed address data.
    uint256 private constant _BITPOS_NUMBER_BURNED = 128;

    // The bit position of `aux` in packed address data.
    uint256 private constant _BITPOS_AUX = 192;

    // Mask of all 256 bits in packed address data except the 64 bits for `aux`.
    uint256 private constant _BITMASK_AUX_COMPLEMENT = (1 << 192) - 1;

    // The bit position of `startTimestamp` in packed ownership.
    uint256 private constant _BITPOS_START_TIMESTAMP = 160;

    // The bit mask of the `burned` bit in packed ownership.
    uint256 private constant _BITMASK_BURNED = 1 << 224;

    // The bit position of the `nextInitialized` bit in packed ownership.
    uint256 private constant _BITPOS_NEXT_INITIALIZED = 225;

    // The bit mask of the `nextInitialized` bit in packed ownership.
    uint256 private constant _BITMASK_NEXT_INITIALIZED = 1 << 225;

    // The bit position of `extraData` in packed ownership.
    uint256 private constant _BITPOS_EXTRA_DATA = 232;

    // Mask of all 256 bits in a packed ownership except the 24 bits for `extraData`.
    uint256 private constant _BITMASK_EXTRA_DATA_COMPLEMENT = (1 << 232) - 1;

    // The mask of the lower 160 bits for addresses.
    uint256 private constant _BITMASK_ADDRESS = (1 << 160) - 1;

    // The maximum `quantity` that can be minted with {_mintERC2309}.
    // This limit is to prevent overflows on the address data entries.
    // For a limit of 5000, a total of 3.689e15 calls to {_mintERC2309}
    // is required to cause an overflow, which is unrealistic.
    uint256 private constant _MAX_MINT_ERC2309_QUANTITY_LIMIT = 5000;

    // The `Transfer` event signature is given by:
    // `keccak256(bytes("Transfer(address,address,uint256)"))`.
    bytes32 private constant _TRANSFER_EVENT_SIGNATURE =
        0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef;

    // =============================================================
    //                            STORAGE
    // =============================================================

    // The next token ID to be minted.
    uint256 private _currentIndex;

    // The number of tokens burned.
    uint256 private _burnCounter;

    // Token name
    string private _name;

    // Token symbol
    string private _symbol;

    // Mapping from token ID to ownership details
    // An empty struct value does not necessarily mean the token is unowned.
    // See {_packedOwnershipOf} implementation for details.
    //
    // Bits Layout:
    // - [0..159]   `addr`
    // - [160..223] `startTimestamp`
    // - [224]      `burned`
    // - [225]      `nextInitialized`
    // - [232..255] `extraData`
    mapping(uint256 => uint256) private _packedOwnerships;

    // Mapping owner address to address data.
    //
    // Bits Layout:
    // - [0..63]    `balance`
    // - [64..127]  `numberMinted`
    // - [128..191] `numberBurned`
    // - [192..255] `aux`
    mapping(address => uint256) private _packedAddressData;

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

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

    // =============================================================
    //                          CONSTRUCTOR
    // =============================================================

    constructor(string memory name_, string memory symbol_) {
        _name = name_;
        _symbol = symbol_;
        _currentIndex = _startTokenId();
    }

    // =============================================================
    //                   TOKEN COUNTING OPERATIONS
    // =============================================================

    /**
     * @dev Returns the starting token ID.
     * To change the starting token ID, please override this function.
     */
    function _startTokenId() internal view virtual returns (uint256) {
        return 0;
    }

    /**
     * @dev Returns the next token ID to be minted.
     */
    function _nextTokenId() internal view virtual returns (uint256) {
        return _currentIndex;
    }

    /**
     * @dev Returns the total number of tokens in existence.
     * Burned tokens will reduce the count.
     * To get the total number of tokens minted, please see {_totalMinted}.
     */
    function totalSupply() public view virtual override returns (uint256) {
        // Counter underflow is impossible as _burnCounter cannot be incremented
        // more than `_currentIndex - _startTokenId()` times.
        unchecked {
            return _currentIndex - _burnCounter - _startTokenId();
        }
    }

    /**
     * @dev Returns the total amount of tokens minted in the contract.
     */
    function _totalMinted() internal view virtual returns (uint256) {
        // Counter underflow is impossible as `_currentIndex` does not decrement,
        // and it is initialized to `_startTokenId()`.
        unchecked {
            return _currentIndex - _startTokenId();
        }
    }

    /**
     * @dev Returns the total number of tokens burned.
     */
    function _totalBurned() internal view virtual returns (uint256) {
        return _burnCounter;
    }

    // =============================================================
    //                    ADDRESS DATA OPERATIONS
    // =============================================================

    /**
     * @dev Returns the number of tokens in `owner`'s account.
     */
    function balanceOf(address owner) public view virtual override returns (uint256) {
        if (owner == address(0)) revert BalanceQueryForZeroAddress();
        return _packedAddressData[owner] & _BITMASK_ADDRESS_DATA_ENTRY;
    }

    /**
     * Returns the number of tokens minted by `owner`.
     */
    function _numberMinted(address owner) internal view returns (uint256) {
        return (_packedAddressData[owner] >> _BITPOS_NUMBER_MINTED) & _BITMASK_ADDRESS_DATA_ENTRY;
    }

    /**
     * Returns the number of tokens burned by or on behalf of `owner`.
     */
    function _numberBurned(address owner) internal view returns (uint256) {
        return (_packedAddressData[owner] >> _BITPOS_NUMBER_BURNED) & _BITMASK_ADDRESS_DATA_ENTRY;
    }

    /**
     * Returns the auxiliary data for `owner`. (e.g. number of whitelist mint slots used).
     */
    function _getAux(address owner) internal view returns (uint64) {
        return uint64(_packedAddressData[owner] >> _BITPOS_AUX);
    }

    /**
     * Sets the auxiliary data for `owner`. (e.g. number of whitelist mint slots used).
     * If there are multiple variables, please pack them into a uint64.
     */
    function _setAux(address owner, uint64 aux) internal virtual {
        uint256 packed = _packedAddressData[owner];
        uint256 auxCasted;
        // Cast `aux` with assembly to avoid redundant masking.
        assembly {
            auxCasted := aux
        }
        packed = (packed & _BITMASK_AUX_COMPLEMENT) | (auxCasted << _BITPOS_AUX);
        _packedAddressData[owner] = packed;
    }

    // =============================================================
    //                            IERC165
    // =============================================================

    /**
     * @dev Returns true if this contract implements the interface defined by
     * `interfaceId`. See the corresponding
     * [EIP section](https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified)
     * to learn more about how these ids are created.
     *
     * This function call must use less than 30000 gas.
     */
    function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
        // The interface IDs are constants representing the first 4 bytes
        // of the XOR of all function selectors in the interface.
        // See: [ERC165](https://eips.ethereum.org/EIPS/eip-165)
        // (e.g. `bytes4(i.functionA.selector ^ i.functionB.selector ^ ...)`)
        return
            interfaceId == 0x01ffc9a7 || // ERC165 interface ID for ERC165.
            interfaceId == 0x80ac58cd || // ERC165 interface ID for ERC721.
            interfaceId == 0x5b5e139f; // ERC165 interface ID for ERC721Metadata.
    }

    // =============================================================
    //                        IERC721Metadata
    // =============================================================

    /**
     * @dev Returns the token collection name.
     */
    function name() public view virtual override returns (string memory) {
        return _name;
    }

    /**
     * @dev Returns the token collection symbol.
     */
    function symbol() public view virtual override returns (string memory) {
        return _symbol;
    }

    /**
     * @dev Returns the Uniform Resource Identifier (URI) for `tokenId` token.
     */
    function tokenURI(uint256 tokenId) public view virtual override returns (string memory) {
        if (!_exists(tokenId)) revert URIQueryForNonexistentToken();

        string memory baseURI = _baseURI();
        return bytes(baseURI).length != 0 ? string(abi.encodePacked(baseURI, _toString(tokenId))) : '';
    }

    /**
     * @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, it can be overridden in child contracts.
     */
    function _baseURI() internal view virtual returns (string memory) {
        return '';
    }

    // =============================================================
    //                     OWNERSHIPS OPERATIONS
    // =============================================================

    /**
     * @dev Returns the owner of the `tokenId` token.
     *
     * Requirements:
     *
     * - `tokenId` must exist.
     */
    function ownerOf(uint256 tokenId) public view virtual override returns (address) {
        return address(uint160(_packedOwnershipOf(tokenId)));
    }

    /**
     * @dev Gas spent here starts off proportional to the maximum mint batch size.
     * It gradually moves to O(1) as tokens get transferred around over time.
     */
    function _ownershipOf(uint256 tokenId) internal view virtual returns (TokenOwnership memory) {
        return _unpackedOwnership(_packedOwnershipOf(tokenId));
    }

    /**
     * @dev Returns the unpacked `TokenOwnership` struct at `index`.
     */
    function _ownershipAt(uint256 index) internal view virtual returns (TokenOwnership memory) {
        return _unpackedOwnership(_packedOwnerships[index]);
    }

    /**
     * @dev Initializes the ownership slot minted at `index` for efficiency purposes.
     */
    function _initializeOwnershipAt(uint256 index) internal virtual {
        if (_packedOwnerships[index] == 0) {
            _packedOwnerships[index] = _packedOwnershipOf(index);
        }
    }

    /**
     * Returns the packed ownership data of `tokenId`.
     */
    function _packedOwnershipOf(uint256 tokenId) private view returns (uint256) {
        uint256 curr = tokenId;

        unchecked {
            if (_startTokenId() <= curr)
                if (curr < _currentIndex) {
                    uint256 packed = _packedOwnerships[curr];
                    // If not burned.
                    if (packed & _BITMASK_BURNED == 0) {
                        // Invariant:
                        // There will always be an initialized ownership slot
                        // (i.e. `ownership.addr != address(0) && ownership.burned == false`)
                        // before an unintialized ownership slot
                        // (i.e. `ownership.addr == address(0) && ownership.burned == false`)
                        // Hence, `curr` will not underflow.
                        //
                        // We can directly compare the packed value.
                        // If the address is zero, packed will be zero.
                        while (packed == 0) {
                            packed = _packedOwnerships[--curr];
                        }
                        return packed;
                    }
                }
        }
        revert OwnerQueryForNonexistentToken();
    }

    /**
     * @dev Returns the unpacked `TokenOwnership` struct from `packed`.
     */
    function _unpackedOwnership(uint256 packed) private pure returns (TokenOwnership memory ownership) {
        ownership.addr = address(uint160(packed));
        ownership.startTimestamp = uint64(packed >> _BITPOS_START_TIMESTAMP);
        ownership.burned = packed & _BITMASK_BURNED != 0;
        ownership.extraData = uint24(packed >> _BITPOS_EXTRA_DATA);
    }

    /**
     * @dev Packs ownership data into a single uint256.
     */
    function _packOwnershipData(address owner, uint256 flags) private view returns (uint256 result) {
        assembly {
            // Mask `owner` to the lower 160 bits, in case the upper bits somehow aren't clean.
            owner := and(owner, _BITMASK_ADDRESS)
            // `owner | (block.timestamp << _BITPOS_START_TIMESTAMP) | flags`.
            result := or(owner, or(shl(_BITPOS_START_TIMESTAMP, timestamp()), flags))
        }
    }

    /**
     * @dev Returns the `nextInitialized` flag set if `quantity` equals 1.
     */
    function _nextInitializedFlag(uint256 quantity) private pure returns (uint256 result) {
        // For branchless setting of the `nextInitialized` flag.
        assembly {
            // `(quantity == 1) << _BITPOS_NEXT_INITIALIZED`.
            result := shl(_BITPOS_NEXT_INITIALIZED, eq(quantity, 1))
        }
    }

    // =============================================================
    //                      APPROVAL OPERATIONS
    // =============================================================

    /**
     * @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) public payable virtual override {
        address owner = ownerOf(tokenId);

        if (_msgSenderERC721A() != owner)
            if (!isApprovedForAll(owner, _msgSenderERC721A())) {
                revert ApprovalCallerNotOwnerNorApproved();
            }

        _tokenApprovals[tokenId].value = to;
        emit Approval(owner, to, tokenId);
    }

    /**
     * @dev Returns the account approved for `tokenId` token.
     *
     * Requirements:
     *
     * - `tokenId` must exist.
     */
    function getApproved(uint256 tokenId) public view virtual override returns (address) {
        if (!_exists(tokenId)) revert ApprovalQueryForNonexistentToken();

        return _tokenApprovals[tokenId].value;
    }

    /**
     * @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) public virtual override {
        _operatorApprovals[_msgSenderERC721A()][operator] = approved;
        emit ApprovalForAll(_msgSenderERC721A(), operator, approved);
    }

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

    /**
     * @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. See {_mint}.
     */
    function _exists(uint256 tokenId) internal view virtual returns (bool) {
        return
            _startTokenId() <= tokenId &&
            tokenId < _currentIndex && // If within bounds,
            _packedOwnerships[tokenId] & _BITMASK_BURNED == 0; // and not burned.
    }

    /**
     * @dev Returns whether `msgSender` is equal to `approvedAddress` or `owner`.
     */
    function _isSenderApprovedOrOwner(
        address approvedAddress,
        address owner,
        address msgSender
    ) private pure returns (bool result) {
        assembly {
            // Mask `owner` to the lower 160 bits, in case the upper bits somehow aren't clean.
            owner := and(owner, _BITMASK_ADDRESS)
            // Mask `msgSender` to the lower 160 bits, in case the upper bits somehow aren't clean.
            msgSender := and(msgSender, _BITMASK_ADDRESS)
            // `msgSender == owner || msgSender == approvedAddress`.
            result := or(eq(msgSender, owner), eq(msgSender, approvedAddress))
        }
    }

    /**
     * @dev Returns the storage slot and value for the approved address of `tokenId`.
     */
    function _getApprovedSlotAndAddress(uint256 tokenId)
        private
        view
        returns (uint256 approvedAddressSlot, address approvedAddress)
    {
        TokenApprovalRef storage tokenApproval = _tokenApprovals[tokenId];
        // The following is equivalent to `approvedAddress = _tokenApprovals[tokenId].value`.
        assembly {
            approvedAddressSlot := tokenApproval.slot
            approvedAddress := sload(approvedAddressSlot)
        }
    }

    // =============================================================
    //                      TRANSFER OPERATIONS
    // =============================================================

    /**
     * @dev Transfers `tokenId` from `from` to `to`.
     *
     * 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
    ) public payable virtual override {
        uint256 prevOwnershipPacked = _packedOwnershipOf(tokenId);

        if (address(uint160(prevOwnershipPacked)) != from) revert TransferFromIncorrectOwner();

        (uint256 approvedAddressSlot, address approvedAddress) = _getApprovedSlotAndAddress(tokenId);

        // The nested ifs save around 20+ gas over a compound boolean condition.
        if (!_isSenderApprovedOrOwner(approvedAddress, from, _msgSenderERC721A()))
            if (!isApprovedForAll(from, _msgSenderERC721A())) revert TransferCallerNotOwnerNorApproved();

        if (to == address(0)) revert TransferToZeroAddress();

        _beforeTokenTransfers(from, to, tokenId, 1);

        // Clear approvals from the previous owner.
        assembly {
            if approvedAddress {
                // This is equivalent to `delete _tokenApprovals[tokenId]`.
                sstore(approvedAddressSlot, 0)
            }
        }

        // Underflow of the sender's balance is impossible because we check for
        // ownership above and the recipient's balance can't realistically overflow.
        // Counter overflow is incredibly unrealistic as `tokenId` would have to be 2**256.
        unchecked {
            // We can directly increment and decrement the balances.
            --_packedAddressData[from]; // Updates: `balance -= 1`.
            ++_packedAddressData[to]; // Updates: `balance += 1`.

            // Updates:
            // - `address` to the next owner.
            // - `startTimestamp` to the timestamp of transfering.
            // - `burned` to `false`.
            // - `nextInitialized` to `true`.
            _packedOwnerships[tokenId] = _packOwnershipData(
                to,
                _BITMASK_NEXT_INITIALIZED | _nextExtraData(from, to, prevOwnershipPacked)
            );

            // If the next slot may not have been initialized (i.e. `nextInitialized == false`) .
            if (prevOwnershipPacked & _BITMASK_NEXT_INITIALIZED == 0) {
                uint256 nextTokenId = tokenId + 1;
                // If the next slot's address is zero and not burned (i.e. packed value is zero).
                if (_packedOwnerships[nextTokenId] == 0) {
                    // If the next slot is within bounds.
                    if (nextTokenId != _currentIndex) {
                        // Initialize the next slot to maintain correctness for `ownerOf(tokenId + 1)`.
                        _packedOwnerships[nextTokenId] = prevOwnershipPacked;
                    }
                }
            }
        }

        emit Transfer(from, to, tokenId);
        _afterTokenTransfers(from, to, tokenId, 1);
    }

    /**
     * @dev Equivalent to `safeTransferFrom(from, to, tokenId, '')`.
     */
    function safeTransferFrom(
        address from,
        address to,
        uint256 tokenId
    ) public payable virtual override {
        safeTransferFrom(from, to, tokenId, '');
    }

    /**
     * @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 memory _data
    ) public payable virtual override {
        transferFrom(from, to, tokenId);
        if (to.code.length != 0)
            if (!_checkContractOnERC721Received(from, to, tokenId, _data)) {
                revert TransferToNonERC721ReceiverImplementer();
            }
    }

    /**
     * @dev Hook that is called before a set of serially-ordered token IDs
     * are about to be transferred. This includes minting.
     * And also called before burning one token.
     *
     * `startTokenId` - the first token ID to be transferred.
     * `quantity` - the amount to be transferred.
     *
     * Calling conditions:
     *
     * - When `from` and `to` are both non-zero, `from`'s `tokenId` will be
     * transferred to `to`.
     * - When `from` is zero, `tokenId` will be minted for `to`.
     * - When `to` is zero, `tokenId` will be burned by `from`.
     * - `from` and `to` are never both zero.
     */
    function _beforeTokenTransfers(
        address from,
        address to,
        uint256 startTokenId,
        uint256 quantity
    ) internal virtual {}

    /**
     * @dev Hook that is called after a set of serially-ordered token IDs
     * have been transferred. This includes minting.
     * And also called after one token has been burned.
     *
     * `startTokenId` - the first token ID to be transferred.
     * `quantity` - the amount to be transferred.
     *
     * Calling conditions:
     *
     * - When `from` and `to` are both non-zero, `from`'s `tokenId` has been
     * transferred to `to`.
     * - When `from` is zero, `tokenId` has been minted for `to`.
     * - When `to` is zero, `tokenId` has been burned by `from`.
     * - `from` and `to` are never both zero.
     */
    function _afterTokenTransfers(
        address from,
        address to,
        uint256 startTokenId,
        uint256 quantity
    ) internal virtual {}

    /**
     * @dev Private function to invoke {IERC721Receiver-onERC721Received} on a target contract.
     *
     * `from` - Previous owner of the given token ID.
     * `to` - Target address that will receive the token.
     * `tokenId` - Token ID to be transferred.
     * `_data` - Optional data to send along with the call.
     *
     * Returns whether the call correctly returned the expected magic value.
     */
    function _checkContractOnERC721Received(
        address from,
        address to,
        uint256 tokenId,
        bytes memory _data
    ) private returns (bool) {
        try ERC721A__IERC721Receiver(to).onERC721Received(_msgSenderERC721A(), from, tokenId, _data) returns (
            bytes4 retval
        ) {
            return retval == ERC721A__IERC721Receiver(to).onERC721Received.selector;
        } catch (bytes memory reason) {
            if (reason.length == 0) {
                revert TransferToNonERC721ReceiverImplementer();
            } else {
                assembly {
                    revert(add(32, reason), mload(reason))
                }
            }
        }
    }

    // =============================================================
    //                        MINT OPERATIONS
    // =============================================================

    /**
     * @dev Mints `quantity` tokens and transfers them to `to`.
     *
     * Requirements:
     *
     * - `to` cannot be the zero address.
     * - `quantity` must be greater than 0.
     *
     * Emits a {Transfer} event for each mint.
     */
    function _mint(address to, uint256 quantity) internal virtual {
        uint256 startTokenId = _currentIndex;
        if (quantity == 0) revert MintZeroQuantity();

        _beforeTokenTransfers(address(0), to, startTokenId, quantity);

        // Overflows are incredibly unrealistic.
        // `balance` and `numberMinted` have a maximum limit of 2**64.
        // `tokenId` has a maximum limit of 2**256.
        unchecked {
            // Updates:
            // - `balance += quantity`.
            // - `numberMinted += quantity`.
            //
            // We can directly add to the `balance` and `numberMinted`.
            _packedAddressData[to] += quantity * ((1 << _BITPOS_NUMBER_MINTED) | 1);

            // Updates:
            // - `address` to the owner.
            // - `startTimestamp` to the timestamp of minting.
            // - `burned` to `false`.
            // - `nextInitialized` to `quantity == 1`.
            _packedOwnerships[startTokenId] = _packOwnershipData(
                to,
                _nextInitializedFlag(quantity) | _nextExtraData(address(0), to, 0)
            );

            uint256 toMasked;
            uint256 end = startTokenId + quantity;

            // Use assembly to loop and emit the `Transfer` event for gas savings.
            // The duplicated `log4` removes an extra check and reduces stack juggling.
            // The assembly, together with the surrounding Solidity code, have been
            // delicately arranged to nudge the compiler into producing optimized opcodes.
            assembly {
                // Mask `to` to the lower 160 bits, in case the upper bits somehow aren't clean.
                toMasked := and(to, _BITMASK_ADDRESS)
                // Emit the `Transfer` event.
                log4(
                    0, // Start of data (0, since no data).
                    0, // End of data (0, since no data).
                    _TRANSFER_EVENT_SIGNATURE, // Signature.
                    0, // `address(0)`.
                    toMasked, // `to`.
                    startTokenId // `tokenId`.
                )

                // The `iszero(eq(,))` check ensures that large values of `quantity`
                // that overflows uint256 will make the loop run out of gas.
                // The compiler will optimize the `iszero` away for performance.
                for {
                    let tokenId := add(startTokenId, 1)
                } iszero(eq(tokenId, end)) {
                    tokenId := add(tokenId, 1)
                } {
                    // Emit the `Transfer` event. Similar to above.
                    log4(0, 0, _TRANSFER_EVENT_SIGNATURE, 0, toMasked, tokenId)
                }
            }
            if (toMasked == 0) revert MintToZeroAddress();

            _currentIndex = end;
        }
        _afterTokenTransfers(address(0), to, startTokenId, quantity);
    }

    /**
     * @dev Mints `quantity` tokens and transfers them to `to`.
     *
     * This function is intended for efficient minting only during contract creation.
     *
     * It emits only one {ConsecutiveTransfer} as defined in
     * [ERC2309](https://eips.ethereum.org/EIPS/eip-2309),
     * instead of a sequence of {Transfer} event(s).
     *
     * Calling this function outside of contract creation WILL make your contract
     * non-compliant with the ERC721 standard.
     * For full ERC721 compliance, substituting ERC721 {Transfer} event(s) with the ERC2309
     * {ConsecutiveTransfer} event is only permissible during contract creation.
     *
     * Requirements:
     *
     * - `to` cannot be the zero address.
     * - `quantity` must be greater than 0.
     *
     * Emits a {ConsecutiveTransfer} event.
     */
    function _mintERC2309(address to, uint256 quantity) internal virtual {
        uint256 startTokenId = _currentIndex;
        if (to == address(0)) revert MintToZeroAddress();
        if (quantity == 0) revert MintZeroQuantity();
        if (quantity > _MAX_MINT_ERC2309_QUANTITY_LIMIT) revert MintERC2309QuantityExceedsLimit();

        _beforeTokenTransfers(address(0), to, startTokenId, quantity);

        // Overflows are unrealistic due to the above check for `quantity` to be below the limit.
        unchecked {
            // Updates:
            // - `balance += quantity`.
            // - `numberMinted += quantity`.
            //
            // We can directly add to the `balance` and `numberMinted`.
            _packedAddressData[to] += quantity * ((1 << _BITPOS_NUMBER_MINTED) | 1);

            // Updates:
            // - `address` to the owner.
            // - `startTimestamp` to the timestamp of minting.
            // - `burned` to `false`.
            // - `nextInitialized` to `quantity == 1`.
            _packedOwnerships[startTokenId] = _packOwnershipData(
                to,
                _nextInitializedFlag(quantity) | _nextExtraData(address(0), to, 0)
            );

            emit ConsecutiveTransfer(startTokenId, startTokenId + quantity - 1, address(0), to);

            _currentIndex = startTokenId + quantity;
        }
        _afterTokenTransfers(address(0), to, startTokenId, quantity);
    }

    /**
     * @dev Safely mints `quantity` tokens and transfers them to `to`.
     *
     * Requirements:
     *
     * - If `to` refers to a smart contract, it must implement
     * {IERC721Receiver-onERC721Received}, which is called for each safe transfer.
     * - `quantity` must be greater than 0.
     *
     * See {_mint}.
     *
     * Emits a {Transfer} event for each mint.
     */
    function _safeMint(
        address to,
        uint256 quantity,
        bytes memory _data
    ) internal virtual {
        _mint(to, quantity);

        unchecked {
            if (to.code.length != 0) {
                uint256 end = _currentIndex;
                uint256 index = end - quantity;
                do {
                    if (!_checkContractOnERC721Received(address(0), to, index++, _data)) {
                        revert TransferToNonERC721ReceiverImplementer();
                    }
                } while (index < end);
                // Reentrancy protection.
                if (_currentIndex != end) revert();
            }
        }
    }

    /**
     * @dev Equivalent to `_safeMint(to, quantity, '')`.
     */
    function _safeMint(address to, uint256 quantity) internal virtual {
        _safeMint(to, quantity, '');
    }

    // =============================================================
    //                        BURN OPERATIONS
    // =============================================================

    /**
     * @dev Equivalent to `_burn(tokenId, false)`.
     */
    function _burn(uint256 tokenId) internal virtual {
        _burn(tokenId, false);
    }

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

        address from = address(uint160(prevOwnershipPacked));

        (uint256 approvedAddressSlot, address approvedAddress) = _getApprovedSlotAndAddress(tokenId);

        if (approvalCheck) {
            // The nested ifs save around 20+ gas over a compound boolean condition.
            if (!_isSenderApprovedOrOwner(approvedAddress, from, _msgSenderERC721A()))
                if (!isApprovedForAll(from, _msgSenderERC721A())) revert TransferCallerNotOwnerNorApproved();
        }

        _beforeTokenTransfers(from, address(0), tokenId, 1);

        // Clear approvals from the previous owner.
        assembly {
            if approvedAddress {
                // This is equivalent to `delete _tokenApprovals[tokenId]`.
                sstore(approvedAddressSlot, 0)
            }
        }

        // Underflow of the sender's balance is impossible because we check for
        // ownership above and the recipient's balance can't realistically overflow.
        // Counter overflow is incredibly unrealistic as `tokenId` would have to be 2**256.
        unchecked {
            // Updates:
            // - `balance -= 1`.
            // - `numberBurned += 1`.
            //
            // We can directly decrement the balance, and increment the number burned.
            // This is equivalent to `packed -= 1; packed += 1 << _BITPOS_NUMBER_BURNED;`.
            _packedAddressData[from] += (1 << _BITPOS_NUMBER_BURNED) - 1;

            // Updates:
            // - `address` to the last owner.
            // - `startTimestamp` to the timestamp of burning.
            // - `burned` to `true`.
            // - `nextInitialized` to `true`.
            _packedOwnerships[tokenId] = _packOwnershipData(
                from,
                (_BITMASK_BURNED | _BITMASK_NEXT_INITIALIZED) | _nextExtraData(from, address(0), prevOwnershipPacked)
            );

            // If the next slot may not have been initialized (i.e. `nextInitialized == false`) .
            if (prevOwnershipPacked & _BITMASK_NEXT_INITIALIZED == 0) {
                uint256 nextTokenId = tokenId + 1;
                // If the next slot's address is zero and not burned (i.e. packed value is zero).
                if (_packedOwnerships[nextTokenId] == 0) {
                    // If the next slot is within bounds.
                    if (nextTokenId != _currentIndex) {
                        // Initialize the next slot to maintain correctness for `ownerOf(tokenId + 1)`.
                        _packedOwnerships[nextTokenId] = prevOwnershipPacked;
                    }
                }
            }
        }

        emit Transfer(from, address(0), tokenId);
        _afterTokenTransfers(from, address(0), tokenId, 1);

        // Overflow not possible, as _burnCounter cannot be exceed _currentIndex times.
        unchecked {
            _burnCounter++;
        }
    }

    // =============================================================
    //                     EXTRA DATA OPERATIONS
    // =============================================================

    /**
     * @dev Directly sets the extra data for the ownership data `index`.
     */
    function _setExtraDataAt(uint256 index, uint24 extraData) internal virtual {
        uint256 packed = _packedOwnerships[index];
        if (packed == 0) revert OwnershipNotInitializedForExtraData();
        uint256 extraDataCasted;
        // Cast `extraData` with assembly to avoid redundant masking.
        assembly {
            extraDataCasted := extraData
        }
        packed = (packed & _BITMASK_EXTRA_DATA_COMPLEMENT) | (extraDataCasted << _BITPOS_EXTRA_DATA);
        _packedOwnerships[index] = packed;
    }

    /**
     * @dev Called during each token transfer to set the 24bit `extraData` field.
     * Intended to be overridden by the cosumer contract.
     *
     * `previousExtraData` - the value of `extraData` before transfer.
     *
     * Calling conditions:
     *
     * - When `from` and `to` are both non-zero, `from`'s `tokenId` will be
     * transferred to `to`.
     * - When `from` is zero, `tokenId` will be minted for `to`.
     * - When `to` is zero, `tokenId` will be burned by `from`.
     * - `from` and `to` are never both zero.
     */
    function _extraData(
        address from,
        address to,
        uint24 previousExtraData
    ) internal view virtual returns (uint24) {}

    /**
     * @dev Returns the next extra data for the packed ownership data.
     * The returned result is shifted into position.
     */
    function _nextExtraData(
        address from,
        address to,
        uint256 prevOwnershipPacked
    ) private view returns (uint256) {
        uint24 extraData = uint24(prevOwnershipPacked >> _BITPOS_EXTRA_DATA);
        return uint256(_extraData(from, to, extraData)) << _BITPOS_EXTRA_DATA;
    }

    // =============================================================
    //                       OTHER OPERATIONS
    // =============================================================

    /**
     * @dev Returns the message sender (defaults to `msg.sender`).
     *
     * If you are writing GSN compatible contracts, you need to override this function.
     */
    function _msgSenderERC721A() internal view virtual returns (address) {
        return msg.sender;
    }

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

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

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

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

File 3 of 31 : Strings.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.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 4 of 31 : LibPRNG.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;

/// @notice Library for generating psuedorandom numbers.
/// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/LibPRNG.sol)
library LibPRNG {
    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                          STRUCTS                           */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev A psuedorandom number state in memory.
    struct PRNG {
        uint256 state;
    }

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

    /// @dev Seeds the `prng` with `state`.
    function seed(PRNG memory prng, bytes32 state) internal pure {
        /// @solidity memory-safe-assembly
        assembly {
            mstore(prng, state)
        }
    }

    /// @dev Returns a psuedorandom uint256, uniformly distributed
    /// between 0 (inclusive) and `upper` (exclusive).
    /// If your modulus is big, this method is recommended
    /// for uniform sampling to avoid modulo bias.
    /// For uniform sampling across all uint256 values,
    /// or for small enough moduli such that the bias is neligible,
    /// use {next} instead.
    function uniform(PRNG memory prng, uint256 upper)
        internal
        pure
        returns (uint256 result)
    {
        /// @solidity memory-safe-assembly
        assembly {
            // prettier-ignore
            for {} 1 {} {
                result := keccak256(prng, 0x20)
                mstore(prng, result)
                // prettier-ignore
                if iszero(lt(result, mod(sub(0, upper), upper))) { break }
            }
            result := mod(result, upper)
        }
    }
}

File 5 of 31 : Utils.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.17;

// Core utils used extensively to format CSS and numbers.
library Utils {
    string internal constant BASE64_TABLE =
        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";

    // converts an unsigned integer to a string
    function uint2str(uint256 _i) internal pure returns (string memory) {
        if (_i == 0) {
            return "0";
        }
        uint256 j = _i;
        uint256 len;
        while (j != 0) {
            ++len;
            j /= 10;
        }
        bytes memory bstr = new bytes(len);
        uint256 k = len;
        while (_i != 0) {
            k = k - 1;
            uint8 temp = (48 + uint8(_i - (_i / 10) * 10));
            bytes1 b1 = bytes1(temp);
            bstr[k] = b1;
            _i /= 10;
        }
        return string(bstr);
    }

    function htmlToURI(string memory _source)
        internal
        pure
        returns (string memory)
    {
        return
            string.concat(
                "data:text/html;base64,",
                encodeBase64(bytes(_source))
            );
    }

    function svgToImageURI(string memory _source)
        internal
        pure
        returns (string memory)
    {
        return
            string.concat(
                "data:image/svg+xml;base64,",
                encodeBase64(bytes(_source))
            );
    }

    function formatTokenURI(
        string memory _imageURI,
        string memory _animationURI,
        string memory _name,
        string memory _description,
        string memory _properties
    ) internal pure returns (string memory) {
        return
            string.concat(
                "data:application/json;base64,",
                encodeBase64(
                    bytes(
                        string.concat(
                            '{"name":"',
                            _name,
                            '","description":"',
                            _description,
                            '","attributes":',
                            _properties,
                            ',"image":"',
                            _imageURI,
                            '","animation_url":"',
                            _animationURI,
                            '"}'
                        )
                    )
                )
            );
    }

    // Encode some bytes in base64
    // https://gist.github.com/mbvissers/8ba9ac1eca9ed0ef6973bd49b3c999ba
    function encodeBase64(bytes memory data)
        internal
        pure
        returns (string memory)
    {
        if (data.length == 0) return "";

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

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

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

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

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

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

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

            // run over the input, 3 bytes at a time
            for {

            } lt(dataPtr, endPtr) {

            } {
                dataPtr := add(dataPtr, 3)

                // read 3 bytes
                let input := mload(dataPtr)

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

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

        return result;
    }
}

File 6 of 31 : MoonCalculations.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.17;

/// @title MoonCalculations
/// @author Aspyn Palatnick (aspyn.eth, stuckinaboot.eth)
library MoonCalculations {
    // Only need the 4 moon phases where the moon is actually changing,
    // as the other phases (new moon, first quarter, full moon, third quarter)
    // are just single points in time (don't define a rate of change)
    enum MoonPhase {
        WAXING_CRESCENT,
        WAXING_GIBBOUS,
        WANING_GIBBOUS,
        WANING_CRESCENT
    }

    uint256 internal constant BASE_NEW_MOON_DATE_IN_MS = 1666694910000;
    uint256 internal constant LUNAR_MONTH_LENGTH_IN_MS = 2551442877;

    uint256 internal constant NUM_PHASES = 4;
    uint256 internal constant PHASE_LENGTH = 10000 / NUM_PHASES;

    function timestampToPhase(uint256 unixUtcTimestamp)
        internal
        pure
        returns (MoonPhase phase, uint256 progressPercentageOutOf10000)
    {
        uint256 distanceIntoLunarCycleOutOf10000 = calculateLunarCycleDistanceFromDate(
                unixUtcTimestamp
            );

        uint256 progress = distanceIntoLunarCycleOutOf10000 / PHASE_LENGTH;
        phase = MoonPhase(progress);
        progressPercentageOutOf10000 =
            (distanceIntoLunarCycleOutOf10000 - progress * PHASE_LENGTH) *
            NUM_PHASES;
    }

    function calculateLunarCycleDistanceFromDate(uint256 currDate)
        internal
        pure
        returns (uint256 distanceIntoLunarCycleOutOf10000)
    {
        uint256 msIntoPhase = (currDate - BASE_NEW_MOON_DATE_IN_MS) %
            LUNAR_MONTH_LENGTH_IN_MS;

        uint256 value = MoonCalculations.roundToNearestMultiple(
            msIntoPhase * 10000,
            LUNAR_MONTH_LENGTH_IN_MS
        ) / LUNAR_MONTH_LENGTH_IN_MS;

        // Return value between 0 and 9999, inclusive
        return value < 10000 ? value : 0;
    }

    // Helpers

    function roundToNearestMultiple(uint256 number, uint256 multiple)
        internal
        pure
        returns (uint256)
    {
        uint256 result = number + multiple / 2;
        return result - (result % multiple);
    }
}

File 7 of 31 : MoonRenderer.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.17;

import {MoonCalculations} from "./MoonCalculations.sol";
import {MoonSvg} from "./MoonSvg.sol";
import {MoonConfig} from "./MoonConfig.sol";
import {MoonImageConfig} from "./MoonStructs.sol";

/// @title MoonRenderer
/// @author Aspyn Palatnick (aspyn.eth, stuckinaboot.eth)
library MoonRenderer {
    function getLunarCycleDistanceFromDateAsRotationInDegrees(uint256 date)
        internal
        pure
        returns (uint16)
    {
        return
            uint16(
                // Round to nearest multiple of 10000, which ensures that progressScaled will be properly rounded rather than having truncation occur during integer division
                MoonCalculations.roundToNearestMultiple(
                    MoonCalculations.calculateLunarCycleDistanceFromDate(date) *
                        360,
                    10000
                ) / 10000
            );
    }

    function _render(
        bytes32 moonSeed,
        MoonCalculations.MoonPhase phase,
        // Represent a fraction as progressOutOf10000 out of 10000
        // e.g. 0.5 -> progressOutOf10000 = 5000, 0.1234 -> 1234
        uint256 progressOutOf10000,
        string memory alienArt,
        string memory alienArtMoonFilter
    ) internal pure returns (string memory) {
        MoonImageConfig memory moonConfig = MoonConfig.getMoonConfig(moonSeed);

        MoonSvg.SvgContainerParams memory svg1 = MoonSvg.SvgContainerParams({
            x: 0,
            y: 0,
            width: moonConfig.moonRadius,
            height: moonConfig.viewHeight
        });
        MoonSvg.SvgContainerParams memory svg2 = MoonSvg.SvgContainerParams({
            x: 0,
            y: 0,
            width: moonConfig.moonRadius,
            height: moonConfig.viewHeight
        });

        MoonSvg.EllipseParams memory ellipse1 = MoonSvg.EllipseParams({
            cx: moonConfig.moonRadius,
            cy: moonConfig.moonRadius,
            rx: moonConfig.moonRadius,
            ry: moonConfig.moonRadius,
            color: moonConfig.colors.moon,
            forceUseBackgroundColor: false
        });

        MoonSvg.EllipseParams memory ellipse2 = MoonSvg.EllipseParams({
            cx: 0,
            cy: moonConfig.moonRadius,
            rx: moonConfig.moonRadius,
            ry: moonConfig.moonRadius,
            color: moonConfig.colors.moon,
            forceUseBackgroundColor: false
        });

        // Round to nearest multiple of 10000, which ensures that progressScaled will be properly rounded rather than having truncation occur during integer division.
        uint256 progressScaled = MoonCalculations.roundToNearestMultiple(
            progressOutOf10000 * moonConfig.moonRadius,
            10000
        ) / 10000;

        if (phase == MoonCalculations.MoonPhase.WANING_GIBBOUS) {
            svg1.x = 0;
            // Subtract 1 from svg2.x, add 1 to svg2.width, add 1 to ellipse2.cx to ensure smooth border between ellipses
            svg2.x = moonConfig.moonRadius - 1;
            svg2.width += 1;

            ellipse1.cx = moonConfig.moonRadius;
            ellipse1.rx = moonConfig.moonRadius;
            ellipse2.cx = 1;
            ellipse2.rx = moonConfig.moonRadius - progressScaled;
        } else if (phase == MoonCalculations.MoonPhase.WANING_CRESCENT) {
            svg1.x = 0;
            svg2.x = 0;

            // Add 1 to svg2.width to ensure smooth border between ellipses
            svg2.width += 1;

            ellipse1.cx = moonConfig.moonRadius;
            ellipse1.rx = moonConfig.moonRadius;
            ellipse2.cx = moonConfig.moonRadius;
            ellipse2.rx = progressScaled;
            ellipse2.forceUseBackgroundColor = true;
        } else if (phase == MoonCalculations.MoonPhase.WAXING_CRESCENT) {
            svg1.x = moonConfig.moonRadius;
            // Subtract 1 from svg2.x, add 1 to ellipse2.cx, add 1 to ellipse2.rx to ensure smooth border between ellipses
            svg2.x = moonConfig.moonRadius - 1;
            svg2.width += 1;

            ellipse1.cx = 0;
            ellipse1.rx = moonConfig.moonRadius;
            ellipse2.cx = 1;
            ellipse2.rx = moonConfig.moonRadius - progressScaled + 1;
            ellipse2.forceUseBackgroundColor = true;
        } else if (phase == MoonCalculations.MoonPhase.WAXING_GIBBOUS) {
            svg1.x = 0;
            svg2.x = moonConfig.moonRadius;

            // Add 1 to svg1.width to ensure smooth border between ellipses
            svg1.width += 1;

            ellipse1.cx = moonConfig.moonRadius;
            ellipse1.rx = progressScaled;
            ellipse2.cx = 0;
            ellipse2.rx = moonConfig.moonRadius;
        }

        // Add svg offsets
        svg1.x += moonConfig.xOffset;
        svg2.x += moonConfig.xOffset;
        svg1.y += moonConfig.yOffset;
        svg2.y += moonConfig.yOffset;

        return
            MoonSvg.generateMoon(
                MoonSvg.RectParams({
                    color: moonConfig.colors.background,
                    gradientColor: moonConfig.colors.backgroundGradientColor,
                    width: moonConfig.viewWidth,
                    height: moonConfig.viewHeight
                }),
                svg1,
                svg2,
                ellipse1,
                ellipse2,
                MoonSvg.BorderParams({
                    radius: moonConfig.borderRadius,
                    width: moonConfig.borderWidth,
                    borderType: moonConfig.borderType,
                    color: moonConfig.colors.border
                }),
                alienArt,
                alienArtMoonFilter
            );
    }

    function renderWithTimestamp(
        bytes32 moonSeed,
        // UTC timestamp.
        uint256 timestamp,
        string memory alienArt,
        string memory alienArtFilter
    ) internal pure returns (string memory) {
        (
            MoonCalculations.MoonPhase phase,
            uint256 progressOutOf10000
        ) = MoonCalculations.timestampToPhase(timestamp);
        return
            _render(
                moonSeed,
                phase,
                progressOutOf10000,
                alienArt,
                alienArtFilter
            );
    }
}

File 8 of 31 : MoonSvg.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.17;

import "./SVG.sol";

/// @title MoonSvg
/// @author Aspyn Palatnick (aspyn.eth, stuckinaboot.eth)
library MoonSvg {
    struct SvgContainerParams {
        uint16 x;
        uint16 y;
        uint16 width;
        uint16 height;
    }

    struct EllipseParams {
        uint16 cx;
        uint16 cy;
        uint256 rx;
        uint16 ry;
        string color;
        bool forceUseBackgroundColor;
    }

    struct RectParams {
        uint16 width;
        uint16 height;
        string color;
        string gradientColor;
    }

    struct BorderParams {
        uint16 radius;
        uint16 width;
        string borderType;
        string color;
    }

    function getBackgroundRadialGradientDefinition(
        RectParams memory rectParams,
        uint256 moonVerticalRadius
    ) internal pure returns (string memory) {
        return
            svg.radialGradient(
                string.concat(
                    svg.prop("id", "brG"),
                    // Set radius to 75% to smooth out the radial gradient against
                    // the background and moon color
                    svg.prop("r", "75%")
                ),
                string.concat(
                    svg.stop(
                        string.concat(
                            svg.prop(
                                "offset",
                                string.concat(
                                    Utils.uint2str(
                                        // Ensure that the gradient has the rect color up to at least the moon radius
                                        // Note: the reason we do moon radius * 100 * 3 / 2 is because
                                        // we multiply by 100 to get a percent, then multiply by 3 and divide by 2
                                        // to get ~1.5 * moon radius, which is sufficiently large given the background radial
                                        // gradient radius is being scaled by 75% (50% would be normal size, 75% is scaled up),
                                        // which smooths out the gradient and reduces the presence of a color band
                                        (((moonVerticalRadius * 100) * 3) / 2) /
                                            rectParams.height
                                    ),
                                    "%"
                                )
                            ),
                            svg.prop("stop-color", rectParams.color)
                        )
                    ),
                    svg.stop(
                        string.concat(
                            svg.prop("offset", "100%"),
                            svg.prop("stop-color", rectParams.gradientColor)
                        )
                    )
                )
            );
    }

    function getMoonFilterDefinition(uint16 moonRadiusY)
        internal
        pure
        returns (string memory)
    {
        uint16 position = moonRadiusY * 2;
        return
            svg.filter(
                string.concat(svg.prop("id", "mF")),
                string.concat(
                    svg.feSpecularLighting(
                        string.concat(
                            svg.prop("result", "out"),
                            svg.prop("specularExponent", "20"),
                            svg.prop("lighting-color", "#bbbbbb")
                        ),
                        svg.fePointLight(
                            string.concat(
                                svg.prop("x", position),
                                svg.prop("y", position),
                                svg.prop("z", position)
                            )
                        )
                    ),
                    svg.feComposite(
                        string.concat(
                            svg.prop("in", "SourceGraphic"),
                            svg.prop("in2", "out"),
                            svg.prop("operator", "arithmetic"),
                            svg.prop("k1", "0"),
                            svg.prop("k2", "1"),
                            svg.prop("k3", "1"),
                            svg.prop("k4", "0")
                        )
                    )
                )
            );
    }

    function getMoonFilterMask(
        SvgContainerParams memory svg1,
        SvgContainerParams memory svg2,
        EllipseParams memory ellipse1,
        EllipseParams memory ellipse2,
        RectParams memory rect
    ) internal pure returns (string memory) {
        return
            svg.mask(
                svg.prop("id", "mfM"),
                string.concat(
                    svg.rect(
                        string.concat(
                            svg.prop("width", rect.width),
                            svg.prop("height", rect.height),
                            svg.prop("fill", "#000")
                        )
                    ),
                    getEllipseElt(
                        svg1,
                        ellipse1,
                        // Black rect for masking purposes; where this rect is visible will be hidden
                        "#000",
                        // White ellipse for masking purposes; where this ellipse is visible will be shown
                        "#FFF"
                    ),
                    getEllipseElt(
                        svg2,
                        ellipse2,
                        // Black rect for masking purposes; where this rect is visible will be hidden
                        "#000",
                        // White ellipse for masking purposes; where this ellipse is visible will be shown
                        "#FFF"
                    )
                )
            );
    }

    function getEllipseElt(
        SvgContainerParams memory svgContainer,
        EllipseParams memory ellipse,
        string memory rectBackgroundColor,
        string memory ellipseColor
    ) internal pure returns (string memory) {
        return
            svg.svgTag(
                string.concat(
                    svg.prop("x", svgContainer.x),
                    svg.prop("y", svgContainer.y),
                    svg.prop("height", svgContainer.height),
                    svg.prop("width", svgContainer.width)
                ),
                svg.ellipse(
                    string.concat(
                        svg.prop("cx", ellipse.cx),
                        svg.prop("cy", ellipse.cy),
                        svg.prop("rx", ellipse.rx),
                        svg.prop("ry", ellipse.ry),
                        svg.prop(
                            "fill",
                            ellipse.forceUseBackgroundColor
                                ? rectBackgroundColor
                                : ellipseColor
                        )
                    )
                )
            );
    }

    function getBorderStyleProp(BorderParams memory border)
        internal
        pure
        returns (string memory)
    {
        return
            svg.prop(
                "style",
                string.concat(
                    "outline:",
                    Utils.uint2str(border.width),
                    "px ",
                    border.borderType,
                    " ",
                    border.color,
                    ";outline-offset:-",
                    Utils.uint2str(border.width),
                    "px;border-radius:",
                    Utils.uint2str(border.radius),
                    "%"
                )
            );
    }

    function getMoonBackgroundMaskDefinition(
        RectParams memory rect,
        uint256 moonRadius
    ) internal pure returns (string memory) {
        return
            svg.mask(
                svg.prop("id", "mbM"),
                string.concat(
                    svg.rect(
                        string.concat(
                            svg.prop("width", rect.width),
                            svg.prop("height", rect.height),
                            // Everything under a white pixel will be visible
                            svg.prop("fill", "#FFF")
                        )
                    ),
                    svg.circle(
                        string.concat(
                            svg.prop("cx", rect.width / 2),
                            svg.prop("cy", rect.height / 2),
                            // Add 1 to moon radius as slight buffer.
                            svg.prop("r", moonRadius + 1)
                        )
                    )
                )
            );
    }

    function getDefinitions(
        RectParams memory rect,
        SvgContainerParams memory svg1,
        SvgContainerParams memory svg2,
        EllipseParams memory ellipse1,
        EllipseParams memory ellipse2,
        string memory alienArtMoonFilterDefinition
    ) internal pure returns (string memory) {
        return
            svg.defs(
                string.concat(
                    getBackgroundRadialGradientDefinition(rect, ellipse1.ry),
                    bytes(alienArtMoonFilterDefinition).length > 0
                        ? alienArtMoonFilterDefinition
                        : getMoonFilterDefinition(ellipse1.ry),
                    getMoonBackgroundMaskDefinition(rect, ellipse1.ry),
                    getMoonFilterMask(svg1, svg2, ellipse1, ellipse2, rect)
                )
            );
    }

    function getMoonSvgProps(uint16 borderRadius)
        internal
        pure
        returns (string memory)
    {
        return
            string.concat(
                svg.prop("xmlns", "http://www.w3.org/2000/svg"),
                // Include id so that the moon element can be accessed by JS
                svg.prop("id", "moon"),
                svg.prop("height", "100%"),
                svg.prop("viewBox", "0 0 200 200"),
                svg.prop(
                    "style",
                    string.concat(
                        "border-radius:",
                        Utils.uint2str(borderRadius),
                        "%;max-height:100vh"
                    )
                )
            );
    }

    function generateMoon(
        RectParams memory rect,
        SvgContainerParams memory svg1,
        SvgContainerParams memory svg2,
        EllipseParams memory ellipse1,
        EllipseParams memory ellipse2,
        BorderParams memory border,
        string memory alienArt,
        string memory alienArtMoonFilterDefinition
    ) internal pure returns (string memory) {
        string memory ellipse1elt = getEllipseElt(
            svg1,
            ellipse1,
            rect.color,
            ellipse1.color
        );
        string memory ellipse2elt = getEllipseElt(
            svg2,
            ellipse2,
            rect.color,
            ellipse2.color
        );

        string memory rectProps = string.concat(
            svg.prop(
                "fill",
                bytes(rect.gradientColor).length > 0 ? "url(#brG)" : rect.color
            ),
            svg.prop("width", rect.width),
            svg.prop("height", rect.height),
            svg.prop("rx", string.concat(Utils.uint2str(border.radius), "%")),
            svg.prop("ry", string.concat(Utils.uint2str(border.radius), "%"))
        );

        string memory definitions = getDefinitions(
            rect,
            svg1,
            svg2,
            ellipse1,
            ellipse2,
            alienArtMoonFilterDefinition
        );

        return
            svg.svgTag(
                getMoonSvgProps(border.radius),
                string.concat(
                    definitions,
                    svg.svgTag(
                        svg.NULL,
                        string.concat(
                            svg.rect(
                                string.concat(
                                    rectProps,
                                    getBorderStyleProp(border)
                                )
                            ),
                            // Intentionally put alien art behind the moon in svg ordering
                            svg.g(
                                // Apply mask to block out the moon area from alien art,
                                // which is necessary in order for the moon to be clearly visible when displayed
                                svg.prop("mask", "url(#mbM)"),
                                alienArt
                            ),
                            svg.g(
                                string.concat(
                                    // Apply filter to moon
                                    svg.prop("filter", "url(#mF)"),
                                    // Apply mask to ensure filter only applies to the visible portion of the moon
                                    svg.prop("mask", "url(#mfM)")
                                ),
                                string.concat(ellipse1elt, ellipse2elt)
                            )
                        )
                    )
                )
            );
    }
}

File 9 of 31 : MoonConfig.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.17;

import {LibPRNG} from "../utils/LibPRNG.sol";
import {Traits} from "../utils/Traits.sol";
import {Utils} from "../utils/Utils.sol";
import {MoonImageConfig, MoonImageColors} from "./MoonStructs.sol";

/// @title MoonConfig
/// @author Aspyn Palatnick (aspyn.eth, stuckinaboot.eth)
library MoonConfig {
    using LibPRNG for LibPRNG.PRNG;

    function getMoonSeed(uint256 tokenId) internal view returns (bytes32) {
        return keccak256(abi.encodePacked(tokenId, block.difficulty));
    }

    function getFrameTraits(
        MoonImageConfig memory moonConfig
    ) internal pure returns (string memory) {
        bool hasFrame = moonConfig.borderWidth > 0;
        return
            string.concat(
                Traits.getTrait(
                    "Frame roundness",
                    moonConfig.borderRadius,
                    true
                ),
                Traits.getTrait(
                    "Frame thickness",
                    moonConfig.borderWidth,
                    true
                ),
                Traits.getTrait(
                    "Frame type",
                    hasFrame ? moonConfig.borderType : "Invisible",
                    true
                ),
                hasFrame ? Traits.getTrait(
                    "Frame tint",
                    uint256(moonConfig.colors.borderSaturation),
                    true
                ) : ""
            );
    }

    function getMoonTraits(
        bytes32 moonSeed,
        string memory alienArtTrait,
        string memory alienArtName,
        string memory alienArtAddressStr,
        bool isDefaultAlienArt
    ) internal pure returns (string memory) {
        MoonImageConfig memory moonConfig = getMoonConfig(moonSeed);

        // Evaluate groups of traits to (1) better organize code (2) avoid stack too deep errors
        string memory frameTraits = getFrameTraits(moonConfig);

        string memory alienArtAllTraits = string.concat(
            Traits.getTrait(
                "Is default alien art",
                // This needs to be included as a boolean rather than a check
                // agains the default name since the name can be impersonated by another contract
                isDefaultAlienArt ? "Yes" : "No",
                true
            ),
            // Include alien art address so others can discover alien art
            // used by different moons
            Traits.getTrait("Alien art address", alienArtAddressStr, true),
            Traits.getTrait(
                "Alien art",
                alienArtName,
                // Include comma if alien art trait is defined
                // by doing length of alienArtTrait comparison
                bytes(alienArtTrait).length > 0
            ),
            alienArtTrait
        );

        return
            string.concat(
                "[",
                Traits.getTrait(
                    "Moon hue",
                    uint256(moonConfig.colors.moonHue),
                    true
                ),
                frameTraits,
                Traits.getTrait(
                    "Space darkness",
                    uint256(moonConfig.colors.backgroundLightness),
                    true
                ),
                Traits.getTrait(
                    "Has space gradient",
                    bytes(moonConfig.colors.backgroundGradientColor).length > 0
                        ? "Yes"
                        : "No",
                    true
                ),
                alienArtAllTraits,
                "]"
            );
    }

    function getBorderType(LibPRNG.PRNG memory prng)
        internal
        pure
        returns (string memory)
    {
        // Choose border type based on different weightings
        uint256 psuedoRandomOutOf100 = prng.uniform(100);
        if (psuedoRandomOutOf100 < 70) {
            return "solid";
        }
        if (psuedoRandomOutOf100 < 90) {
            return "inset";
        }
        return "outset";
    }

    function getMoonImageColors(LibPRNG.PRNG memory prng)
        internal
        pure
        returns (MoonImageColors memory)
    {
        uint16 moonHue = uint16(prng.uniform(360));
        uint8 borderSaturation = uint8(prng.uniform(71));
        uint8 backgroundLightness = uint8(prng.uniform(11));

        return
            MoonImageColors({
                moon: hslaString(moonHue, 50, 50),
                moonHue: moonHue,
                border: hslaString(moonHue, borderSaturation, 50),
                borderSaturation: borderSaturation,
                background: hslaString(0, 0, backgroundLightness),
                backgroundLightness: backgroundLightness,
                backgroundGradientColor: // Bias gradient to occur 33% of the time
                prng.uniform(3) == 0
                    ? hslaString(
                        // Derive hue from moon hue
                        moonHue,
                        50,
                        50
                    )
                    : ""
            });
    }

    function getMoonConfig(bytes32 moonSeed)
        internal
        pure
        returns (MoonImageConfig memory)
    {
        uint16 moonRadius = 32;
        uint16 viewSize = 200;
        uint16 offset = (viewSize - 2 * moonRadius) / 2;

        LibPRNG.PRNG memory prng;
        prng.seed(keccak256(abi.encodePacked(moonSeed, uint256(5))));

        // Border radius can vary from 0 to 50%
        uint16 borderRadius = prng.uniform(9) == 0 // 11% chance of having a circular border
            ? 50 // Otherwise, choose a border radius between 0 and 5
            : uint16(prng.uniform(6));

        // Border width can vary from 0 to 4
        uint16 borderWidth = uint16(prng.uniform(5));

        MoonImageColors memory colors = getMoonImageColors(prng);
        string memory borderType = getBorderType(prng);
        
        return
            MoonImageConfig({
                colors: colors,
                moonRadius: moonRadius,
                xOffset: offset,
                yOffset: offset,
                viewWidth: viewSize,
                viewHeight: viewSize,
                borderRadius: borderRadius,
                borderWidth: borderWidth,
                borderType: borderType
            });
    }

    // Helpers

    function hslaString(
        uint16 hue,
        uint8 saturation,
        uint8 lightness
    ) internal pure returns (string memory) {
        return
            string.concat(
                "hsla(",
                Utils.uint2str(hue),
                ",",
                Utils.uint2str(saturation),
                "%,",
                Utils.uint2str(lightness),
                "%,100%)"
            );
    }
}

File 10 of 31 : DynamicNftRegistryInterface.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.17;

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

/**
 * @title  DynamicNftRegistry
 * @author James Wenzel (emo.eth)
 * @notice Interface for an open registry for allowed updaters of token contracts to register that a (potentially
 *         off-chain) metadata update has occurred on-chain, inheriting from OwnerPermissionedTokenRegistryInterface.
 */
interface DynamicNftRegistryInterface is
    OwnerPermissionedTokenRegistryInterface
{
    /**
     * @notice update token's last modified timestamp to timestamp of current block
     * @param tokenAddress address of the token contract
     * @param tokenId that has been updated
     * @param cooldownPeriod in seconds
     */
    function updateToken(
        address tokenAddress,
        uint256 tokenId,
        uint64 cooldownPeriod,
        bool invalidateCollectionOrders
    ) external;

    /**
     * @notice update token's last modified timestamp to a timestamp in the past
     * @param tokenAddress address of the token contract
     * @param tokenId that has been updated
     * @param timestamp specific timestamp when token was last updated
     * @param cooldownPeriod in seconds
     */
    function updateToken(
        address tokenAddress,
        uint256 tokenId,
        uint64 timestamp,
        uint64 cooldownPeriod,
        bool invalidateCollectionOrders
    ) external;
}

File 11 of 31 : AlienArtBase.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.17;

import {IERC165} from "../ext/IERC165.sol";
import {MoonImageConfig} from "../../moon/MoonStructs.sol";

/// @title AlienArtBase
/// @author Aspyn Palatnick (aspyn.eth, stuckinaboot.eth)
/// @notice Alien Art is an on-chain NFT composability standard for on-chain art and traits.
abstract contract AlienArtBase is IERC165 {
    // Define functions that alien art contracts can override. These intentionally
    // use function state mutability as view to allow for reading on-chain data.

    /// @notice get art name.
    /// @return art name.
    function getArtName() external view virtual returns (string memory);

    /// @notice get alien art image for a particular token.
    /// @param tokenId token id.
    /// @param moonSeed moon seed.
    /// @param moonImageConfig moon image config.
    /// @param rotationInDegrees rotation in degrees.
    /// @return alien art image.
    function getArt(
        uint256 tokenId,
        bytes32 moonSeed,
        MoonImageConfig calldata moonImageConfig,
        uint256 rotationInDegrees
    ) external view virtual returns (string memory);

    /// @notice get moon filter for a particular token.
    /// @param tokenId token id.
    /// @param moonSeed moon seed.
    /// @param moonImageConfig moon image config.
    /// @param rotationInDegrees rotation in degrees.
    /// @return moon filter.
    function getMoonFilter(
        uint256 tokenId,
        bytes32 moonSeed,
        MoonImageConfig calldata moonImageConfig,
        uint256 rotationInDegrees
    ) external view virtual returns (string memory) {
        return "";
    }

    /// @notice get alien art traits for a particular token.
    /// @param tokenId token id.
    /// @param moonSeed moon seed.
    /// @param moonImageConfig moon image config.
    /// @param rotationInDegrees rotation in degrees.
    /// @return alien art traits.
    function getTraits(
        uint256 tokenId,
        bytes32 moonSeed,
        MoonImageConfig calldata moonImageConfig,
        uint256 rotationInDegrees
    ) external view virtual returns (string memory) {
        return "";
    }
}

File 12 of 31 : AlienArtConstellation.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.17;

import {DynamicNftRegistryInterface} from "../../interfaces/dynamicNftRegistry/DynamicNftRegistryInterface.sol";
import {AlienArtBase} from "../../interfaces/alienArt/AlienArtBase.sol";
import {MoonImageConfig, MoonImageColors} from "../../moon/MoonStructs.sol";
import {AlienArtConstellationEventsAndErrors} from "./AlienArtConstellationEventsAndErrors.sol";
import {ConstellationLib} from "./ConstellationLib.sol";
import {IERC165} from "../../interfaces/ext/IERC165.sol";
import {IERC721} from "../../interfaces/ext/IERC721.sol";
import {ERC1155} from "../../ext/ERC1155.sol";
import {Ownable} from "../../ext/Ownable.sol";
import {Utils} from "../../utils/Utils.sol";
import {Traits} from "../../utils/Traits.sol";
import {LibPRNG} from "../../utils/LibPRNG.sol";
import {svg} from "./SVG.sol";

/// @title AlienArtConstellation
/// @author Aspyn Palatnick (aspyn.eth, stuckinaboot.eth)
/// @notice On-chain constellation NFTs that conform to the Alien Art (AlienArtBase) on-chain NFT composability standard and support swapping constellations between Non-Fungible Moon NFTs.
contract AlienArtConstellation is
    ERC1155,
    AlienArtBase,
    AlienArtConstellationEventsAndErrors,
    Ownable
{
    using LibPRNG for LibPRNG.PRNG;

    struct ConstellationParams {
        Constellation constellationType;
        // In degrees
        uint16 rotation;
        bool fluxConstellation;
    }

    enum Constellation {
        LITTLE_DIPPER,
        BIG_DIPPER,
        // Zodiac
        ARIES,
        PISCES,
        AQUARIUS,
        CAPRICORNUS,
        SAGITTARIUS,
        OPHIUCHUS,
        SCORPIUS,
        LIBRA,
        VIRGO,
        LEO,
        CANCER,
        GEMINI,
        TAURUS,
        NONE
    }

    // These constants ensure that Etherscan/etc can read the name and symbol for this contract
    string public constant name = "Constellations";
    string public constant symbol = "CLN";

    uint16 internal constant DEFAULT_VIEW_SIZE = 200;
    uint16 internal constant DEFAULT_MOON_RADIUS = 32;

    address internal moonAddress;

    mapping(uint256 => uint256) public moonTokenIdToConstellationTokenId;
    uint16 internal constant RANDOMNESS_FACTOR = 1337;

    address dynamicNftRegistryAddress;
    uint64 internal constant COOLDOWN_PERIOD = 120;

    /// @notice set moon address.
    /// @param _moonAddress moon address.
    function setMoonAddress(address _moonAddress) external onlyOwner {
        if (moonAddress != address(0)) {
            revert MoonAddressAlreadySet();
        }
        moonAddress = _moonAddress;
    }

    /// @notice swap constellation associated moon 1 with the constellation associated with moon 2.
    /// Both moons must be owned by the same user.
    /// @param moon1 moon 1 token id.
    /// @param moon2 moon 2 token id.
    function swapConstellations(uint256 moon1, uint256 moon2) external {
        // Checks

        // Check both moons are owned by this account
        if (
            IERC721(moonAddress).ownerOf(moon1) != msg.sender ||
            IERC721(moonAddress).ownerOf(moon2) != msg.sender
        ) {
            revert SwapMoonsOwnerMustBeMsgSender();
        }

        // Effects

        // Perform swap
        uint256 originalMoon1Constellation = moonTokenIdToConstellationTokenId[
            moon1
        ];
        moonTokenIdToConstellationTokenId[
            moon1
        ] = moonTokenIdToConstellationTokenId[moon2];
        moonTokenIdToConstellationTokenId[moon2] = originalMoon1Constellation;

        // Emit event indicating swap occurred
        emit SwapConstellations(
            msg.sender,
            moon1,
            moon2,
            moonTokenIdToConstellationTokenId[moon1],
            moonTokenIdToConstellationTokenId[moon2]
        );

        // Interactions
        if (dynamicNftRegistryAddress != address(0)) {
            // Call update token on zone registry (if defined) for both moons
            // and do not invalidate collection orders.
            DynamicNftRegistryInterface(dynamicNftRegistryAddress).updateToken(
                moonAddress,
                moon1,
                COOLDOWN_PERIOD,
                false
            );
            DynamicNftRegistryInterface(dynamicNftRegistryAddress).updateToken(
                moonAddress,
                moon2,
                COOLDOWN_PERIOD,
                false
            );
        }
    }

    /// @notice get constellation type that corresponds to a particular moon token id when the constellation is to be minted
    /// @param moonTokenId moon token id
    /// @return Constellation
    function getConstellationTypeForMoonTokenIdAtMint(uint256 moonTokenId)
        public
        view
        returns (Constellation)
    {
        LibPRNG.PRNG memory prng;
        prng.seed(
            keccak256(
                abi.encodePacked(
                    moonTokenId,
                    block.difficulty,
                    RANDOMNESS_FACTOR
                )
            )
        );

        uint256 randomFrom0To99 = prng.uniform(100);
        if (randomFrom0To99 <= 1) {
            // 2% chance of returning little dipper
            return Constellation.LITTLE_DIPPER;
        }
        if (randomFrom0To99 == 2) {
            // 1% chance of returning big dipper
            return Constellation.BIG_DIPPER;
        }

        // Length of zodiac constellation values and None is the value of the last enum - first zodiac constellation + 1 for the none value
        uint256 totalZodiacConstellations = uint256(Constellation.NONE) -
            uint256(Constellation.ARIES) +
            1;
        // Return any value from the zodiac constellations or None.
        return
            Constellation(
                prng.uniform(totalZodiacConstellations) +
                    uint256(Constellation.ARIES)
            );
    }

    /// @notice get art name for this alien art contract.
    /// @return art name.
    function getArtName() external pure override returns (string memory) {
        return name;
    }

    /// @notice get on-chain Constellation art image, adhering to Alien Art abstract class.
    /// @param tokenId moon token id.
    /// @param moonSeed moon seed.
    /// @param moonImageConfig moon image config.
    /// @param rotationInDegrees rotation in degrees.
    /// @return on-chain Constellation SVG.
    function getArt(
        uint256 tokenId,
        bytes32 moonSeed,
        MoonImageConfig calldata moonImageConfig,
        uint256 rotationInDegrees
    ) external view override returns (string memory) {
        Constellation constellation = Constellation(
            moonTokenIdToConstellationTokenId[tokenId]
        );
        return
            getArtForConstellation(
                constellation,
                moonSeed,
                moonImageConfig,
                rotationInDegrees
            );
    }

    // For a given moon seed, returns bool indicating if flux constellation should be used, bool indicating if
    // moon color for star color should be used
    function getConstellationUseFluxAndUseMoonColor(bytes32 moonSeed)
        internal
        pure
        returns (bool, bool)
    {
        if (moonSeed == bytes32(0)) {
            // If moon seed is bytes32(0), return false for both use flux and use moon color for star color
            return (false, false);
        }
        LibPRNG.PRNG memory prng;
        prng.seed(moonSeed);
        return (prng.uniform(4) == 0, prng.uniform(20) == 0);
    }

    /// @notice get on-chain Constellation SVG.
    /// @param constellation constellation to get SVG for.
    /// @param moonSeed moon seed of moon mapping to constellation.
    /// @param moonImageConfig moon image config.
    /// @param rotationInDegrees rotation in degrees.
    /// @return Constellation SVG.
    function getArtForConstellation(
        Constellation constellation,
        bytes32 moonSeed,
        MoonImageConfig memory moonImageConfig,
        uint256 rotationInDegrees
    ) public pure returns (string memory) {
        (
            bool useFlux,
            bool useMoonColorForStarColor
        ) = getConstellationUseFluxAndUseMoonColor(moonSeed);
        return
            getConstellation(
                ConstellationParams({
                    constellationType: constellation,
                    rotation: uint16(rotationInDegrees),
                    fluxConstellation: useFlux
                }),
                moonImageConfig.viewWidth,
                moonImageConfig.viewHeight,
                useMoonColorForStarColor
                    ? moonImageConfig.colors.moon
                    : "#FDFD96",
                moonSeed
            );
    }

    /// @notice get traits for Constellation.
    /// @param tokenId token id.
    /// @param moonSeed moon seed.
    /// @return traits.
    function getTraits(
        uint256 tokenId,
        bytes32 moonSeed,
        MoonImageConfig calldata,
        uint256
    ) external view override returns (string memory) {
        (
            bool useFlux,
            bool useMoonColorForStarColor
        ) = getConstellationUseFluxAndUseMoonColor(moonSeed);
        return
            string.concat(
                Traits.getTrait(
                    "Star brightness",
                    useFlux ? "Flux" : "Fixed",
                    true
                ),
                Traits.getTrait(
                    "Star color",
                    useMoonColorForStarColor ? "Moon" : "Classic",
                    true
                ),
                _getTraitForConstellation(
                    Constellation(moonTokenIdToConstellationTokenId[tokenId])
                )
            );
    }

    function _getTraitForConstellation(Constellation constellation)
        internal
        pure
        returns (string memory)
    {
        return
            Traits.getTrait(
                "Constellation",
                getConstellationTypeString(constellation),
                false
            );
    }

    function getConstellationTypeString(Constellation constellation)
        internal
        pure
        returns (string memory)
    {
        if (constellation == Constellation.LITTLE_DIPPER) {
            return "Little dipper";
        }
        if (constellation == Constellation.BIG_DIPPER) {
            return "Big dipper";
        }
        if (constellation == Constellation.ARIES) {
            return "Aries";
        }
        if (constellation == Constellation.PISCES) {
            return "Pisces";
        }
        if (constellation == Constellation.AQUARIUS) {
            return "Aquarius";
        }
        if (constellation == Constellation.CAPRICORNUS) {
            return "Capricornus";
        }
        if (constellation == Constellation.SAGITTARIUS) {
            return "Sagittarius";
        }
        if (constellation == Constellation.OPHIUCHUS) {
            return "Ophiuchus";
        }
        if (constellation == Constellation.SCORPIUS) {
            return "Scorpius";
        }
        if (constellation == Constellation.LIBRA) {
            return "Libra";
        }
        if (constellation == Constellation.VIRGO) {
            return "Virgo";
        }
        if (constellation == Constellation.LEO) {
            return "Leo";
        }
        if (constellation == Constellation.CANCER) {
            return "Cancer";
        }
        if (constellation == Constellation.GEMINI) {
            return "Gemini";
        }
        if (constellation == Constellation.TAURUS) {
            return "Taurus";
        }
        return "None";
    }

    function getConstellation(
        ConstellationParams memory constellation,
        uint256 rectWidth,
        uint256 rectHeight,
        string memory starColor,
        bytes32 moonSeed
    ) internal pure returns (string memory) {
        if (constellation.constellationType == Constellation.NONE) {
            return "";
        }

        ConstellationLib.GenerateConstellationParams
            memory params = ConstellationLib.GenerateConstellationParams(
                0,
                0,
                constellation.rotation,
                uint16(rectWidth) / 2,
                uint16(rectHeight) / 2,
                starColor,
                constellation.fluxConstellation,
                moonSeed
            );

        if (constellation.constellationType == Constellation.LITTLE_DIPPER) {
            params.x = 60;
            params.y = 150;
            return ConstellationLib.getLittleDipper(params);
        }
        if (constellation.constellationType == Constellation.BIG_DIPPER) {
            params.x = 89;
            params.y = 13;
            return ConstellationLib.getBigDipper(params);
        }
        if (constellation.constellationType == Constellation.ARIES) {
            params.x = 75;
            params.y = 40;
            return ConstellationLib.getAries(params);
        }
        if (constellation.constellationType == Constellation.PISCES) {
            params.x = 25;
            params.y = 147;
            return ConstellationLib.getPisces(params);
        }
        if (constellation.constellationType == Constellation.AQUARIUS) {
            params.x = 35;
            params.y = 156;
            return ConstellationLib.getAquarius(params);
        }
        if (constellation.constellationType == Constellation.CAPRICORNUS) {
            params.x = 35;
            params.y = 145;
            return ConstellationLib.getCapricornus(params);
        }
        if (constellation.constellationType == Constellation.SAGITTARIUS) {
            params.x = 35;
            params.y = 160;
            return ConstellationLib.getSagittarius(params);
        }
        if (constellation.constellationType == Constellation.OPHIUCHUS) {
            params.x = 35;
            params.y = 160;
            return ConstellationLib.getOphiuchus(params);
        }
        if (constellation.constellationType == Constellation.SCORPIUS) {
            params.x = 35;
            params.y = 140;
            return ConstellationLib.getScorpius(params);
        }
        if (constellation.constellationType == Constellation.LIBRA) {
            params.x = 75;
            params.y = 167;
            return ConstellationLib.getLibra(params);
        }
        if (constellation.constellationType == Constellation.VIRGO) {
            params.x = 15;
            params.y = 120;
            return ConstellationLib.getVirgo(params);
        }
        if (constellation.constellationType == Constellation.LEO) {
            params.x = 55;
            params.y = 165;
            return ConstellationLib.getLeo(params);
        }
        if (constellation.constellationType == Constellation.CANCER) {
            params.x = 110;
            params.y = 185;
            return ConstellationLib.getCancer(params);
        }
        if (constellation.constellationType == Constellation.GEMINI) {
            params.x = 75;
            params.y = 152;
            return ConstellationLib.getGemini(params);
        }
        if (constellation.constellationType == Constellation.TAURUS) {
            params.x = 67;
            params.y = 155;
            return ConstellationLib.getTaurus(params);
        }

        return "";
    }

    /// @notice get standalone Constellation, which is
    /// an on-chain Constellation SVG that can properly be rendered standalone (without being embedded in another SVG).
    /// @param constellation constellation.
    /// @param moonSeed moon seed of moon mapping to constellation.
    /// @param config moon image config.
    /// @return standalone Constellation SVG.
    function getStandaloneConstellation(
        Constellation constellation,
        bytes32 moonSeed,
        MoonImageConfig memory config
    ) public pure returns (string memory) {
        return
            svg.svgTag(
                string.concat(
                    svg.prop("xmlns", "http://www.w3.org/2000/svg"),
                    svg.prop("width", "400"),
                    svg.prop("height", "400"),
                    svg.prop("viewBox", "0 0 200 200")
                ),
                string.concat(
                    svg.rect(
                        string.concat(
                            svg.prop("width", "200"),
                            svg.prop("height", "200"),
                            svg.prop("fill", "#0e1111")
                        )
                    ),
                    getArtForConstellation(constellation, moonSeed, config, 0)
                )
            );
    }

    /// @notice burn and mint constellation for particular moon. Only callable by moon contract.
    /// @param moonTokenId moon token id.
    function burnAndMint(uint256 moonTokenId) external {
        // Only moon contract can burn
        if (msg.sender != moonAddress) {
            revert MsgSenderNotMoonAddress();
        }

        // Burn existing Constellation token
        _burn(msg.sender, moonTokenIdToConstellationTokenId[moonTokenId], 1);
        // Mint new token
        mint(moonTokenId, 1);
    }

    /// @notice mint Constellation NFTs corresponding with moons.
    /// @param startMoonTokenId start moon token id.
    /// @param numMoonsMinted number of moons minted.
    function mint(uint256 startMoonTokenId, uint256 numMoonsMinted) public {
        // Only moon contract can mint
        if (msg.sender != moonAddress) {
            revert MsgSenderNotMoonAddress();
        }

        for (
            uint256 moonTokenId = startMoonTokenId;
            moonTokenId < startMoonTokenId + numMoonsMinted;
            ++moonTokenId
        ) {
            // Determine constellation to mint based on moon token
            uint256 constellationIdx = uint256(
                getConstellationTypeForMoonTokenIdAtMint(moonTokenId)
            );
            // Map moon token id to this constellation token id (index)
            moonTokenIdToConstellationTokenId[moonTokenId] = constellationIdx;
            // Mint to msg.sender, which is moon contract since we only
            // allow minting by moon contract
            _mint(msg.sender, constellationIdx, 1, "");
        }
    }

    /// @notice get fully on-chain uri for a particular token.
    /// @param tokenId token id, which is an index in Constellation enum.
    /// @return Constellation uri for tokenId.
    function uri(uint256 tokenId)
        public
        view
        virtual
        override(ERC1155)
        returns (string memory)
    {
        if (tokenId > uint256(Constellation.NONE)) {
            revert InvalidConstellationIndex();
        }

        // Only define fields relevant for generating image for uri
        MoonImageConfig memory moonImageConfig;
        moonImageConfig.viewWidth = DEFAULT_VIEW_SIZE;
        moonImageConfig.viewHeight = DEFAULT_VIEW_SIZE;
        moonImageConfig.moonRadius = DEFAULT_MOON_RADIUS;

        string memory constellationSvg = Utils.svgToImageURI(
            getStandaloneConstellation(
                Constellation(tokenId),
                bytes32(0),
                moonImageConfig
            )
        );
        return
            Utils.formatTokenURI(
                constellationSvg,
                constellationSvg,
                getConstellationTypeString(Constellation(tokenId)),
                "Constellations are on-chain constellation NFTs. Constellations are on-chain art owned by on-chain art; Constellations are all owned by Non-Fungible Moon NFTs.",
                string.concat(
                    "[",
                    _getTraitForConstellation(Constellation(tokenId)),
                    "]"
                )
            );
    }

    // Dynamic NFT registry setup

    /// @notice set up dynamic NFT registry.
    /// @param _dynamicNftRegistryAddress dynamic NFT registry address.
    function setupDynamicNftRegistry(address _dynamicNftRegistryAddress)
        external
        onlyOwner
    {
        dynamicNftRegistryAddress = _dynamicNftRegistryAddress;
    }

    // IERC165 functions

    /// @notice check if this contract supports a given interface.
    /// @param interfaceId interface id.
    /// @return true if contract supports interfaceId, false otherwise.
    function supportsInterface(bytes4 interfaceId)
        public
        view
        virtual
        override(IERC165, ERC1155)
        returns (bool)
    {
        return
            super.supportsInterface(interfaceId) ||
            // AlienArtBase interface id
            interfaceId == type(AlienArtBase).interfaceId;
    }
}

File 13 of 31 : ERC1155.sol
// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity 0.8.17;

/// @notice Minimalist and gas efficient standard ERC1155 implementation.
/// @author Solmate (https://github.com/Rari-Capital/solmate/blob/main/src/tokens/ERC1155.sol)
abstract contract ERC1155 {
    /*//////////////////////////////////////////////////////////////
                                 EVENTS
    //////////////////////////////////////////////////////////////*/

    event TransferSingle(
        address indexed operator,
        address indexed from,
        address indexed to,
        uint256 id,
        uint256 amount
    );

    event TransferBatch(
        address indexed operator,
        address indexed from,
        address indexed to,
        uint256[] ids,
        uint256[] amounts
    );

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

    event URI(string value, uint256 indexed id);

    /*//////////////////////////////////////////////////////////////
                             ERC1155 STORAGE
    //////////////////////////////////////////////////////////////*/

    mapping(address => mapping(uint256 => uint256)) public balanceOf;

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

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

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

    /*//////////////////////////////////////////////////////////////
                              ERC1155 LOGIC
    //////////////////////////////////////////////////////////////*/

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

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

    function safeTransferFrom(
        address from,
        address to,
        uint256 id,
        uint256 amount,
        bytes calldata data
    ) public virtual {
        require(
            msg.sender == from || isApprovedForAll[from][msg.sender],
            "NOT_AUTHORIZED"
        );

        balanceOf[from][id] -= amount;
        balanceOf[to][id] += amount;

        emit TransferSingle(msg.sender, from, to, id, amount);

        require(
            to.code.length == 0
                ? to != address(0)
                : ERC1155TokenReceiver(to).onERC1155Received(
                    msg.sender,
                    from,
                    id,
                    amount,
                    data
                ) == ERC1155TokenReceiver.onERC1155Received.selector,
            "UNSAFE_RECIPIENT"
        );
    }

    function safeBatchTransferFrom(
        address from,
        address to,
        uint256[] calldata ids,
        uint256[] calldata amounts,
        bytes calldata data
    ) public virtual {
        require(ids.length == amounts.length, "LENGTH_MISMATCH");

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

        // Storing these outside the loop saves ~15 gas per iteration.
        uint256 id;
        uint256 amount;

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

            balanceOf[from][id] -= amount;
            balanceOf[to][id] += amount;

            // An array can't have a total length
            // larger than the max uint256 value.
            unchecked {
                ++i;
            }
        }

        emit TransferBatch(msg.sender, from, to, ids, amounts);

        require(
            to.code.length == 0
                ? to != address(0)
                : ERC1155TokenReceiver(to).onERC1155BatchReceived(
                    msg.sender,
                    from,
                    ids,
                    amounts,
                    data
                ) == ERC1155TokenReceiver.onERC1155BatchReceived.selector,
            "UNSAFE_RECIPIENT"
        );
    }

    function balanceOfBatch(address[] calldata owners, uint256[] calldata ids)
        public
        view
        virtual
        returns (uint256[] memory balances)
    {
        require(owners.length == ids.length, "LENGTH_MISMATCH");

        balances = new uint256[](owners.length);

        // Unchecked because the only math done is incrementing
        // the array index counter which cannot possibly overflow.
        unchecked {
            for (uint256 i = 0; i < owners.length; ++i) {
                balances[i] = balanceOf[owners[i]][ids[i]];
            }
        }
    }

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

    function supportsInterface(bytes4 interfaceId)
        public
        view
        virtual
        returns (bool)
    {
        return
            interfaceId == 0x01ffc9a7 || // ERC165 Interface ID for ERC165
            interfaceId == 0xd9b67a26 || // ERC165 Interface ID for ERC1155
            interfaceId == 0x0e89341c; // ERC165 Interface ID for ERC1155MetadataURI
    }

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

    function _mint(
        address to,
        uint256 id,
        uint256 amount,
        bytes memory data
    ) internal virtual {
        balanceOf[to][id] += amount;

        emit TransferSingle(msg.sender, address(0), to, id, amount);

        require(
            to.code.length == 0
                ? to != address(0)
                : ERC1155TokenReceiver(to).onERC1155Received(
                    msg.sender,
                    address(0),
                    id,
                    amount,
                    data
                ) == ERC1155TokenReceiver.onERC1155Received.selector,
            "UNSAFE_RECIPIENT"
        );
    }

    function _batchMint(
        address to,
        uint256[] memory ids,
        uint256[] memory amounts,
        bytes memory data
    ) internal virtual {
        uint256 idsLength = ids.length; // Saves MLOADs.

        require(idsLength == amounts.length, "LENGTH_MISMATCH");

        for (uint256 i = 0; i < idsLength; ) {
            balanceOf[to][ids[i]] += amounts[i];

            // An array can't have a total length
            // larger than the max uint256 value.
            unchecked {
                ++i;
            }
        }

        emit TransferBatch(msg.sender, address(0), to, ids, amounts);

        require(
            to.code.length == 0
                ? to != address(0)
                : ERC1155TokenReceiver(to).onERC1155BatchReceived(
                    msg.sender,
                    address(0),
                    ids,
                    amounts,
                    data
                ) == ERC1155TokenReceiver.onERC1155BatchReceived.selector,
            "UNSAFE_RECIPIENT"
        );
    }

    function _batchBurn(
        address from,
        uint256[] memory ids,
        uint256[] memory amounts
    ) internal virtual {
        uint256 idsLength = ids.length; // Saves MLOADs.

        require(idsLength == amounts.length, "LENGTH_MISMATCH");

        for (uint256 i = 0; i < idsLength; ) {
            balanceOf[from][ids[i]] -= amounts[i];

            // An array can't have a total length
            // larger than the max uint256 value.
            unchecked {
                ++i;
            }
        }

        emit TransferBatch(msg.sender, from, address(0), ids, amounts);
    }

    function _burn(
        address from,
        uint256 id,
        uint256 amount
    ) internal virtual {
        balanceOf[from][id] -= amount;

        emit TransferSingle(msg.sender, from, address(0), id, amount);
    }
}

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

    function onERC1155BatchReceived(
        address,
        address,
        uint256[] calldata,
        uint256[] calldata,
        bytes calldata
    ) external virtual returns (bytes4) {
        return ERC1155TokenReceiver.onERC1155BatchReceived.selector;
    }
}

File 14 of 31 : MoonNFTEventsAndErrors.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.17;

/// @title MoonNFTEventsAndErrors
/// @author Aspyn Palatnick (aspyn.eth, stuckinaboot.eth)
contract MoonNFTEventsAndErrors {
    // Event to be emitted when alien art address is updated
    event AlienArtAddressUpdated(
        uint256 indexed tokenId,
        address indexed alienArtAddress
    );

    // Event to be emitted when mint with referrer occurs
    event MintedWithReferrer(
        // Referrer address
        address indexed referrerAddress,
        // Referrer token
        uint256 indexed referrerToken,
        // Minter address
        address indexed minterAddress,
        // Token id of first token minted during this mint
        uint256 mintStartTokenId,
        // Amount of tokens minted
        uint256 amount,
        // Value paid to referrer
        uint256 referrerPayout,
        // Value paid to referred
        uint256 referredPayout
    );

    // Event to emitted when moon regeneration occurs
    event MoonRegenerated(
        address indexed moonOwner,
        uint256 indexed tokenId,
        bytes32 indexed newMoonSeed,
        bytes32 previousMoonSeed,
        uint8 regenerationsUsed
    );

    // Mint errors
    error MaxSupplyReached();
    error WrongEtherAmount();

    // Regeneration errors
    error NoRegenerationsRemaining();

    // Alien art token-level errors
    error AlienArtContractFailedValidation();
    error OwnerNotMsgSender();
}

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

pragma solidity 0.8.17;

import "./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 16 of 31 : IERC2981.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.6.0) (interfaces/IERC2981.sol)

pragma solidity ^0.8.0;

import "./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 17 of 31 : IERC165.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/introspection/IERC165.sol)

pragma solidity 0.8.17;

/**
 * @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 18 of 31 : DefaultOperatorFilterer.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.13;

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

abstract contract DefaultOperatorFilterer is OperatorFilterer {
    address constant DEFAULT_SUBSCRIPTION =
        address(0x3cc6CddA760b79bAfa08dF41ECFA224f810dCeB6);

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

File 19 of 31 : IERC721A.sol
// SPDX-License-Identifier: MIT
// ERC721A Contracts v4.2.3
// Creator: Chiru Labs

pragma solidity ^0.8.4;

/**
 * @dev Interface of ERC721A.
 */
interface IERC721A {
    /**
     * The caller must own the token or be an approved operator.
     */
    error ApprovalCallerNotOwnerNorApproved();

    /**
     * The token does not exist.
     */
    error ApprovalQueryForNonexistentToken();

    /**
     * Cannot query the balance for the zero address.
     */
    error BalanceQueryForZeroAddress();

    /**
     * Cannot mint to the zero address.
     */
    error MintToZeroAddress();

    /**
     * The quantity of tokens minted must be more than zero.
     */
    error MintZeroQuantity();

    /**
     * The token does not exist.
     */
    error OwnerQueryForNonexistentToken();

    /**
     * The caller must own the token or be an approved operator.
     */
    error TransferCallerNotOwnerNorApproved();

    /**
     * The token must be owned by `from`.
     */
    error TransferFromIncorrectOwner();

    /**
     * Cannot safely transfer to a contract that does not implement the
     * ERC721Receiver interface.
     */
    error TransferToNonERC721ReceiverImplementer();

    /**
     * Cannot transfer to the zero address.
     */
    error TransferToZeroAddress();

    /**
     * The token does not exist.
     */
    error URIQueryForNonexistentToken();

    /**
     * The `quantity` minted with ERC2309 exceeds the safety limit.
     */
    error MintERC2309QuantityExceedsLimit();

    /**
     * The `extraData` cannot be set on an unintialized ownership slot.
     */
    error OwnershipNotInitializedForExtraData();

    // =============================================================
    //                            STRUCTS
    // =============================================================

    struct TokenOwnership {
        // The address of the owner.
        address addr;
        // Stores the start time of ownership with minimal overhead for tokenomics.
        uint64 startTimestamp;
        // Whether the token has been burned.
        bool burned;
        // Arbitrary data similar to `startTimestamp` that can be set via {_extraData}.
        uint24 extraData;
    }

    // =============================================================
    //                         TOKEN COUNTERS
    // =============================================================

    /**
     * @dev Returns the total number of tokens in existence.
     * Burned tokens will reduce the count.
     * To get the total number of tokens minted, please see {_totalMinted}.
     */
    function totalSupply() external view returns (uint256);

    // =============================================================
    //                            IERC165
    // =============================================================

    /**
     * @dev Returns true if this contract implements the interface defined by
     * `interfaceId`. See the corresponding
     * [EIP section](https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified)
     * to learn more about how these ids are created.
     *
     * This function call must use less than 30000 gas.
     */
    function supportsInterface(bytes4 interfaceId) external view returns (bool);

    // =============================================================
    //                            IERC721
    // =============================================================

    /**
     * @dev Emitted when `tokenId` token is transferred from `from` to `to`.
     */
    event Transfer(address indexed from, address indexed to, uint256 indexed tokenId);

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

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

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

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

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

    /**
     * @dev Equivalent to `safeTransferFrom(from, to, tokenId, '')`.
     */
    function safeTransferFrom(
        address from,
        address to,
        uint256 tokenId
    ) external payable;

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

    /**
     * @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 payable;

    /**
     * @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);

    // =============================================================
    //                        IERC721Metadata
    // =============================================================

    /**
     * @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);

    // =============================================================
    //                           IERC2309
    // =============================================================

    /**
     * @dev Emitted when tokens in `fromTokenId` to `toTokenId`
     * (inclusive) is transferred from `from` to `to`, as defined in the
     * [ERC2309](https://eips.ethereum.org/EIPS/eip-2309) standard.
     *
     * See {_mintERC2309} for more details.
     */
    event ConsecutiveTransfer(uint256 indexed fromTokenId, uint256 toTokenId, address indexed from, address indexed to);
}

File 20 of 31 : Math.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.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 21 of 31 : MoonStructs.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.17;

// Colors describing the moon image.
struct MoonImageColors {
    string moon;
    uint16 moonHue;
    string border;
    uint8 borderSaturation;
    string background;
    uint8 backgroundLightness;
    string backgroundGradientColor;
}

// Config describing the complete moon image, with colors, positioning, and sizing.
struct MoonImageConfig {
    MoonImageColors colors;
    uint16 moonRadius;
    uint16 xOffset;
    uint16 yOffset;
    uint16 viewWidth;
    uint16 viewHeight;
    uint16 borderRadius;
    uint16 borderWidth;
    string borderType;
}

File 22 of 31 : SVG.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.17;

import {Utils} from "../utils/Utils.sol";

// Core SVG utility library which helps us construct
// onchain SVG's with a simple, web-like API.
// Props to w1nt3r.eth for creating the core of this SVG utility library.
library svg {
    string internal constant NULL = "";

    /* MAIN ELEMENTS */
    function svgTag(string memory _props, string memory _children)
        internal
        pure
        returns (string memory)
    {
        return el("svg", _props, _children);
    }

    function defs(string memory _children)
        internal
        pure
        returns (string memory)
    {
        return el("defs", NULL, _children);
    }

    function g(string memory _props, string memory _children)
        internal
        pure
        returns (string memory)
    {
        return el("g", _props, _children);
    }

    function circle(string memory _props)
        internal
        pure
        returns (string memory)
    {
        return el("circle", _props, NULL);
    }

    function mask(string memory _props, string memory _children)
        internal
        pure
        returns (string memory)
    {
        return el("mask", _props, _children);
    }

    function radialGradient(string memory _props, string memory _children)
        internal
        pure
        returns (string memory)
    {
        return el("radialGradient", _props, _children);
    }

    function stop(string memory _props) internal pure returns (string memory) {
        return el("stop", _props, NULL);
    }

    function ellipse(string memory _props)
        internal
        pure
        returns (string memory)
    {
        return el("ellipse", _props, NULL);
    }

    function rect(string memory _props) internal pure returns (string memory) {
        return el("rect", _props, NULL);
    }

    function filter(string memory _props, string memory _children)
        internal
        pure
        returns (string memory)
    {
        return el("filter", _props, _children);
    }

    function feSpecularLighting(string memory _props, string memory _children)
        internal
        pure
        returns (string memory)
    {
        return el("feSpecularLighting", _props, _children);
    }

    function fePointLight(string memory _props)
        internal
        pure
        returns (string memory)
    {
        return el("fePointLight", _props, NULL);
    }

    function feComposite(string memory _props)
        internal
        pure
        returns (string memory)
    {
        return el("feComposite", _props, NULL);
    }

    /* COMMON */
    // A generic element, can be used to construct any SVG (or HTML) element
    function el(
        string memory _tag,
        string memory _props,
        string memory _children
    ) internal pure returns (string memory) {
        return
            string.concat(
                "<",
                _tag,
                " ",
                _props,
                ">",
                _children,
                "</",
                _tag,
                ">"
            );
    }

    // an SVG attribute
    function prop(string memory _key, string memory _val)
        internal
        pure
        returns (string memory)
    {
        return string.concat(_key, '="', _val, '" ');
    }

    function prop(string memory _key, uint256 _val)
        internal
        pure
        returns (string memory)
    {
        return prop(_key, Utils.uint2str(_val));
    }
}

File 23 of 31 : Traits.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.17;

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

/// @title Traits
/// @author Aspyn Palatnick (aspyn.eth, stuckinaboot.eth)
library Traits {
    function _getTrait(
        string memory traitType,
        string memory value,
        bool includeTrailingComma,
        bool includeValueQuotes
    ) internal pure returns (string memory) {
        return
            string.concat(
                '{"trait_type":"',
                traitType,
                '","value":',
                includeValueQuotes ? string.concat('"', value, '"') : value,
                "}",
                includeTrailingComma ? "," : ""
            );
    }

    function getTrait(
        string memory traitType,
        string memory value,
        bool includeTrailingComma
    ) internal pure returns (string memory) {
        return _getTrait(traitType, value, includeTrailingComma, true);
    }

    function getTrait(
        string memory traitType,
        uint256 value,
        bool includeTrailingComma
    ) internal pure returns (string memory) {
        return
            _getTrait(
                traitType,
                Utils.uint2str(value),
                includeTrailingComma,
                false
            );
    }
}

File 24 of 31 : OwnerPermissionedTokenRegistryInterface.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.17;

/**
 * @title  OwnerPermissionedTokenRegistry
 * @author James Wenzel (emo.eth)
 * @notice Interface for a generic registry of tokens, where the owner of a token contract (as specified by the Ownable
 *         interface) is allowed to register the token as part of the registry and configure addresses allowed to call
 *         into subclass methods, as permissioned by the onlyTokenOrAllowedUpdater modifier.
 *
 *         This base registry interface includes methods to see if a token is registered, and the allowedUpdaters,
 *         if any, for registered tokens.
 */
interface OwnerPermissionedTokenRegistryInterface {
    error TokenNotRegistered(address tokenAddress);
    error TokenAlreadyRegistered(address tokenAddress);
    error NotAllowedUpdater();
    error NotTokenOrOwner(address token, address actualOwner);

    event TokenRegistered(address indexed tokenAddress);

    function registerToken(address tokenAddress) external;

    function addAllowedUpdater(address tokenAddress, address newAllowedUpdater)
        external;

    function removeAllowedUpdater(
        address tokenAddress,
        address allowedUpdaterToRemove
    ) external;

    function getAllowedUpdaters(address tokenAddress)
        external
        returns (address[] memory);

    function isAllowedUpdater(address tokenAddress, address updater)
        external
        returns (bool);

    function isTokenRegistered(address tokenAddress)
        external
        returns (bool isRegistered);
}

File 25 of 31 : AlienArtConstellationEventsAndErrors.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.17;

/// @title AlienArtConstellationEventsAndErrors
/// @author Aspyn Palatnick (aspyn.eth, stuckinaboot.eth)
contract AlienArtConstellationEventsAndErrors {
    // Event to be emitted when swap constellations occurs
    event SwapConstellations(
        address indexed owner,
        uint256 indexed moon1,
        uint256 indexed moon2,
        uint256 newConstellationForMoon1,
        uint256 newConstellationForMoon2
    );

    // Set moon address errors
    error MoonAddressAlreadySet();

    // Mint errors
    error MsgSenderNotMoonAddress();

    // Swap constellations errors
    error SwapMoonsOwnerMustBeMsgSender();

    // Uri errors
    error InvalidConstellationIndex();
}

File 26 of 31 : ConstellationLib.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.17;

import "./SVG.sol";
import {Utils} from "../../utils/Utils.sol";
import {LibPRNG} from "../../utils/LibPRNG.sol";

/// @title ConstellationLib
/// @author Aspyn Palatnick (aspyn.eth, stuckinaboot.eth)
library ConstellationLib {
    // Constellations
    using LibPRNG for LibPRNG.PRNG;

    struct GenerateConstellationParams {
        uint256 x;
        uint256 y;
        uint16 rotationInDegrees;
        uint16 rotationCenterX;
        uint16 rotationCenterY;
        string starColor;
        bool fluxConstellation;
        bytes32 moonSeed;
    }

    function getLittleDipper(GenerateConstellationParams memory params)
        internal
        pure
        returns (string memory)
    {
        uint256 x = params.x;
        uint256 y = params.y;

        string memory handle = string.concat(
            getStar(params, x, y),
            getStar(params, x + 11, y + 9),
            getStar(params, x + 26, y + 15),
            getStar(params, x + 43, y + 14)
        );
        string memory cup = string.concat(
            getStar(params, x + 57, y + 5),
            getStar(params, x + 64, y + 14),
            getStar(params, x + 47, y + 23)
        );

        return
            makeConstellation(
                params.rotationInDegrees,
                params.rotationCenterX,
                params.rotationCenterY,
                string.concat(cup, handle)
            );
    }

    function getBigDipper(GenerateConstellationParams memory params)
        internal
        pure
        returns (string memory)
    {
        uint256 x = params.x;
        uint256 y = params.y;

        string memory cup = string.concat(
            getStar(params, x, y + 16),
            getStar(params, x + 11, y),
            getStar(params, x + 38, y + 13),
            getStar(params, x + 33, y + 30)
        );
        string memory handle = string.concat(
            getStar(params, x + 46, y + 45),
            getStar(params, x + 54, y + 58),
            getStar(params, x + 78, y + 66)
        );

        return
            makeConstellation(
                params.rotationInDegrees,
                params.rotationCenterX,
                params.rotationCenterY,
                string.concat(cup, handle)
            );
    }

    function getAries(GenerateConstellationParams memory params)
        internal
        pure
        returns (string memory)
    {
        uint256 x = params.x;
        uint256 y = params.y;

        string memory stars = string.concat(
            getStar(params, x, y),
            getStar(params, x + 35, y - 19),
            getStar(params, x + 50, y - 21),
            getStar(params, x + 55, y - 16)
        );

        return
            makeConstellation(
                params.rotationInDegrees,
                params.rotationCenterX,
                params.rotationCenterY,
                stars
            );
    }

    function getPisces(GenerateConstellationParams memory params)
        internal
        pure
        returns (string memory)
    {
        uint256 x = params.x;
        uint256 y = params.y;

        string memory upperLine = string.concat(
            getStar(params, x, y),
            getStar(params, x + 7, y - 8),
            getStar(params, x + 17, y - 20),
            getStar(params, x + 24, y - 32),
            getStar(params, x + 21, y - 41),
            getStar(params, x + 30, y - 47)
        );
        string memory lowerLine = string.concat(
            getStar(params, x + 9, y - 2),
            getStar(params, x + 28, y - 7),
            getStar(params, x + 36, y - 5),
            getStar(params, x + 52, y - 6)
        );
        string memory lowerCirclePart1 = string.concat(
            getStar(params, x + 60, y - 2),
            getStar(params, x + 65, y - 6),
            getStar(params, x + 70, y - 2),
            getStar(params, x + 71, y + 5)
        );
        string memory lowerCirclePart2 = string.concat(
            getStar(params, x + 66, y + 9),
            getStar(params, x + 58, y + 8),
            getStar(params, x + 57, y + 1)
        );

        string memory stars = string.concat(
            upperLine,
            lowerLine,
            lowerCirclePart1,
            lowerCirclePart2
        );
        return
            makeConstellation(
                params.rotationInDegrees,
                params.rotationCenterX,
                params.rotationCenterY,
                stars
            );
    }

    function getAquarius(GenerateConstellationParams memory params)
        internal
        pure
        returns (string memory)
    {
        uint256 x = params.x;
        uint256 y = params.y;

        string memory bottomDownLine = string.concat(
            getStar(params, x, y),
            getStar(params, x + 12, y - 3),
            getStar(params, x + 20, y + 5),
            getStar(params, x + 22, y + 21)
        );
        string memory topAcrossLine = string.concat(
            getStar(params, x + 8, y - 21),
            getStar(params, x + 14, y - 26),
            getStar(params, x + 18, y - 21),
            getStar(params, x + 26, y - 27),
            getStar(params, x + 68, y - 10)
        );
        string memory middleDownLine = string.concat(
            getStar(params, x + 29, y - 11),
            getStar(params, x + 39, y - 1)
        );

        string memory stars = string.concat(
            bottomDownLine,
            topAcrossLine,
            middleDownLine
        );
        return
            makeConstellation(
                params.rotationInDegrees,
                params.rotationCenterX,
                params.rotationCenterY,
                stars
            );
    }

    function getCapricornus(GenerateConstellationParams memory params)
        internal
        pure
        returns (string memory)
    {
        uint256 x = params.x;
        uint256 y = params.y;

        string memory top = string.concat(
            getStar(params, x, y),
            getStar(params, x + 8, y - 1),
            getStar(params, x + 30, y + 5)
        );
        string memory left = string.concat(
            getStar(params, x + 7, y + 7),
            getStar(params, x + 13, y + 16),
            getStar(params, x + 30, y + 29)
        );
        string memory right = string.concat(
            getStar(params, x + 34, y + 26),
            getStar(params, x + 59, y + 3),
            getStar(params, x + 65, y - 3)
        );
        string memory stars = string.concat(top, left, right);
        return
            makeConstellation(
                params.rotationInDegrees,
                params.rotationCenterX,
                params.rotationCenterY,
                stars
            );
    }

    function getSagittarius(GenerateConstellationParams memory params)
        internal
        pure
        returns (string memory)
    {
        string memory stars = string.concat(
            getSagittariusLeft(params),
            getSagittariusMiddle(params),
            getSagittariusRight(params)
        );
        return
            makeConstellation(
                params.rotationInDegrees,
                params.rotationCenterX,
                params.rotationCenterY,
                stars
            );
    }

    function getOphiuchus(GenerateConstellationParams memory params)
        internal
        pure
        returns (string memory)
    {
        uint256 x = params.x;
        uint256 y = params.y;

        string memory stars = string.concat(
            getStar(params, x, y),
            getStar(params, x + 3, y - 22),
            getStar(params, x + 11, y - 32),
            getStar(params, x + 19, y - 24),
            getStar(params, x + 22, y + 5),
            getStar(params, x + 9, y + 4)
        );

        return
            makeConstellation(
                params.rotationInDegrees,
                params.rotationCenterX,
                params.rotationCenterY,
                // Avoid stack too deep error by adding last star here
                string.concat(stars, getStar(params, x + 33, y + 12))
            );
    }

    function getScorpius(GenerateConstellationParams memory params)
        internal
        pure
        returns (string memory)
    {
        uint256 x = params.x;
        uint256 y = params.y;

        string memory top = string.concat(
            getStar(params, x, y),
            getStar(params, x + 3, y - 10),
            getStar(params, x + 9, y - 15),
            getStar(params, x + 14, y - 1)
        );
        string memory middle = string.concat(
            getStar(params, x + 19, y + 2),
            getStar(params, x + 21, y + 6),
            getStar(params, x + 25, y + 16),
            getStar(params, x + 25, y + 32)
        );
        string memory bottom1 = string.concat(
            getStar(params, x + 32, y + 37),
            getStar(params, x + 42, y + 39),
            getStar(params, x + 50, y + 33)
        );
        string memory bottom2 = string.concat(
            getStar(params, x + 47, y + 30),
            getStar(params, x + 44, y + 23)
        );
        string memory stars = string.concat(top, middle, bottom1, bottom2);
        return
            makeConstellation(
                params.rotationInDegrees,
                params.rotationCenterX,
                params.rotationCenterY,
                stars
            );
    }

    function getLibra(GenerateConstellationParams memory params)
        internal
        pure
        returns (string memory)
    {
        uint256 x = params.x;
        uint256 y = params.y;

        string memory triangle = string.concat(
            getStar(params, x, y),
            getStar(params, x + 6, y - 17),
            getStar(params, x + 23, y - 19)
        );
        string memory left = string.concat(
            getStar(params, x + 9, y + 13),
            getStar(params, x + 7, y + 18)
        );
        string memory right = string.concat(
            getStar(params, x + 21, y - 6),
            getStar(params, x + 32, y + 5)
        );
        string memory stars = string.concat(triangle, left, right);
        return
            makeConstellation(
                params.rotationInDegrees,
                params.rotationCenterX,
                params.rotationCenterY,
                stars
            );
    }

    function getVirgo(GenerateConstellationParams memory params)
        internal
        pure
        returns (string memory)
    {
        uint256 x = params.x;
        uint256 y = params.y;

        string memory middle = string.concat(
            getStar(params, x + 8, y),
            getStar(params, x + 11, y - 11),
            getStar(params, x + 10, y - 26),
            getStar(params, x + 22, y - 28),
            getStar(params, x + 28, y - 10)
        );
        string memory top = string.concat(
            getStar(params, x + 4, y - 32),
            getStar(params, x, y - 46),
            getStar(params, x + 34, y - 34)
        );
        string memory bottomLeft = string.concat(
            getStar(params, x + 21, y + 12),
            getStar(params, x + 24, y + 10),
            getStar(params, x + 30, y + 18)
        );
        string memory bottomRight = string.concat(
            getStar(params, x + 33, y - 7),
            getStar(params, x + 37, y - 4),
            getStar(params, x + 48, y + 9)
        );
        string memory stars = string.concat(
            middle,
            top,
            bottomLeft,
            bottomRight
        );
        return
            makeConstellation(
                params.rotationInDegrees,
                params.rotationCenterX,
                params.rotationCenterY,
                stars
            );
    }

    function getLeo(GenerateConstellationParams memory params)
        internal
        pure
        returns (string memory)
    {
        uint256 x = params.x;
        uint256 y = params.y;

        string memory loop = string.concat(
            getStar(params, x, y),
            getStar(params, x + 4, y - 10),
            getStar(params, x + 14, y - 12),
            getStar(params, x + 35, y + 3),
            getStar(params, x + 45, y + 21),
            getStar(params, x + 30, y + 12)
        );
        string memory top = string.concat(
            getStar(params, x + 17, y - 19),
            getStar(params, x + 11, y - 30),
            getStar(params, x + 2, y - 29)
        );

        return
            makeConstellation(
                params.rotationInDegrees,
                params.rotationCenterX,
                params.rotationCenterY,
                string.concat(loop, top)
            );
    }

    function getCancer(GenerateConstellationParams memory params)
        internal
        pure
        returns (string memory)
    {
        uint256 x = params.x;
        uint256 y = params.y;

        string memory stars = string.concat(
            getStar(params, x, y),
            getStar(params, x + 14, y - 21),
            getStar(params, x + 28, y - 12),
            getStar(params, x + 12, y - 29),
            getStar(params, x + 11, y - 49)
        );

        return
            makeConstellation(
                params.rotationInDegrees,
                params.rotationCenterX,
                params.rotationCenterY,
                stars
            );
    }

    function getGemini(GenerateConstellationParams memory params)
        internal
        pure
        returns (string memory)
    {
        string memory stars = string.concat(
            getGeminiLeftPerson(params),
            getGeminiRightPerson(params)
        );
        return
            makeConstellation(
                params.rotationInDegrees,
                params.rotationCenterX,
                params.rotationCenterY,
                stars
            );
    }

    function getTaurus(GenerateConstellationParams memory params)
        internal
        pure
        returns (string memory)
    {
        uint256 x = params.x;
        uint256 y = params.y;

        string memory left = string.concat(
            getStar(params, x, y),
            getStar(params, x + 5, y - 13),
            getStar(params, x + 18, y - 2)
        );
        string memory middle1 = string.concat(
            getStar(params, x + 18, y + 11),
            getStar(params, x + 22, y + 5),
            getStar(params, x + 22, y + 9)
        );
        string memory middle2 = string.concat(
            getStar(params, x + 23, y + 13),
            getStar(params, x + 26, y + 9),
            getStar(params, x + 27, y + 13)
        );
        string memory bottom = string.concat(
            getStar(params, x + 34, y + 19),
            getStar(params, x + 49, y + 24),
            getStar(params, x + 51, y + 29)
        );
        string memory stars = string.concat(left, middle1, middle2, bottom);
        return
            makeConstellation(
                params.rotationInDegrees,
                params.rotationCenterX,
                params.rotationCenterY,
                stars
            );
    }

    // Helpers

    function getTransform(
        uint16 rotationInDegrees,
        uint16 rotationCenterX,
        uint16 rotationCenterY
    ) internal pure returns (string memory) {
        return
            svg.prop(
                "transform",
                string.concat(
                    "rotate(",
                    Utils.uint2str(rotationInDegrees),
                    " ",
                    Utils.uint2str(rotationCenterX),
                    " ",
                    Utils.uint2str(rotationCenterY),
                    ")"
                )
            );
    }

    function getStarTransform(uint256 x, uint256 y)
        internal
        pure
        returns (string memory)
    {
        return
            svg.prop(
                "transform",
                string.concat(
                    "translate(",
                    Utils.uint2str(x),
                    ",",
                    Utils.uint2str(y),
                    ") scale(0.03)"
                )
            );
    }

    function getStar(
        GenerateConstellationParams memory params,
        uint256 x,
        uint256 y
    ) internal pure returns (string memory) {
        string memory opacity;
        if (params.fluxConstellation) {
            LibPRNG.PRNG memory prng;
            prng.seed(
                keccak256(
                    abi.encodePacked(
                        params.rotationInDegrees,
                        params.moonSeed,
                        x,
                        y
                    )
                )
            );
            // Minimum 30, max 100
            opacity = Utils.uint2str(prng.uniform(71) + 30);
        } else {
            opacity = "100";
        }

        return
            svg.path(
                string.concat(
                    svg.prop(
                        "d",
                        "M 40 60 L 63.511 72.361 L 59.021 46.180 L 78.042 27.639 L 51.756 23.820 L 40 0 L 28.244 23.820 L 1.958 27.639 L 20.979 46.180 L 16.489 72.361 L 40 60"
                    ),
                    svg.prop("fill", params.starColor),
                    svg.prop("filter", "url(#glo)"),
                    svg.prop("opacity", string.concat(opacity, "%")),
                    getStarTransform(x, y)
                )
            );
    }

    function makeConstellation(
        uint16 rotationInDegrees,
        uint16 rotationCenterX,
        uint16 rotationCenterY,
        string memory starElt
    ) internal pure returns (string memory) {
        return
            svg.g(
                getTransform(
                    rotationInDegrees,
                    rotationCenterX,
                    rotationCenterY
                ),
                string.concat(
                    // Glow filter
                    svg.filter(
                        svg.prop("id", "glo"),
                        string.concat(
                            svg.feGaussianBlur(
                                string.concat(
                                    svg.prop("stdDeviation", "4"),
                                    svg.prop("result", "blur")
                                )
                            ),
                            svg.feMerge(
                                string.concat(
                                    svg.feMergeNode(svg.prop("in", "blur")),
                                    svg.feMergeNode(svg.prop("in", "blur")),
                                    svg.feMergeNode(svg.prop("in", "blur")),
                                    svg.feMergeNode(
                                        svg.prop("in", "SourceGraphic")
                                    )
                                )
                            )
                        )
                    ),
                    starElt
                )
            );
    }

    // Individual constellation helpers

    // Sagittarius helpers for groups of stars as we get stack too deep errors
    // including all stars in one function

    function getSagittariusLeft(GenerateConstellationParams memory params)
        internal
        pure
        returns (string memory)
    {
        uint256 x = params.x;
        uint256 y = params.y;
        return
            string.concat(
                getStar(params, x, y),
                getStar(params, x + 11, y + 5),
                getStar(params, x + 18, y + 2),
                getStar(params, x + 22, y + 7),
                getStar(params, x + 19, y + 13),
                getStar(params, x + 19, y - 7),
                getStar(params, x + 11, y - 17)
            );
    }

    function getSagittariusMiddle(GenerateConstellationParams memory params)
        internal
        pure
        returns (string memory)
    {
        uint256 x = params.x;
        uint256 y = params.y;
        return
            string.concat(
                getStar(params, x + 27, y - 6),
                getStar(params, x + 30, y - 10),
                getStar(params, x + 31, y - 20),
                getStar(params, x + 26, y - 21),
                getStar(params, x + 36, y - 20),
                getStar(params, x + 42, y - 28)
            );
    }

    function getSagittariusRight(GenerateConstellationParams memory params)
        internal
        pure
        returns (string memory)
    {
        uint256 x = params.x;
        uint256 y = params.y;
        return
            string.concat(
                getStar(params, x + 33, y - 3),
                getStar(params, x + 36, y - 9),
                getStar(params, x + 45, y - 15),
                getStar(params, x + 55, y - 11),
                getStar(params, x + 60, y - 7),
                getStar(params, x + 55, y + 6),
                getStar(params, x + 53, y + 14),
                getStar(params, x + 44, y + 12),
                getStar(params, x + 43, y + 23)
            );
    }

    // Gemini helpers for groups of stars as we get stack too deep errors
    // including all stars in one function

    function getGeminiLeftPerson(GenerateConstellationParams memory params)
        internal
        pure
        returns (string memory)
    {
        uint256 x = params.x;
        uint256 y = params.y;
        string memory leftPersonTop = string.concat(
            getStar(params, x, y),
            getStar(params, x + 10, y - 12),
            getStar(params, x + 13, y - 6),
            getStar(params, x + 20, y - 7)
        );
        string memory leftPersonBottom1 = string.concat(
            getStar(params, x + 13, y + 4),
            getStar(params, x + 13, y + 15),
            getStar(params, x + 11, y + 23)
        );
        string memory leftPersonBottom2 = string.concat(
            getStar(params, x + 13, y + 34),
            getStar(params, x + 1, y + 21),
            getStar(params, x + 3, y + 38)
        );
        return
            string.concat(leftPersonTop, leftPersonBottom1, leftPersonBottom2);
    }

    function getGeminiRightPerson(GenerateConstellationParams memory params)
        internal
        pure
        returns (string memory)
    {
        uint256 x = params.x;
        uint256 y = params.y;
        string memory rightPersonTop = string.concat(
            getStar(params, x + 28, y - 16),
            getStar(params, x + 29, y - 6),
            getStar(params, x + 38, y - 7)
        );
        string memory rightPersonBottom1 = string.concat(
            getStar(params, x + 28, y + 9),
            getStar(params, x + 30, y + 18),
            getStar(params, x + 30, y + 30)
        );
        string memory rightPersonBottom2 = string.concat(
            getStar(params, x + 25, y + 35),
            getStar(params, x + 40, y + 32)
        );
        return
            string.concat(
                rightPersonTop,
                rightPersonBottom1,
                rightPersonBottom2
            );
    }
}

File 27 of 31 : IERC721.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (token/ERC721/IERC721.sol)

pragma solidity 0.8.17;

import "./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 be have been allowed to move this token by either {approve} or {setApprovalForAll}.
     * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
     *
     * Emits a {Transfer} event.
     */
    function safeTransferFrom(
        address from,
        address to,
        uint256 tokenId
    ) external;

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

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

    /**
     * @dev 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 28 of 31 : SVG.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.17;

// Core SVG utility library which helps us construct
// onchain SVG's with a simple, web-like API.
// Props to w1nt3r.eth for creating the core of this SVG utility library.
library svg {
    string internal constant NULL = "";

    /* MAIN ELEMENTS */
    function svgTag(string memory _props, string memory _children)
        internal
        pure
        returns (string memory)
    {
        return el("svg", _props, _children);
    }

    function g(string memory _props, string memory _children)
        internal
        pure
        returns (string memory)
    {
        return el("g", _props, _children);
    }

    function rect(string memory _props) internal pure returns (string memory) {
        return el("rect", _props, NULL);
    }

    function path(string memory _props) internal pure returns (string memory) {
        return el("path", _props, NULL);
    }

    function filter(string memory _props, string memory _children)
        internal
        pure
        returns (string memory)
    {
        return el("filter", _props, _children);
    }

    function feGaussianBlur(string memory _props)
        internal
        pure
        returns (string memory)
    {
        return el("feGaussianBlur", _props, NULL);
    }

    function feMerge(string memory _children)
        internal
        pure
        returns (string memory)
    {
        return el("feMerge", NULL, _children);
    }

    function feMergeNode(string memory _props)
        internal
        pure
        returns (string memory)
    {
        return el("feMergeNode", _props, NULL);
    }

    /* COMMON */
    // A generic element, can be used to construct any SVG (or HTML) element
    function el(
        string memory _tag,
        string memory _props,
        string memory _children
    ) internal pure returns (string memory) {
        return
            string.concat(
                "<",
                _tag,
                " ",
                _props,
                ">",
                _children,
                "</",
                _tag,
                ">"
            );
    }

    // an SVG attribute
    function prop(string memory _key, string memory _val)
        internal
        pure
        returns (string memory)
    {
        return string.concat(_key, '="', _val, '" ');
    }
}

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

pragma solidity 0.8.17;

/**
 * @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 30 of 31 : OperatorFilterer.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.13;

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

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

    IOperatorFilterRegistry public constant OPERATOR_FILTER_REGISTRY =
        IOperatorFilterRegistry(0x000000000000AAeB6D7670E522A718067333cd4E);

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

    modifier onlyAllowedOperator(address from) virtual {
        // Check registry code length to facilitate testing in environments without a deployed registry.
        if (address(OPERATOR_FILTER_REGISTRY).code.length > 0) {
            // 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) {
                _;
                return;
            }
            if (
                !OPERATOR_FILTER_REGISTRY.isOperatorAllowed(
                    address(this),
                    msg.sender
                )
            ) {
                revert OperatorNotAllowed(msg.sender);
            }
        }
        _;
    }

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

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

interface IOperatorFilterRegistry {
    function isOperatorAllowed(address registrant, address operator)
        external
        view
        returns (bool);

    function register(address registrant) external;

    function registerAndSubscribe(address registrant, address subscription)
        external;

    function registerAndCopyEntries(
        address registrant,
        address registrantToCopy
    ) external;

    function unregister(address addr) external;

    function updateOperator(
        address registrant,
        address operator,
        bool filtered
    ) external;

    function updateOperators(
        address registrant,
        address[] calldata operators,
        bool filtered
    ) external;

    function updateCodeHash(
        address registrant,
        bytes32 codehash,
        bool filtered
    ) external;

    function updateCodeHashes(
        address registrant,
        bytes32[] calldata codeHashes,
        bool filtered
    ) external;

    function subscribe(address registrant, address registrantToSubscribe)
        external;

    function unsubscribe(address registrant, bool copyExistingEntries) external;

    function subscriptionOf(address addr) external returns (address registrant);

    function subscribers(address registrant)
        external
        returns (address[] memory);

    function subscriberAt(address registrant, uint256 index)
        external
        returns (address);

    function copyEntriesOf(address registrant, address registrantToCopy)
        external;

    function isOperatorFiltered(address registrant, address operator)
        external
        returns (bool);

    function isCodeHashOfFiltered(address registrant, address operatorWithCode)
        external
        returns (bool);

    function isCodeHashFiltered(address registrant, bytes32 codeHash)
        external
        returns (bool);

    function filteredOperators(address addr)
        external
        returns (address[] memory);

    function filteredCodeHashes(address addr)
        external
        returns (bytes32[] memory);

    function filteredOperatorAt(address registrant, uint256 index)
        external
        returns (address);

    function filteredCodeHashAt(address registrant, uint256 index)
        external
        returns (bytes32);

    function isRegistered(address addr) external returns (bool);

    function codeHashOf(address addr) external returns (bytes32);
}

Settings
{
  "remappings": [
    "@erc721a/=lib/erc721a/",
    "@openzeppelin/=lib/openzeppelin-contracts/",
    "@solmate/=lib/solmate/src/",
    "@std/=lib/forge-std/src/",
    "ds-test/=lib/ds-test/src/",
    "erc721a/=lib/erc721a/contracts/",
    "forge-std/=lib/forge-std/src/",
    "openzeppelin-contracts/=lib/openzeppelin-contracts/",
    "solmate/=lib/solmate/src/"
  ],
  "optimizer": {
    "enabled": true,
    "runs": 200
  },
  "metadata": {
    "bytecodeHash": "ipfs"
  },
  "outputSelection": {
    "*": {
      "*": [
        "evm.bytecode",
        "evm.deployedBytecode",
        "devdoc",
        "userdoc",
        "metadata",
        "abi"
      ]
    }
  },
  "evmVersion": "london",
  "libraries": {}
}

Contract Security Audit

Contract ABI

[{"inputs":[{"internalType":"string","name":"_name","type":"string"},{"internalType":"string","name":"_symbol","type":"string"},{"internalType":"address","name":"_defaultAlienArtAddress","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"AlienArtContractFailedValidation","type":"error"},{"inputs":[],"name":"ApprovalCallerNotOwnerNorApproved","type":"error"},{"inputs":[],"name":"ApprovalQueryForNonexistentToken","type":"error"},{"inputs":[],"name":"BalanceQueryForZeroAddress","type":"error"},{"inputs":[],"name":"MaxSupplyReached","type":"error"},{"inputs":[],"name":"MintERC2309QuantityExceedsLimit","type":"error"},{"inputs":[],"name":"MintToZeroAddress","type":"error"},{"inputs":[],"name":"MintZeroQuantity","type":"error"},{"inputs":[],"name":"NoRegenerationsRemaining","type":"error"},{"inputs":[{"internalType":"address","name":"operator","type":"address"}],"name":"OperatorNotAllowed","type":"error"},{"inputs":[],"name":"OwnerNotMsgSender","type":"error"},{"inputs":[],"name":"OwnerQueryForNonexistentToken","type":"error"},{"inputs":[],"name":"OwnershipNotInitializedForExtraData","type":"error"},{"inputs":[],"name":"TransferCallerNotOwnerNorApproved","type":"error"},{"inputs":[],"name":"TransferFromIncorrectOwner","type":"error"},{"inputs":[],"name":"TransferToNonERC721ReceiverImplementer","type":"error"},{"inputs":[],"name":"TransferToZeroAddress","type":"error"},{"inputs":[],"name":"URIQueryForNonexistentToken","type":"error"},{"inputs":[],"name":"WrongEtherAmount","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"},{"indexed":true,"internalType":"address","name":"alienArtAddress","type":"address"}],"name":"AlienArtAddressUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"approved","type":"address"},{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"operator","type":"address"},{"indexed":false,"internalType":"bool","name":"approved","type":"bool"}],"name":"ApprovalForAll","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"fromTokenId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"toTokenId","type":"uint256"},{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"}],"name":"ConsecutiveTransfer","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"referrerAddress","type":"address"},{"indexed":true,"internalType":"uint256","name":"referrerToken","type":"uint256"},{"indexed":true,"internalType":"address","name":"minterAddress","type":"address"},{"indexed":false,"internalType":"uint256","name":"mintStartTokenId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"referrerPayout","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"referredPayout","type":"uint256"}],"name":"MintedWithReferrer","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"moonOwner","type":"address"},{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"},{"indexed":true,"internalType":"bytes32","name":"newMoonSeed","type":"bytes32"},{"indexed":false,"internalType":"bytes32","name":"previousMoonSeed","type":"bytes32"},{"indexed":false,"internalType":"uint8","name":"regenerationsUsed","type":"uint8"}],"name":"MoonRegenerated","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":"MAX_SUPPLY","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"OPERATOR_FILTER_REGISTRY","outputs":[{"internalType":"contract IOperatorFilterRegistry","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"PRICE","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"alienArtAddressMap","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"operator","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"approve","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"defaultAlienArtAddress","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"dynamicNftRegistryAddress","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"getAlienArtContractForToken","outputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"contract AlienArtBase","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":"referrer","type":"address"},{"internalType":"address","name":"referred","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"}],"name":"getReferralAmounts","outputs":[{"internalType":"uint256","name":"referrerValue","type":"uint256"},{"internalType":"uint256","name":"referredValue","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"operator","type":"address"}],"name":"isApprovedForAll","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"mint","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"address payable","name":"referrer","type":"address"},{"internalType":"uint256","name":"referrerTokenId","type":"uint256"}],"name":"mintWithReferrer","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"moonSeeds","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256[]","name":"","type":"uint256[]"},{"internalType":"uint256[]","name":"","type":"uint256[]"},{"internalType":"bytes","name":"","type":"bytes"}],"name":"onERC1155BatchReceived","outputs":[{"internalType":"bytes4","name":"","type":"bytes4"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bytes","name":"","type":"bytes"}],"name":"onERC1155Received","outputs":[{"internalType":"bytes4","name":"","type":"bytes4"}],"stateMutability":"nonpayable","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":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"regenerateMoon","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"regeneratesUsedByCurrentOwner","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"salePrice","type":"uint256"}],"name":"royaltyInfo","outputs":[{"internalType":"address","name":"receiver","type":"address"},{"internalType":"uint256","name":"royaltyAmount","type":"uint256"}],"stateMutability":"pure","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":"payable","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":"payable","type":"function"},{"inputs":[{"internalType":"uint256[]","name":"tokenIds","type":"uint256[]"},{"internalType":"address","name":"alienArtAddress","type":"address"}],"name":"setAlienArtAddresses","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":"address","name":"_dynamicNftRegistryAddress","type":"address"}],"name":"setupDynamicNftRegistry","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":"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":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"withdraw","outputs":[],"stateMutability":"nonpayable","type":"function"},{"stateMutability":"payable","type":"receive"}]

60806040523480156200001157600080fd5b50604051620063c5380380620063c5833981016040819052620000349162000304565b8282733cc6cdda760b79bafa08df41ecfa224f810dceb660016daaeb6d7670e522a718067333cd4e3b1562000192578015620000e057604051633e9f1edf60e11b81523060048201526001600160a01b03831660248201526daaeb6d7670e522a718067333cd4e90637d3e3dbe906044015b600060405180830381600087803b158015620000c157600080fd5b505af1158015620000d6573d6000803e3d6000fd5b5050505062000192565b6001600160a01b03821615620001315760405163a0af290360e01b81523060048201526001600160a01b03831660248201526daaeb6d7670e522a718067333cd4e9063a0af290390604401620000a6565b604051632210724360e11b81523060048201526daaeb6d7670e522a718067333cd4e90634420e48690602401600060405180830381600087803b1580156200017857600080fd5b505af11580156200018d573d6000803e3d6000fd5b505050505b5060029050620001a3838262000420565b506003620001b2828262000420565b50506000805550620001c433620001ed565b600c80546001600160a01b0319166001600160a01b039290921691909117905550620004ec9050565b600880546001600160a01b038381166001600160a01b0319831681179093556040519116919082907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a35050565b634e487b7160e01b600052604160045260246000fd5b600082601f8301126200026757600080fd5b81516001600160401b03808211156200028457620002846200023f565b604051601f8301601f19908116603f01168101908282118183101715620002af57620002af6200023f565b81604052838152602092508683858801011115620002cc57600080fd5b600091505b83821015620002f05785820183015181830184015290820190620002d1565b600093810190920192909252949350505050565b6000806000606084860312156200031a57600080fd5b83516001600160401b03808211156200033257600080fd5b620003408783880162000255565b945060208601519150808211156200035757600080fd5b50620003668682870162000255565b604086015190935090506001600160a01b03811681146200038657600080fd5b809150509250925092565b600181811c90821680620003a657607f821691505b602082108103620003c757634e487b7160e01b600052602260045260246000fd5b50919050565b601f8211156200041b57600081815260208120601f850160051c81016020861015620003f65750805b601f850160051c820191505b81811015620004175782815560010162000402565b5050505b505050565b81516001600160401b038111156200043c576200043c6200023f565b62000454816200044d845462000391565b84620003cd565b602080601f8311600181146200048c5760008415620004735750858301515b600019600386901b1c1916600185901b17855562000417565b600085815260208120601f198616915b82811015620004bd578886015182559484019460019091019084016200049c565b5085821015620004dc5787850151600019600388901b60f8161c191681555b5050505050600190811b01905550565b615ec980620004fc6000396000f3fe6080604052600436106102135760003560e01c806370a0823111610118578063bc197c81116100a0578063d58fa50d1161006f578063d58fa50d14610633578063e4681f2914610669578063e985e9c5146106a8578063f23a6e61146106c8578063f2fde38b146106f557600080fd5b8063bc197c81146105a5578063c455dbca146105ed578063c49b7b9714610600578063c87b56dd1461061357600080fd5b80638da5cb5b116100e75780638da5cb5b1461052c57806395d89b411461054a578063a0712d681461055f578063a22cb46514610572578063b88d4fde1461059257600080fd5b806370a08231146104af578063715018a6146104cf5780638675d158146104e45780638d859f3e1461051157600080fd5b806332cb6b0c1161019b57806347cfbacd1161016a57806347cfbacd146103ed5780635ca2708f1461040d5780635ce572da1461042d5780636352211e1461044d5780636c60614c1461046d57600080fd5b806332cb6b0c1461038d5780633ccfd60b146103a357806341f43434146103b857806342842e0e146103da57600080fd5b8063095ea7b3116101e2578063095ea7b3146102ce57806318160ddd146102e357806323b872dd146103065780632a55205a1461031957806331748c531461035857600080fd5b806301ffc9a71461021f578063037cab581461025457806306fdde031461028c578063081812fc146102ae57600080fd5b3661021a57005b600080fd5b34801561022b57600080fd5b5061023f61023a3660046147f4565b610715565b60405190151581526020015b60405180910390f35b34801561026057600080fd5b50600c54610274906001600160a01b031681565b6040516001600160a01b03909116815260200161024b565b34801561029857600080fd5b506102a1610740565b60405161024b9190614861565b3480156102ba57600080fd5b506102746102c9366004614874565b6107d2565b6102e16102dc3660046148a2565b610816565b005b3480156102ef57600080fd5b50600154600054035b60405190815260200161024b565b6102e16103143660046148ce565b6108e4565b34801561032557600080fd5b5061033961033436600461490f565b6109bd565b604080516001600160a01b03909316835260208301919091520161024b565b34801561036457600080fd5b506103786103733660046148ce565b6109f9565b6040805192835260208301919091520161024b565b34801561039957600080fd5b506102f861020181565b3480156103af57600080fd5b506102e1610abf565b3480156103c457600080fd5b506102746daaeb6d7670e522a718067333cd4e81565b6102e16103e83660046148ce565b610b02565b3480156103f957600080fd5b506102e1610408366004614975565b610bd0565b34801561041957600080fd5b506102e16104283660046149cb565b610d5d565b34801561043957600080fd5b50600b54610274906001600160a01b031681565b34801561045957600080fd5b50610274610468366004614874565b610e9c565b34801561047957600080fd5b5061049d610488366004614874565b600a6020526000908152604090205460ff1681565b60405160ff909116815260200161024b565b3480156104bb57600080fd5b506102f86104ca3660046149cb565b610ea7565b3480156104db57600080fd5b506102e1610ef5565b3480156104f057600080fd5b506102f86104ff366004614874565b60096020526000908152604090205481565b34801561051d57600080fd5b506102f8668e1bc9bf04000081565b34801561053857600080fd5b506008546001600160a01b0316610274565b34801561055657600080fd5b506102a1610f09565b6102e161056d366004614874565b610f18565b34801561057e57600080fd5b506102e161058d3660046149f6565b610f25565b6102e16105a0366004614a9c565b610fe9565b3480156105b157600080fd5b506105d46105c0366004614b8b565b63bc197c8160e01b98975050505050505050565b6040516001600160e01b0319909116815260200161024b565b6102e16105fb366004614c49565b6110c5565b6102e161060e366004614874565b6111f4565b34801561061f57600080fd5b506102a161062e366004614874565b6113ed565b34801561063f57600080fd5b5061027461064e366004614874565b600d602052600090815260409020546001600160a01b031681565b34801561067557600080fd5b50610689610684366004614874565b6115a8565b6040805192151583526001600160a01b0390911660208301520161024b565b3480156106b457600080fd5b5061023f6106c3366004614c70565b61163e565b3480156106d457600080fd5b506105d46106e3366004614c9e565b63f23a6e6160e01b9695505050505050565b34801561070157600080fd5b506102e16107103660046149cb565b61166c565b60006001600160e01b0319821663152a902d60e11b148061073a575061073a826116e2565b92915050565b60606002805461074f90614d19565b80601f016020809104026020016040519081016040528092919081815260200182805461077b90614d19565b80156107c85780601f1061079d576101008083540402835291602001916107c8565b820191906000526020600020905b8154815290600101906020018083116107ab57829003601f168201915b5050505050905090565b60006107dd82611730565b6107fa576040516333d1c03960e21b815260040160405180910390fd5b506000908152600660205260409020546001600160a01b031690565b816daaeb6d7670e522a718067333cd4e3b156108d557604051633185c44d60e21b81523060048201526001600160a01b03821660248201526daaeb6d7670e522a718067333cd4e9063c617113490604401602060405180830381865afa158015610884573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906108a89190614d53565b6108d557604051633b79c77360e21b81526001600160a01b03821660048201526024015b60405180910390fd5b6108df8383611757565b505050565b826daaeb6d7670e522a718067333cd4e3b156109ac57336001600160a01b0382160361091a576109158484846117f7565b6109b7565b604051633185c44d60e21b81523060048201523360248201526daaeb6d7670e522a718067333cd4e9063c617113490604401602060405180830381865afa158015610969573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061098d9190614d53565b6109ac57604051633b79c77360e21b81523360048201526024016108cc565b6109b78484846117f7565b50505050565b6000807339ab90066cec746a032d67e4fe3378f16294cf6b6127106109e38560fa614d86565b6109ed9190614db3565b915091505b9250929050565b60008080610a08600485614db3565b9050610a206040518060200160405280600081525090565b610a84448888604051602001610a5f93929190928352606091821b6bffffffffffffffffffffffff199081166020850152911b16603482015260480190565b604051602081830303815290604052805190602001208261199490919063ffffffff16565b612710610a9382612711611998565b610a9d9084614d86565b610aa79190614db3565b9250610ab38383614dc7565b93505050935093915050565b6040517339ab90066cec746a032d67e4fe3378f16294cf6b904780156108fc02916000818181858888f19350505050158015610aff573d6000803e3d6000fd5b50565b826daaeb6d7670e522a718067333cd4e3b15610bc557336001600160a01b03821603610b33576109158484846119b6565b604051633185c44d60e21b81523060048201523360248201526daaeb6d7670e522a718067333cd4e9063c617113490604401602060405180830381865afa158015610b82573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610ba69190614d53565b610bc557604051633b79c77360e21b81523360048201526024016108cc565b6109b78484846119b6565b610201821115610bf35760405163d05cb60960e01b815260040160405180910390fd5b6001600160a01b03811615801590610c7757506040516301ffc9a760e01b8152634332ac1760e11b60048201526001600160a01b038216906301ffc9a790602401602060405180830381865afa158015610c51573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610c759190614d53565b155b15610c955760405163cc76443960e01b815260040160405180910390fd5b60005b828110156109b7576000848483818110610cb457610cb4614dda565b905060200201359050336001600160a01b0316610cd082610e9c565b6001600160a01b031614610cf7576040516305e8ff9f60e11b815260040160405180910390fd5b6000818152600d602052604080822080546001600160a01b0319166001600160a01b0387169081179091559051909183917fd05e65504e36f710b5ae22e41b5163363a8aef58726c3dbd7f24a702277ae1109190a350610d5681614df0565b9050610c98565b610d656119d1565b600b80546001600160a01b0319166001600160a01b0383169081179091556040516213049560e71b815230600482015281906309824a8090602401600060405180830381600087803b158015610dba57600080fd5b505af1158015610dce573d6000803e3d6000fd5b5050600c54604051634cd54e5760e01b81523060048201526001600160a01b0391821660248201529084169250634cd54e579150604401600060405180830381600087803b158015610e1f57600080fd5b505af1158015610e33573d6000803e3d6000fd5b5050604051634cd54e5760e01b8152306004820181905260248201526001600160a01b0384169250634cd54e5791506044015b600060405180830381600087803b158015610e8057600080fd5b505af1158015610e94573d6000803e3d6000fd5b505050505050565b600061073a82611a2b565b60006001600160a01b038216610ed0576040516323d3ad8160e21b815260040160405180910390fd5b506001600160a01b03166000908152600560205260409020546001600160401b031690565b610efd6119d1565b610f076000611a99565b565b60606003805461074f90614d19565b610f2181611aeb565b5050565b816daaeb6d7670e522a718067333cd4e3b15610fdf57604051633185c44d60e21b81523060048201526001600160a01b03821660248201526daaeb6d7670e522a718067333cd4e9063c617113490604401602060405180830381865afa158015610f93573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610fb79190614d53565b610fdf57604051633b79c77360e21b81526001600160a01b03821660048201526024016108cc565b6108df8383611c08565b836daaeb6d7670e522a718067333cd4e3b156110b257336001600160a01b038216036110205761101b85858585611c74565b6110be565b604051633185c44d60e21b81523060048201523360248201526daaeb6d7670e522a718067333cd4e9063c617113490604401602060405180830381865afa15801561106f573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906110939190614d53565b6110b257604051633b79c77360e21b81523360048201526024016108cc565b6110be85858585611c74565b5050505050565b60006110d084611aeb565b90506001600160a01b038316158015906110f357506001600160a01b0383163314155b8015611118575061110382610e9c565b6001600160a01b0316836001600160a01b0316145b156109b75760008061112b8533346109f9565b60408051868152602081018a9052908101839052606081018290529193509150339085906001600160a01b038816907f1e146227c31c93c94e66918096db1e3940b7e061209e5bbd373595bf60cd25d09060800160405180910390a46040516001600160a01b0386169083156108fc029084906000818181858888f193505050501580156111bd573d6000803e3d6000fd5b50604051339082156108fc029083906000818181858888f193505050501580156111eb573d6000803e3d6000fd5b50505050505050565b6000818152600a602052604090205460ff166002190161122657604051623ebab160e71b815260040160405180910390fd5b668e1bc9bf040000341461124d576040516331fc877f60e01b815260040160405180910390fd5b3361125782610e9c565b6001600160a01b03161461127e576040516305e8ff9f60e11b815260040160405180910390fd5b60008181526009602052604090205461129682611cb8565b600083815260096020908152604080832093909355600a905290812080549091906112c39060ff16614e09565b82546101009290920a60ff818102199093169183160217909155600083815260096020908152604080832054600a8352928190205481518681529416918401919091529091849133917f7a449dca7e96c146396697c76b05ddcb78a62f572cc6b90be959387f6377c939910160405180910390a4600c5460405163044413a560e21b8152600481018490526001600160a01b03909116906311104e9490602401600060405180830381600087803b15801561137d57600080fd5b505af1158015611391573d6000803e3d6000fd5b5050600b546001600160a01b0316159150610f21905057600b546040516348489b1960e11b81523060048201526024810184905260786044820152600060648201526001600160a01b0390911690639091363290608401610e66565b60606113f882610e9c565b5060405163e4681f2960e01b8152600481018390526000908190309063e4681f29906024016040805180830381865afa158015611439573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061145d9190614e28565b90925090506000611470426103e8614d86565b9050600061148b838761148285611cf3565b61ffff16611d22565b9250505060006009600088815260200190815260200160002054905060006115238284876001600160a01b031663941cbad06040518163ffffffff1660e01b8152600401600060405180830381865afa1580156114ec573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526115149190810190614e57565b61151d89611eba565b8a611ed0565b90506000611530896120f2565b6040516020016115409190614ec4565b604051602081830303815290604052905060008061155f8b888a61221a565b9150915061159961156f83612319565b6115788361234a565b8560405180610120016040528060ec8152602001615da860ec913988612365565b9b9a5050505050505050505050565b6000818152600d6020526040812054819081906001600160a01b0316156115e757506000838152600d60205260409020546001600160a01b03166115f5565b50600c546001600160a01b03165b600c546000858152600d60205260409020546001600160a01b039081169116148061163557506000848152600d60205260409020546001600160a01b0316155b94909350915050565b6001600160a01b03918216600090815260076020908152604080832093909416825291909152205460ff1690565b6116746119d1565b6001600160a01b0381166116d95760405162461bcd60e51b815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201526564647265737360d01b60648201526084016108cc565b610aff81611a99565b60006301ffc9a760e01b6001600160e01b03198316148061171357506380ac58cd60e01b6001600160e01b03198316145b8061073a5750506001600160e01b031916635b5e139f60e01b1490565b600080548210801561073a575050600090815260046020526040902054600160e01b161590565b600061176282610e9c565b9050336001600160a01b0382161461179b5761177e813361163e565b61179b576040516367d9dca160e11b815260040160405180910390fd5b60008281526006602052604080822080546001600160a01b0319166001600160a01b0387811691821790925591518593918516917f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92591a4505050565b600061180282611a2b565b9050836001600160a01b0316816001600160a01b0316146118355760405162a1148160e81b815260040160405180910390fd5b60008281526006602052604090208054338082146001600160a01b0388169091141761188257611865863361163e565b61188257604051632ce44b5f60e11b815260040160405180910390fd5b6001600160a01b0385166118a957604051633a954ecd60e21b815260040160405180910390fd5b80156118b457600082555b6001600160a01b038681166000908152600560205260408082208054600019019055918716808252919020805460010190554260a01b17600160e11b17600085815260046020526040812091909155600160e11b84169003611946576001840160008181526004602052604081205490036119445760005481146119445760008181526004602052604090208490555b505b83856001600160a01b0316876001600160a01b03167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef60405160405180910390a4610e9486868660016123c1565b9052565b60005b602083209050808352818260000306811061199b5706919050565b6108df83838360405180602001604052806000815250610fe9565b6008546001600160a01b03163314610f075760405162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e657260448201526064016108cc565b600081600054811015611a805760008181526004602052604081205490600160e01b82169003611a7e575b80600003611a77575060001901600081815260046020526040902054611a56565b9392505050565b505b604051636f96cda160e11b815260040160405180910390fd5b600880546001600160a01b038381166001600160a01b0319831681179093556040519116919082907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a35050565b600081611af760005490565b611b019190614eff565b6102011015611b235760405163d05cb60960e01b815260040160405180910390fd5b611b3482668e1bc9bf040000614d86565b3414611b53576040516331fc877f60e01b815260040160405180910390fd5b600054805b611b628483614eff565b811015611b9157611b7281611cb8565b600082815260096020526040902055611b8a81614df0565b9050611b58565b50611b9c33846123fa565b600c54604051630d9778e560e11b815260048101839052602481018590526001600160a01b0390911690631b2ef1ca90604401600060405180830381600087803b158015611be957600080fd5b505af1158015611bfd573d6000803e3d6000fd5b509295945050505050565b3360008181526007602090815260408083206001600160a01b03871680855290835292819020805460ff191686151590811790915590519081529192917f17307eab39ab6107e8899845ad3d59bd9653f200f220920489ca2b5937696c31910160405180910390a35050565b611c7f8484846108e4565b6001600160a01b0383163b156109b757611c9b84848484612501565b6109b7576040516368d2bf6b60e11b815260040160405180910390fd5b60008144604051602001611cd6929190918252602082015260400190565b604051602081830303815290604052805190602001209050919050565b6000612710611d18611d04846125ec565b611d1090610168614d86565b61271061264f565b61073a9190614db3565b60008281526009602052604081205460609182918291611d4182612685565b6040516313a4c89160e31b81529091506001600160a01b03891690639d26448890611d76908a90869086908c90600401614f9c565b600060405180830381865afa158015611d93573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052611dbb9190810190614e57565b60405163abfeb63160e01b81526001600160a01b038a169063abfeb63190611ded908b90879087908d90600401614f9c565b600060405180830381865afa158015611e0a573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052611e329190810190614e57565b6040516324a1104760e01b81526001600160a01b038b16906324a1104790611e64908c90889088908e90600401614f9c565b600060405180830381865afa158015611e81573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052611ea99190810190614e57565b945094509450505093509350939050565b606061073a6001600160a01b0383166014612790565b60606000611edd87612685565b90506000611eea8261292b565b90506000611f6760405180604001604052806014815260200173125cc8191959985d5b1d08185b1a595b88185c9d60621b81525086611f4357604051806040016040528060028152602001614e6f60f01b815250611f60565b6040518060400160405280600381526020016259657360e81b8152505b6001612a8e565b611f9c60405180604001604052806011815260200170416c69656e20617274206164647265737360781b815250886001612a8e565b611fcc60405180604001604052806009815260200168105b1a595b88185c9d60ba1b8152508a60008d5111612a8e565b8a604051602001611fe09493929190615071565b60408051601f1981840301815282820190915260088252674d6f6f6e2068756560c01b60208381019190915285510151909250612023919061ffff166001612a9d565b826120616040518060400160405280600e81526020016d5370616365206461726b6e65737360901b815250866000015160a0015160ff166001612a9d565b6120c06040518060400160405280601281526020017112185cc81cdc1858d94819dc98591a595b9d60721b8152506000886000015160c001515111611f4357604051806040016040528060028152602001614e6f60f01b815250611f60565b846040516020016120d59594939291906150c8565b604051602081830303815290604052935050505095945050505050565b6060816000036121195750506040805180820190915260018152600360fc1b602082015290565b8160005b81156121405761212c81614df0565b9050612139600a83614db3565b915061211d565b6000816001600160401b0381111561215a5761215a614a2f565b6040519080825280601f01601f191660200182016040528015612184576020820181803683370190505b509050815b85156122115761219a600182614dc7565b905060006121a9600a88614db3565b6121b490600a614d86565b6121be9088614dc7565b6121c9906030615153565b905060008160f81b9050808484815181106121e6576121e6614dda565b60200101906001600160f81b031916908160001a905350612208600a89614db3565b97505050612189565b50949350505050565b60008381526009602052604090205460609081908180865b612240639813edbd89614eff565b8110156122e857600080612258898c61148286611cf3565b5091509150600061226b87858585612ab4565b90508a840361229e5780945080604051602001612288919061516c565b60405160208183030381529060405295506122c3565b85816040516020016122b1929190615270565b60405160208183030381529060405295505b5050506078639813edbd6122d79190614db3565b6122e19082614eff565b9050612232565b5080826040516020016122fb91906152bb565b60405160208183030381529060405294509450505050935093915050565b606061232482612ade565b604051602001612334919061557c565b6040516020818303038152906040529050919050565b606061235582612ade565b60405160200161233491906155c1565b606061239784848489896040516020016123839594939291906155ff565b604051602081830303815290604052612ade565b6040516020016123a79190615704565b604051602081830303815290604052905095945050505050565b815b6123cd8284614eff565b8110156110be576000818152600a60205260409020805460ff191690556123f381614df0565b90506123c3565b600080549082900361241f5760405163b562e8dd60e01b815260040160405180910390fd5b6001600160a01b03831660008181526005602090815260408083208054680100000000000000018802019055848352600490915281206001851460e11b4260a01b178317905582840190839083907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef8180a4600183015b8181146124ce57808360007fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef600080a4600101612496565b50816000036124ef57604051622e076360e81b815260040160405180910390fd5b60009081556108df91508483856123c1565b604051630a85bd0160e11b81526000906001600160a01b0385169063150b7a0290612536903390899088908890600401615749565b6020604051808303816000875af1925050508015612571575060408051601f3d908101601f1916820190925261256e9181019061577c565b60015b6125cf573d80801561259f576040519150601f19603f3d011682016040523d82523d6000602084013e6125a4565b606091505b5080516000036125c7576040516368d2bf6b60e11b815260040160405180910390fd5b805181602001fd5b6001600160e01b031916630a85bd0160e11b149050949350505050565b600080639813edbd6126046501840ec2103085614dc7565b61260e9190615799565b90506000639813edbd61263061262684612710614d86565b639813edbd61264f565b61263a9190614db3565b90506127108110611a77576000949350505050565b60008061265d600284614db3565b6126679085614eff565b90506126738382615799565b61267d9082614dc7565b949350505050565b61268d614742565b602060c86000600261269f84826157ad565b6126a990846157d3565b6126b391906157f5565b90506126cb6040518060200160405280600081525090565b6126eb866005604051602001610a5f929190918252602082015260400190565b60006126f8826009611998565b1561270d57612708826006611998565b612710565b60325b9050600061271f836005611998565b9050600061272c84612c44565b9050600061273985612d10565b604080516101208101825293845261ffff998a1660208501529689169683018790526060830196909652509486166080860181905260a086015290851660c085015290931660e08301526101008201529392505050565b6060600061279f836002614d86565b6127aa906002614eff565b6001600160401b038111156127c1576127c1614a2f565b6040519080825280601f01601f1916602001820160405280156127eb576020820181803683370190505b509050600360fc1b8160008151811061280657612806614dda565b60200101906001600160f81b031916908160001a905350600f60fb1b8160018151811061283557612835614dda565b60200101906001600160f81b031916908160001a9053506000612859846002614d86565b612864906001614eff565b90505b60018111156128dc576f181899199a1a9b1b9c1cb0b131b232b360811b85600f166010811061289857612898614dda565b1a60f81b8282815181106128ae576128ae614dda565b60200101906001600160f81b031916908160001a90535060049490941c936128d581615816565b9050612867565b508315611a775760405162461bcd60e51b815260206004820181905260248201527f537472696e67733a20686578206c656e67746820696e73756666696369656e7460448201526064016108cc565b60606000808360e0015161ffff161190506129776040518060400160405280600f81526020016e4672616d6520726f756e646e65737360881b8152508460c0015161ffff166001612a9d565b6129b26040518060400160405280600f81526020016e4672616d6520746869636b6e65737360881b8152508560e0015161ffff166001612a9d565b612a116040518060400160405280600a8152602001694672616d65207479706560b01b81525084612a045760405180604001604052806009815260200168496e76697369626c6560b81b815250611f60565b8661010001516001612a8e565b83612a2b5760405180602001604052806000815250612a64565b612a646040518060400160405280600a815260200169119c985b59481d1a5b9d60b21b81525087600001516060015160ff166001612a9d565b604051602001612a779493929190615071565b604051602081830303815290604052915050919050565b606061267d8484846001612d9d565b606061267d84612aac856120f2565b846000612d9d565b6060600080612ac286612e2c565b91509150612ad38783838888612ea0565b979650505050505050565b60608151600003612afd57505060408051602081019091526000815290565b6000604051806060016040528060408152602001615d686040913990506000600384516002612b2c9190614eff565b612b369190614db3565b612b41906004614d86565b90506000612b50826020614eff565b6001600160401b03811115612b6757612b67614a2f565b6040519080825280601f01601f191660200182016040528015612b91576020820181803683370190505b509050818152600183018586518101602084015b81831015612bff5760039283018051603f601282901c811687015160f890811b8552600c83901c8216880151811b6001860152600683901c8216880151811b60028601529116860151901b93820193909352600401612ba5565b600389510660018114612c195760028114612c2a57612c36565b613d3d60f01b600119830152612c36565b603d60f81b6000198301525b509398975050505050505050565b612c4c614797565b6000612c5a83610168611998565b90506000612c69846047611998565b90506000612c7885600b611998565b90506040518060e00160405280612c918560328061329e565b81526020018461ffff168152602001612cac8585603261329e565b81526020018360ff168152602001612cc66000808561329e565b815260ff83166020820152604001612cdf876003611998565b15612cf95760405180602001604052806000815250612d05565b612d058560328061329e565b905295945050505050565b60606000612d1f836064611998565b90506046811015612d4d5750506040805180820190915260058152641cdbdb1a5960da1b6020820152919050565b605a811015612d795750506040805180820190915260058152641a5b9cd95d60da1b6020820152919050565b50506040805180820190915260068152651bdd5d1cd95d60d21b6020820152919050565b60608482612dab5784612dcc565b84604051602001612dbc919061582d565b6040516020818303038152906040525b84612de65760405180602001604052806000815250612e01565b604051806040016040528060018152602001600b60fa1b8152505b604051602001612e139392919061585b565b6040516020818303038152906040529050949350505050565b6000806000612e3a846125ec565b90506000612e4b6004612710614db3565b612e559083614db3565b9050806003811115612e6957612e696158df565b93506004612e7981612710614db3565b612e839083614d86565b612e8d9084614dc7565b612e979190614d86565b92505050915091565b60606000612ead87612685565b604080516080808201835260008083526020808401829052808601805161ffff9081168688015260a0808901805183166060808a019190915289518089018b5287815280870188905285518516818c01529151841682820152895160c08082018c52865186168252865186168289015286518616828d015286518616828401528c5151828b01528185018990528b519081018c528881528651861697810197909752855185169a87019a909a52845184169086015289515196850196909652830184905290519697509395929493909261271091612f9091611d1091168d614d86565b612f9a9190614db3565b905060028b6003811115612fb057612fb06158df565b0361301d57600085526020860151612fca906001906157d3565b61ffff16845260408401805160019190612fe59083906158f5565b61ffff9081169091526020880180518216865280518216604087015260018552516130139250839116614dc7565b6040830152613187565b60038b6003811115613031576130316158df565b0361308b5760008086528452604084018051600191906130529083906158f5565b61ffff908116909152602088018051821686528051821660408088019190915290519091168452830182905250600160a0830152613187565b60008b600381111561309f5761309f6158df565b036131205760208601805161ffff168652516130bd906001906157d3565b61ffff168452604084018051600191906130d89083906158f5565b61ffff908116909152600085526020880180518216604087015260018552516131049250839116614dc7565b61310f906001614eff565b6040830152600160a0830152613187565b60018b6003811115613134576131346158df565b036131875760008552602086015161ffff1684526040850180516001919061315d9083906158f5565b61ffff90811690915260208801805182168652604080870185905260008652905190911690840152505b60408601518551869061319b9083906158f5565b61ffff169052506040860151845185906131b69083906158f5565b61ffff1690525060608601516020860180516131d39083906158f5565b61ffff1690525060608601516020850180516131f09083906158f5565b91509061ffff16908161ffff168152505061328e6040518060800160405280886080015161ffff1681526020018860a0015161ffff1681526020018860000151608001518152602001886000015160c001518152508686868660405180608001604052808d60c0015161ffff1681526020018d60e0015161ffff1681526020018d610100015181526020018d60000151604001518152508f8f6132ef565b9c9b505050505050505050505050565b60606132ad8461ffff166120f2565b6132b98460ff166120f2565b6132c58460ff166120f2565b6040516020016132d793929190615910565b60405160208183030381529060405290509392505050565b6060600061330789888c604001518a60800151613661565b9050600061331f89888d604001518a60800151613661565b9050600061338260405180604001604052806004815260200163199a5b1b60e21b81525060008e60600151511161335a578d60400151613851565b6040518060400160405280600981526020016875726c28236272472960b81b8152505b613851565b6133b1604051806040016040528060058152602001640eed2c8e8d60db1b8152508e6000015161ffff1661387d565b6133e1604051806040016040528060068152602001651a195a59da1d60d21b8152508f6020015161ffff1661387d565b613434604051806040016040528060028152602001610e4f60f31b8152506134108c6000015161ffff166120f2565b6040516020016134209190615995565b604051602081830303815290604052613851565b61346360405180604001604052806002815260200161727960f01b8152506134108d6000015161ffff166120f2565b6040516020016134779594939291906159ba565b604051602081830303815290604052905060006134988d8d8d8d8d8b61388c565b90506136506134aa8960000151613906565b8261363f604051806020016040528060008152506134f0876134cb8f613a86565b6040516020016134dc929190615a25565b604051602081830303815290604052613af8565b61354061353a604051806040016040528060048152602001636d61736b60e01b8152506040518060400160405280600981526020016875726c28236d624d2960b81b815250613851565b8e613b30565b61361961358b604051806040016040528060068152602001653334b63a32b960d11b8152506040518060400160405280600881526020016775726c28236d462960c01b815250613851565b6135d2604051806040016040528060048152602001636d61736b60e01b8152506040518060400160405280600981526020016875726c28236d664d2960b81b815250613851565b6040516020016135e3929190615a25565b6040516020818303038152906040528c8c604051602001613605929190615a25565b604051602081830303815290604052613b30565b60405160200161362b93929190615a54565b604051602081830303815290604052613b56565b60405160200161362b929190615a25565b9d9c50505050505050505050505050565b6060613848613691604051806040016040528060018152602001600f60fb1b815250876000015161ffff1661387d565b6136bc604051806040016040528060018152602001607960f81b815250886020015161ffff1661387d565b6136ec604051806040016040528060068152602001651a195a59da1d60d21b815250896060015161ffff1661387d565b61371b604051806040016040528060058152602001640eed2c8e8d60db1b8152508a6040015161ffff1661387d565b60405160200161372e9493929190615071565b60408051601f1981840301815282820190915260028252610c6f60f31b60208301528651909161384391613766919061ffff1661387d565b61379260405180604001604052806002815260200161637960f01b815250896020015161ffff1661387d565b6137ba604051806040016040528060028152602001610e4f60f31b8152508a6040015161387d565b6137e660405180604001604052806002815260200161727960f01b8152508b6060015161ffff1661387d565b61381b60405180604001604052806004815260200163199a5b1b60e21b8152508c60a00151613815578a613851565b8b613851565b60405160200161382f9594939291906159ba565b604051602081830303815290604052613b7e565b613b56565b95945050505050565b60608282604051602001613866929190615a97565b604051602081830303815290604052905092915050565b6060611a778361337d846120f2565b6060612ad36138a388866060015161ffff16613bb9565b60008451116138be576138b98660600151613db0565b6138c0565b835b6138d28a886060015161ffff166141dc565b6138df8a8a8a8a8f6143ba565b6040516020016138f29493929190615071565b604051602081830303815290604052614546565b606061396460405180604001604052806005815260200164786d6c6e7360d81b8152506040518060400160405280601a81526020017f687474703a2f2f7777772e77332e6f72672f323030302f737667000000000000815250613851565b6139a4604051806040016040528060028152602001611a5960f21b8152506040518060400160405280600481526020016336b7b7b760e11b815250613851565b6139e8604051806040016040528060068152602001651a195a59da1d60d21b815250604051806040016040528060048152602001633130302560e01b815250613851565b613a34604051806040016040528060078152602001660ecd2caee84def60cb1b8152506040518060400160405280600b81526020016a030203020323030203230360ac1b815250613851565b613a72604051806040016040528060058152602001647374796c6560d81b815250613a628861ffff166120f2565b6040516020016134209190615ae3565b6040516020016123349594939291906159ba565b606061073a604051806040016040528060058152602001647374796c6560d81b815250613aba846020015161ffff166120f2565b84604001518560600151613ad5876020015161ffff166120f2565b8751613ae49061ffff166120f2565b604051602001613420959493929190615b35565b606061073a604051806040016040528060048152602001631c9958dd60e21b815250836040518060200160405280600081525061457a565b6060611a77604051806040016040528060018152602001606760f81b815250848461457a565b6060611a776040518060400160405280600381526020016273766760e81b815250848461457a565b606061073a60405180604001604052806007815260200166656c6c6970736560c81b815250836040518060200160405280600081525061457a565b6060611a77613bfd604051806040016040528060028152602001611a5960f21b8152506040518060400160405280600381526020016262724760e81b815250613851565b613c3b604051806040016040528060018152602001603960f91b8152506040518060400160405280600381526020016237352560e81b815250613851565b604051602001613c4c929190615a25565b604051602081830303815290604052613d14613cbf604051806040016040528060068152602001651bd9999cd95d60d21b815250613410886020015161ffff166002896064613c9b9190614d86565b613ca6906003614d86565b613cb09190614db3565b613cba9190614db3565b6120f2565b613cef6040518060400160405280600a81526020016939ba37b816b1b7b637b960b11b8152508860400151613851565b604051602001613d00929190615a25565b604051602081830303815290604052614593565b613d8b613d5b604051806040016040528060068152602001651bd9999cd95d60d21b815250604051806040016040528060048152602001633130302560e01b815250613851565b613cef6040518060400160405280600a81526020016939ba37b816b1b7b637b960b11b8152508960600151613851565b604051602001613d9c929190615a25565b6040516020818303038152906040526145cb565b60606000613dbf8360026157ad565b9050611a77613e02604051806040016040528060028152602001611a5960f21b8152506040518060400160405280600281526020016136a360f11b815250613851565b604051602001613e129190615c20565b604051602081830303815290604052613fc1613e67604051806040016040528060068152602001651c995cdd5b1d60d21b815250604051806040016040528060038152602001621bdd5d60ea1b815250613851565b613eb36040518060400160405280601081526020016f1cdc1958dd5b185c915e1c1bdb995b9d60821b81525060405180604001604052806002815260200161032360f41b815250613851565b613f026040518060400160405280600e81526020016d3634b3b43a34b73396b1b7b637b960911b8152506040518060400160405280600781526020016611b1313131313160c91b815250613851565b604051602001613f1493929190615a54565b60408051601f1981840301815282820190915260018252600f60fb1b602083015290613fbc90613f489061ffff881661387d565b613f6f604051806040016040528060018152602001607960f81b8152508861ffff1661387d565b613f96604051806040016040528060018152602001603d60f91b8152508961ffff1661387d565b604051602001613fa893929190615a54565b6040516020818303038152906040526145fe565b61463e565b6141b761400d6040518060400160405280600281526020016134b760f11b8152506040518060400160405280600d81526020016c536f757263654772617068696360981b815250613851565b61404d6040518060400160405280600381526020016234b71960e91b815250604051806040016040528060038152602001621bdd5d60ea1b815250613851565b6140996040518060400160405280600881526020016737b832b930ba37b960c11b8152506040518060400160405280600a81526020016961726974686d6574696360b01b815250613851565b6140d6604051806040016040528060028152602001616b3160f01b815250604051806040016040528060018152602001600360fc1b815250613851565b61411360405180604001604052806002815260200161359960f11b815250604051806040016040528060018152602001603160f81b815250613851565b614150604051806040016040528060028152602001616b3360f01b815250604051806040016040528060018152602001603160f81b815250613851565b61418d604051806040016040528060028152602001611acd60f21b815250604051806040016040528060018152602001600360fc1b815250613851565b6040516020016141a39796959493929190615c3c565b604051602081830303815290604052614675565b6040516020016141c8929190615a25565b6040516020818303038152906040526146b4565b6060611a77614220604051806040016040528060028152602001611a5960f21b815250604051806040016040528060038152602001626d624d60e81b815250613851565b6142d6614252604051806040016040528060058152602001640eed2c8e8d60db1b815250876000015161ffff1661387d565b614282604051806040016040528060068152602001651a195a59da1d60d21b815250886020015161ffff1661387d565b6142c460405180604001604052806004815260200163199a5b1b60e21b8152506040518060400160405280600481526020016311a3232360e11b815250613851565b6040516020016134dc93929190615a54565b614395614311604051806040016040528060028152602001610c6f60f31b8152506002896000015161430891906157f5565b61ffff1661387d565b61434060405180604001604052806002815260200161637960f01b81525060028a6020015161430891906157f5565b61436f604051806040016040528060018152602001603960f91b81525089600161436a9190614eff565b61387d565b60405160200161438193929190615a54565b6040516020818303038152906040526146df565b6040516020016143a6929190615a25565b604051602081830303815290604052614719565b606061453c6143fe604051806040016040528060028152602001611a5960f21b815250604051806040016040528060038152602001626d664d60e81b815250613851565b6144a2614430604051806040016040528060058152602001640eed2c8e8d60db1b815250866000015161ffff1661387d565b614460604051806040016040528060068152602001651a195a59da1d60d21b815250876020015161ffff1661387d565b6142c460405180604001604052806004815260200163199a5b1b60e21b815250604051806040016040528060048152602001630233030360e41b815250613851565b6144e68988604051806040016040528060048152602001630233030360e41b8152506040518060400160405280600481526020016311a3232360e11b815250613661565b61452a8988604051806040016040528060048152602001630233030360e41b8152506040518060400160405280600481526020016311a3232360e11b815250613661565b6040516020016143a693929190615a54565b9695505050505050565b606061073a604051806040016040528060048152602001636465667360e01b81525060405180602001604052806000815250845b6060838383866040516020016132d79493929190615cce565b606061073a60405180604001604052806004815260200163073746f760e41b815250836040518060200160405280600081525061457a565b6060611a776040518060400160405280600e81526020016d1c98591a585b11dc98591a595b9d60921b815250848461457a565b606061073a6040518060400160405280600c81526020016b1999541bda5b9d131a59da1d60a21b815250836040518060200160405280600081525061457a565b6060611a7760405180604001604052806012815260200171666553706563756c61724c69676874696e6760701b815250848461457a565b606061073a6040518060400160405280600b81526020016a6665436f6d706f7369746560a81b815250836040518060200160405280600081525061457a565b6060611a77604051806040016040528060068152602001653334b63a32b960d11b815250848461457a565b606061073a60405180604001604052806006815260200165636972636c6560d01b815250836040518060200160405280600081525061457a565b6060611a77604051806040016040528060048152602001636d61736b60e01b815250848461457a565b604051806101200160405280614756614797565b81526000602082018190526040820181905260608083018290526080830182905260a0830182905260c0830182905260e08301919091526101009091015290565b6040518060e0016040528060608152602001600061ffff16815260200160608152602001600060ff16815260200160608152602001600060ff168152602001606081525090565b6001600160e01b031981168114610aff57600080fd5b60006020828403121561480657600080fd5b8135611a77816147de565b60005b8381101561482c578181015183820152602001614814565b50506000910152565b6000815180845261484d816020860160208601614811565b601f01601f19169290920160200192915050565b602081526000611a776020830184614835565b60006020828403121561488657600080fd5b5035919050565b6001600160a01b0381168114610aff57600080fd5b600080604083850312156148b557600080fd5b82356148c08161488d565b946020939093013593505050565b6000806000606084860312156148e357600080fd5b83356148ee8161488d565b925060208401356148fe8161488d565b929592945050506040919091013590565b6000806040838503121561492257600080fd5b50508035926020909101359150565b60008083601f84011261494357600080fd5b5081356001600160401b0381111561495a57600080fd5b6020830191508360208260051b85010111156109f257600080fd5b60008060006040848603121561498a57600080fd5b83356001600160401b038111156149a057600080fd5b6149ac86828701614931565b90945092505060208401356149c08161488d565b809150509250925092565b6000602082840312156149dd57600080fd5b8135611a778161488d565b8015158114610aff57600080fd5b60008060408385031215614a0957600080fd5b8235614a148161488d565b91506020830135614a24816149e8565b809150509250929050565b634e487b7160e01b600052604160045260246000fd5b604051601f8201601f191681016001600160401b0381118282101715614a6d57614a6d614a2f565b604052919050565b60006001600160401b03821115614a8e57614a8e614a2f565b50601f01601f191660200190565b60008060008060808587031215614ab257600080fd5b8435614abd8161488d565b93506020850135614acd8161488d565b92506040850135915060608501356001600160401b03811115614aef57600080fd5b8501601f81018713614b0057600080fd5b8035614b13614b0e82614a75565b614a45565b818152886020838501011115614b2857600080fd5b8160208401602083013760006020838301015280935050505092959194509250565b60008083601f840112614b5c57600080fd5b5081356001600160401b03811115614b7357600080fd5b6020830191508360208285010111156109f257600080fd5b60008060008060008060008060a0898b031215614ba757600080fd5b8835614bb28161488d565b97506020890135614bc28161488d565b965060408901356001600160401b0380821115614bde57600080fd5b614bea8c838d01614931565b909850965060608b0135915080821115614c0357600080fd5b614c0f8c838d01614931565b909650945060808b0135915080821115614c2857600080fd5b50614c358b828c01614b4a565b999c989b5096995094979396929594505050565b600080600060608486031215614c5e57600080fd5b8335925060208401356148fe8161488d565b60008060408385031215614c8357600080fd5b8235614c8e8161488d565b91506020830135614a248161488d565b60008060008060008060a08789031215614cb757600080fd5b8635614cc28161488d565b95506020870135614cd28161488d565b9450604087013593506060870135925060808701356001600160401b03811115614cfb57600080fd5b614d0789828a01614b4a565b979a9699509497509295939492505050565b600181811c90821680614d2d57607f821691505b602082108103614d4d57634e487b7160e01b600052602260045260246000fd5b50919050565b600060208284031215614d6557600080fd5b8151611a77816149e8565b634e487b7160e01b600052601160045260246000fd5b808202811582820484141761073a5761073a614d70565b634e487b7160e01b600052601260045260246000fd5b600082614dc257614dc2614d9d565b500490565b8181038181111561073a5761073a614d70565b634e487b7160e01b600052603260045260246000fd5b600060018201614e0257614e02614d70565b5060010190565b600060ff821660ff8103614e1f57614e1f614d70565b60010192915050565b60008060408385031215614e3b57600080fd5b8251614e46816149e8565b6020840151909250614a248161488d565b600060208284031215614e6957600080fd5b81516001600160401b03811115614e7f57600080fd5b8201601f81018413614e9057600080fd5b8051614e9e614b0e82614a75565b818152856020838501011115614eb357600080fd5b613848826020830160208601614811565b724e6f6e2d46756e6769626c65204d6f6f6e202360681b815260008251614ef2816013850160208701614811565b9190910160130192915050565b8082018082111561073a5761073a614d70565b6000815160e08452614f2760e0850182614835565b905061ffff602084015116602085015260408301518482036040860152614f4e8282614835565b91505060ff606084015116606085015260808301518482036080860152614f758282614835565b91505060ff60a08401511660a085015260c083015184820360c08601526138488282614835565b8481528360208201526080604082015260008351610120806080850152614fc76101a0850183614f12565b91506020860151614fde60a086018261ffff169052565b50604086015161ffff811660c086015250606086015161ffff811660e08601525060808601516101006150168187018361ffff169052565b60a088015161ffff9081169387019390935260c0880151831661014087015260e088015190921661016086015250850151838203607f190161018085015261505e8282614835565b9250505082606083015295945050505050565b60008551615083818460208a01614811565b855190830190615097818360208a01614811565b85519101906150aa818360208901614811565b84519101906150bd818360208801614811565b019695505050505050565b605b60f81b81526000600187516150e58183860160208c01614811565b8751908401906150fb8184840160208c01614811565b87519101906151108184840160208b01614811565b86519101906151258184840160208a01614811565b855191019061513a8184840160208901614811565b605d60f81b910191820152600201979650505050505050565b60ff818116838216019081111561073a5761073a614d70565b7f3c21444f43545950452068746d6c3e3c68746d6c3e3c686561643e3c7374796c81527f6520747970653d22746578742f637373223e68746d6c7b6f766572666c6f773a60208201527f68696464656e7d626f64797b6d617267696e3a307d236d6f6f6e7b646973706c60408201527f61793a626c6f636b3b6d617267696e3a6175746f7d3c2f7374796c653e3c2f6860608201527f6561643e3c626f64793e3c6469762069643d226d6f6f6e446976223e3c2f646960808201527203b1f1e39b1b934b83a1f3632ba1033b99eadb606d1b60a0820152600082516152588160b3850160208701614811565b600360fd1b60b393909101928301525060b401919050565b60008351615282818460208801614811565b61016360f51b90830190815283516152a1816002840160208801614811565b600360fd1b60029290910191820152600301949350505050565b600082516152cd818460208701614811565b7f5d3b6c657420243d646f63756d656e742e676574456c656d656e74427949642e9201918252507f62696e6428646f63756d656e74293b2428226d6f6f6e44697622292e696e6e6560208201527f7248544d4c3d67735b305d3b6c6574206d6f3d2428226d6f6f6e44697622293b60408201527f6c657420753d653d3e7b6c657420743d2428226d6f6f6e22292e676574426f7560608201527f6e64696e67436c69656e745265637428293b2428226d6f6f6e44697622292e6960808201527f6e6e657248544d4c3d67735b4d6174682e6d617828302c4d6174682e6d696e2860a08201527f4d6174682e666c6f6f72282828652d742e6c656674292f742e7769647468292a60c08201527f67732e6c656e677468292c67732e6c656e6774682d3129295d3b7d3b6d6f2e6f60e08201527f6e6d6f7573656d6f76653d653d3e7528652e636c69656e7458293b6d6f2e61646101008201527f644576656e744c697374656e65722822746f7563687374617274222c653d3e7b6101208201527f6c657420743d653d3e7528652e746f75636865735b305d2e636c69656e7458296101408201527f3b6e3d28293d3e7b652e7461726765742e72656d6f76654576656e744c6973746101608201527f656e65722822746f7563686d6f7665222c74292c652e7461726765742e72656d6101808201527f6f76654576656e744c697374656e65722822746f756368656e64222c6e293b7d6101a08201527f3b652e7461726765742e6164644576656e744c697374656e65722822746f75636101c08201527f686d6f7665222c74293b652e7461726765742e6164644576656e744c697374656101e08201527f6e65722822746f756368656e64222c6e293b7d293b3c2f7363726970743e3c2f6102008201526b3137b23c9f1e17b43a36b61f60a11b61022082015261022c01919050565b7f646174613a696d6167652f7376672b786d6c3b6261736536342c0000000000008152600082516155b481601a850160208701614811565b91909101601a0192915050565b7519185d184e9d195e1d0bda1d1b5b0ed8985cd94d8d0b60521b8152600082516155f2816016850160208701614811565b9190910160160192915050565b683d913730b6b2911d1160b91b81528551600090615624816009850160208b01614811565b701116113232b9b1b934b83a34b7b7111d1160791b600991840191820152865161565581601a840160208b01614811565b6e11161130ba3a3934b13aba32b9911d60891b601a92909101918201528551615685816029840160208a01614811565b69161134b6b0b3b2911d1160b11b6029929091019182015284516156b0816033840160208901614811565b7211161130b734b6b0ba34b7b72fbab936111d1160691b6033929091019182015283516156e4816046840160208801614811565b016156f66046820161227d60f01b9052565b604801979650505050505050565b7f646174613a6170706c69636174696f6e2f6a736f6e3b6261736536342c00000081526000825161573c81601d850160208701614811565b91909101601d0192915050565b6001600160a01b038581168252841660208201526040810183905260806060820181905260009061453c90830184614835565b60006020828403121561578e57600080fd5b8151611a77816147de565b6000826157a8576157a8614d9d565b500690565b61ffff8181168382160280821691908281146157cb576157cb614d70565b505092915050565b61ffff8281168282160390808211156157ee576157ee614d70565b5092915050565b600061ffff8084168061580a5761580a614d9d565b92169190910492915050565b60008161582557615825614d70565b506000190190565b6000601160f91b808352835161584a816001860160208801614811565b600193019283015250600201919050565b6e3d913a3930b4ba2fba3cb832911d1160891b8152835160009061588681600f850160208901614811565b691116113b30b63ab2911d60b11b600f9184019182015284516158b0816019840160208901614811565b607d60f81b6019929091019182015283516158d281601a840160208801614811565b01601a0195945050505050565b634e487b7160e01b600052602160045260246000fd5b61ffff8181168382160190808211156157ee576157ee614d70565b640d0e6d8c2560db1b815260008451615930816005850160208901614811565b600b60fa1b6005918401918201528451615951816006840160208901614811565b61094b60f21b600692909101918201528351615974816008840160208801614811565b66252c313030252960c81b60089290910191820152600f0195945050505050565b600082516159a7818460208701614811565b602560f81b920191825250600101919050565b600086516159cc818460208b01614811565b8651908301906159e0818360208b01614811565b86519101906159f3818360208a01614811565b8551910190615a06818360208901614811565b8451910190615a19818360208801614811565b01979650505050505050565b60008351615a37818460208801614811565b835190830190615a4b818360208801614811565b01949350505050565b60008451615a66818460208901614811565b845190830190615a7a818360208901614811565b8451910190615a8d818360208801614811565b0195945050505050565b60008351615aa9818460208801614811565b611e9160f11b9083019081528351615ac8816002840160208801614811565b61011160f51b60029290910191820152600401949350505050565b6d3137b93232b916b930b234bab99d60911b815260008251615b0c81600e850160208701614811565b7104a76dac2f05ad0cad2ced0e874626060ecd60731b600e939091019283015250602001919050565b6737baba3634b7329d60c11b815260008651615b58816008850160208b01614811565b620383c160ed1b6008918401918201528651615b7b81600b840160208b01614811565b600160fd1b600b92909101918201528551615b9d81600c840160208a01614811565b703b6f75746c696e652d6f66667365743a2d60781b600c92909101918201528451615bcf81601d840160208901614811565b70383c1db137b93232b916b930b234bab99d60791b601d92909101918201528351615c0181602e840160208801614811565b01615c12602e8201602560f81b9052565b602f01979650505050505050565b60008251615c32818460208701614811565b9190910192915050565b600088516020615c4f8285838e01614811565b895191840191615c628184848e01614811565b8951920191615c748184848d01614811565b8851920191615c868184848c01614811565b8751920191615c988184848b01614811565b8651920191615caa8184848a01614811565b8551920191615cbc8184848901614811565b919091019a9950505050505050505050565b600f60fa1b815260008551615cea816001850160208a01614811565b600160fd1b6001918401918201528551615d0b816002840160208a01614811565b808201915050601f60f91b8060028301528551615d2f816003850160208a01614811565b613c2f60f01b600393909101928301528451615d52816005850160208901614811565b6005920191820152600601969550505050505056fe4142434445464748494a4b4c4d4e4f505152535455565758595a6162636465666768696a6b6c6d6e6f707172737475767778797a303132333435363738392b2f4e6f6e2d46756e6769626c65204d6f6f6e7320617265206f6e2d636861696e2067656e65726174697665206d6f6f6e204e4654732e20416c6c206d6f6f6e206172742069732067656e657261746564206f6e2d636861696e20616e64207570646174657320696e207265616c2d74696d652c206261736564206f6e2063757272656e7420626c6f636b2074696d6520616e64207573696e6720616e206f6e2d636861696e20535647206c6962726172792c20746f20636c6f73656c79206d6972726f7220746865207068617365206f6620746865206d6f6f6e20696e20746865207265616c20776f726c642ea2646970667358221220b5c02847fbc506537feae86e5b67641440496bbe38df9fc12078b4029d1df26164736f6c63430008110033000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000f61706120e12b303ca2418fc5bc9ee7961d166c400000000000000000000000000000000000000000000000000000000000000124e6f6e2d46756e6769626c65204d6f6f6e73000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000034e464d0000000000000000000000000000000000000000000000000000000000

Deployed Bytecode

0x6080604052600436106102135760003560e01c806370a0823111610118578063bc197c81116100a0578063d58fa50d1161006f578063d58fa50d14610633578063e4681f2914610669578063e985e9c5146106a8578063f23a6e61146106c8578063f2fde38b146106f557600080fd5b8063bc197c81146105a5578063c455dbca146105ed578063c49b7b9714610600578063c87b56dd1461061357600080fd5b80638da5cb5b116100e75780638da5cb5b1461052c57806395d89b411461054a578063a0712d681461055f578063a22cb46514610572578063b88d4fde1461059257600080fd5b806370a08231146104af578063715018a6146104cf5780638675d158146104e45780638d859f3e1461051157600080fd5b806332cb6b0c1161019b57806347cfbacd1161016a57806347cfbacd146103ed5780635ca2708f1461040d5780635ce572da1461042d5780636352211e1461044d5780636c60614c1461046d57600080fd5b806332cb6b0c1461038d5780633ccfd60b146103a357806341f43434146103b857806342842e0e146103da57600080fd5b8063095ea7b3116101e2578063095ea7b3146102ce57806318160ddd146102e357806323b872dd146103065780632a55205a1461031957806331748c531461035857600080fd5b806301ffc9a71461021f578063037cab581461025457806306fdde031461028c578063081812fc146102ae57600080fd5b3661021a57005b600080fd5b34801561022b57600080fd5b5061023f61023a3660046147f4565b610715565b60405190151581526020015b60405180910390f35b34801561026057600080fd5b50600c54610274906001600160a01b031681565b6040516001600160a01b03909116815260200161024b565b34801561029857600080fd5b506102a1610740565b60405161024b9190614861565b3480156102ba57600080fd5b506102746102c9366004614874565b6107d2565b6102e16102dc3660046148a2565b610816565b005b3480156102ef57600080fd5b50600154600054035b60405190815260200161024b565b6102e16103143660046148ce565b6108e4565b34801561032557600080fd5b5061033961033436600461490f565b6109bd565b604080516001600160a01b03909316835260208301919091520161024b565b34801561036457600080fd5b506103786103733660046148ce565b6109f9565b6040805192835260208301919091520161024b565b34801561039957600080fd5b506102f861020181565b3480156103af57600080fd5b506102e1610abf565b3480156103c457600080fd5b506102746daaeb6d7670e522a718067333cd4e81565b6102e16103e83660046148ce565b610b02565b3480156103f957600080fd5b506102e1610408366004614975565b610bd0565b34801561041957600080fd5b506102e16104283660046149cb565b610d5d565b34801561043957600080fd5b50600b54610274906001600160a01b031681565b34801561045957600080fd5b50610274610468366004614874565b610e9c565b34801561047957600080fd5b5061049d610488366004614874565b600a6020526000908152604090205460ff1681565b60405160ff909116815260200161024b565b3480156104bb57600080fd5b506102f86104ca3660046149cb565b610ea7565b3480156104db57600080fd5b506102e1610ef5565b3480156104f057600080fd5b506102f86104ff366004614874565b60096020526000908152604090205481565b34801561051d57600080fd5b506102f8668e1bc9bf04000081565b34801561053857600080fd5b506008546001600160a01b0316610274565b34801561055657600080fd5b506102a1610f09565b6102e161056d366004614874565b610f18565b34801561057e57600080fd5b506102e161058d3660046149f6565b610f25565b6102e16105a0366004614a9c565b610fe9565b3480156105b157600080fd5b506105d46105c0366004614b8b565b63bc197c8160e01b98975050505050505050565b6040516001600160e01b0319909116815260200161024b565b6102e16105fb366004614c49565b6110c5565b6102e161060e366004614874565b6111f4565b34801561061f57600080fd5b506102a161062e366004614874565b6113ed565b34801561063f57600080fd5b5061027461064e366004614874565b600d602052600090815260409020546001600160a01b031681565b34801561067557600080fd5b50610689610684366004614874565b6115a8565b6040805192151583526001600160a01b0390911660208301520161024b565b3480156106b457600080fd5b5061023f6106c3366004614c70565b61163e565b3480156106d457600080fd5b506105d46106e3366004614c9e565b63f23a6e6160e01b9695505050505050565b34801561070157600080fd5b506102e16107103660046149cb565b61166c565b60006001600160e01b0319821663152a902d60e11b148061073a575061073a826116e2565b92915050565b60606002805461074f90614d19565b80601f016020809104026020016040519081016040528092919081815260200182805461077b90614d19565b80156107c85780601f1061079d576101008083540402835291602001916107c8565b820191906000526020600020905b8154815290600101906020018083116107ab57829003601f168201915b5050505050905090565b60006107dd82611730565b6107fa576040516333d1c03960e21b815260040160405180910390fd5b506000908152600660205260409020546001600160a01b031690565b816daaeb6d7670e522a718067333cd4e3b156108d557604051633185c44d60e21b81523060048201526001600160a01b03821660248201526daaeb6d7670e522a718067333cd4e9063c617113490604401602060405180830381865afa158015610884573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906108a89190614d53565b6108d557604051633b79c77360e21b81526001600160a01b03821660048201526024015b60405180910390fd5b6108df8383611757565b505050565b826daaeb6d7670e522a718067333cd4e3b156109ac57336001600160a01b0382160361091a576109158484846117f7565b6109b7565b604051633185c44d60e21b81523060048201523360248201526daaeb6d7670e522a718067333cd4e9063c617113490604401602060405180830381865afa158015610969573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061098d9190614d53565b6109ac57604051633b79c77360e21b81523360048201526024016108cc565b6109b78484846117f7565b50505050565b6000807339ab90066cec746a032d67e4fe3378f16294cf6b6127106109e38560fa614d86565b6109ed9190614db3565b915091505b9250929050565b60008080610a08600485614db3565b9050610a206040518060200160405280600081525090565b610a84448888604051602001610a5f93929190928352606091821b6bffffffffffffffffffffffff199081166020850152911b16603482015260480190565b604051602081830303815290604052805190602001208261199490919063ffffffff16565b612710610a9382612711611998565b610a9d9084614d86565b610aa79190614db3565b9250610ab38383614dc7565b93505050935093915050565b6040517339ab90066cec746a032d67e4fe3378f16294cf6b904780156108fc02916000818181858888f19350505050158015610aff573d6000803e3d6000fd5b50565b826daaeb6d7670e522a718067333cd4e3b15610bc557336001600160a01b03821603610b33576109158484846119b6565b604051633185c44d60e21b81523060048201523360248201526daaeb6d7670e522a718067333cd4e9063c617113490604401602060405180830381865afa158015610b82573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610ba69190614d53565b610bc557604051633b79c77360e21b81523360048201526024016108cc565b6109b78484846119b6565b610201821115610bf35760405163d05cb60960e01b815260040160405180910390fd5b6001600160a01b03811615801590610c7757506040516301ffc9a760e01b8152634332ac1760e11b60048201526001600160a01b038216906301ffc9a790602401602060405180830381865afa158015610c51573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610c759190614d53565b155b15610c955760405163cc76443960e01b815260040160405180910390fd5b60005b828110156109b7576000848483818110610cb457610cb4614dda565b905060200201359050336001600160a01b0316610cd082610e9c565b6001600160a01b031614610cf7576040516305e8ff9f60e11b815260040160405180910390fd5b6000818152600d602052604080822080546001600160a01b0319166001600160a01b0387169081179091559051909183917fd05e65504e36f710b5ae22e41b5163363a8aef58726c3dbd7f24a702277ae1109190a350610d5681614df0565b9050610c98565b610d656119d1565b600b80546001600160a01b0319166001600160a01b0383169081179091556040516213049560e71b815230600482015281906309824a8090602401600060405180830381600087803b158015610dba57600080fd5b505af1158015610dce573d6000803e3d6000fd5b5050600c54604051634cd54e5760e01b81523060048201526001600160a01b0391821660248201529084169250634cd54e579150604401600060405180830381600087803b158015610e1f57600080fd5b505af1158015610e33573d6000803e3d6000fd5b5050604051634cd54e5760e01b8152306004820181905260248201526001600160a01b0384169250634cd54e5791506044015b600060405180830381600087803b158015610e8057600080fd5b505af1158015610e94573d6000803e3d6000fd5b505050505050565b600061073a82611a2b565b60006001600160a01b038216610ed0576040516323d3ad8160e21b815260040160405180910390fd5b506001600160a01b03166000908152600560205260409020546001600160401b031690565b610efd6119d1565b610f076000611a99565b565b60606003805461074f90614d19565b610f2181611aeb565b5050565b816daaeb6d7670e522a718067333cd4e3b15610fdf57604051633185c44d60e21b81523060048201526001600160a01b03821660248201526daaeb6d7670e522a718067333cd4e9063c617113490604401602060405180830381865afa158015610f93573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610fb79190614d53565b610fdf57604051633b79c77360e21b81526001600160a01b03821660048201526024016108cc565b6108df8383611c08565b836daaeb6d7670e522a718067333cd4e3b156110b257336001600160a01b038216036110205761101b85858585611c74565b6110be565b604051633185c44d60e21b81523060048201523360248201526daaeb6d7670e522a718067333cd4e9063c617113490604401602060405180830381865afa15801561106f573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906110939190614d53565b6110b257604051633b79c77360e21b81523360048201526024016108cc565b6110be85858585611c74565b5050505050565b60006110d084611aeb565b90506001600160a01b038316158015906110f357506001600160a01b0383163314155b8015611118575061110382610e9c565b6001600160a01b0316836001600160a01b0316145b156109b75760008061112b8533346109f9565b60408051868152602081018a9052908101839052606081018290529193509150339085906001600160a01b038816907f1e146227c31c93c94e66918096db1e3940b7e061209e5bbd373595bf60cd25d09060800160405180910390a46040516001600160a01b0386169083156108fc029084906000818181858888f193505050501580156111bd573d6000803e3d6000fd5b50604051339082156108fc029083906000818181858888f193505050501580156111eb573d6000803e3d6000fd5b50505050505050565b6000818152600a602052604090205460ff166002190161122657604051623ebab160e71b815260040160405180910390fd5b668e1bc9bf040000341461124d576040516331fc877f60e01b815260040160405180910390fd5b3361125782610e9c565b6001600160a01b03161461127e576040516305e8ff9f60e11b815260040160405180910390fd5b60008181526009602052604090205461129682611cb8565b600083815260096020908152604080832093909355600a905290812080549091906112c39060ff16614e09565b82546101009290920a60ff818102199093169183160217909155600083815260096020908152604080832054600a8352928190205481518681529416918401919091529091849133917f7a449dca7e96c146396697c76b05ddcb78a62f572cc6b90be959387f6377c939910160405180910390a4600c5460405163044413a560e21b8152600481018490526001600160a01b03909116906311104e9490602401600060405180830381600087803b15801561137d57600080fd5b505af1158015611391573d6000803e3d6000fd5b5050600b546001600160a01b0316159150610f21905057600b546040516348489b1960e11b81523060048201526024810184905260786044820152600060648201526001600160a01b0390911690639091363290608401610e66565b60606113f882610e9c565b5060405163e4681f2960e01b8152600481018390526000908190309063e4681f29906024016040805180830381865afa158015611439573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061145d9190614e28565b90925090506000611470426103e8614d86565b9050600061148b838761148285611cf3565b61ffff16611d22565b9250505060006009600088815260200190815260200160002054905060006115238284876001600160a01b031663941cbad06040518163ffffffff1660e01b8152600401600060405180830381865afa1580156114ec573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526115149190810190614e57565b61151d89611eba565b8a611ed0565b90506000611530896120f2565b6040516020016115409190614ec4565b604051602081830303815290604052905060008061155f8b888a61221a565b9150915061159961156f83612319565b6115788361234a565b8560405180610120016040528060ec8152602001615da860ec913988612365565b9b9a5050505050505050505050565b6000818152600d6020526040812054819081906001600160a01b0316156115e757506000838152600d60205260409020546001600160a01b03166115f5565b50600c546001600160a01b03165b600c546000858152600d60205260409020546001600160a01b039081169116148061163557506000848152600d60205260409020546001600160a01b0316155b94909350915050565b6001600160a01b03918216600090815260076020908152604080832093909416825291909152205460ff1690565b6116746119d1565b6001600160a01b0381166116d95760405162461bcd60e51b815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201526564647265737360d01b60648201526084016108cc565b610aff81611a99565b60006301ffc9a760e01b6001600160e01b03198316148061171357506380ac58cd60e01b6001600160e01b03198316145b8061073a5750506001600160e01b031916635b5e139f60e01b1490565b600080548210801561073a575050600090815260046020526040902054600160e01b161590565b600061176282610e9c565b9050336001600160a01b0382161461179b5761177e813361163e565b61179b576040516367d9dca160e11b815260040160405180910390fd5b60008281526006602052604080822080546001600160a01b0319166001600160a01b0387811691821790925591518593918516917f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92591a4505050565b600061180282611a2b565b9050836001600160a01b0316816001600160a01b0316146118355760405162a1148160e81b815260040160405180910390fd5b60008281526006602052604090208054338082146001600160a01b0388169091141761188257611865863361163e565b61188257604051632ce44b5f60e11b815260040160405180910390fd5b6001600160a01b0385166118a957604051633a954ecd60e21b815260040160405180910390fd5b80156118b457600082555b6001600160a01b038681166000908152600560205260408082208054600019019055918716808252919020805460010190554260a01b17600160e11b17600085815260046020526040812091909155600160e11b84169003611946576001840160008181526004602052604081205490036119445760005481146119445760008181526004602052604090208490555b505b83856001600160a01b0316876001600160a01b03167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef60405160405180910390a4610e9486868660016123c1565b9052565b60005b602083209050808352818260000306811061199b5706919050565b6108df83838360405180602001604052806000815250610fe9565b6008546001600160a01b03163314610f075760405162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e657260448201526064016108cc565b600081600054811015611a805760008181526004602052604081205490600160e01b82169003611a7e575b80600003611a77575060001901600081815260046020526040902054611a56565b9392505050565b505b604051636f96cda160e11b815260040160405180910390fd5b600880546001600160a01b038381166001600160a01b0319831681179093556040519116919082907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a35050565b600081611af760005490565b611b019190614eff565b6102011015611b235760405163d05cb60960e01b815260040160405180910390fd5b611b3482668e1bc9bf040000614d86565b3414611b53576040516331fc877f60e01b815260040160405180910390fd5b600054805b611b628483614eff565b811015611b9157611b7281611cb8565b600082815260096020526040902055611b8a81614df0565b9050611b58565b50611b9c33846123fa565b600c54604051630d9778e560e11b815260048101839052602481018590526001600160a01b0390911690631b2ef1ca90604401600060405180830381600087803b158015611be957600080fd5b505af1158015611bfd573d6000803e3d6000fd5b509295945050505050565b3360008181526007602090815260408083206001600160a01b03871680855290835292819020805460ff191686151590811790915590519081529192917f17307eab39ab6107e8899845ad3d59bd9653f200f220920489ca2b5937696c31910160405180910390a35050565b611c7f8484846108e4565b6001600160a01b0383163b156109b757611c9b84848484612501565b6109b7576040516368d2bf6b60e11b815260040160405180910390fd5b60008144604051602001611cd6929190918252602082015260400190565b604051602081830303815290604052805190602001209050919050565b6000612710611d18611d04846125ec565b611d1090610168614d86565b61271061264f565b61073a9190614db3565b60008281526009602052604081205460609182918291611d4182612685565b6040516313a4c89160e31b81529091506001600160a01b03891690639d26448890611d76908a90869086908c90600401614f9c565b600060405180830381865afa158015611d93573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052611dbb9190810190614e57565b60405163abfeb63160e01b81526001600160a01b038a169063abfeb63190611ded908b90879087908d90600401614f9c565b600060405180830381865afa158015611e0a573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052611e329190810190614e57565b6040516324a1104760e01b81526001600160a01b038b16906324a1104790611e64908c90889088908e90600401614f9c565b600060405180830381865afa158015611e81573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052611ea99190810190614e57565b945094509450505093509350939050565b606061073a6001600160a01b0383166014612790565b60606000611edd87612685565b90506000611eea8261292b565b90506000611f6760405180604001604052806014815260200173125cc8191959985d5b1d08185b1a595b88185c9d60621b81525086611f4357604051806040016040528060028152602001614e6f60f01b815250611f60565b6040518060400160405280600381526020016259657360e81b8152505b6001612a8e565b611f9c60405180604001604052806011815260200170416c69656e20617274206164647265737360781b815250886001612a8e565b611fcc60405180604001604052806009815260200168105b1a595b88185c9d60ba1b8152508a60008d5111612a8e565b8a604051602001611fe09493929190615071565b60408051601f1981840301815282820190915260088252674d6f6f6e2068756560c01b60208381019190915285510151909250612023919061ffff166001612a9d565b826120616040518060400160405280600e81526020016d5370616365206461726b6e65737360901b815250866000015160a0015160ff166001612a9d565b6120c06040518060400160405280601281526020017112185cc81cdc1858d94819dc98591a595b9d60721b8152506000886000015160c001515111611f4357604051806040016040528060028152602001614e6f60f01b815250611f60565b846040516020016120d59594939291906150c8565b604051602081830303815290604052935050505095945050505050565b6060816000036121195750506040805180820190915260018152600360fc1b602082015290565b8160005b81156121405761212c81614df0565b9050612139600a83614db3565b915061211d565b6000816001600160401b0381111561215a5761215a614a2f565b6040519080825280601f01601f191660200182016040528015612184576020820181803683370190505b509050815b85156122115761219a600182614dc7565b905060006121a9600a88614db3565b6121b490600a614d86565b6121be9088614dc7565b6121c9906030615153565b905060008160f81b9050808484815181106121e6576121e6614dda565b60200101906001600160f81b031916908160001a905350612208600a89614db3565b97505050612189565b50949350505050565b60008381526009602052604090205460609081908180865b612240639813edbd89614eff565b8110156122e857600080612258898c61148286611cf3565b5091509150600061226b87858585612ab4565b90508a840361229e5780945080604051602001612288919061516c565b60405160208183030381529060405295506122c3565b85816040516020016122b1929190615270565b60405160208183030381529060405295505b5050506078639813edbd6122d79190614db3565b6122e19082614eff565b9050612232565b5080826040516020016122fb91906152bb565b60405160208183030381529060405294509450505050935093915050565b606061232482612ade565b604051602001612334919061557c565b6040516020818303038152906040529050919050565b606061235582612ade565b60405160200161233491906155c1565b606061239784848489896040516020016123839594939291906155ff565b604051602081830303815290604052612ade565b6040516020016123a79190615704565b604051602081830303815290604052905095945050505050565b815b6123cd8284614eff565b8110156110be576000818152600a60205260409020805460ff191690556123f381614df0565b90506123c3565b600080549082900361241f5760405163b562e8dd60e01b815260040160405180910390fd5b6001600160a01b03831660008181526005602090815260408083208054680100000000000000018802019055848352600490915281206001851460e11b4260a01b178317905582840190839083907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef8180a4600183015b8181146124ce57808360007fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef600080a4600101612496565b50816000036124ef57604051622e076360e81b815260040160405180910390fd5b60009081556108df91508483856123c1565b604051630a85bd0160e11b81526000906001600160a01b0385169063150b7a0290612536903390899088908890600401615749565b6020604051808303816000875af1925050508015612571575060408051601f3d908101601f1916820190925261256e9181019061577c565b60015b6125cf573d80801561259f576040519150601f19603f3d011682016040523d82523d6000602084013e6125a4565b606091505b5080516000036125c7576040516368d2bf6b60e11b815260040160405180910390fd5b805181602001fd5b6001600160e01b031916630a85bd0160e11b149050949350505050565b600080639813edbd6126046501840ec2103085614dc7565b61260e9190615799565b90506000639813edbd61263061262684612710614d86565b639813edbd61264f565b61263a9190614db3565b90506127108110611a77576000949350505050565b60008061265d600284614db3565b6126679085614eff565b90506126738382615799565b61267d9082614dc7565b949350505050565b61268d614742565b602060c86000600261269f84826157ad565b6126a990846157d3565b6126b391906157f5565b90506126cb6040518060200160405280600081525090565b6126eb866005604051602001610a5f929190918252602082015260400190565b60006126f8826009611998565b1561270d57612708826006611998565b612710565b60325b9050600061271f836005611998565b9050600061272c84612c44565b9050600061273985612d10565b604080516101208101825293845261ffff998a1660208501529689169683018790526060830196909652509486166080860181905260a086015290851660c085015290931660e08301526101008201529392505050565b6060600061279f836002614d86565b6127aa906002614eff565b6001600160401b038111156127c1576127c1614a2f565b6040519080825280601f01601f1916602001820160405280156127eb576020820181803683370190505b509050600360fc1b8160008151811061280657612806614dda565b60200101906001600160f81b031916908160001a905350600f60fb1b8160018151811061283557612835614dda565b60200101906001600160f81b031916908160001a9053506000612859846002614d86565b612864906001614eff565b90505b60018111156128dc576f181899199a1a9b1b9c1cb0b131b232b360811b85600f166010811061289857612898614dda565b1a60f81b8282815181106128ae576128ae614dda565b60200101906001600160f81b031916908160001a90535060049490941c936128d581615816565b9050612867565b508315611a775760405162461bcd60e51b815260206004820181905260248201527f537472696e67733a20686578206c656e67746820696e73756666696369656e7460448201526064016108cc565b60606000808360e0015161ffff161190506129776040518060400160405280600f81526020016e4672616d6520726f756e646e65737360881b8152508460c0015161ffff166001612a9d565b6129b26040518060400160405280600f81526020016e4672616d6520746869636b6e65737360881b8152508560e0015161ffff166001612a9d565b612a116040518060400160405280600a8152602001694672616d65207479706560b01b81525084612a045760405180604001604052806009815260200168496e76697369626c6560b81b815250611f60565b8661010001516001612a8e565b83612a2b5760405180602001604052806000815250612a64565b612a646040518060400160405280600a815260200169119c985b59481d1a5b9d60b21b81525087600001516060015160ff166001612a9d565b604051602001612a779493929190615071565b604051602081830303815290604052915050919050565b606061267d8484846001612d9d565b606061267d84612aac856120f2565b846000612d9d565b6060600080612ac286612e2c565b91509150612ad38783838888612ea0565b979650505050505050565b60608151600003612afd57505060408051602081019091526000815290565b6000604051806060016040528060408152602001615d686040913990506000600384516002612b2c9190614eff565b612b369190614db3565b612b41906004614d86565b90506000612b50826020614eff565b6001600160401b03811115612b6757612b67614a2f565b6040519080825280601f01601f191660200182016040528015612b91576020820181803683370190505b509050818152600183018586518101602084015b81831015612bff5760039283018051603f601282901c811687015160f890811b8552600c83901c8216880151811b6001860152600683901c8216880151811b60028601529116860151901b93820193909352600401612ba5565b600389510660018114612c195760028114612c2a57612c36565b613d3d60f01b600119830152612c36565b603d60f81b6000198301525b509398975050505050505050565b612c4c614797565b6000612c5a83610168611998565b90506000612c69846047611998565b90506000612c7885600b611998565b90506040518060e00160405280612c918560328061329e565b81526020018461ffff168152602001612cac8585603261329e565b81526020018360ff168152602001612cc66000808561329e565b815260ff83166020820152604001612cdf876003611998565b15612cf95760405180602001604052806000815250612d05565b612d058560328061329e565b905295945050505050565b60606000612d1f836064611998565b90506046811015612d4d5750506040805180820190915260058152641cdbdb1a5960da1b6020820152919050565b605a811015612d795750506040805180820190915260058152641a5b9cd95d60da1b6020820152919050565b50506040805180820190915260068152651bdd5d1cd95d60d21b6020820152919050565b60608482612dab5784612dcc565b84604051602001612dbc919061582d565b6040516020818303038152906040525b84612de65760405180602001604052806000815250612e01565b604051806040016040528060018152602001600b60fa1b8152505b604051602001612e139392919061585b565b6040516020818303038152906040529050949350505050565b6000806000612e3a846125ec565b90506000612e4b6004612710614db3565b612e559083614db3565b9050806003811115612e6957612e696158df565b93506004612e7981612710614db3565b612e839083614d86565b612e8d9084614dc7565b612e979190614d86565b92505050915091565b60606000612ead87612685565b604080516080808201835260008083526020808401829052808601805161ffff9081168688015260a0808901805183166060808a019190915289518089018b5287815280870188905285518516818c01529151841682820152895160c08082018c52865186168252865186168289015286518616828d015286518616828401528c5151828b01528185018990528b519081018c528881528651861697810197909752855185169a87019a909a52845184169086015289515196850196909652830184905290519697509395929493909261271091612f9091611d1091168d614d86565b612f9a9190614db3565b905060028b6003811115612fb057612fb06158df565b0361301d57600085526020860151612fca906001906157d3565b61ffff16845260408401805160019190612fe59083906158f5565b61ffff9081169091526020880180518216865280518216604087015260018552516130139250839116614dc7565b6040830152613187565b60038b6003811115613031576130316158df565b0361308b5760008086528452604084018051600191906130529083906158f5565b61ffff908116909152602088018051821686528051821660408088019190915290519091168452830182905250600160a0830152613187565b60008b600381111561309f5761309f6158df565b036131205760208601805161ffff168652516130bd906001906157d3565b61ffff168452604084018051600191906130d89083906158f5565b61ffff908116909152600085526020880180518216604087015260018552516131049250839116614dc7565b61310f906001614eff565b6040830152600160a0830152613187565b60018b6003811115613134576131346158df565b036131875760008552602086015161ffff1684526040850180516001919061315d9083906158f5565b61ffff90811690915260208801805182168652604080870185905260008652905190911690840152505b60408601518551869061319b9083906158f5565b61ffff169052506040860151845185906131b69083906158f5565b61ffff1690525060608601516020860180516131d39083906158f5565b61ffff1690525060608601516020850180516131f09083906158f5565b91509061ffff16908161ffff168152505061328e6040518060800160405280886080015161ffff1681526020018860a0015161ffff1681526020018860000151608001518152602001886000015160c001518152508686868660405180608001604052808d60c0015161ffff1681526020018d60e0015161ffff1681526020018d610100015181526020018d60000151604001518152508f8f6132ef565b9c9b505050505050505050505050565b60606132ad8461ffff166120f2565b6132b98460ff166120f2565b6132c58460ff166120f2565b6040516020016132d793929190615910565b60405160208183030381529060405290509392505050565b6060600061330789888c604001518a60800151613661565b9050600061331f89888d604001518a60800151613661565b9050600061338260405180604001604052806004815260200163199a5b1b60e21b81525060008e60600151511161335a578d60400151613851565b6040518060400160405280600981526020016875726c28236272472960b81b8152505b613851565b6133b1604051806040016040528060058152602001640eed2c8e8d60db1b8152508e6000015161ffff1661387d565b6133e1604051806040016040528060068152602001651a195a59da1d60d21b8152508f6020015161ffff1661387d565b613434604051806040016040528060028152602001610e4f60f31b8152506134108c6000015161ffff166120f2565b6040516020016134209190615995565b604051602081830303815290604052613851565b61346360405180604001604052806002815260200161727960f01b8152506134108d6000015161ffff166120f2565b6040516020016134779594939291906159ba565b604051602081830303815290604052905060006134988d8d8d8d8d8b61388c565b90506136506134aa8960000151613906565b8261363f604051806020016040528060008152506134f0876134cb8f613a86565b6040516020016134dc929190615a25565b604051602081830303815290604052613af8565b61354061353a604051806040016040528060048152602001636d61736b60e01b8152506040518060400160405280600981526020016875726c28236d624d2960b81b815250613851565b8e613b30565b61361961358b604051806040016040528060068152602001653334b63a32b960d11b8152506040518060400160405280600881526020016775726c28236d462960c01b815250613851565b6135d2604051806040016040528060048152602001636d61736b60e01b8152506040518060400160405280600981526020016875726c28236d664d2960b81b815250613851565b6040516020016135e3929190615a25565b6040516020818303038152906040528c8c604051602001613605929190615a25565b604051602081830303815290604052613b30565b60405160200161362b93929190615a54565b604051602081830303815290604052613b56565b60405160200161362b929190615a25565b9d9c50505050505050505050505050565b6060613848613691604051806040016040528060018152602001600f60fb1b815250876000015161ffff1661387d565b6136bc604051806040016040528060018152602001607960f81b815250886020015161ffff1661387d565b6136ec604051806040016040528060068152602001651a195a59da1d60d21b815250896060015161ffff1661387d565b61371b604051806040016040528060058152602001640eed2c8e8d60db1b8152508a6040015161ffff1661387d565b60405160200161372e9493929190615071565b60408051601f1981840301815282820190915260028252610c6f60f31b60208301528651909161384391613766919061ffff1661387d565b61379260405180604001604052806002815260200161637960f01b815250896020015161ffff1661387d565b6137ba604051806040016040528060028152602001610e4f60f31b8152508a6040015161387d565b6137e660405180604001604052806002815260200161727960f01b8152508b6060015161ffff1661387d565b61381b60405180604001604052806004815260200163199a5b1b60e21b8152508c60a00151613815578a613851565b8b613851565b60405160200161382f9594939291906159ba565b604051602081830303815290604052613b7e565b613b56565b95945050505050565b60608282604051602001613866929190615a97565b604051602081830303815290604052905092915050565b6060611a778361337d846120f2565b6060612ad36138a388866060015161ffff16613bb9565b60008451116138be576138b98660600151613db0565b6138c0565b835b6138d28a886060015161ffff166141dc565b6138df8a8a8a8a8f6143ba565b6040516020016138f29493929190615071565b604051602081830303815290604052614546565b606061396460405180604001604052806005815260200164786d6c6e7360d81b8152506040518060400160405280601a81526020017f687474703a2f2f7777772e77332e6f72672f323030302f737667000000000000815250613851565b6139a4604051806040016040528060028152602001611a5960f21b8152506040518060400160405280600481526020016336b7b7b760e11b815250613851565b6139e8604051806040016040528060068152602001651a195a59da1d60d21b815250604051806040016040528060048152602001633130302560e01b815250613851565b613a34604051806040016040528060078152602001660ecd2caee84def60cb1b8152506040518060400160405280600b81526020016a030203020323030203230360ac1b815250613851565b613a72604051806040016040528060058152602001647374796c6560d81b815250613a628861ffff166120f2565b6040516020016134209190615ae3565b6040516020016123349594939291906159ba565b606061073a604051806040016040528060058152602001647374796c6560d81b815250613aba846020015161ffff166120f2565b84604001518560600151613ad5876020015161ffff166120f2565b8751613ae49061ffff166120f2565b604051602001613420959493929190615b35565b606061073a604051806040016040528060048152602001631c9958dd60e21b815250836040518060200160405280600081525061457a565b6060611a77604051806040016040528060018152602001606760f81b815250848461457a565b6060611a776040518060400160405280600381526020016273766760e81b815250848461457a565b606061073a60405180604001604052806007815260200166656c6c6970736560c81b815250836040518060200160405280600081525061457a565b6060611a77613bfd604051806040016040528060028152602001611a5960f21b8152506040518060400160405280600381526020016262724760e81b815250613851565b613c3b604051806040016040528060018152602001603960f91b8152506040518060400160405280600381526020016237352560e81b815250613851565b604051602001613c4c929190615a25565b604051602081830303815290604052613d14613cbf604051806040016040528060068152602001651bd9999cd95d60d21b815250613410886020015161ffff166002896064613c9b9190614d86565b613ca6906003614d86565b613cb09190614db3565b613cba9190614db3565b6120f2565b613cef6040518060400160405280600a81526020016939ba37b816b1b7b637b960b11b8152508860400151613851565b604051602001613d00929190615a25565b604051602081830303815290604052614593565b613d8b613d5b604051806040016040528060068152602001651bd9999cd95d60d21b815250604051806040016040528060048152602001633130302560e01b815250613851565b613cef6040518060400160405280600a81526020016939ba37b816b1b7b637b960b11b8152508960600151613851565b604051602001613d9c929190615a25565b6040516020818303038152906040526145cb565b60606000613dbf8360026157ad565b9050611a77613e02604051806040016040528060028152602001611a5960f21b8152506040518060400160405280600281526020016136a360f11b815250613851565b604051602001613e129190615c20565b604051602081830303815290604052613fc1613e67604051806040016040528060068152602001651c995cdd5b1d60d21b815250604051806040016040528060038152602001621bdd5d60ea1b815250613851565b613eb36040518060400160405280601081526020016f1cdc1958dd5b185c915e1c1bdb995b9d60821b81525060405180604001604052806002815260200161032360f41b815250613851565b613f026040518060400160405280600e81526020016d3634b3b43a34b73396b1b7b637b960911b8152506040518060400160405280600781526020016611b1313131313160c91b815250613851565b604051602001613f1493929190615a54565b60408051601f1981840301815282820190915260018252600f60fb1b602083015290613fbc90613f489061ffff881661387d565b613f6f604051806040016040528060018152602001607960f81b8152508861ffff1661387d565b613f96604051806040016040528060018152602001603d60f91b8152508961ffff1661387d565b604051602001613fa893929190615a54565b6040516020818303038152906040526145fe565b61463e565b6141b761400d6040518060400160405280600281526020016134b760f11b8152506040518060400160405280600d81526020016c536f757263654772617068696360981b815250613851565b61404d6040518060400160405280600381526020016234b71960e91b815250604051806040016040528060038152602001621bdd5d60ea1b815250613851565b6140996040518060400160405280600881526020016737b832b930ba37b960c11b8152506040518060400160405280600a81526020016961726974686d6574696360b01b815250613851565b6140d6604051806040016040528060028152602001616b3160f01b815250604051806040016040528060018152602001600360fc1b815250613851565b61411360405180604001604052806002815260200161359960f11b815250604051806040016040528060018152602001603160f81b815250613851565b614150604051806040016040528060028152602001616b3360f01b815250604051806040016040528060018152602001603160f81b815250613851565b61418d604051806040016040528060028152602001611acd60f21b815250604051806040016040528060018152602001600360fc1b815250613851565b6040516020016141a39796959493929190615c3c565b604051602081830303815290604052614675565b6040516020016141c8929190615a25565b6040516020818303038152906040526146b4565b6060611a77614220604051806040016040528060028152602001611a5960f21b815250604051806040016040528060038152602001626d624d60e81b815250613851565b6142d6614252604051806040016040528060058152602001640eed2c8e8d60db1b815250876000015161ffff1661387d565b614282604051806040016040528060068152602001651a195a59da1d60d21b815250886020015161ffff1661387d565b6142c460405180604001604052806004815260200163199a5b1b60e21b8152506040518060400160405280600481526020016311a3232360e11b815250613851565b6040516020016134dc93929190615a54565b614395614311604051806040016040528060028152602001610c6f60f31b8152506002896000015161430891906157f5565b61ffff1661387d565b61434060405180604001604052806002815260200161637960f01b81525060028a6020015161430891906157f5565b61436f604051806040016040528060018152602001603960f91b81525089600161436a9190614eff565b61387d565b60405160200161438193929190615a54565b6040516020818303038152906040526146df565b6040516020016143a6929190615a25565b604051602081830303815290604052614719565b606061453c6143fe604051806040016040528060028152602001611a5960f21b815250604051806040016040528060038152602001626d664d60e81b815250613851565b6144a2614430604051806040016040528060058152602001640eed2c8e8d60db1b815250866000015161ffff1661387d565b614460604051806040016040528060068152602001651a195a59da1d60d21b815250876020015161ffff1661387d565b6142c460405180604001604052806004815260200163199a5b1b60e21b815250604051806040016040528060048152602001630233030360e41b815250613851565b6144e68988604051806040016040528060048152602001630233030360e41b8152506040518060400160405280600481526020016311a3232360e11b815250613661565b61452a8988604051806040016040528060048152602001630233030360e41b8152506040518060400160405280600481526020016311a3232360e11b815250613661565b6040516020016143a693929190615a54565b9695505050505050565b606061073a604051806040016040528060048152602001636465667360e01b81525060405180602001604052806000815250845b6060838383866040516020016132d79493929190615cce565b606061073a60405180604001604052806004815260200163073746f760e41b815250836040518060200160405280600081525061457a565b6060611a776040518060400160405280600e81526020016d1c98591a585b11dc98591a595b9d60921b815250848461457a565b606061073a6040518060400160405280600c81526020016b1999541bda5b9d131a59da1d60a21b815250836040518060200160405280600081525061457a565b6060611a7760405180604001604052806012815260200171666553706563756c61724c69676874696e6760701b815250848461457a565b606061073a6040518060400160405280600b81526020016a6665436f6d706f7369746560a81b815250836040518060200160405280600081525061457a565b6060611a77604051806040016040528060068152602001653334b63a32b960d11b815250848461457a565b606061073a60405180604001604052806006815260200165636972636c6560d01b815250836040518060200160405280600081525061457a565b6060611a77604051806040016040528060048152602001636d61736b60e01b815250848461457a565b604051806101200160405280614756614797565b81526000602082018190526040820181905260608083018290526080830182905260a0830182905260c0830182905260e08301919091526101009091015290565b6040518060e0016040528060608152602001600061ffff16815260200160608152602001600060ff16815260200160608152602001600060ff168152602001606081525090565b6001600160e01b031981168114610aff57600080fd5b60006020828403121561480657600080fd5b8135611a77816147de565b60005b8381101561482c578181015183820152602001614814565b50506000910152565b6000815180845261484d816020860160208601614811565b601f01601f19169290920160200192915050565b602081526000611a776020830184614835565b60006020828403121561488657600080fd5b5035919050565b6001600160a01b0381168114610aff57600080fd5b600080604083850312156148b557600080fd5b82356148c08161488d565b946020939093013593505050565b6000806000606084860312156148e357600080fd5b83356148ee8161488d565b925060208401356148fe8161488d565b929592945050506040919091013590565b6000806040838503121561492257600080fd5b50508035926020909101359150565b60008083601f84011261494357600080fd5b5081356001600160401b0381111561495a57600080fd5b6020830191508360208260051b85010111156109f257600080fd5b60008060006040848603121561498a57600080fd5b83356001600160401b038111156149a057600080fd5b6149ac86828701614931565b90945092505060208401356149c08161488d565b809150509250925092565b6000602082840312156149dd57600080fd5b8135611a778161488d565b8015158114610aff57600080fd5b60008060408385031215614a0957600080fd5b8235614a148161488d565b91506020830135614a24816149e8565b809150509250929050565b634e487b7160e01b600052604160045260246000fd5b604051601f8201601f191681016001600160401b0381118282101715614a6d57614a6d614a2f565b604052919050565b60006001600160401b03821115614a8e57614a8e614a2f565b50601f01601f191660200190565b60008060008060808587031215614ab257600080fd5b8435614abd8161488d565b93506020850135614acd8161488d565b92506040850135915060608501356001600160401b03811115614aef57600080fd5b8501601f81018713614b0057600080fd5b8035614b13614b0e82614a75565b614a45565b818152886020838501011115614b2857600080fd5b8160208401602083013760006020838301015280935050505092959194509250565b60008083601f840112614b5c57600080fd5b5081356001600160401b03811115614b7357600080fd5b6020830191508360208285010111156109f257600080fd5b60008060008060008060008060a0898b031215614ba757600080fd5b8835614bb28161488d565b97506020890135614bc28161488d565b965060408901356001600160401b0380821115614bde57600080fd5b614bea8c838d01614931565b909850965060608b0135915080821115614c0357600080fd5b614c0f8c838d01614931565b909650945060808b0135915080821115614c2857600080fd5b50614c358b828c01614b4a565b999c989b5096995094979396929594505050565b600080600060608486031215614c5e57600080fd5b8335925060208401356148fe8161488d565b60008060408385031215614c8357600080fd5b8235614c8e8161488d565b91506020830135614a248161488d565b60008060008060008060a08789031215614cb757600080fd5b8635614cc28161488d565b95506020870135614cd28161488d565b9450604087013593506060870135925060808701356001600160401b03811115614cfb57600080fd5b614d0789828a01614b4a565b979a9699509497509295939492505050565b600181811c90821680614d2d57607f821691505b602082108103614d4d57634e487b7160e01b600052602260045260246000fd5b50919050565b600060208284031215614d6557600080fd5b8151611a77816149e8565b634e487b7160e01b600052601160045260246000fd5b808202811582820484141761073a5761073a614d70565b634e487b7160e01b600052601260045260246000fd5b600082614dc257614dc2614d9d565b500490565b8181038181111561073a5761073a614d70565b634e487b7160e01b600052603260045260246000fd5b600060018201614e0257614e02614d70565b5060010190565b600060ff821660ff8103614e1f57614e1f614d70565b60010192915050565b60008060408385031215614e3b57600080fd5b8251614e46816149e8565b6020840151909250614a248161488d565b600060208284031215614e6957600080fd5b81516001600160401b03811115614e7f57600080fd5b8201601f81018413614e9057600080fd5b8051614e9e614b0e82614a75565b818152856020838501011115614eb357600080fd5b613848826020830160208601614811565b724e6f6e2d46756e6769626c65204d6f6f6e202360681b815260008251614ef2816013850160208701614811565b9190910160130192915050565b8082018082111561073a5761073a614d70565b6000815160e08452614f2760e0850182614835565b905061ffff602084015116602085015260408301518482036040860152614f4e8282614835565b91505060ff606084015116606085015260808301518482036080860152614f758282614835565b91505060ff60a08401511660a085015260c083015184820360c08601526138488282614835565b8481528360208201526080604082015260008351610120806080850152614fc76101a0850183614f12565b91506020860151614fde60a086018261ffff169052565b50604086015161ffff811660c086015250606086015161ffff811660e08601525060808601516101006150168187018361ffff169052565b60a088015161ffff9081169387019390935260c0880151831661014087015260e088015190921661016086015250850151838203607f190161018085015261505e8282614835565b9250505082606083015295945050505050565b60008551615083818460208a01614811565b855190830190615097818360208a01614811565b85519101906150aa818360208901614811565b84519101906150bd818360208801614811565b019695505050505050565b605b60f81b81526000600187516150e58183860160208c01614811565b8751908401906150fb8184840160208c01614811565b87519101906151108184840160208b01614811565b86519101906151258184840160208a01614811565b855191019061513a8184840160208901614811565b605d60f81b910191820152600201979650505050505050565b60ff818116838216019081111561073a5761073a614d70565b7f3c21444f43545950452068746d6c3e3c68746d6c3e3c686561643e3c7374796c81527f6520747970653d22746578742f637373223e68746d6c7b6f766572666c6f773a60208201527f68696464656e7d626f64797b6d617267696e3a307d236d6f6f6e7b646973706c60408201527f61793a626c6f636b3b6d617267696e3a6175746f7d3c2f7374796c653e3c2f6860608201527f6561643e3c626f64793e3c6469762069643d226d6f6f6e446976223e3c2f646960808201527203b1f1e39b1b934b83a1f3632ba1033b99eadb606d1b60a0820152600082516152588160b3850160208701614811565b600360fd1b60b393909101928301525060b401919050565b60008351615282818460208801614811565b61016360f51b90830190815283516152a1816002840160208801614811565b600360fd1b60029290910191820152600301949350505050565b600082516152cd818460208701614811565b7f5d3b6c657420243d646f63756d656e742e676574456c656d656e74427949642e9201918252507f62696e6428646f63756d656e74293b2428226d6f6f6e44697622292e696e6e6560208201527f7248544d4c3d67735b305d3b6c6574206d6f3d2428226d6f6f6e44697622293b60408201527f6c657420753d653d3e7b6c657420743d2428226d6f6f6e22292e676574426f7560608201527f6e64696e67436c69656e745265637428293b2428226d6f6f6e44697622292e6960808201527f6e6e657248544d4c3d67735b4d6174682e6d617828302c4d6174682e6d696e2860a08201527f4d6174682e666c6f6f72282828652d742e6c656674292f742e7769647468292a60c08201527f67732e6c656e677468292c67732e6c656e6774682d3129295d3b7d3b6d6f2e6f60e08201527f6e6d6f7573656d6f76653d653d3e7528652e636c69656e7458293b6d6f2e61646101008201527f644576656e744c697374656e65722822746f7563687374617274222c653d3e7b6101208201527f6c657420743d653d3e7528652e746f75636865735b305d2e636c69656e7458296101408201527f3b6e3d28293d3e7b652e7461726765742e72656d6f76654576656e744c6973746101608201527f656e65722822746f7563686d6f7665222c74292c652e7461726765742e72656d6101808201527f6f76654576656e744c697374656e65722822746f756368656e64222c6e293b7d6101a08201527f3b652e7461726765742e6164644576656e744c697374656e65722822746f75636101c08201527f686d6f7665222c74293b652e7461726765742e6164644576656e744c697374656101e08201527f6e65722822746f756368656e64222c6e293b7d293b3c2f7363726970743e3c2f6102008201526b3137b23c9f1e17b43a36b61f60a11b61022082015261022c01919050565b7f646174613a696d6167652f7376672b786d6c3b6261736536342c0000000000008152600082516155b481601a850160208701614811565b91909101601a0192915050565b7519185d184e9d195e1d0bda1d1b5b0ed8985cd94d8d0b60521b8152600082516155f2816016850160208701614811565b9190910160160192915050565b683d913730b6b2911d1160b91b81528551600090615624816009850160208b01614811565b701116113232b9b1b934b83a34b7b7111d1160791b600991840191820152865161565581601a840160208b01614811565b6e11161130ba3a3934b13aba32b9911d60891b601a92909101918201528551615685816029840160208a01614811565b69161134b6b0b3b2911d1160b11b6029929091019182015284516156b0816033840160208901614811565b7211161130b734b6b0ba34b7b72fbab936111d1160691b6033929091019182015283516156e4816046840160208801614811565b016156f66046820161227d60f01b9052565b604801979650505050505050565b7f646174613a6170706c69636174696f6e2f6a736f6e3b6261736536342c00000081526000825161573c81601d850160208701614811565b91909101601d0192915050565b6001600160a01b038581168252841660208201526040810183905260806060820181905260009061453c90830184614835565b60006020828403121561578e57600080fd5b8151611a77816147de565b6000826157a8576157a8614d9d565b500690565b61ffff8181168382160280821691908281146157cb576157cb614d70565b505092915050565b61ffff8281168282160390808211156157ee576157ee614d70565b5092915050565b600061ffff8084168061580a5761580a614d9d565b92169190910492915050565b60008161582557615825614d70565b506000190190565b6000601160f91b808352835161584a816001860160208801614811565b600193019283015250600201919050565b6e3d913a3930b4ba2fba3cb832911d1160891b8152835160009061588681600f850160208901614811565b691116113b30b63ab2911d60b11b600f9184019182015284516158b0816019840160208901614811565b607d60f81b6019929091019182015283516158d281601a840160208801614811565b01601a0195945050505050565b634e487b7160e01b600052602160045260246000fd5b61ffff8181168382160190808211156157ee576157ee614d70565b640d0e6d8c2560db1b815260008451615930816005850160208901614811565b600b60fa1b6005918401918201528451615951816006840160208901614811565b61094b60f21b600692909101918201528351615974816008840160208801614811565b66252c313030252960c81b60089290910191820152600f0195945050505050565b600082516159a7818460208701614811565b602560f81b920191825250600101919050565b600086516159cc818460208b01614811565b8651908301906159e0818360208b01614811565b86519101906159f3818360208a01614811565b8551910190615a06818360208901614811565b8451910190615a19818360208801614811565b01979650505050505050565b60008351615a37818460208801614811565b835190830190615a4b818360208801614811565b01949350505050565b60008451615a66818460208901614811565b845190830190615a7a818360208901614811565b8451910190615a8d818360208801614811565b0195945050505050565b60008351615aa9818460208801614811565b611e9160f11b9083019081528351615ac8816002840160208801614811565b61011160f51b60029290910191820152600401949350505050565b6d3137b93232b916b930b234bab99d60911b815260008251615b0c81600e850160208701614811565b7104a76dac2f05ad0cad2ced0e874626060ecd60731b600e939091019283015250602001919050565b6737baba3634b7329d60c11b815260008651615b58816008850160208b01614811565b620383c160ed1b6008918401918201528651615b7b81600b840160208b01614811565b600160fd1b600b92909101918201528551615b9d81600c840160208a01614811565b703b6f75746c696e652d6f66667365743a2d60781b600c92909101918201528451615bcf81601d840160208901614811565b70383c1db137b93232b916b930b234bab99d60791b601d92909101918201528351615c0181602e840160208801614811565b01615c12602e8201602560f81b9052565b602f01979650505050505050565b60008251615c32818460208701614811565b9190910192915050565b600088516020615c4f8285838e01614811565b895191840191615c628184848e01614811565b8951920191615c748184848d01614811565b8851920191615c868184848c01614811565b8751920191615c988184848b01614811565b8651920191615caa8184848a01614811565b8551920191615cbc8184848901614811565b919091019a9950505050505050505050565b600f60fa1b815260008551615cea816001850160208a01614811565b600160fd1b6001918401918201528551615d0b816002840160208a01614811565b808201915050601f60f91b8060028301528551615d2f816003850160208a01614811565b613c2f60f01b600393909101928301528451615d52816005850160208901614811565b6005920191820152600601969550505050505056fe4142434445464748494a4b4c4d4e4f505152535455565758595a6162636465666768696a6b6c6d6e6f707172737475767778797a303132333435363738392b2f4e6f6e2d46756e6769626c65204d6f6f6e7320617265206f6e2d636861696e2067656e65726174697665206d6f6f6e204e4654732e20416c6c206d6f6f6e206172742069732067656e657261746564206f6e2d636861696e20616e64207570646174657320696e207265616c2d74696d652c206261736564206f6e2063757272656e7420626c6f636b2074696d6520616e64207573696e6720616e206f6e2d636861696e20535647206c6962726172792c20746f20636c6f73656c79206d6972726f7220746865207068617365206f6620746865206d6f6f6e20696e20746865207265616c20776f726c642ea2646970667358221220b5c02847fbc506537feae86e5b67641440496bbe38df9fc12078b4029d1df26164736f6c63430008110033

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

000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000f61706120e12b303ca2418fc5bc9ee7961d166c400000000000000000000000000000000000000000000000000000000000000124e6f6e2d46756e6769626c65204d6f6f6e73000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000034e464d0000000000000000000000000000000000000000000000000000000000

-----Decoded View---------------
Arg [0] : _name (string): Non-Fungible Moons
Arg [1] : _symbol (string): NFM
Arg [2] : _defaultAlienArtAddress (address): 0xF61706120e12b303cA2418Fc5bC9EE7961D166c4

-----Encoded View---------------
7 Constructor Arguments found :
Arg [0] : 0000000000000000000000000000000000000000000000000000000000000060
Arg [1] : 00000000000000000000000000000000000000000000000000000000000000a0
Arg [2] : 000000000000000000000000f61706120e12b303ca2418fc5bc9ee7961d166c4
Arg [3] : 0000000000000000000000000000000000000000000000000000000000000012
Arg [4] : 4e6f6e2d46756e6769626c65204d6f6f6e730000000000000000000000000000
Arg [5] : 0000000000000000000000000000000000000000000000000000000000000003
Arg [6] : 4e464d0000000000000000000000000000000000000000000000000000000000


Block Transaction Difficulty Gas Used Reward
View All Blocks Produced

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

Validator Index Block Amount
View All Withdrawals

Transaction Hash Block Value Eth2 PubKey Valid
View All Deposits
Loading...
Loading
[ Download: CSV Export  ]
[ 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.