ETH Price: $2,527.31 (+0.64%)

Contract

0x2fb6FEB663c481E9854a251002C772FEad3974d6
 

Overview

ETH Balance

0 ETH

Eth Value

$0.00

Multichain Info

No addresses found
Transaction Hash
Method
Block
From
To
Mint Allow List154915462022-09-07 17:14:14723 days ago1662570854IN
0x2fb6FEB6...Ead3974d6
0.00003 ETH0.0007185726.1873102
Mint Public154888282022-09-07 6:37:29723 days ago1662532649IN
0x2fb6FEB6...Ead3974d6
0.00001 ETH0.0003925610.37056226
Mint Public154875282022-09-07 1:42:31724 days ago1662514951IN
0x2fb6FEB6...Ead3974d6
0.00001 ETH0.0010639528.11571632
Mint Public154875282022-09-07 1:42:31724 days ago1662514951IN
0x2fb6FEB6...Ead3974d6
0.00001 ETH0.0010642928.11571632
Mint Public154875282022-09-07 1:42:31724 days ago1662514951IN
0x2fb6FEB6...Ead3974d6
0.00001 ETH0.0032426128.11571632
Mint Public154875282022-09-07 1:42:31724 days ago1662514951IN
0x2fb6FEB6...Ead3974d6
0.00001 ETH0.0032426128.11571632
Mint Public154875282022-09-07 1:42:31724 days ago1662514951IN
0x2fb6FEB6...Ead3974d6
0.00001 ETH0.0032426128.11571632
Mint Public154875282022-09-07 1:42:31724 days ago1662514951IN
0x2fb6FEB6...Ead3974d6
0.00001 ETH0.0032426128.11571632
Mint Public154875282022-09-07 1:42:31724 days ago1662514951IN
0x2fb6FEB6...Ead3974d6
0.00001 ETH0.0032426128.11571632
Mint Public154875282022-09-07 1:42:31724 days ago1662514951IN
0x2fb6FEB6...Ead3974d6
0.00001 ETH0.0032426128.11571632
Mint Public154875282022-09-07 1:42:31724 days ago1662514951IN
0x2fb6FEB6...Ead3974d6
0.00001 ETH0.0032426128.11571632
Mint Public154875282022-09-07 1:42:31724 days ago1662514951IN
0x2fb6FEB6...Ead3974d6
0.00001 ETH0.0032426128.11571632
Mint Public154875282022-09-07 1:42:31724 days ago1662514951IN
0x2fb6FEB6...Ead3974d6
0.00001 ETH0.0032426128.11571632
Mint Public154875282022-09-07 1:42:31724 days ago1662514951IN
0x2fb6FEB6...Ead3974d6
0.00001 ETH0.0032426128.11571632
Mint Public154875282022-09-07 1:42:31724 days ago1662514951IN
0x2fb6FEB6...Ead3974d6
0.00001 ETH0.0032426128.11571632
Mint Public154875282022-09-07 1:42:31724 days ago1662514951IN
0x2fb6FEB6...Ead3974d6
0.00001 ETH0.0032426128.11571632
Mint Public154875282022-09-07 1:42:31724 days ago1662514951IN
0x2fb6FEB6...Ead3974d6
0.00001 ETH0.0032426128.11571632
Mint Public154875282022-09-07 1:42:31724 days ago1662514951IN
0x2fb6FEB6...Ead3974d6
0.00001 ETH0.0032426128.11571632
Mint Public154875282022-09-07 1:42:31724 days ago1662514951IN
0x2fb6FEB6...Ead3974d6
0.00001 ETH0.0032426128.11571632
Mint Public154875282022-09-07 1:42:31724 days ago1662514951IN
0x2fb6FEB6...Ead3974d6
0.00001 ETH0.0039922634.61571632
Mint Public154875282022-09-07 1:42:31724 days ago1662514951IN
0x2fb6FEB6...Ead3974d6
0.00001 ETH0.0039922634.61571632
Mint Public154875282022-09-07 1:42:31724 days ago1662514951IN
0x2fb6FEB6...Ead3974d6
0.00001 ETH0.0039922634.61571632
Mint Public154875282022-09-07 1:42:31724 days ago1662514951IN
0x2fb6FEB6...Ead3974d6
0.00001 ETH0.0039922634.61571632
Mint Public154875282022-09-07 1:42:31724 days ago1662514951IN
0x2fb6FEB6...Ead3974d6
0.00001 ETH0.0039922634.61571632
Mint Public154875282022-09-07 1:42:31724 days ago1662514951IN
0x2fb6FEB6...Ead3974d6
0.00001 ETH0.0039922634.61571632
View all transactions

Latest 25 internal transactions (View All)

Advanced mode:
Parent Transaction Hash Block From To
154875282022-09-07 1:42:31724 days ago1662514951
0x2fb6FEB6...Ead3974d6
0.000009 ETH
154875282022-09-07 1:42:31724 days ago1662514951
0x2fb6FEB6...Ead3974d6
0.000001 ETH
154875282022-09-07 1:42:31724 days ago1662514951
0x2fb6FEB6...Ead3974d6
0.000009 ETH
154875282022-09-07 1:42:31724 days ago1662514951
0x2fb6FEB6...Ead3974d6
0.000001 ETH
154875282022-09-07 1:42:31724 days ago1662514951
0x2fb6FEB6...Ead3974d6
0.000009 ETH
154875282022-09-07 1:42:31724 days ago1662514951
0x2fb6FEB6...Ead3974d6
0.000001 ETH
154875282022-09-07 1:42:31724 days ago1662514951
0x2fb6FEB6...Ead3974d6
0.000009 ETH
154875282022-09-07 1:42:31724 days ago1662514951
0x2fb6FEB6...Ead3974d6
0.000001 ETH
154875282022-09-07 1:42:31724 days ago1662514951
0x2fb6FEB6...Ead3974d6
0.000009 ETH
154875282022-09-07 1:42:31724 days ago1662514951
0x2fb6FEB6...Ead3974d6
0.000001 ETH
154875282022-09-07 1:42:31724 days ago1662514951
0x2fb6FEB6...Ead3974d6
0.000009 ETH
154875282022-09-07 1:42:31724 days ago1662514951
0x2fb6FEB6...Ead3974d6
0.000001 ETH
154875282022-09-07 1:42:31724 days ago1662514951
0x2fb6FEB6...Ead3974d6
0.000009 ETH
154875282022-09-07 1:42:31724 days ago1662514951
0x2fb6FEB6...Ead3974d6
0.000001 ETH
154875282022-09-07 1:42:31724 days ago1662514951
0x2fb6FEB6...Ead3974d6
0.000009 ETH
154875282022-09-07 1:42:31724 days ago1662514951
0x2fb6FEB6...Ead3974d6
0.000001 ETH
154875282022-09-07 1:42:31724 days ago1662514951
0x2fb6FEB6...Ead3974d6
0.000009 ETH
154875282022-09-07 1:42:31724 days ago1662514951
0x2fb6FEB6...Ead3974d6
0.000001 ETH
154875282022-09-07 1:42:31724 days ago1662514951
0x2fb6FEB6...Ead3974d6
0.000009 ETH
154875282022-09-07 1:42:31724 days ago1662514951
0x2fb6FEB6...Ead3974d6
0.000001 ETH
154875282022-09-07 1:42:31724 days ago1662514951
0x2fb6FEB6...Ead3974d6
0.000009 ETH
154875282022-09-07 1:42:31724 days ago1662514951
0x2fb6FEB6...Ead3974d6
0.000001 ETH
154875282022-09-07 1:42:31724 days ago1662514951
0x2fb6FEB6...Ead3974d6
0.000009 ETH
154875282022-09-07 1:42:31724 days ago1662514951
0x2fb6FEB6...Ead3974d6
0.000001 ETH
154875282022-09-07 1:42:31724 days ago1662514951
0x2fb6FEB6...Ead3974d6
0.000009 ETH
View All Internal Transactions
Loading...
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
SeaDrop

Compiler Version
v0.8.15+commit.e14f2714

Optimization Enabled:
Yes with 200 runs

Other Settings:
default evmVersion
File 1 of 13 : SeaDrop.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.11;

import { ISeaDrop } from "./interfaces/ISeaDrop.sol";

import {
    AllowListData,
    MintParams,
    PublicDrop,
    TokenGatedDropStage,
    TokenGatedMintParams
} from "./lib/SeaDropStructs.sol";

import { IERC721SeaDrop } from "./interfaces/IERC721SeaDrop.sol";

import { ERC20, SafeTransferLib } from "solmate/utils/SafeTransferLib.sol";

import { MerkleProofLib } from "solady/utils/MerkleProofLib.sol";

import {
    IERC721
} from "openzeppelin-contracts/contracts/token/ERC721/IERC721.sol";

import {
    IERC165
} from "openzeppelin-contracts/contracts/utils/introspection/IERC165.sol";

import {
    ECDSA
} from "openzeppelin-contracts/contracts/utils/cryptography/ECDSA.sol";

/**
 * @title  SeaDrop
 * @author jameswenzel, ryanio, stephankmin
 * @notice SeaDrop is a contract to help facilitate ERC721 token drops
 *         with functionality for public, allow list, server-side signed,
 *         and token-gated drops.
 */
contract SeaDrop is ISeaDrop {
    using ECDSA for bytes32;

    // Track the public drops.
    mapping(address => PublicDrop) private _publicDrops;

    // Track the drop URIs.
    mapping(address => string) private _dropURIs;

    // Track the creator payout addresses.
    mapping(address => address) private _creatorPayoutAddresses;

    // Track the allow list merkle roots.
    mapping(address => bytes32) private _allowListMerkleRoots;

    // Track the allowed fee recipients.
    mapping(address => mapping(address => bool)) private _allowedFeeRecipients;

    // Track the allowed signers for server side drops.
    mapping(address => mapping(address => bool)) private _signers;

    // Track the signers for each server side drop.
    mapping(address => address[]) private _enumeratedSigners;

    // Track token gated drop stages.
    mapping(address => mapping(address => TokenGatedDropStage))
        private _tokenGatedDrops;

    // Track the tokens for token gated drops.
    mapping(address => address[]) private _enumeratedTokenGatedTokens;

    // Track redeemed token IDs for token gated drop stages.
    mapping(address => mapping(address => mapping(uint256 => bool)))
        private _tokenGatedRedeemed;

    // EIP-712: Typed structured data hashing and signing
    bytes32 public immutable DOMAIN_SEPARATOR;
    bytes32 public immutable MINT_DATA_TYPEHASH;

    /**
     * @notice Ensure only tokens implementing IERC721SeaDrop can
     *         call the update methods.
     */
    modifier onlyIERC721SeaDrop() virtual {
        if (
            !IERC165(msg.sender).supportsInterface(
                type(IERC721SeaDrop).interfaceId
            )
        ) {
            revert OnlyIERC721SeaDrop(msg.sender);
        }
        _;
    }

    /**
     * @notice Constructor for the contract deployment.
     */
    constructor() {
        DOMAIN_SEPARATOR = keccak256(
            abi.encode(
                keccak256(
                    "EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"
                ),
                keccak256(bytes("SeaDrop")),
                keccak256(bytes("1")),
                block.chainid,
                address(this)
            )
        );

        MINT_DATA_TYPEHASH = keccak256(
            "MintParams(address minter, uint256 mintPrice, uint256 maxTotalMintableByWallet, uint256 startTime, uint256 endTime, uint256 dropStageIndex, uint256 feeBps, bool restrictFeeRecipients)"
        );
    }

    /**
     * @notice Mint a public drop.
     *
     * @param nftContract   The nft contract to mint.
     * @param feeRecipient  The fee recipient.
     * @param minter        The mint recipient.
     * @param quantity      The number of tokens to mint.
     */
    function mintPublic(
        address nftContract,
        address feeRecipient,
        address minter,
        uint256 quantity
    ) external payable override {
        // Get the public drop data.
        PublicDrop memory publicDrop = _publicDrops[nftContract];

        // Ensure that the drop has started.
        if (block.timestamp < publicDrop.startTime) {
            revert NotActive(
                block.timestamp,
                publicDrop.startTime,
                type(uint64).max
            );
        }

        // Validate payment is correct for number minted.
        _checkCorrectPayment(quantity, publicDrop.mintPrice);

        // Check that the minter is allowed to mint the desired quantity.
        _checkMintQuantity(
            nftContract,
            minter,
            quantity,
            publicDrop.maxMintsPerWallet,
            0
        );

        // Check that the fee recipient is allowed if restricted.
        _checkFeeRecipientIsAllowed(
            nftContract,
            feeRecipient,
            publicDrop.restrictFeeRecipients
        );

        // Split the payout, mint the token, emit an event.
        _payAndMint(
            nftContract,
            minter,
            quantity,
            publicDrop.mintPrice,
            0,
            publicDrop.feeBps,
            feeRecipient
        );
    }

    /**
     * @notice Mint from an allow list.
     *
     * @param nftContract  The nft contract to mint.
     * @param feeRecipient The fee recipient.
     * @param minter       The mint recipient.
     * @param quantity     The number of tokens to mint.
     * @param mintParams   The mint parameters.
     * @param proof        The proof for the leaf of the allow list.
     */
    function mintAllowList(
        address nftContract,
        address feeRecipient,
        address minter,
        uint256 quantity,
        MintParams calldata mintParams,
        bytes32[] calldata proof
    ) external payable override {
        // Check that the drop stage is active.
        _checkActive(mintParams.startTime, mintParams.endTime);

        // Validate payment is correct for number minted.
        _checkCorrectPayment(quantity, mintParams.mintPrice);

        // Check that the minter is allowed to mint the desired quantity.
        _checkMintQuantity(
            nftContract,
            minter,
            quantity,
            mintParams.maxTotalMintableByWallet,
            mintParams.maxTokenSupplyForStage
        );

        // Check that the fee recipient is allowed if restricted.
        _checkFeeRecipientIsAllowed(
            nftContract,
            feeRecipient,
            mintParams.restrictFeeRecipients
        );

        // Verify the proof.
        if (
            !MerkleProofLib.verify(
                proof,
                _allowListMerkleRoots[nftContract],
                keccak256(abi.encode(minter, mintParams))
            )
        ) {
            revert InvalidProof();
        }

        // Split the payout, mint the token, emit an event.
        _payAndMint(
            nftContract,
            minter,
            quantity,
            mintParams.mintPrice,
            mintParams.dropStageIndex,
            mintParams.feeBps,
            feeRecipient
        );
    }

    /**
     * @notice Mint with a server side signature.
     *
     * @param nftContract   The nft contract to mint.
     * @param feeRecipient  The fee recipient.
     * @param minter        The mint recipient.
     * @param quantity      The number of tokens to mint.
     * @param mintParams    The mint parameters.
     * @param signature     The server side signature, must be an allowed
     *                      signer.
     */
    function mintSigned(
        address nftContract,
        address feeRecipient,
        address minter,
        uint256 quantity,
        MintParams calldata mintParams,
        bytes calldata signature
    ) external payable override {
        // Check that the drop stage is active.
        _checkActive(mintParams.startTime, mintParams.endTime);

        // Validate payment is correct for number minted.
        _checkCorrectPayment(quantity, mintParams.mintPrice);

        // Check that the minter is allowed to mint the desired quantity.
        _checkMintQuantity(
            nftContract,
            minter,
            quantity,
            mintParams.maxTotalMintableByWallet,
            mintParams.maxTokenSupplyForStage
        );

        // Check that the fee recipient is allowed if restricted.
        _checkFeeRecipientIsAllowed(
            nftContract,
            feeRecipient,
            mintParams.restrictFeeRecipients
        );

        // Verify EIP-712 signature by recreating the data structure
        // that we signed on the client side, and then using that to recover
        // the address that signed the signature for this data.
        bytes32 digest = keccak256(
            abi.encodePacked(
                bytes2(0x1901),
                DOMAIN_SEPARATOR,
                keccak256(abi.encode(MINT_DATA_TYPEHASH, minter, mintParams))
            )
        );

        // Use the recover method to see what address was used to create
        // the signature on this data.
        // Note that if the digest doesn't exactly match what was signed we'll
        // get a random recovered address.
        address recoveredAddress = digest.recover(signature);
        if (!_signers[nftContract][recoveredAddress]) {
            revert InvalidSignature(recoveredAddress);
        }

        // Split the payout, mint the token, emit an event.
        _payAndMint(
            nftContract,
            minter,
            quantity,
            mintParams.mintPrice,
            mintParams.dropStageIndex,
            mintParams.feeBps,
            feeRecipient
        );
    }

    /**
     * @notice Mint as an allowed token holder.
     *         This will mark the token id as reedemed and will revert if the
     *         same token id is attempted to be redeemed twice.
     *
     * @param nftContract          The nft contract to mint.
     * @param feeRecipient         The fee recipient.
     * @param minter               The mint recipient.
     * @param tokenGatedMintParams The token gated mint params.
     */
    function mintAllowedTokenHolder(
        address nftContract,
        address feeRecipient,
        address minter,
        TokenGatedMintParams[] calldata tokenGatedMintParams
    ) external payable override {
        // Track the total mint cost to compare against value sent with tx.
        uint256 totalCost;

        // Iterate through each allowedNftToken.
        for (uint256 i = 0; i < tokenGatedMintParams.length; ) {
            // Set the mintParams to a variable.
            TokenGatedMintParams calldata mintParams = tokenGatedMintParams[i];

            // Set the dropStage to a variable.
            TokenGatedDropStage storage dropStage = _tokenGatedDrops[
                nftContract
            ][mintParams.allowedNftToken];

            // Validate that the dropStage is active.
            _checkActive(dropStage.startTime, dropStage.endTime);

            // Check that the fee recipient is allowed if restricted.
            _checkFeeRecipientIsAllowed(
                nftContract,
                feeRecipient,
                dropStage.restrictFeeRecipients
            );

            // Put the mint quantity on the stack for more efficient access.
            uint256 mintQuantity = mintParams.allowedNftTokenIds.length;

            // Add to total cost.
            totalCost += mintQuantity * dropStage.mintPrice;

            // Check that the minter is allowed to mint the desired quantity.
            _checkMintQuantity(
                nftContract,
                minter,
                mintQuantity,
                dropStage.maxTotalMintableByWallet,
                dropStage.maxTokenSupplyForStage
            );

            // Iterate through each allowedNftTokenId
            // to ensure it is not already reedemed.
            for (uint256 j = 0; j < mintQuantity; ) {
                // Put the tokenId on the stack.
                uint256 tokenId = mintParams.allowedNftTokenIds[j];

                // Check that the sender is the owner of the allowedNftTokenId.
                if (
                    IERC721(mintParams.allowedNftToken).ownerOf(tokenId) !=
                    minter
                ) {
                    revert TokenGatedNotTokenOwner(
                        nftContract,
                        mintParams.allowedNftToken,
                        tokenId
                    );
                }

                // Check that the token id has not already been redeemed.
                bool redeemed = _tokenGatedRedeemed[nftContract][
                    mintParams.allowedNftToken
                ][tokenId];

                if (redeemed == true) {
                    revert TokenGatedTokenIdAlreadyRedeemed(
                        nftContract,
                        mintParams.allowedNftToken,
                        tokenId
                    );
                }

                // Mark the token id as reedemed.
                redeemed = true;

                unchecked {
                    ++j;
                }
            }

            // Split the payout, mint the token, emit an event.
            _payAndMint(
                nftContract,
                minter,
                mintQuantity,
                dropStage.mintPrice,
                dropStage.dropStageIndex,
                dropStage.feeBps,
                feeRecipient
            );

            unchecked {
                ++i;
            }
        }

        // Validate correct payment.
        if (msg.value != totalCost) {
            revert IncorrectPayment(msg.value, totalCost);
        }
    }

    /**
     * @notice Check that the drop stage is active.
     *
     * @param startTime The drop stage start time.
     * @param endTime   The drop stage end time.
     */
    function _checkActive(uint256 startTime, uint256 endTime) internal view {
        if (block.timestamp < startTime || block.timestamp > endTime) {
            // Revert if the drop stage is not active.
            revert NotActive(block.timestamp, startTime, endTime);
        }
    }

    /**
     * @notice Check that the fee recipient is allowed.
     *
     * @param nftContract           The nft contract.
     * @param feeRecipient          The fee recipient.
     * @param restrictFeeRecipients If the fee recipients are restricted.
     */
    function _checkFeeRecipientIsAllowed(
        address nftContract,
        address feeRecipient,
        bool restrictFeeRecipients
    ) internal view {
        // Ensure the fee recipient is not the zero address.
        if (feeRecipient == address(0)) {
            revert FeeRecipientCannotBeZeroAddress();
        }

        // Revert if the fee recipient is restricted and not allowed.
        if (
            restrictFeeRecipients == true &&
            _allowedFeeRecipients[nftContract][feeRecipient] == false
        ) {
            revert FeeRecipientNotAllowed();
        }
    }

    /**
     * @notice Check that the wallet is allowed to mint the desired quantity.
     *
     * @param nftContract       The nft contract.
     * @param minter            The mint recipient.
     * @param quantity          The number of tokens to mint.
     * @param maxMintsPerWallet The allowed max mints per wallet.
     * @param nftContract       The nft contract.
     */
    function _checkMintQuantity(
        address nftContract,
        address minter,
        uint256 quantity,
        uint256 maxMintsPerWallet,
        uint256 maxTokenSupplyForStage
    ) internal view {
        // Get the mint stats.
        (
            uint256 minterNumMinted,
            uint256 currentTotalSupply,
            uint256 maxSupply
        ) = IERC721SeaDrop(nftContract).getMintStats(minter);

        // Ensure mint quantity doesn't exceed maxMintsPerWallet.
        if (quantity + minterNumMinted > maxMintsPerWallet) {
            revert MintQuantityExceedsMaxMintedPerWallet(
                quantity + minterNumMinted,
                maxMintsPerWallet
            );
        }

        // Ensure mint quantity doesn't exceed maxSupply.
        if (quantity + currentTotalSupply > maxSupply) {
            revert MintQuantityExceedsMaxSupply(
                quantity + currentTotalSupply,
                maxSupply
            );
        }

        // Ensure mint quantity doesn't exceed maxTokenSupplyForStage
        // when provided.
        if (maxTokenSupplyForStage != 0) {
            if (quantity + currentTotalSupply > maxTokenSupplyForStage) {
                revert MintQuantityExceedsMaxTokenSupplyForStage(
                    quantity + currentTotalSupply,
                    maxTokenSupplyForStage
                );
            }
        }
    }

    /**
     * @notice Revert if the payment is not the quantity times the mint price.
     *
     * @param quantity  The number of tokens to mint.
     * @param mintPrice The mint price per token.
     */
    function _checkCorrectPayment(uint256 quantity, uint256 mintPrice)
        internal
        view
    {
        // Revert if the tx's value doesn't match the total cost.
        if (msg.value != quantity * mintPrice) {
            revert IncorrectPayment(msg.value, quantity * mintPrice);
        }
    }

    /**
     * @notice Split the payment payout for the creator and fee recipient.
     *
     * @param nftContract  The nft contract.
     * @param feeRecipient The fee recipient.
     * @param feeBps       The fee basis points.
     */
    function _splitPayout(
        address nftContract,
        address feeRecipient,
        uint256 feeBps
    ) internal {
        // Get the creator payout address.
        address creatorPayoutAddress = _creatorPayoutAddresses[nftContract];

        // Ensure the creator payout address is not the zero address.
        if (creatorPayoutAddress == address(0)) {
            revert CreatorPayoutAddressCannotBeZeroAddress();
        }

        // Get the fee amount.
        uint256 feeAmount = (msg.value * feeBps) / 10_000;

        // Get the creator payout amount.
        uint256 payoutAmount = msg.value - feeAmount;

        // Transfer to the fee recipient.
        SafeTransferLib.safeTransferETH(feeRecipient, feeAmount);

        // Transfer  to the creator.
        SafeTransferLib.safeTransferETH(creatorPayoutAddress, payoutAmount);
    }

    /**
     * @notice Splits the payment, mints a number of tokens,
     *         and emits an event.
     *
     * @param nftContract    The nft contract.
     * @param minter         The mint recipient.
     * @param quantity       The number of tokens to mint.
     * @param mintPrice      The mint price per token.
     * @param dropStageIndex The drop stage index.
     * @param feeBps         The fee basis points.
     * @param feeRecipient   The fee recipient.
     */
    function _payAndMint(
        address nftContract,
        address minter,
        uint256 quantity,
        uint256 mintPrice,
        uint256 dropStageIndex,
        uint256 feeBps,
        address feeRecipient
    ) internal {
        // Split the payment between the creator and fee recipient.
        _splitPayout(nftContract, feeRecipient, feeBps);

        // Mint the token(s).
        IERC721SeaDrop(nftContract).mintSeaDrop(minter, quantity);

        // Emit an event for the mint.
        emit SeaDropMint(
            nftContract,
            minter,
            feeRecipient,
            msg.sender,
            quantity,
            mintPrice,
            feeBps,
            dropStageIndex
        );
    }

    /**
     * @notice Returns the drop URI for the nft contract.
     *
     * @param nftContract The nft contract.
     */
    function getDropURI(address nftContract)
        external
        view
        returns (string memory)
    {
        return _dropURIs[nftContract];
    }

    /**
     * @notice Returns the public drop data for the nft contract.
     *
     * @param nftContract The nft contract.
     */
    function getPublicDrop(address nftContract)
        external
        view
        returns (PublicDrop memory)
    {
        return _publicDrops[nftContract];
    }

    /**
     * @notice Returns the creator payout address for the nft contract.
     *
     * @param nftContract The nft contract.
     */
    function getCreatorPayoutAddress(address nftContract)
        external
        view
        returns (address)
    {
        return _creatorPayoutAddresses[nftContract];
    }

    /**
     * @notice Returns the allow list merkle root for the nft contract.
     *
     * @param nftContract The nft contract.
     */
    function getAllowListMerkleRoot(address nftContract)
        external
        view
        returns (bytes32)
    {
        return _allowListMerkleRoots[nftContract];
    }

    /**
     * @notice Returns if the specified fee recipient is allowed
     *         for the nft contract.
     *
     * @param nftContract The nft contract.
     */
    function getFeeRecipientIsAllowed(address nftContract, address feeRecipient)
        external
        view
        returns (bool)
    {
        return _allowedFeeRecipients[nftContract][feeRecipient];
    }

    /**
     * @notice Returns the server side signers for the nft contract.
     *
     * @param nftContract The nft contract.
     */
    function getSigners(address nftContract)
        external
        view
        returns (address[] memory)
    {
        return _enumeratedSigners[nftContract];
    }

    /**
     * @notice Returns the allowed token gated drop tokens for the nft contract.
     *
     * @param nftContract The nft contract.
     */
    function getTokenGatedAllowedTokens(address nftContract)
        external
        view
        returns (address[] memory)
    {
        return _enumeratedTokenGatedTokens[nftContract];
    }

    /**
     * @notice Returns the token gated drop data for the nft contract
     *         and token gated nft.
     *
     * @param nftContract     The nft contract.
     * @param allowedNftToken The token gated nft token.
     */
    function getTokenGatedDrop(address nftContract, address allowedNftToken)
        external
        view
        returns (TokenGatedDropStage memory)
    {
        return _tokenGatedDrops[nftContract][allowedNftToken];
    }

    /**
     * @notice Updates the drop URI and emits an event.
     *
     * @param newDropURI The new drop URI.
     */
    function updateDropURI(string calldata newDropURI)
        external
        onlyIERC721SeaDrop
    {
        // Set the new drop URI.
        _dropURIs[msg.sender] = newDropURI;

        // Emit an event with the update.
        emit DropURIUpdated(msg.sender, newDropURI);
    }

    /**
     * @notice Updates the public drop for the nft contract and emits an event.
     *
     * @param publicDrop The public drop data.
     */
    function updatePublicDrop(PublicDrop calldata publicDrop)
        external
        override
        onlyIERC721SeaDrop
    {
        // Set the public drop data.
        _publicDrops[msg.sender] = publicDrop;

        // Emit an event with the update.
        emit PublicDropUpdated(msg.sender, publicDrop);
    }

    /**
     * @notice Updates the allow list merkle root for the nft contract
     *         and emits an event.
     *
     * @param allowListData The allow list data.
     */
    function updateAllowList(AllowListData calldata allowListData)
        external
        override
        onlyIERC721SeaDrop
    {
        // Track the previous root.
        bytes32 prevRoot = _allowListMerkleRoots[msg.sender];

        // Update the merkle root.
        _allowListMerkleRoots[msg.sender] = allowListData.merkleRoot;

        // Emit an event with the update.
        emit AllowListUpdated(
            msg.sender,
            prevRoot,
            allowListData.merkleRoot,
            allowListData.publicKeyURIs,
            allowListData.allowListURI
        );
    }

    /**
     * @notice Updates the token gated drop stage for the nft contract
     *         and emits an event.
     *
     * @param nftContract     The nft contract.
     * @param allowedNftToken The token gated nft token.
     * @param dropStage       The token gated drop stage data.
     */
    function updateTokenGatedDrop(
        address nftContract,
        address allowedNftToken,
        TokenGatedDropStage calldata dropStage
    ) external override onlyIERC721SeaDrop {
        // Set the drop stage.
        _tokenGatedDrops[nftContract][allowedNftToken] = dropStage;

        // If the maxTotalMintableByWallet is greater than zero
        // then we are setting an active drop stage.
        if (dropStage.maxTotalMintableByWallet > 0) {
            // Add allowedNftToken to enumerated list if not present.
            bool allowedNftTokenExistsInEnumeration = false;

            // Iterate through enumerated token gated tokens for nft contract.
            for (
                uint256 i = 0;
                i < _enumeratedTokenGatedTokens[nftContract].length;

            ) {
                if (
                    _enumeratedTokenGatedTokens[nftContract][i] ==
                    allowedNftToken
                ) {
                    // Set the bool to true if found.
                    allowedNftTokenExistsInEnumeration = true;
                }
                unchecked {
                    ++i;
                }
            }

            // Add allowedNftToken to enumerated list if not present.
            if (allowedNftTokenExistsInEnumeration == false) {
                _enumeratedTokenGatedTokens[nftContract].push(allowedNftToken);
            }
        }

        // Emit an event with the update.
        emit TokenGatedDropStageUpdated(
            nftContract,
            allowedNftToken,
            dropStage
        );
    }

    /**
     * @notice Updates the creator payout address and emits an event.
     *
     * @param _payoutAddress The creator payout address.
     */
    function updateCreatorPayoutAddress(address _payoutAddress)
        external
        onlyIERC721SeaDrop
    {
        // Set the creator payout address.
        _creatorPayoutAddresses[msg.sender] = _payoutAddress;

        // Emit an event with the update.
        emit CreatorPayoutAddressUpdated(msg.sender, _payoutAddress);
    }

    /**
     * @notice Updates the allowed fee recipient and emits an event.
     *
     * @param feeRecipient The fee recipient.
     * @param allowed      If the fee recipient is allowed.
     */
    function updateAllowedFeeRecipient(address feeRecipient, bool allowed)
        external
        onlyIERC721SeaDrop
    {
        // Set the allowed fee recipient.
        _allowedFeeRecipients[msg.sender][feeRecipient] = allowed;

        // Emit an event with the update.
        emit AllowedFeeRecipientUpdated(msg.sender, feeRecipient, allowed);
    }

    /**
     * @notice Updates the allowed server side signers and emits an event.
     *
     * @param newSigners The new list of signers.
     */
    function updateSigners(address[] calldata newSigners)
        external
        onlyIERC721SeaDrop
    {
        // Track the enumerated storage.
        address[] storage enumeratedStorage = _enumeratedSigners[msg.sender];

        // Track the old signers.
        address[] memory oldSigners = enumeratedStorage;

        // Delete old enumeration.
        delete _enumeratedSigners[msg.sender];

        // Add new enumeration.
        for (uint256 i = 0; i < newSigners.length; ) {
            enumeratedStorage.push(newSigners[i]);
            unchecked {
                ++i;
            }
        }

        // Create a mapping of the signers.
        mapping(address => bool) storage signersMap = _signers[msg.sender];

        // Delete old signers.
        for (uint256 i = 0; i < oldSigners.length; ) {
            signersMap[oldSigners[i]] = false;
            unchecked {
                ++i;
            }
        }
        // Add new signers.
        for (uint256 i = 0; i < newSigners.length; ) {
            signersMap[newSigners[i]] = true;
            unchecked {
                ++i;
            }
        }

        // Emit an event with the update.
        emit SignersUpdated(msg.sender, oldSigners, newSigners);
    }
}

File 2 of 13 : ISeaDrop.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.11;

import {
    AllowListData,
    MintParams,
    PublicDrop,
    TokenGatedDropStage,
    TokenGatedMintParams
} from "../lib/SeaDropStructs.sol";

import { SeaDropErrorsAndEvents } from "../lib/SeaDropErrorsAndEvents.sol";

interface ISeaDrop is SeaDropErrorsAndEvents {
    /**
     * @notice Mint a public drop.
     *
     * @param nftContract   The nft contract to mint.
     * @param feeRecipient  The fee recipient.
     * @param minter        The mint recipient.
     * @param quantity      The number of tokens to mint.
     */
    function mintPublic(
        address nftContract,
        address feeRecipient,
        address minter,
        uint256 quantity
    ) external payable;

    /**
     * @notice Mint from an allow list.
     *
     * @param nftContract   The nft contract to mint.
     * @param feeRecipient  The fee recipient.
     * @param minter        The mint recipient.
     * @param quantity      The number of tokens to mint.
     * @param mintParams    The mint parameters.
     * @param proof         The proof for the leaf of the allow list.
     */
    function mintAllowList(
        address nftContract,
        address feeRecipient,
        address minter,
        uint256 quantity,
        MintParams calldata mintParams,
        bytes32[] calldata proof
    ) external payable;

    /**
     * @notice Mint with a server side signature.
     *
     * @param nftContract   The nft contract to mint.
     * @param feeRecipient  The fee recipient.
     * @param minter        The mint recipient.
     * @param quantity      The number of tokens to mint.
     * @param mintParams    The mint parameters.
     * @param signature     The server side signature, must be an allowed signer.
     */
    function mintSigned(
        address nftContract,
        address feeRecipient,
        address minter,
        uint256 quantity,
        MintParams calldata mintParams,
        bytes calldata signature
    ) external payable;

    /**
     * @notice Mint as an allowed token holder.
     *         This will mark the token id as reedemed and will revert if the
     *         same token id is attempted to be redeemed twice.
     *
     * @param nftContract          The nft contract to mint.
     * @param feeRecipient         The fee recipient.
     * @param minter               The mint recipient.
     * @param tokenGatedMintParams The token gated mint params.
     */
    function mintAllowedTokenHolder(
        address nftContract,
        address feeRecipient,
        address minter,
        TokenGatedMintParams[] calldata tokenGatedMintParams
    ) external payable;

    /**
     * @notice Returns the drop URI for the nft contract.
     *
     * @param nftContract The nft contract.
     */
    function getDropURI(address nftContract)
        external
        view
        returns (string memory);

    /**
     * @notice Returns the public drop data for the nft contract.
     *
     * @param nftContract The nft contract.
     */
    function getPublicDrop(address nftContract)
        external
        view
        returns (PublicDrop memory);

    /**
     * @notice Returns the creator payout address for the nft contract.
     *
     * @param nftContract The nft contract.
     */
    function getCreatorPayoutAddress(address nftContract)
        external
        view
        returns (address);

    /**
     * @notice Returns the allow list merkle root for the nft contract.
     *
     * @param nftContract The nft contract.
     */
    function getAllowListMerkleRoot(address nftContract)
        external
        view
        returns (bytes32);

    /**
     * @notice Returns if the specified fee recipient is allowed
     *         for the nft contract.
     *
     * @param nftContract The nft contract.
     */
    function getFeeRecipientIsAllowed(address nftContract, address feeRecipient)
        external
        view
        returns (bool);

    /**
     * @notice Returns the server side signers for the nft contract.
     *
     * @param nftContract The nft contract.
     */
    function getSigners(address nftContract)
        external
        view
        returns (address[] memory);

    /**
     * @notice Returns the allowed token gated drop tokens for the nft contract.
     *
     * @param nftContract The nft contract.
     */
    function getTokenGatedAllowedTokens(address nftContract)
        external
        view
        returns (address[] memory);

    /**
     * @notice Returns the token gated drop data for the nft contract
     *         and token gated nft.
     *
     * @param nftContract     The nft contract.
     * @param allowedNftToken The token gated nft token.
     */
    function getTokenGatedDrop(address nftContract, address allowedNftToken)
        external
        view
        returns (TokenGatedDropStage memory);

    /**
     * The following methods assume msg.sender is an nft contract
     * and its ERC165 interface id matches IERC721SeaDrop.
     */

    /**
     * @notice Updates the drop URI and emits an event.
     *
     * @param dropURI The new drop URI.
     */
    function updateDropURI(string calldata dropURI) external;

    /**
     * @notice Updates the public drop data for the nft contract
     *         and emits an event.
     *
     * @param publicDrop The public drop data.
     */
    function updatePublicDrop(PublicDrop calldata publicDrop) external;

    /**
     * @notice Updates the allow list merkle root for the nft contract
     *         and emits an event.
     *
     * @param allowListData The allow list data.
     */
    function updateAllowList(AllowListData calldata allowListData) external;

    /**
     * @notice Updates the token gated drop stage for the nft contract
     *         and emits an event.
     *
     * @param nftContract     The nft contract.
     * @param allowedNftToken The token gated nft token.
     * @param dropStage       The token gated drop stage data.
     */
    function updateTokenGatedDrop(
        address nftContract,
        address allowedNftToken,
        TokenGatedDropStage calldata dropStage
    ) external;

    /**
     * @notice Updates the creator payout address and emits an event.
     *
     * @param payoutAddress The creator payout address.
     */
    function updateCreatorPayoutAddress(address payoutAddress) external;

    /**
     * @notice Updates the allowed fee recipient and emits an event.
     *
     * @param feeRecipient The fee recipient.
     * @param allowed      If the fee recipient is allowed.
     */
    function updateAllowedFeeRecipient(address feeRecipient, bool allowed)
        external;

    /**
     * @notice Updates the allowed server side signers and emits an event.
     *
     * @param newSigners The new list of signers.
     */
    function updateSigners(address[] calldata newSigners) external;
}

File 3 of 13 : SeaDropStructs.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.11;

/**
 * @notice A struct defining public drop data.
 *         Designed to fit efficiently in one storage slot.
 * 
 * @param mintPrice             The mint price per token.
 *                              (Up to 1.2m of native token, e.g.: ETH, MATIC)
 * @param startTime             The start time, ensure this is not zero.
 * @param maxMintsPerWallet     Maximum total number of mints a user is
 *                              allowed.
 * @param feeBps                Fee out of 10,000 basis points to be collected.
 * @param restrictFeeRecipients If false, allow any fee recipient;
 *                              if true, check fee recipient is allowed.
 */
struct PublicDrop {
    uint80 mintPrice; // 80/256 bits
    uint64 startTime; // 144/256 bits
    uint40 maxMintsPerWallet; // 184/256 bits
    uint16 feeBps; // 200/256 bits
    bool restrictFeeRecipients; // 208/256 bits
}

/**
 * @notice Stages from dropURI are strictly for front-end consumption,
 *         and are trusted to match information in the
 *         PublicDrop, AllowLists or TokenGatedDropStage
 *         (we may want to surface discrepancies on the front-end)
 */

/**
 * @notice A struct defining token gated drop stage data.
 *         Designed to fit efficiently in one storage slot.
 * 
 * @param mintPrice                The mint price per token.
 *                                 (Up to 1.2m of native token, e.g.: ETH, MATIC)
 * @param maxTotalMintableByWallet The limit of items this wallet can mint.
 * @param startTime                The start time, ensure this is not zero.
 * @param endTime                  The end time, ensure this is not zero.
 * @param dropStageIndex           The drop stage index to emit with the event
 *                                 for analytical purposes. This should be 
 *                                 non-zero since the public mint emits
 *                                 with index zero.
 * @param maxTokenSupplyForStage   The limit of token supply this stage can
 *                                 mint within.
 * @param feeBps                   Fee out of 10,000 basis points to be
 *                                 collected.
 * @param restrictFeeRecipients    If false, allow any fee recipient;
 *                                 if true, check fee recipient is allowed.
 */
struct TokenGatedDropStage {
    uint80 mintPrice; // 80/256 bits
    uint16 maxTotalMintableByWallet;
    uint48 startTime;
    uint48 endTime;
    uint8 dropStageIndex; // non-zero
    uint40 maxTokenSupplyForStage;
    uint16 feeBps;
    bool restrictFeeRecipients;
}

/**
 * @notice A struct defining mint params for an allow list.
 *         An allow list leaf will be composed of `msg.sender` and
 *         the following params.
 * 
 *         Note: Since feeBps is encoded in the leaf, backend should ensure
 *         that feeBps is acceptable before generating a proof.
 * 
 * @param mintPrice                The mint price per token.
 * @param maxTotalMintableByWallet The limit of items this wallet can mint.
 * @param startTime                The start time, ensure this is not zero.
 * @param endTime                  The end time, ensure this is not zero.
 * @param dropStageIndex           The drop stage index to emit with the event
 *                                 for analytical purposes. This should be
 *                                 non-zero since the public mint emits with
 *                                 index zero.
 * @param maxTokenSupplyForStage   The limit of token supply this stage can
 *                                 mint within.
 * @param feeBps                   Fee out of 10,000 basis points to be
 *                                 collected.
 * @param restrictFeeRecipients    If false, allow any fee recipient;
 *                                 if true, check fee recipient is allowed.
 */
struct MintParams {
    uint256 mintPrice; 
    uint256 maxTotalMintableByWallet;
    uint256 startTime;
    uint256 endTime;
    uint256 dropStageIndex; // non-zero
    uint256 maxTokenSupplyForStage;
    uint256 feeBps;
    bool restrictFeeRecipients;
}

/**
 * @notice A struct defining token gated mint params.
 * 
 * @param allowedNftToken    The allowed nft token contract address.
 * @param allowedNftTokenIds The token ids to redeem.
 */
struct TokenGatedMintParams {
    address allowedNftToken;
    uint256[] allowedNftTokenIds;
}

/**
 * @notice A struct defining allow list data (for minting an allow list).
 * 
 * @param merkleRoot    The merkle root for the allow list.
 * @param publicKeyURIs If the allowListURI is encrypted, a list of URIs
 *                      pointing to the public keys. Empty if unencrypted.
 * @param allowListURI  The URI for the allow list.
 */
struct AllowListData {
    bytes32 merkleRoot;
    string[] publicKeyURIs;
    string allowListURI;
}

File 4 of 13 : IERC721SeaDrop.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.11;

import {
    IERC721ContractMetadata
} from "../interfaces/IERC721ContractMetadata.sol";

import {
    AllowListData,
    PublicDrop,
    TokenGatedDropStage
} from "../lib/SeaDropStructs.sol";

import {
    IERC165
} from "openzeppelin-contracts/contracts/utils/introspection/IERC165.sol";

interface IERC721SeaDrop is IERC721ContractMetadata, IERC165 {
    /**
     * @dev Revert with an error if a contract other than an allowed
     *      SeaDrop address calls an update function.
     */
    error OnlySeaDrop();

    /**
     * @dev Emit an event when allowed SeaDrop contracts are updated.
     */
    event AllowedSeaDropUpdated(address[] allowedSeaDrop);

    /**
     * @notice Update the allowed SeaDrop contracts.
     *
     * @param allowedSeaDrop The allowed SeaDrop addresses.
     */
    function updateAllowedSeaDrop(address[] calldata allowedSeaDrop) external;

    /**
     * @notice Mint tokens, restricted to the SeaDrop contract.
     *
     * @param minter   The address to mint to.
     * @param quantity The number of tokens to mint.
     */
    function mintSeaDrop(address minter, uint256 quantity) external payable;

    /**
     * @notice Returns a set of mint stats for the address.
     *         This assists SeaDrop in enforcing maxSupply,
     *         maxMintsPerWallet, and maxTokenSupplyForStage checks.
     *
     * @param minter The minter address.
     */
    function getMintStats(address minter)
        external
        view
        returns (
            uint256 minterNumMinted,
            uint256 currentTotalSupply,
            uint256 maxSupply
        );

    /**
     * @notice Update public drop data for this nft contract on SeaDrop.
     *         Use `updatePublicDropFee` to update the fee recipient or feeBps.
     *
     * @param seaDropImpl The allowed SeaDrop contract.
     * @param publicDrop  The public drop data.
     */
    function updatePublicDrop(
        address seaDropImpl,
        PublicDrop calldata publicDrop
    ) external;

    /**
     * @notice Update allow list data for this nft contract on SeaDrop.
     *
     * @param seaDropImpl   The allowed SeaDrop contract.
     * @param allowListData The allow list data.
     */
    function updateAllowList(
        address seaDropImpl,
        AllowListData calldata allowListData
    ) external;

    /**
     * @notice Update token gated drop stage data for this nft contract
     *         on SeaDrop.
     *
     * @param seaDropImpl     The allowed SeaDrop contract.
     * @param allowedNftToken The allowed nft token.
     * @param dropStage       The token gated drop stage data.
     */
    function updateTokenGatedDrop(
        address seaDropImpl,
        address allowedNftToken,
        TokenGatedDropStage calldata dropStage
    ) external;

    /**
     * @notice Update the drop URI for this nft contract on SeaDrop.
     *
     * @param seaDropImpl The allowed SeaDrop contract.
     * @param dropURI     The new drop URI.
     */
    function updateDropURI(address seaDropImpl, string calldata dropURI)
        external;

    /**
     * @notice Update the creator payout address for this nft contract on SeaDrop.
     *         Only the owner can set the creator payout address.
     *
     * @param seaDropImpl   The allowed SeaDrop contract.
     * @param payoutAddress The new payout address.
     */
    function updateCreatorPayoutAddress(
        address seaDropImpl,
        address payoutAddress
    ) external;

    /**
     * @notice Update the allowed fee recipient for this nft contract
     *         on SeaDrop.
     *         Only the administrator can set the allowed fee recipient.
     *
     * @param seaDropImpl  The allowed SeaDrop contract.
     * @param feeRecipient The new fee recipient.
     */
    function updateAllowedFeeRecipient(
        address seaDropImpl,
        address feeRecipient,
        bool allowed
    ) external;

    /**
     * @notice Update the server side signers for this nft contract
     *         on SeaDrop.
     *         Only the owner or administrator can update the signers.
     *
     * @param seaDropImpl The allowed SeaDrop contract.
     * @param newSigners  The new signers.
     */
    function updateSigners(address seaDropImpl, address[] calldata newSigners)
        external;
}

File 5 of 13 : SafeTransferLib.sol
// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity >=0.8.0;

import {ERC20} from "../tokens/ERC20.sol";

/// @notice Safe ETH and ERC20 transfer library that gracefully handles missing return values.
/// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/SafeTransferLib.sol)
/// @dev Use with caution! Some functions in this library knowingly create dirty bits at the destination of the free memory pointer.
/// @dev Note that none of the functions in this library check that a token has code at all! That responsibility is delegated to the caller.
library SafeTransferLib {
    /*//////////////////////////////////////////////////////////////
                             ETH OPERATIONS
    //////////////////////////////////////////////////////////////*/

    function safeTransferETH(address to, uint256 amount) internal {
        bool success;

        assembly {
            // Transfer the ETH and store if it succeeded or not.
            success := call(gas(), to, amount, 0, 0, 0, 0)
        }

        require(success, "ETH_TRANSFER_FAILED");
    }

    /*//////////////////////////////////////////////////////////////
                            ERC20 OPERATIONS
    //////////////////////////////////////////////////////////////*/

    function safeTransferFrom(
        ERC20 token,
        address from,
        address to,
        uint256 amount
    ) internal {
        bool success;

        assembly {
            // Get a pointer to some free memory.
            let freeMemoryPointer := mload(0x40)

            // Write the abi-encoded calldata into memory, beginning with the function selector.
            mstore(freeMemoryPointer, 0x23b872dd00000000000000000000000000000000000000000000000000000000)
            mstore(add(freeMemoryPointer, 4), from) // Append the "from" argument.
            mstore(add(freeMemoryPointer, 36), to) // Append the "to" argument.
            mstore(add(freeMemoryPointer, 68), amount) // Append the "amount" argument.

            success := and(
                // Set success to whether the call reverted, if not we check it either
                // returned exactly 1 (can't just be non-zero data), or had no return data.
                or(and(eq(mload(0), 1), gt(returndatasize(), 31)), iszero(returndatasize())),
                // We use 100 because the length of our calldata totals up like so: 4 + 32 * 3.
                // We use 0 and 32 to copy up to 32 bytes of return data into the scratch space.
                // Counterintuitively, this call must be positioned second to the or() call in the
                // surrounding and() call or else returndatasize() will be zero during the computation.
                call(gas(), token, 0, freeMemoryPointer, 100, 0, 32)
            )
        }

        require(success, "TRANSFER_FROM_FAILED");
    }

    function safeTransfer(
        ERC20 token,
        address to,
        uint256 amount
    ) internal {
        bool success;

        assembly {
            // Get a pointer to some free memory.
            let freeMemoryPointer := mload(0x40)

            // Write the abi-encoded calldata into memory, beginning with the function selector.
            mstore(freeMemoryPointer, 0xa9059cbb00000000000000000000000000000000000000000000000000000000)
            mstore(add(freeMemoryPointer, 4), to) // Append the "to" argument.
            mstore(add(freeMemoryPointer, 36), amount) // Append the "amount" argument.

            success := and(
                // Set success to whether the call reverted, if not we check it either
                // returned exactly 1 (can't just be non-zero data), or had no return data.
                or(and(eq(mload(0), 1), gt(returndatasize(), 31)), iszero(returndatasize())),
                // We use 68 because the length of our calldata totals up like so: 4 + 32 * 2.
                // We use 0 and 32 to copy up to 32 bytes of return data into the scratch space.
                // Counterintuitively, this call must be positioned second to the or() call in the
                // surrounding and() call or else returndatasize() will be zero during the computation.
                call(gas(), token, 0, freeMemoryPointer, 68, 0, 32)
            )
        }

        require(success, "TRANSFER_FAILED");
    }

    function safeApprove(
        ERC20 token,
        address to,
        uint256 amount
    ) internal {
        bool success;

        assembly {
            // Get a pointer to some free memory.
            let freeMemoryPointer := mload(0x40)

            // Write the abi-encoded calldata into memory, beginning with the function selector.
            mstore(freeMemoryPointer, 0x095ea7b300000000000000000000000000000000000000000000000000000000)
            mstore(add(freeMemoryPointer, 4), to) // Append the "to" argument.
            mstore(add(freeMemoryPointer, 36), amount) // Append the "amount" argument.

            success := and(
                // Set success to whether the call reverted, if not we check it either
                // returned exactly 1 (can't just be non-zero data), or had no return data.
                or(and(eq(mload(0), 1), gt(returndatasize(), 31)), iszero(returndatasize())),
                // We use 68 because the length of our calldata totals up like so: 4 + 32 * 2.
                // We use 0 and 32 to copy up to 32 bytes of return data into the scratch space.
                // Counterintuitively, this call must be positioned second to the or() call in the
                // surrounding and() call or else returndatasize() will be zero during the computation.
                call(gas(), token, 0, freeMemoryPointer, 68, 0, 32)
            )
        }

        require(success, "APPROVE_FAILED");
    }
}

File 6 of 13 : MerkleProofLib.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;

/// @notice Gas optimized verification of proof of inclusion for a leaf in a Merkle tree.
/// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/MerkleProofLib.sol)
/// @author Modified from Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/MerkleProofLib.sol)
/// @author Modified from OpenZeppelin (https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/utils/cryptography/MerkleProof.sol)
library MerkleProofLib {
    function verify(
        bytes32[] calldata proof,
        bytes32 root,
        bytes32 leaf
    ) internal pure returns (bool isValid) {
        assembly {
            if proof.length {
                // Left shift by 5 is equivalent to multiplying by 0x20.
                let end := add(proof.offset, shl(5, proof.length))
                // Initialize `offset` to the offset of `proof` in the calldata.
                let offset := proof.offset
                // Iterate over proof elements to compute root hash.
                // prettier-ignore
                for {} 1 {} {
                    // Slot of `leaf` in scratch space.
                    // If the condition is true: 0x20, otherwise: 0x00.
                    let scratch := shl(5, gt(leaf, calldataload(offset)))
                    // Store elements to hash contiguously in scratch space.
                    // Scratch space is 64 bytes (0x00 - 0x3f) and both elements are 32 bytes.
                    mstore(scratch, leaf)
                    mstore(xor(scratch, 0x20), calldataload(offset))
                    // Reuse `leaf` to store the hash to reduce stack operations.
                    leaf := keccak256(0x00, 0x40)
                    offset := add(offset, 0x20)
                    // prettier-ignore
                    if iszero(lt(offset, end)) { break }
                }
            }
            isValid := eq(leaf, root)
        }
    }

    function verifyMultiProof(
        bytes32[] calldata proof,
        bytes32 root,
        bytes32[] calldata leafs,
        bool[] calldata flags
    ) internal pure returns (bool isValid) {
        // Rebuilds the root by consuming and producing values on a queue.
        // The queue starts with the `leafs` array, and goes into a `hashes` array.
        // After the process, the last element on the queue is verified
        // to be equal to the `root`.
        //
        // The `flags` array denotes whether the sibling
        // should be popped from the queue (`flag == true`), or
        // should be popped from the `proof` (`flag == false`).
        assembly {
            // If the number of flags is correct.
            // prettier-ignore
            for {} eq(add(leafs.length, proof.length), add(flags.length, 1)) {} {
                // Left shift by 5 is equivalent to multiplying by 0x20.
                // Compute the end calldata offset of `leafs`.
                let leafsEnd := add(leafs.offset, shl(5, leafs.length))
                // These are the calldata offsets.
                let leafsOffset := leafs.offset
                let flagsOffset := flags.offset
                let proofOffset := proof.offset

                // We can use the free memory space for the queue.
                // We don't need to allocate, since the queue is temporary.
                let hashesFront := mload(0x40)
                let hashesBack := hashesFront
                // This is the end of the memory for the queue.
                let end := add(hashesBack, shl(5, flags.length))

                // For the case where `proof.length + leafs.length == 1`.
                if iszero(flags.length) {
                    // If `proof.length` is zero, `leafs.length` is 1.
                    if iszero(proof.length) {
                        isValid := eq(calldataload(leafsOffset), root)
                        break
                    }
                    // If `leafs.length` is zero, `proof.length` is 1.
                    if iszero(leafs.length) {
                        isValid := eq(calldataload(proofOffset), root)
                        break
                    }
                }

                // prettier-ignore
                for {} 1 {} {
                    let a := 0
                    // Pops a value from the queue into `a`.
                    switch lt(leafsOffset, leafsEnd)
                    case 0 {
                        // Pop from `hashes` if there are no more leafs.
                        a := mload(hashesFront)
                        hashesFront := add(hashesFront, 0x20)
                    }
                    default {
                        // Otherwise, pop from `leafs`.
                        a := calldataload(leafsOffset)
                        leafsOffset := add(leafsOffset, 0x20)
                    }

                    let b := 0
                    // If the flag is false, load the next proof,
                    // else, pops from the queue.
                    switch calldataload(flagsOffset)
                    case 0 {
                        // Loads the next proof.
                        b := calldataload(proofOffset)
                        proofOffset := add(proofOffset, 0x20)
                    }
                    default {
                        // Pops a value from the queue into `a`.
                        switch lt(leafsOffset, leafsEnd)
                        case 0 {
                            // Pop from `hashes` if there are no more leafs.
                            b := mload(hashesFront)
                            hashesFront := add(hashesFront, 0x20)
                        }
                        default {
                            // Otherwise, pop from `leafs`.
                            b := calldataload(leafsOffset)
                            leafsOffset := add(leafsOffset, 0x20)
                        }
                    }
                    // Advance to the next flag offset.
                    flagsOffset := add(flagsOffset, 0x20)

                    // Slot of `a` in scratch space.
                    // If the condition is true: 0x20, otherwise: 0x00.
                    let scratch := shl(5, gt(a, b))
                    // Hash the scratch space and push the result onto the queue.
                    mstore(scratch, a)
                    mstore(xor(scratch, 0x20), b)
                    mstore(hashesBack, keccak256(0x00, 0x40))
                    hashesBack := add(hashesBack, 0x20)
                    // prettier-ignore
                    if iszero(lt(hashesBack, end)) { break }
                }
                // Checks if the last value in the queue is same as the root.
                isValid := eq(mload(sub(hashesBack, 0x20)), root)
                break
            }
        }
    }
}

File 7 of 13 : IERC721.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.0) (token/ERC721/IERC721.sol)

pragma solidity ^0.8.0;

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

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

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

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

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

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

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

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

    /**
     * @dev Transfers `tokenId` token from `from` to `to`.
     *
     * WARNING: 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 8 of 13 : IERC165.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/introspection/IERC165.sol)

pragma solidity ^0.8.0;

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

File 9 of 13 : ECDSA.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.0) (utils/cryptography/ECDSA.sol)

pragma solidity ^0.8.0;

import "../Strings.sol";

/**
 * @dev Elliptic Curve Digital Signature Algorithm (ECDSA) operations.
 *
 * These functions can be used to verify that a message was signed by the holder
 * of the private keys of a given address.
 */
library ECDSA {
    enum RecoverError {
        NoError,
        InvalidSignature,
        InvalidSignatureLength,
        InvalidSignatureS,
        InvalidSignatureV
    }

    function _throwError(RecoverError error) private pure {
        if (error == RecoverError.NoError) {
            return; // no error: do nothing
        } else if (error == RecoverError.InvalidSignature) {
            revert("ECDSA: invalid signature");
        } else if (error == RecoverError.InvalidSignatureLength) {
            revert("ECDSA: invalid signature length");
        } else if (error == RecoverError.InvalidSignatureS) {
            revert("ECDSA: invalid signature 's' value");
        } else if (error == RecoverError.InvalidSignatureV) {
            revert("ECDSA: invalid signature 'v' value");
        }
    }

    /**
     * @dev Returns the address that signed a hashed message (`hash`) with
     * `signature` or error string. This address can then be used for verification purposes.
     *
     * The `ecrecover` EVM opcode allows for malleable (non-unique) signatures:
     * this function rejects them by requiring the `s` value to be in the lower
     * half order, and the `v` value to be either 27 or 28.
     *
     * IMPORTANT: `hash` _must_ be the result of a hash operation for the
     * verification to be secure: it is possible to craft signatures that
     * recover to arbitrary addresses for non-hashed data. A safe way to ensure
     * this is by receiving a hash of the original message (which may otherwise
     * be too long), and then calling {toEthSignedMessageHash} on it.
     *
     * Documentation for signature generation:
     * - with https://web3js.readthedocs.io/en/v1.3.4/web3-eth-accounts.html#sign[Web3.js]
     * - with https://docs.ethers.io/v5/api/signer/#Signer-signMessage[ethers]
     *
     * _Available since v4.3._
     */
    function tryRecover(bytes32 hash, bytes memory signature) internal pure returns (address, RecoverError) {
        // Check the signature length
        // - case 65: r,s,v signature (standard)
        // - case 64: r,vs signature (cf https://eips.ethereum.org/EIPS/eip-2098) _Available since v4.1._
        if (signature.length == 65) {
            bytes32 r;
            bytes32 s;
            uint8 v;
            // ecrecover takes the signature parameters, and the only way to get them
            // currently is to use assembly.
            /// @solidity memory-safe-assembly
            assembly {
                r := mload(add(signature, 0x20))
                s := mload(add(signature, 0x40))
                v := byte(0, mload(add(signature, 0x60)))
            }
            return tryRecover(hash, v, r, s);
        } else if (signature.length == 64) {
            bytes32 r;
            bytes32 vs;
            // ecrecover takes the signature parameters, and the only way to get them
            // currently is to use assembly.
            /// @solidity memory-safe-assembly
            assembly {
                r := mload(add(signature, 0x20))
                vs := mload(add(signature, 0x40))
            }
            return tryRecover(hash, r, vs);
        } else {
            return (address(0), RecoverError.InvalidSignatureLength);
        }
    }

    /**
     * @dev Returns the address that signed a hashed message (`hash`) with
     * `signature`. This address can then be used for verification purposes.
     *
     * The `ecrecover` EVM opcode allows for malleable (non-unique) signatures:
     * this function rejects them by requiring the `s` value to be in the lower
     * half order, and the `v` value to be either 27 or 28.
     *
     * IMPORTANT: `hash` _must_ be the result of a hash operation for the
     * verification to be secure: it is possible to craft signatures that
     * recover to arbitrary addresses for non-hashed data. A safe way to ensure
     * this is by receiving a hash of the original message (which may otherwise
     * be too long), and then calling {toEthSignedMessageHash} on it.
     */
    function recover(bytes32 hash, bytes memory signature) internal pure returns (address) {
        (address recovered, RecoverError error) = tryRecover(hash, signature);
        _throwError(error);
        return recovered;
    }

    /**
     * @dev Overload of {ECDSA-tryRecover} that receives the `r` and `vs` short-signature fields separately.
     *
     * See https://eips.ethereum.org/EIPS/eip-2098[EIP-2098 short signatures]
     *
     * _Available since v4.3._
     */
    function tryRecover(
        bytes32 hash,
        bytes32 r,
        bytes32 vs
    ) internal pure returns (address, RecoverError) {
        bytes32 s = vs & bytes32(0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff);
        uint8 v = uint8((uint256(vs) >> 255) + 27);
        return tryRecover(hash, v, r, s);
    }

    /**
     * @dev Overload of {ECDSA-recover} that receives the `r and `vs` short-signature fields separately.
     *
     * _Available since v4.2._
     */
    function recover(
        bytes32 hash,
        bytes32 r,
        bytes32 vs
    ) internal pure returns (address) {
        (address recovered, RecoverError error) = tryRecover(hash, r, vs);
        _throwError(error);
        return recovered;
    }

    /**
     * @dev Overload of {ECDSA-tryRecover} that receives the `v`,
     * `r` and `s` signature fields separately.
     *
     * _Available since v4.3._
     */
    function tryRecover(
        bytes32 hash,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) internal pure returns (address, RecoverError) {
        // EIP-2 still allows signature malleability for ecrecover(). Remove this possibility and make the signature
        // unique. Appendix F in the Ethereum Yellow paper (https://ethereum.github.io/yellowpaper/paper.pdf), defines
        // the valid range for s in (301): 0 < s < secp256k1n ÷ 2 + 1, and for v in (302): v ∈ {27, 28}. Most
        // signatures from current libraries generate a unique signature with an s-value in the lower half order.
        //
        // If your library generates malleable signatures, such as s-values in the upper range, calculate a new s-value
        // with 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141 - s1 and flip v from 27 to 28 or
        // vice versa. If your library also generates signatures with 0/1 for v instead 27/28, add 27 to v to accept
        // these malleable signatures as well.
        if (uint256(s) > 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0) {
            return (address(0), RecoverError.InvalidSignatureS);
        }
        if (v != 27 && v != 28) {
            return (address(0), RecoverError.InvalidSignatureV);
        }

        // If the signature is valid (and not malleable), return the signer address
        address signer = ecrecover(hash, v, r, s);
        if (signer == address(0)) {
            return (address(0), RecoverError.InvalidSignature);
        }

        return (signer, RecoverError.NoError);
    }

    /**
     * @dev Overload of {ECDSA-recover} that receives the `v`,
     * `r` and `s` signature fields separately.
     */
    function recover(
        bytes32 hash,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) internal pure returns (address) {
        (address recovered, RecoverError error) = tryRecover(hash, v, r, s);
        _throwError(error);
        return recovered;
    }

    /**
     * @dev Returns an Ethereum Signed Message, created from a `hash`. This
     * produces hash corresponding to the one signed with the
     * https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`]
     * JSON-RPC method as part of EIP-191.
     *
     * See {recover}.
     */
    function toEthSignedMessageHash(bytes32 hash) internal pure returns (bytes32) {
        // 32 is the length in bytes of hash,
        // enforced by the type signature above
        return keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n32", hash));
    }

    /**
     * @dev Returns an Ethereum Signed Message, created from `s`. This
     * produces hash corresponding to the one signed with the
     * https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`]
     * JSON-RPC method as part of EIP-191.
     *
     * See {recover}.
     */
    function toEthSignedMessageHash(bytes memory s) internal pure returns (bytes32) {
        return keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n", Strings.toString(s.length), s));
    }

    /**
     * @dev Returns an Ethereum Signed Typed Data, created from a
     * `domainSeparator` and a `structHash`. This produces hash corresponding
     * to the one signed with the
     * https://eips.ethereum.org/EIPS/eip-712[`eth_signTypedData`]
     * JSON-RPC method as part of EIP-712.
     *
     * See {recover}.
     */
    function toTypedDataHash(bytes32 domainSeparator, bytes32 structHash) internal pure returns (bytes32) {
        return keccak256(abi.encodePacked("\x19\x01", domainSeparator, structHash));
    }
}

File 10 of 13 : SeaDropErrorsAndEvents.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.11;

import { PublicDrop, TokenGatedDropStage } from "./SeaDropStructs.sol";

interface SeaDropErrorsAndEvents {
    /**
     * @dev Revert with an error if the drop stage is not active.
     */
    error NotActive(
        uint256 currentTimestamp,
        uint256 startTimestamp,
        uint256 endTimestamp
    );

    /**
     * @dev Revert with an error if the mint quantity exceeds the max allowed
     *      per transaction.
     */
    error MintQuantityExceedsMaxPerTransaction(uint256 quantity, uint256 allowed);

    /**
     * @dev Revert with an error if the mint quantity exceeds the max allowed
     *      to be minted per wallet.
     */
    error MintQuantityExceedsMaxMintedPerWallet(uint256 total, uint256 allowed);

    /**
     * @dev Revert with an error if the mint quantity exceeds the max token
     *      supply.
     */
    error MintQuantityExceedsMaxSupply(uint256 total, uint256 maxSupply);

    /**
     * @dev Revert with an error if the mint quantity exceeds the max token
     *      supply for the stage.
     */
    error MintQuantityExceedsMaxTokenSupplyForStage(uint256 total, uint256 maxTokenSupplyForStage);
    
    /**
     * @dev Revert if the fee recipient is the zero address.
     */
    error FeeRecipientCannotBeZeroAddress();

    /**
     * @dev Revert if the fee recipient is restricted and not allowe.
     */
    error FeeRecipientNotAllowed();

    /**
     * @dev Revert if the creator payout address is the zero address.
     */
    error CreatorPayoutAddressCannotBeZeroAddress();

    /**
     * @dev Revert with an error if the allow list is already redeemed.
     *      TODO should you only be able to redeem from an allow list once?
     *           would otherwise be capped by maxTotalMintableByWallet
     */
    error AllowListRedeemed(address minter);

    /**
     * @dev Revert with an error if the received payment is incorrect.
     */
    error IncorrectPayment(uint256 got, uint256 want);

    /**
     * @dev Revert with an error if the allow list proof is invalid.
     */
    error InvalidProof();

    /**
     * @dev Revert with an error if signer's signatuer is invalid.
     */
    error InvalidSignature(address recoveredSigner);

    /**
     * @dev Revert with an error if the sender does not
     *      match the IERC721SeaDrop interface.
     */
    error OnlyIERC721SeaDrop(address sender);

    /**
     * @dev Revert with an error if the sender of a token gated supplied
     *      drop stage redeem is not the owner of the token.
     */
    error TokenGatedNotTokenOwner(address nftContract, address allowedNftContract, uint256 tokenId);

    /**
     * @dev Revert with an error if the token id has already been used to
     *      redeem a token gated drop stage.
     */
    error TokenGatedTokenIdAlreadyRedeemed(address nftContract, address allowedNftContract, uint256 tokenId);

    /**
     * @dev An event with details of a SeaDrop mint, for analytical purposes.
     * 
     * @param nftContract    The nft contract.
     * @param minter         The mint recipient.
     * @param feeRecipient   The fee recipient.
     * @param payer          The address who payed for the tx.
     * @param quantityMinted The number of tokens minted.
     * @param unitMintPrice  The amount paid for each token.
     * @param feeBps         The fee out of 10_000 basis points collected.
     * @param dropStageIndex The drop stage index. Items minted
     *                       through mintPublic() have
     *                       dropStageIndex of 0.
     */
    event SeaDropMint(
        address indexed nftContract,
        address indexed minter,
        address indexed feeRecipient,
        address payer,
        uint256 quantityMinted,
        uint256 unitMintPrice,
        uint256 feeBps,
        uint256 dropStageIndex
    );

    /**
     * @dev An event with updated public drop data for an nft contract.
     */
    event PublicDropUpdated(address indexed nftContract, PublicDrop publicDrop);

    /**
     * @dev An event with updated token gated drop stage data
     *      for an nft contract.
     */
    event TokenGatedDropStageUpdated(
        address indexed nftContract,
        address indexed allowedNftToken,
        TokenGatedDropStage dropStage
    );

/**
 * @dev An event with updated allow list data for an nft contract.
 * 
 * @param nftContract        The nft contract.
 * @param previousMerkleRoot The previous allow list merkle root.
 * @param newMerkleRoot      The new allow list merkle root.
 * @param publicKeyURI       If the allow list is encrypted, the public key
 *                           URIs that can decrypt the list.
 *                           Empty if unencrypted.
 * @param allowListURI       The URI for the allow list.
 */
event AllowListUpdated(
    address indexed nftContract,
    bytes32 indexed previousMerkleRoot,
    bytes32 indexed newMerkleRoot,
    string[] publicKeyURI,
    string allowListURI
);

    /**
     * @dev An event with updated drop URI for an nft contract.
     */
    event DropURIUpdated(address indexed nftContract, string newDropURI);

    /**
     * @dev An event with the updated creator payout address for an nft contract.
     */
    event CreatorPayoutAddressUpdated(
        address indexed nftContract,
        address indexed newPayoutAddress
    );

    /**
     * @dev An event with the updated allowed fee recipient for an nft contract.
     */
    event AllowedFeeRecipientUpdated(
        address indexed nftContract,
        address indexed feeRecipient,
        bool indexed allowed
    );

    /**
     * @dev An event with the updated server side signers for an nft contract.
     */
    event SignersUpdated(
        address indexed nftContract,
        address[] oldSigners,
        address[] newSigners
    );
}

File 11 of 13 : IERC721ContractMetadata.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.11;

interface IERC721ContractMetadata {
    /**
     * @dev Emit an event when the max token supply is updated.
     */
    event MaxSupplyUpdated(uint256 newMaxSupply);

    /**
     * @dev Emit an event with the previous and new provenance hash after
     *      being updated.
     */
    event ProvenanceHashUpdated(bytes32 previousHash, bytes32 newHash);

    /**
     * @dev Emit an event when the URI for the collection-level metadata
     *      is updated.
     */
    event ContractURIUpdated(string newContractURI);

    /**
     * @dev Emit an event for partial reveals/updates.
     *      Batch update implementation should be left to contract.
     *
     * @param startTokenId The start token id.
     * @param endTokenId   The end token id.
     */
    event TokenURIUpdated(
        uint256 indexed startTokenId,
        uint256 indexed endTokenId
    );

    /**
     * @dev Emit an event for full token metadata reveals/updates.
     *
     * @param baseURI The base URI.
     */
    event BaseURIUpdated(string baseURI);

    /**
     * @notice Returns the contract URI.
     */
    function contractURI() external view returns (string memory);

    /**
     * @notice Sets the contract URI for contract metadata.
     *
     * @param newContractURI The new contract URI.
     */
    function setContractURI(string calldata newContractURI) external;

    /**
     * @notice Returns the base URI for token metadata.
     */
    function baseURI() external view returns (string memory);

    /**
     * @notice Sets the base URI for the token metadata and emits an event.
     *
     * @param tokenURI The new base URI to set.
     */
    function setBaseURI(string calldata tokenURI) external;

    /**
     * @notice Returns the max token supply.
     */
    function maxSupply() external view returns (uint256);

    /**
     * @notice Sets the max supply and emits an event.
     *
     * @param newMaxSupply The new max supply to set.
     */
    function setMaxSupply(uint256 newMaxSupply) external;

    /**
     * @notice Returns the total token supply.
     */
    function totalSupply() external view returns (uint256);

    /**
     * @notice Returns the provenance hash.
     *         The provenance hash is used for random reveals, which
     *         is a hash of the ordered metadata to show it is unmodified
     *         after mint has started.
     */
    function provenanceHash() external view returns (bytes32);

    /**
     * @notice Sets the provenance hash and emits an event.
     *         The provenance hash is used for random reveals, which
     *         is a hash of the ordered metadata to show it is unmodified
     *         after mint has started.
     *         This function will revert after the first item has been minted.
     *
     * @param newProvenanceHash The new provenance hash to set.
     */
    function setProvenanceHash(bytes32 newProvenanceHash) external;

    /**
     * @dev Revert with an error when attempting to set the provenance
     *      hash after the mint has started.
     */
    error ProvenanceHashCannotBeSetAfterMintStarted();
}

File 12 of 13 : ERC20.sol
// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity >=0.8.0;

/// @notice Modern and gas efficient ERC20 + EIP-2612 implementation.
/// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/tokens/ERC20.sol)
/// @author Modified from Uniswap (https://github.com/Uniswap/uniswap-v2-core/blob/master/contracts/UniswapV2ERC20.sol)
/// @dev Do not manually set balances without updating totalSupply, as the sum of all user balances must not exceed it.
abstract contract ERC20 {
    /*//////////////////////////////////////////////////////////////
                                 EVENTS
    //////////////////////////////////////////////////////////////*/

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

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

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

    string public name;

    string public symbol;

    uint8 public immutable decimals;

    /*//////////////////////////////////////////////////////////////
                              ERC20 STORAGE
    //////////////////////////////////////////////////////////////*/

    uint256 public totalSupply;

    mapping(address => uint256) public balanceOf;

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

    /*//////////////////////////////////////////////////////////////
                            EIP-2612 STORAGE
    //////////////////////////////////////////////////////////////*/

    uint256 internal immutable INITIAL_CHAIN_ID;

    bytes32 internal immutable INITIAL_DOMAIN_SEPARATOR;

    mapping(address => uint256) public nonces;

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

    constructor(
        string memory _name,
        string memory _symbol,
        uint8 _decimals
    ) {
        name = _name;
        symbol = _symbol;
        decimals = _decimals;

        INITIAL_CHAIN_ID = block.chainid;
        INITIAL_DOMAIN_SEPARATOR = computeDomainSeparator();
    }

    /*//////////////////////////////////////////////////////////////
                               ERC20 LOGIC
    //////////////////////////////////////////////////////////////*/

    function approve(address spender, uint256 amount) public virtual returns (bool) {
        allowance[msg.sender][spender] = amount;

        emit Approval(msg.sender, spender, amount);

        return true;
    }

    function transfer(address to, uint256 amount) public virtual returns (bool) {
        balanceOf[msg.sender] -= amount;

        // Cannot overflow because the sum of all user
        // balances can't exceed the max uint256 value.
        unchecked {
            balanceOf[to] += amount;
        }

        emit Transfer(msg.sender, to, amount);

        return true;
    }

    function transferFrom(
        address from,
        address to,
        uint256 amount
    ) public virtual returns (bool) {
        uint256 allowed = allowance[from][msg.sender]; // Saves gas for limited approvals.

        if (allowed != type(uint256).max) allowance[from][msg.sender] = allowed - amount;

        balanceOf[from] -= amount;

        // Cannot overflow because the sum of all user
        // balances can't exceed the max uint256 value.
        unchecked {
            balanceOf[to] += amount;
        }

        emit Transfer(from, to, amount);

        return true;
    }

    /*//////////////////////////////////////////////////////////////
                             EIP-2612 LOGIC
    //////////////////////////////////////////////////////////////*/

    function permit(
        address owner,
        address spender,
        uint256 value,
        uint256 deadline,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) public virtual {
        require(deadline >= block.timestamp, "PERMIT_DEADLINE_EXPIRED");

        // Unchecked because the only math done is incrementing
        // the owner's nonce which cannot realistically overflow.
        unchecked {
            address recoveredAddress = ecrecover(
                keccak256(
                    abi.encodePacked(
                        "\x19\x01",
                        DOMAIN_SEPARATOR(),
                        keccak256(
                            abi.encode(
                                keccak256(
                                    "Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)"
                                ),
                                owner,
                                spender,
                                value,
                                nonces[owner]++,
                                deadline
                            )
                        )
                    )
                ),
                v,
                r,
                s
            );

            require(recoveredAddress != address(0) && recoveredAddress == owner, "INVALID_SIGNER");

            allowance[recoveredAddress][spender] = value;
        }

        emit Approval(owner, spender, value);
    }

    function DOMAIN_SEPARATOR() public view virtual returns (bytes32) {
        return block.chainid == INITIAL_CHAIN_ID ? INITIAL_DOMAIN_SEPARATOR : computeDomainSeparator();
    }

    function computeDomainSeparator() internal view virtual returns (bytes32) {
        return
            keccak256(
                abi.encode(
                    keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"),
                    keccak256(bytes(name)),
                    keccak256("1"),
                    block.chainid,
                    address(this)
                )
            );
    }

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

    function _mint(address to, uint256 amount) internal virtual {
        totalSupply += amount;

        // Cannot overflow because the sum of all user
        // balances can't exceed the max uint256 value.
        unchecked {
            balanceOf[to] += amount;
        }

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

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

        // Cannot underflow because a user's balance
        // will never be larger than the total supply.
        unchecked {
            totalSupply -= amount;
        }

        emit Transfer(from, address(0), amount);
    }
}

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

pragma solidity ^0.8.0;

/**
 * @dev String operations.
 */
library Strings {
    bytes16 private constant _HEX_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) {
        // Inspired by OraclizeAPI's implementation - MIT licence
        // https://github.com/oraclize/ethereum-api/blob/b42146b063c7d6ee1358846c198246239e9360e8/oraclizeAPI_0.4.25.sol

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

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

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

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

Settings
{
  "remappings": [
    "ERC721A/=lib/ERC721A/contracts/",
    "ds-test/=lib/forge-std/lib/ds-test/src/",
    "forge-std/=lib/forge-std/src/",
    "murky/=lib/murky/src/",
    "openzeppelin-contracts/=lib/openzeppelin-contracts/",
    "seadrop/=src/",
    "solady/=lib/solady/src/",
    "solmate/=lib/solmate/src/",
    "utility-contracts/=lib/utility-contracts/src/",
    "src/=src/",
    "test/=test/",
    "script/=script/"
  ],
  "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":[],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[{"internalType":"address","name":"minter","type":"address"}],"name":"AllowListRedeemed","type":"error"},{"inputs":[],"name":"CreatorPayoutAddressCannotBeZeroAddress","type":"error"},{"inputs":[],"name":"FeeRecipientCannotBeZeroAddress","type":"error"},{"inputs":[],"name":"FeeRecipientNotAllowed","type":"error"},{"inputs":[{"internalType":"uint256","name":"got","type":"uint256"},{"internalType":"uint256","name":"want","type":"uint256"}],"name":"IncorrectPayment","type":"error"},{"inputs":[],"name":"InvalidProof","type":"error"},{"inputs":[{"internalType":"address","name":"recoveredSigner","type":"address"}],"name":"InvalidSignature","type":"error"},{"inputs":[{"internalType":"uint256","name":"total","type":"uint256"},{"internalType":"uint256","name":"allowed","type":"uint256"}],"name":"MintQuantityExceedsMaxMintedPerWallet","type":"error"},{"inputs":[{"internalType":"uint256","name":"quantity","type":"uint256"},{"internalType":"uint256","name":"allowed","type":"uint256"}],"name":"MintQuantityExceedsMaxPerTransaction","type":"error"},{"inputs":[{"internalType":"uint256","name":"total","type":"uint256"},{"internalType":"uint256","name":"maxSupply","type":"uint256"}],"name":"MintQuantityExceedsMaxSupply","type":"error"},{"inputs":[{"internalType":"uint256","name":"total","type":"uint256"},{"internalType":"uint256","name":"maxTokenSupplyForStage","type":"uint256"}],"name":"MintQuantityExceedsMaxTokenSupplyForStage","type":"error"},{"inputs":[{"internalType":"uint256","name":"currentTimestamp","type":"uint256"},{"internalType":"uint256","name":"startTimestamp","type":"uint256"},{"internalType":"uint256","name":"endTimestamp","type":"uint256"}],"name":"NotActive","type":"error"},{"inputs":[{"internalType":"address","name":"sender","type":"address"}],"name":"OnlyIERC721SeaDrop","type":"error"},{"inputs":[{"internalType":"address","name":"nftContract","type":"address"},{"internalType":"address","name":"allowedNftContract","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"TokenGatedNotTokenOwner","type":"error"},{"inputs":[{"internalType":"address","name":"nftContract","type":"address"},{"internalType":"address","name":"allowedNftContract","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"TokenGatedTokenIdAlreadyRedeemed","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"nftContract","type":"address"},{"indexed":true,"internalType":"bytes32","name":"previousMerkleRoot","type":"bytes32"},{"indexed":true,"internalType":"bytes32","name":"newMerkleRoot","type":"bytes32"},{"indexed":false,"internalType":"string[]","name":"publicKeyURI","type":"string[]"},{"indexed":false,"internalType":"string","name":"allowListURI","type":"string"}],"name":"AllowListUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"nftContract","type":"address"},{"indexed":true,"internalType":"address","name":"feeRecipient","type":"address"},{"indexed":true,"internalType":"bool","name":"allowed","type":"bool"}],"name":"AllowedFeeRecipientUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"nftContract","type":"address"},{"indexed":true,"internalType":"address","name":"newPayoutAddress","type":"address"}],"name":"CreatorPayoutAddressUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"nftContract","type":"address"},{"indexed":false,"internalType":"string","name":"newDropURI","type":"string"}],"name":"DropURIUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"nftContract","type":"address"},{"components":[{"internalType":"uint80","name":"mintPrice","type":"uint80"},{"internalType":"uint64","name":"startTime","type":"uint64"},{"internalType":"uint40","name":"maxMintsPerWallet","type":"uint40"},{"internalType":"uint16","name":"feeBps","type":"uint16"},{"internalType":"bool","name":"restrictFeeRecipients","type":"bool"}],"indexed":false,"internalType":"struct PublicDrop","name":"publicDrop","type":"tuple"}],"name":"PublicDropUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"nftContract","type":"address"},{"indexed":true,"internalType":"address","name":"minter","type":"address"},{"indexed":true,"internalType":"address","name":"feeRecipient","type":"address"},{"indexed":false,"internalType":"address","name":"payer","type":"address"},{"indexed":false,"internalType":"uint256","name":"quantityMinted","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"unitMintPrice","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"feeBps","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"dropStageIndex","type":"uint256"}],"name":"SeaDropMint","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"nftContract","type":"address"},{"indexed":false,"internalType":"address[]","name":"oldSigners","type":"address[]"},{"indexed":false,"internalType":"address[]","name":"newSigners","type":"address[]"}],"name":"SignersUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"nftContract","type":"address"},{"indexed":true,"internalType":"address","name":"allowedNftToken","type":"address"},{"components":[{"internalType":"uint80","name":"mintPrice","type":"uint80"},{"internalType":"uint16","name":"maxTotalMintableByWallet","type":"uint16"},{"internalType":"uint48","name":"startTime","type":"uint48"},{"internalType":"uint48","name":"endTime","type":"uint48"},{"internalType":"uint8","name":"dropStageIndex","type":"uint8"},{"internalType":"uint40","name":"maxTokenSupplyForStage","type":"uint40"},{"internalType":"uint16","name":"feeBps","type":"uint16"},{"internalType":"bool","name":"restrictFeeRecipients","type":"bool"}],"indexed":false,"internalType":"struct TokenGatedDropStage","name":"dropStage","type":"tuple"}],"name":"TokenGatedDropStageUpdated","type":"event"},{"inputs":[],"name":"DOMAIN_SEPARATOR","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MINT_DATA_TYPEHASH","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"nftContract","type":"address"}],"name":"getAllowListMerkleRoot","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"nftContract","type":"address"}],"name":"getCreatorPayoutAddress","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"nftContract","type":"address"}],"name":"getDropURI","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"nftContract","type":"address"},{"internalType":"address","name":"feeRecipient","type":"address"}],"name":"getFeeRecipientIsAllowed","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"nftContract","type":"address"}],"name":"getPublicDrop","outputs":[{"components":[{"internalType":"uint80","name":"mintPrice","type":"uint80"},{"internalType":"uint64","name":"startTime","type":"uint64"},{"internalType":"uint40","name":"maxMintsPerWallet","type":"uint40"},{"internalType":"uint16","name":"feeBps","type":"uint16"},{"internalType":"bool","name":"restrictFeeRecipients","type":"bool"}],"internalType":"struct PublicDrop","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"nftContract","type":"address"}],"name":"getSigners","outputs":[{"internalType":"address[]","name":"","type":"address[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"nftContract","type":"address"}],"name":"getTokenGatedAllowedTokens","outputs":[{"internalType":"address[]","name":"","type":"address[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"nftContract","type":"address"},{"internalType":"address","name":"allowedNftToken","type":"address"}],"name":"getTokenGatedDrop","outputs":[{"components":[{"internalType":"uint80","name":"mintPrice","type":"uint80"},{"internalType":"uint16","name":"maxTotalMintableByWallet","type":"uint16"},{"internalType":"uint48","name":"startTime","type":"uint48"},{"internalType":"uint48","name":"endTime","type":"uint48"},{"internalType":"uint8","name":"dropStageIndex","type":"uint8"},{"internalType":"uint40","name":"maxTokenSupplyForStage","type":"uint40"},{"internalType":"uint16","name":"feeBps","type":"uint16"},{"internalType":"bool","name":"restrictFeeRecipients","type":"bool"}],"internalType":"struct TokenGatedDropStage","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"nftContract","type":"address"},{"internalType":"address","name":"feeRecipient","type":"address"},{"internalType":"address","name":"minter","type":"address"},{"internalType":"uint256","name":"quantity","type":"uint256"},{"components":[{"internalType":"uint256","name":"mintPrice","type":"uint256"},{"internalType":"uint256","name":"maxTotalMintableByWallet","type":"uint256"},{"internalType":"uint256","name":"startTime","type":"uint256"},{"internalType":"uint256","name":"endTime","type":"uint256"},{"internalType":"uint256","name":"dropStageIndex","type":"uint256"},{"internalType":"uint256","name":"maxTokenSupplyForStage","type":"uint256"},{"internalType":"uint256","name":"feeBps","type":"uint256"},{"internalType":"bool","name":"restrictFeeRecipients","type":"bool"}],"internalType":"struct MintParams","name":"mintParams","type":"tuple"},{"internalType":"bytes32[]","name":"proof","type":"bytes32[]"}],"name":"mintAllowList","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"nftContract","type":"address"},{"internalType":"address","name":"feeRecipient","type":"address"},{"internalType":"address","name":"minter","type":"address"},{"components":[{"internalType":"address","name":"allowedNftToken","type":"address"},{"internalType":"uint256[]","name":"allowedNftTokenIds","type":"uint256[]"}],"internalType":"struct TokenGatedMintParams[]","name":"tokenGatedMintParams","type":"tuple[]"}],"name":"mintAllowedTokenHolder","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"nftContract","type":"address"},{"internalType":"address","name":"feeRecipient","type":"address"},{"internalType":"address","name":"minter","type":"address"},{"internalType":"uint256","name":"quantity","type":"uint256"}],"name":"mintPublic","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"nftContract","type":"address"},{"internalType":"address","name":"feeRecipient","type":"address"},{"internalType":"address","name":"minter","type":"address"},{"internalType":"uint256","name":"quantity","type":"uint256"},{"components":[{"internalType":"uint256","name":"mintPrice","type":"uint256"},{"internalType":"uint256","name":"maxTotalMintableByWallet","type":"uint256"},{"internalType":"uint256","name":"startTime","type":"uint256"},{"internalType":"uint256","name":"endTime","type":"uint256"},{"internalType":"uint256","name":"dropStageIndex","type":"uint256"},{"internalType":"uint256","name":"maxTokenSupplyForStage","type":"uint256"},{"internalType":"uint256","name":"feeBps","type":"uint256"},{"internalType":"bool","name":"restrictFeeRecipients","type":"bool"}],"internalType":"struct MintParams","name":"mintParams","type":"tuple"},{"internalType":"bytes","name":"signature","type":"bytes"}],"name":"mintSigned","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"components":[{"internalType":"bytes32","name":"merkleRoot","type":"bytes32"},{"internalType":"string[]","name":"publicKeyURIs","type":"string[]"},{"internalType":"string","name":"allowListURI","type":"string"}],"internalType":"struct AllowListData","name":"allowListData","type":"tuple"}],"name":"updateAllowList","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"feeRecipient","type":"address"},{"internalType":"bool","name":"allowed","type":"bool"}],"name":"updateAllowedFeeRecipient","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_payoutAddress","type":"address"}],"name":"updateCreatorPayoutAddress","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string","name":"newDropURI","type":"string"}],"name":"updateDropURI","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"uint80","name":"mintPrice","type":"uint80"},{"internalType":"uint64","name":"startTime","type":"uint64"},{"internalType":"uint40","name":"maxMintsPerWallet","type":"uint40"},{"internalType":"uint16","name":"feeBps","type":"uint16"},{"internalType":"bool","name":"restrictFeeRecipients","type":"bool"}],"internalType":"struct PublicDrop","name":"publicDrop","type":"tuple"}],"name":"updatePublicDrop","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address[]","name":"newSigners","type":"address[]"}],"name":"updateSigners","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"nftContract","type":"address"},{"internalType":"address","name":"allowedNftToken","type":"address"},{"components":[{"internalType":"uint80","name":"mintPrice","type":"uint80"},{"internalType":"uint16","name":"maxTotalMintableByWallet","type":"uint16"},{"internalType":"uint48","name":"startTime","type":"uint48"},{"internalType":"uint48","name":"endTime","type":"uint48"},{"internalType":"uint8","name":"dropStageIndex","type":"uint8"},{"internalType":"uint40","name":"maxTokenSupplyForStage","type":"uint40"},{"internalType":"uint16","name":"feeBps","type":"uint16"},{"internalType":"bool","name":"restrictFeeRecipients","type":"bool"}],"internalType":"struct TokenGatedDropStage","name":"dropStage","type":"tuple"}],"name":"updateTokenGatedDrop","outputs":[],"stateMutability":"nonpayable","type":"function"}]

60c060405234801561001057600080fd5b506040805180820182526007815266053656144726f760cc1b6020918201528151808301835260018152603160f81b9082015281517f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f918101919091527f752a02269614d51d9b7bd0a2f05cf03e553ce6be8b487650a6a2a4990208d804918101919091527fc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc660608201524660808201523060a082015260c00160408051601f1981840301815291905280516020909101206080527f5d2d2748b9e0b62f8bb884fa4fa88030198735648d300a06521feab7a3d6552d60a05260805160a051612fd461013c600039600081816103a601526115fa01526000818161037201526115d90152612fd46000f3fe60806040526004361061012a5760003560e01c80636d999226116100ab5780639d1636f31161006f5780639d1636f3146104bf578063b957d0cb146104ec578063bbf1ab0c1461050c578063bc6a629c1461052c578063bf52a8a914610653578063ebb4a55f1461066657600080fd5b80636d9992261461042c57806379627a321461044c5780637e3ba6af1461045f5780638e7d1e431461047f57806396751ae91461049f57600080fd5b806332bf11f5116100f257806332bf11f51461031c5780633644e5151461036057806337ca8c03146103945780634300a4e6146103c85780635cb3c4d3146103db57600080fd5b80630b0e8a6e1461012f57806312738db814610261578063161ac21f146102835780632db526eb14610296578063322e75d1146102c3575b600080fd5b34801561013b57600080fd5b5061024b61014a366004612012565b6040805161010081018252600080825260208201819052918101829052606081018290526080810182905260a0810182905260c0810182905260e0810191909152506001600160a01b03808316600090815260076020908152604080832093851683529281529082902082516101008101845281546001600160501b038116825261ffff600160501b820481169483019490945265ffffffffffff600160601b8204811695830195909552600160901b8104909416606082015260ff600160c01b85048116608083015264ffffffffff600160c81b86041660a0830152600160f01b90940490921660c083015260010154909116151560e082015292915050565b604051610258919061204b565b60405180910390f35b34801561026d57600080fd5b5061028161027c3660046120ef565b610686565b005b610281610291366004612113565b61076c565b3480156102a257600080fd5b506102b66102b13660046120ef565b610893565b60405161025891906121a8565b3480156102cf57600080fd5b5061030c6102de366004612012565b6001600160a01b03918216600090815260046020908152604080832093909416825291909152205460ff1690565b6040519015158152602001610258565b34801561032857600080fd5b506103526103373660046120ef565b6001600160a01b031660009081526003602052604090205490565b604051908152602001610258565b34801561036c57600080fd5b506103527f000000000000000000000000000000000000000000000000000000000000000081565b3480156103a057600080fd5b506103527f000000000000000000000000000000000000000000000000000000000000000081565b6102816103d6366004612218565b610909565b3480156103e757600080fd5b506104146103f63660046120ef565b6001600160a01b039081166000908152600260205260409020541690565b6040516001600160a01b039091168152602001610258565b34801561043857600080fd5b506102816104473660046122b0565b6109ed565b61028161045a3660046122c2565b610ad1565b34801561046b57600080fd5b506102b661047a3660046120ef565b610e10565b34801561048b57600080fd5b5061028161049a36600461235b565b610e84565b3480156104ab57600080fd5b506102816104ba366004612389565b610f6e565b3480156104cb57600080fd5b506104df6104da3660046120ef565b611209565b60405161025891906123ca565b3480156104f857600080fd5b50610281610507366004612460565b6112ab565b34801561051857600080fd5b50610281610527366004612495565b611397565b34801561053857600080fd5b506105f26105473660046120ef565b6040805160a081018252600080825260208201819052918101829052606081018290526080810191909152506001600160a01b031660009081526020818152604091829020825160a08101845290546001600160501b0381168252600160501b81046001600160401b031692820192909252600160901b820464ffffffffff1692810192909252600160b81b810461ffff166060830152600160c81b900460ff161515608082015290565b6040516102589190600060a0820190506001600160501b0383511682526001600160401b03602084015116602083015264ffffffffff604084015116604083015261ffff606084015116606083015260808301511515608083015292915050565b6102816106613660046124de565b611587565b34801561067257600080fd5b50610281610681366004612563565b61173c565b6040516301ffc9a760e01b815233906301ffc9a7906106b09063290d607b60e21b9060040161259d565b602060405180830381865afa1580156106cd573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906106f191906125b2565b6107155760405163104fa0ef60e11b81523360048201526024015b60405180910390fd5b3360008181526002602052604080822080546001600160a01b0319166001600160a01b03861690811790915590519092917f0c69f21751e800ea5960436c9a94370c7adbf54c733a20a025293fbbe8f1625291a350565b6001600160a01b03841660009081526020818152604091829020825160a08101845290546001600160501b0381168252600160501b81046001600160401b0316928201839052600160901b810464ffffffffff1693820193909352600160b81b830461ffff166060820152600160c81b90920460ff16151560808301524210156108285760208101516040516309ed117960e11b81524260048201526001600160401b039182166024820152604481019190915260640161070c565b61083f8282600001516001600160501b031661183d565b610858858484846040015164ffffffffff16600061187e565b610867858583608001516119c4565b61088c85848484600001516001600160501b03166000866060015161ffff168a611a46565b5050505050565b6001600160a01b0381166000908152600860209081526040918290208054835181840281018401909452808452606093928301828280156108fd57602002820191906000526020600020905b81546001600160a01b031681526001909101906020018083116108df575b50505050509050919050565b61091b83604001358460600135611b1e565b61092684843561183d565b61093b87868686602001358760a0013561187e565b6109568787610951610100870160e088016125cf565b6119c4565b6109af8282600360008b6001600160a01b03166001600160a01b03168152602001908152602001600020548887604051602001610994929190612647565b60405160208183030381529060405280519060200120611b59565b6109cc576040516309bde33960e01b815260040160405180910390fd5b6109e48786868635608088013560c08901358c611a46565b50505050505050565b6040516301ffc9a760e01b815233906301ffc9a790610a179063290d607b60e21b9060040161259d565b602060405180830381865afa158015610a34573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610a5891906125b2565b610a775760405163104fa0ef60e11b815233600482015260240161070c565b3360009081526020819052604090208190610a9282826126df565b505060405133907ffb8089f2dff2c2a47c0a399dd9bd178cd9885036fd9dcea23f8ad0dcb6ffbbf890610ac69084906127f6565b60405180910390a250565b6000805b82811015610dde5736848483818110610af057610af061287a565b9050602002810190610b029190612890565b6001600160a01b038916600090815260076020908152604082209293509091908290610b30908501856120ef565b6001600160a01b0316815260208101919091526040016000208054909150610b709065ffffffffffff600160601b8204811691600160901b900416611b1e565b6001810154610b85908a908a9060ff166119c4565b6000610b9460208401846128b0565b8354909250610bad91506001600160501b03168261290f565b610bb7908661292e565b8254909550610be5908b908a908490600160501b810461ffff1690600160c81b900464ffffffffff1661187e565b60005b81811015610d9c576000610bff60208601866128b0565b83818110610c0f57610c0f61287a565b905060200201359050896001600160a01b0316856000016020810190610c3591906120ef565b6001600160a01b0316636352211e836040518263ffffffff1660e01b8152600401610c6291815260200190565b602060405180830381865afa158015610c7f573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610ca39190612946565b6001600160a01b031614610cf2578b610cbf60208701876120ef565b60405163da8c7bc760e01b81526001600160a01b039283166004820152911660248201526044810182905260640161070c565b6001600160a01b038c1660009081526009602090815260408220908290610d1b908901896120ef565b6001600160a01b031681526020808201929092526040908101600090812085825290925290205460ff169050801515600103610d92578c610d5f60208801886120ef565b60405163a93f299b60e01b81526001600160a01b039283166004820152911660248201526044810183905260640161070c565b5050600101610be8565b508154610dd0908b908a9084906001600160501b03811690600160c01b810460ff1690600160f01b900461ffff168f611a46565b836001019350505050610ad5565b50803414610e0857604051630d35e92160e01b81523460048201526024810182905260440161070c565b505050505050565b6001600160a01b0381166000908152600660209081526040918290208054835181840281018401909452808452606093928301828280156108fd576020028201919060005260206000209081546001600160a01b031681526001909101906020018083116108df5750505050509050919050565b6040516301ffc9a760e01b815233906301ffc9a790610eae9063290d607b60e21b9060040161259d565b602060405180830381865afa158015610ecb573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610eef91906125b2565b610f0e5760405163104fa0ef60e11b815233600482015260240161070c565b3360008181526004602090815260408083206001600160a01b0387168085529252808320805460ff19168615159081179091559051909391927f6486c31f9d664e241acf94ec2541d328f6b9e97257ae16a1d887f296f879719f91a45050565b6040516301ffc9a760e01b815233906301ffc9a790610f989063290d607b60e21b9060040161259d565b602060405180830381865afa158015610fb5573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610fd991906125b2565b610ff85760405163104fa0ef60e11b815233600482015260240161070c565b336000908152600660209081526040808320805482518185028101850190935280835290939284919083018282801561105a57602002820191906000526020600020905b81546001600160a01b0316815260019091019060200180831161103c575b505033600090815260066020526040812094955061107d9493509150611fcb9050565b60005b838110156110e8578285858381811061109b5761109b61287a565b90506020020160208101906110b091906120ef565b815460018082018455600093845260209093200180546001600160a01b0319166001600160a01b039290921691909117905501611080565b50336000908152600560205260408120905b825181101561115457600082600085848151811061111a5761111a61287a565b6020908102919091018101516001600160a01b03168252810191909152604001600020805460ff19169115159190911790556001016110fa565b5060005b848110156111bc5760018260008888858181106111775761117761287a565b905060200201602081019061118c91906120ef565b6001600160a01b031681526020810191909152604001600020805460ff1916911515919091179055600101611158565b50336001600160a01b03167ff91188cded8aeb0b400eb42edd175d6dc8d5e5a9f457adfc27e0be7bd612caab8387876040516111fa93929190612963565b60405180910390a25050505050565b6001600160a01b0381166000908152600160205260409020805460609190611230906129c6565b80601f016020809104026020016040519081016040528092919081815260200182805461125c906129c6565b80156108fd5780601f1061127e576101008083540402835291602001916108fd565b820191906000526020600020905b81548152906001019060200180831161128c5750939695505050505050565b6040516301ffc9a760e01b815233906301ffc9a7906112d59063290d607b60e21b9060040161259d565b602060405180830381865afa1580156112f2573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061131691906125b2565b6113355760405163104fa0ef60e11b815233600482015260240161070c565b33600090815260016020526040902061134f828483612a56565b50336001600160a01b03167fa0295608d25b3033c2e2c41cbac8746c2d08767bcfde6d47fae1ed7ba1d32150838360405161138b929190612b3e565b60405180910390a25050565b6040516301ffc9a760e01b815233906301ffc9a7906113c19063290d607b60e21b9060040161259d565b602060405180830381865afa1580156113de573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061140291906125b2565b6114215760405163104fa0ef60e11b815233600482015260240161070c565b6001600160a01b03808416600090815260076020908152604080832093861683529290522081906114528282612b97565b50600090506114676040830160208401612d13565b61ffff161115611537576000805b6001600160a01b0385166000908152600860205260409020548110156114eb576001600160a01b038581166000908152600860205260409020805491861691839081106114c4576114c461287a565b6000918252602090912001546001600160a01b0316036114e357600191505b600101611475565b50801515600003611535576001600160a01b0384811660009081526008602090815260408220805460018101825590835291200180546001600160a01b0319169185169190911790555b505b816001600160a01b0316836001600160a01b03167f5ddb09355563c3e5d9f8472902701579edb3aca9bc1c2d53a316eafd91df9ebb8360405161157a9190612d46565b60405180910390a3505050565b61159983604001358460600135611b1e565b6115a484843561183d565b6115b987868686602001358760a0013561187e565b6115cf8787610951610100870160e088016125cf565b600061190160f01b7f00000000000000000000000000000000000000000000000000000000000000007f0000000000000000000000000000000000000000000000000000000000000000888760405160200161162d93929190612e06565b60408051601f198184030181529082905280516020918201206001600160f01b0319909416908201526022810191909152604281019190915260620160405160208183030381529060405280519060200120905060006116c584848080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152508693925050611b939050565b6001600160a01b03808b1660009081526005602090815260408083209385168352929052205490915060ff1661171957604051633615713d60e21b81526001600160a01b038216600482015260240161070c565b611731898888883560808a013560c08b01358e611a46565b505050505050505050565b6040516301ffc9a760e01b815233906301ffc9a7906117669063290d607b60e21b9060040161259d565b602060405180830381865afa158015611783573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906117a791906125b2565b6117c65760405163104fa0ef60e11b815233600482015260240161070c565b33600081815260036020908152604090912080548435918290559290918391907fefcd7e019bc8b47d27881fd59e2619280ca5894f285950f10ab049870652efa590611814908701876128b0565b6118216040890189612e2a565b6040516118319493929190612e70565b60405180910390a45050565b611847818361290f565b341461187a5734611858828461290f565b604051630d35e92160e01b81526004810192909252602482015260440161070c565b5050565b604051632103857560e21b81526001600160a01b038581166004830152600091829182919089169063840e15d490602401606060405180830381865afa1580156118cc573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906118f09190612f21565b9194509250905084611902848861292e565b111561193657611912838761292e565b60405163edc0127360e01b815260048101919091526024810186905260440161070c565b80611941838861292e565b111561197557611951828761292e565b60405163384b48c560e21b815260048101919091526024810182905260440161070c565b83156119ba5783611986838861292e565b11156119ba57611996828761292e565b604051635cc6d5f560e11b815260048101919091526024810185905260440161070c565b5050505050505050565b6001600160a01b0382166119eb57604051635136e8d560e01b815260040160405180910390fd5b6001811515148015611a2357506001600160a01b0380841660009081526004602090815260408083209386168352929052205460ff16155b15611a415760405163f477d26f60e01b815260040160405180910390fd5b505050565b611a51878284611bb7565b6040516364869dad60e01b81526001600160a01b038781166004830152602482018790528816906364869dad90604401600060405180830381600087803b158015611a9b57600080fd5b505af1158015611aaf573d6000803e3d6000fd5b5050604080513381526020810189905290810187905260608101859052608081018690526001600160a01b03808516935089811692508a16907fe90cf9cc0a552cf52ea6ff74ece0f1c8ae8cc9ad630d3181f55ac43ca076b7d69060a00160405180910390a450505050505050565b81421080611b2b57508042115b1561187a576040516309ed117960e11b8152426004820152602481018390526044810182905260640161070c565b60008315611b8b578360051b8501855b803580851160051b94855260209485185260406000209301818110611b695750505b501492915050565b6000806000611ba28585611c2d565b91509150611baf81611c9b565b509392505050565b6001600160a01b038084166000908152600260205260409020541680611bf057604051633f00976960e01b815260040160405180910390fd5b6000612710611bff843461290f565b611c099190612f4f565b90506000611c178234612f71565b9050611c238583611e54565b610e088382611e54565b6000808251604103611c635760208301516040840151606085015160001a611c5787828585611ea5565b94509450505050611c94565b8251604003611c8c5760208301516040840151611c81868383611f92565b935093505050611c94565b506000905060025b9250929050565b6000816004811115611caf57611caf612f88565b03611cb75750565b6001816004811115611ccb57611ccb612f88565b03611d185760405162461bcd60e51b815260206004820152601860248201527f45434453413a20696e76616c6964207369676e61747572650000000000000000604482015260640161070c565b6002816004811115611d2c57611d2c612f88565b03611d795760405162461bcd60e51b815260206004820152601f60248201527f45434453413a20696e76616c6964207369676e6174757265206c656e67746800604482015260640161070c565b6003816004811115611d8d57611d8d612f88565b03611de55760405162461bcd60e51b815260206004820152602260248201527f45434453413a20696e76616c6964207369676e6174757265202773272076616c604482015261756560f01b606482015260840161070c565b6004816004811115611df957611df9612f88565b03611e515760405162461bcd60e51b815260206004820152602260248201527f45434453413a20696e76616c6964207369676e6174757265202776272076616c604482015261756560f01b606482015260840161070c565b50565b600080600080600085875af1905080611a415760405162461bcd60e51b815260206004820152601360248201527211551217d514905394d1915497d19052531151606a1b604482015260640161070c565b6000807f7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a0831115611edc5750600090506003611f89565b8460ff16601b14158015611ef457508460ff16601c14155b15611f055750600090506004611f89565b6040805160008082526020820180845289905260ff881692820192909252606081018690526080810185905260019060a0016020604051602081039080840390855afa158015611f59573d6000803e3d6000fd5b5050604051601f1901519150506001600160a01b038116611f8257600060019250925050611f89565b9150600090505b94509492505050565b6000806001600160ff1b03831681611faf60ff86901c601b61292e565b9050611fbd87828885611ea5565b935093505050935093915050565b5080546000825590600052602060002090810190611e5191905b80821115611ff95760008155600101611fe5565b5090565b6001600160a01b0381168114611e5157600080fd5b6000806040838503121561202557600080fd5b823561203081611ffd565b9150602083013561204081611ffd565b809150509250929050565b6000610100820190506001600160501b03835116825261ffff6020840151166020830152604083015165ffffffffffff8082166040850152806060860151166060850152505060808301516120a5608084018260ff169052565b5060a08301516120be60a084018264ffffffffff169052565b5060c08301516120d460c084018261ffff169052565b5060e08301516120e860e084018215159052565b5092915050565b60006020828403121561210157600080fd5b813561210c81611ffd565b9392505050565b6000806000806080858703121561212957600080fd5b843561213481611ffd565b9350602085013561214481611ffd565b9250604085013561215481611ffd565b9396929550929360600135925050565b600081518084526020808501945080840160005b8381101561219d5781516001600160a01b031687529582019590820190600101612178565b509495945050505050565b60208152600061210c6020830184612164565b600061010082840312156121ce57600080fd5b50919050565b60008083601f8401126121e657600080fd5b5081356001600160401b038111156121fd57600080fd5b6020830191508360208260051b8501011115611c9457600080fd5b60008060008060008060006101a0888a03121561223457600080fd5b873561223f81611ffd565b9650602088013561224f81611ffd565b9550604088013561225f81611ffd565b9450606088013593506122758960808a016121bb565b92506101808801356001600160401b0381111561229157600080fd5b61229d8a828b016121d4565b989b979a50959850939692959293505050565b600060a082840312156121ce57600080fd5b6000806000806000608086880312156122da57600080fd5b85356122e581611ffd565b945060208601356122f581611ffd565b9350604086013561230581611ffd565b925060608601356001600160401b0381111561232057600080fd5b61232c888289016121d4565b969995985093965092949392505050565b8015158114611e5157600080fd5b80356123568161233d565b919050565b6000806040838503121561236e57600080fd5b823561237981611ffd565b915060208301356120408161233d565b6000806020838503121561239c57600080fd5b82356001600160401b038111156123b257600080fd5b6123be858286016121d4565b90969095509350505050565b600060208083528351808285015260005b818110156123f7578581018301518582016040015282016123db565b81811115612409576000604083870101525b50601f01601f1916929092016040019392505050565b60008083601f84011261243157600080fd5b5081356001600160401b0381111561244857600080fd5b602083019150836020828501011115611c9457600080fd5b6000806020838503121561247357600080fd5b82356001600160401b0381111561248957600080fd5b6123be8582860161241f565b600080600061014084860312156124ab57600080fd5b83356124b681611ffd565b925060208401356124c681611ffd565b91506124d585604086016121bb565b90509250925092565b60008060008060008060006101a0888a0312156124fa57600080fd5b873561250581611ffd565b9650602088013561251581611ffd565b9550604088013561252581611ffd565b94506060880135935061253b8960808a016121bb565b92506101808801356001600160401b0381111561255757600080fd5b61229d8a828b0161241f565b60006020828403121561257557600080fd5b81356001600160401b0381111561258b57600080fd5b82016060818503121561210c57600080fd5b6001600160e01b031991909116815260200190565b6000602082840312156125c457600080fd5b815161210c8161233d565b6000602082840312156125e157600080fd5b813561210c8161233d565b803582526020810135602083015260408101356040830152606081013560608301526080810135608083015260a081013560a083015260c081013560c083015260e081013561263a8161233d565b80151560e0840152505050565b6001600160a01b0383168152610120810161210c60208301846125ec565b6001600160501b0381168114611e5157600080fd5b6001600160401b0381168114611e5157600080fd5b64ffffffffff81168114611e5157600080fd5b600081356126af8161268f565b92915050565b61ffff81168114611e5157600080fd5b600081356126af816126b5565b600081356126af8161233d565b81356126ea81612665565b815469ffffffffffffffffffff19166001600160501b0382161782555060208201356127158161267a565b815467ffffffffffffffff60501b19811660509290921b67ffffffffffffffff60501b169182178355604084013561274c8161268f565b64ffffffffff60901b60909190911b166cffffffffffffffffffffffffff60501b1982168317811784556060850135612784816126b5565b6effffffffffffffffffffffffffffff60501b19929092169092179190911760b89190911b61ffff60b81b1617815561187a6127c2608084016126d2565b82805460ff60c81b191691151560c81b60ff60c81b16919091179055565b80356123568161268f565b8035612356816126b5565b60a08101823561280581612665565b6001600160501b03168252602083013561281e8161267a565b6001600160401b03166020830152604083013561283a8161268f565b64ffffffffff1660408301526060830135612854816126b5565b61ffff166060830152608083013561286b8161233d565b80151560808401525092915050565b634e487b7160e01b600052603260045260246000fd5b60008235603e198336030181126128a657600080fd5b9190910192915050565b6000808335601e198436030181126128c757600080fd5b8301803591506001600160401b038211156128e157600080fd5b6020019150600581901b3603821315611c9457600080fd5b634e487b7160e01b600052601160045260246000fd5b6000816000190483118215151615612929576129296128f9565b500290565b60008219821115612941576129416128f9565b500190565b60006020828403121561295857600080fd5b815161210c81611ffd565b6040815260006129766040830186612164565b8281036020848101919091528482528591810160005b868110156129ba57833561299f81611ffd565b6001600160a01b03168252928201929082019060010161298c565b50979650505050505050565b600181811c908216806129da57607f821691505b6020821081036121ce57634e487b7160e01b600052602260045260246000fd5b634e487b7160e01b600052604160045260246000fd5b601f821115611a4157600081815260208120601f850160051c81016020861015612a375750805b601f850160051c820191505b81811015610e0857828155600101612a43565b6001600160401b03831115612a6d57612a6d6129fa565b612a8183612a7b83546129c6565b83612a10565b6000601f841160018114612ab55760008515612a9d5750838201355b600019600387901b1c1916600186901b17835561088c565b600083815260209020601f19861690835b82811015612ae65786850135825560209485019460019092019101612ac6565b5086821015612b035760001960f88860031b161c19848701351681555b505060018560011b0183555050505050565b81835281816020850137506000828201602090810191909152601f909101601f19169091010190565b602081526000612b52602083018486612b15565b949350505050565b65ffffffffffff81168114611e5157600080fd5b600081356126af81612b5a565b60ff81168114611e5157600080fd5b600081356126af81612b7b565b8135612ba281612665565b815469ffffffffffffffffffff19166001600160501b038216178255506020820135612bcd816126b5565b815461ffff60501b19811660509290921b61ffff60501b1691821783556040840135612bf881612b5a565b67ffffffffffffffff60501b1991909116909117606091821b65ffffffffffff60601b16178255612c5690612c2e908401612b6e565b82805465ffffffffffff60901b191660909290921b65ffffffffffff60901b16919091179055565b612c83612c6560808401612b8a565b82805460ff60c01b191660c09290921b60ff60c01b16919091179055565b612cb8612c9260a084016126a2565b82805464ffffffffff60c81b191660c89290921b64ffffffffff60c81b16919091179055565b612ceb612cc760c084016126c5565b8280546001600160f01b031660f09290921b6001600160f01b031916919091179055565b61187a612cfa60e084016126d2565b6001830160ff1981541660ff8315151681178255505050565b600060208284031215612d2557600080fd5b813561210c816126b5565b803561235681612b5a565b803561235681612b7b565b61010081018235612d5681612665565b6001600160501b031682526020830135612d6f816126b5565b61ffff1660208301526040830135612d8681612b5a565b65ffffffffffff166040830152612d9f60608401612d30565b65ffffffffffff166060830152612db860808401612d3b565b60ff166080830152612dcc60a084016127e0565b64ffffffffff1660a0830152612de460c084016127eb565b61ffff1660c0830152612df960e0840161234b565b80151560e08401526120e8565b8381526001600160a01b03831660208201526101408101612b5260408301846125ec565b6000808335601e19843603018112612e4157600080fd5b8301803591506001600160401b03821115612e5b57600080fd5b602001915036819003821315611c9457600080fd5b6040808252810184905260006060600586901b8301810190830187835b88811015612f0057858403605f190183528135368b9003601e19018112612eb357600080fd5b8a0160208181019135906001600160401b03821115612ed157600080fd5b813603831315612ee057600080fd5b612eeb878385612b15565b96509485019493909301925050600101612e8d565b5050508281036020840152612f16818587612b15565b979650505050505050565b600080600060608486031215612f3657600080fd5b8351925060208401519150604084015190509250925092565b600082612f6c57634e487b7160e01b600052601260045260246000fd5b500490565b600082821015612f8357612f836128f9565b500390565b634e487b7160e01b600052602160045260246000fdfea26469706673582212206e73effaf3764248c116ee3a72258ca43e9caeab94b45b782b420433b05beeb864736f6c634300080f0033

Deployed Bytecode

0x60806040526004361061012a5760003560e01c80636d999226116100ab5780639d1636f31161006f5780639d1636f3146104bf578063b957d0cb146104ec578063bbf1ab0c1461050c578063bc6a629c1461052c578063bf52a8a914610653578063ebb4a55f1461066657600080fd5b80636d9992261461042c57806379627a321461044c5780637e3ba6af1461045f5780638e7d1e431461047f57806396751ae91461049f57600080fd5b806332bf11f5116100f257806332bf11f51461031c5780633644e5151461036057806337ca8c03146103945780634300a4e6146103c85780635cb3c4d3146103db57600080fd5b80630b0e8a6e1461012f57806312738db814610261578063161ac21f146102835780632db526eb14610296578063322e75d1146102c3575b600080fd5b34801561013b57600080fd5b5061024b61014a366004612012565b6040805161010081018252600080825260208201819052918101829052606081018290526080810182905260a0810182905260c0810182905260e0810191909152506001600160a01b03808316600090815260076020908152604080832093851683529281529082902082516101008101845281546001600160501b038116825261ffff600160501b820481169483019490945265ffffffffffff600160601b8204811695830195909552600160901b8104909416606082015260ff600160c01b85048116608083015264ffffffffff600160c81b86041660a0830152600160f01b90940490921660c083015260010154909116151560e082015292915050565b604051610258919061204b565b60405180910390f35b34801561026d57600080fd5b5061028161027c3660046120ef565b610686565b005b610281610291366004612113565b61076c565b3480156102a257600080fd5b506102b66102b13660046120ef565b610893565b60405161025891906121a8565b3480156102cf57600080fd5b5061030c6102de366004612012565b6001600160a01b03918216600090815260046020908152604080832093909416825291909152205460ff1690565b6040519015158152602001610258565b34801561032857600080fd5b506103526103373660046120ef565b6001600160a01b031660009081526003602052604090205490565b604051908152602001610258565b34801561036c57600080fd5b506103527fc5a5659b242a6663b12840058c7853f303b645b3c900c86203f2e12030167f1781565b3480156103a057600080fd5b506103527f5d2d2748b9e0b62f8bb884fa4fa88030198735648d300a06521feab7a3d6552d81565b6102816103d6366004612218565b610909565b3480156103e757600080fd5b506104146103f63660046120ef565b6001600160a01b039081166000908152600260205260409020541690565b6040516001600160a01b039091168152602001610258565b34801561043857600080fd5b506102816104473660046122b0565b6109ed565b61028161045a3660046122c2565b610ad1565b34801561046b57600080fd5b506102b661047a3660046120ef565b610e10565b34801561048b57600080fd5b5061028161049a36600461235b565b610e84565b3480156104ab57600080fd5b506102816104ba366004612389565b610f6e565b3480156104cb57600080fd5b506104df6104da3660046120ef565b611209565b60405161025891906123ca565b3480156104f857600080fd5b50610281610507366004612460565b6112ab565b34801561051857600080fd5b50610281610527366004612495565b611397565b34801561053857600080fd5b506105f26105473660046120ef565b6040805160a081018252600080825260208201819052918101829052606081018290526080810191909152506001600160a01b031660009081526020818152604091829020825160a08101845290546001600160501b0381168252600160501b81046001600160401b031692820192909252600160901b820464ffffffffff1692810192909252600160b81b810461ffff166060830152600160c81b900460ff161515608082015290565b6040516102589190600060a0820190506001600160501b0383511682526001600160401b03602084015116602083015264ffffffffff604084015116604083015261ffff606084015116606083015260808301511515608083015292915050565b6102816106613660046124de565b611587565b34801561067257600080fd5b50610281610681366004612563565b61173c565b6040516301ffc9a760e01b815233906301ffc9a7906106b09063290d607b60e21b9060040161259d565b602060405180830381865afa1580156106cd573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906106f191906125b2565b6107155760405163104fa0ef60e11b81523360048201526024015b60405180910390fd5b3360008181526002602052604080822080546001600160a01b0319166001600160a01b03861690811790915590519092917f0c69f21751e800ea5960436c9a94370c7adbf54c733a20a025293fbbe8f1625291a350565b6001600160a01b03841660009081526020818152604091829020825160a08101845290546001600160501b0381168252600160501b81046001600160401b0316928201839052600160901b810464ffffffffff1693820193909352600160b81b830461ffff166060820152600160c81b90920460ff16151560808301524210156108285760208101516040516309ed117960e11b81524260048201526001600160401b039182166024820152604481019190915260640161070c565b61083f8282600001516001600160501b031661183d565b610858858484846040015164ffffffffff16600061187e565b610867858583608001516119c4565b61088c85848484600001516001600160501b03166000866060015161ffff168a611a46565b5050505050565b6001600160a01b0381166000908152600860209081526040918290208054835181840281018401909452808452606093928301828280156108fd57602002820191906000526020600020905b81546001600160a01b031681526001909101906020018083116108df575b50505050509050919050565b61091b83604001358460600135611b1e565b61092684843561183d565b61093b87868686602001358760a0013561187e565b6109568787610951610100870160e088016125cf565b6119c4565b6109af8282600360008b6001600160a01b03166001600160a01b03168152602001908152602001600020548887604051602001610994929190612647565b60405160208183030381529060405280519060200120611b59565b6109cc576040516309bde33960e01b815260040160405180910390fd5b6109e48786868635608088013560c08901358c611a46565b50505050505050565b6040516301ffc9a760e01b815233906301ffc9a790610a179063290d607b60e21b9060040161259d565b602060405180830381865afa158015610a34573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610a5891906125b2565b610a775760405163104fa0ef60e11b815233600482015260240161070c565b3360009081526020819052604090208190610a9282826126df565b505060405133907ffb8089f2dff2c2a47c0a399dd9bd178cd9885036fd9dcea23f8ad0dcb6ffbbf890610ac69084906127f6565b60405180910390a250565b6000805b82811015610dde5736848483818110610af057610af061287a565b9050602002810190610b029190612890565b6001600160a01b038916600090815260076020908152604082209293509091908290610b30908501856120ef565b6001600160a01b0316815260208101919091526040016000208054909150610b709065ffffffffffff600160601b8204811691600160901b900416611b1e565b6001810154610b85908a908a9060ff166119c4565b6000610b9460208401846128b0565b8354909250610bad91506001600160501b03168261290f565b610bb7908661292e565b8254909550610be5908b908a908490600160501b810461ffff1690600160c81b900464ffffffffff1661187e565b60005b81811015610d9c576000610bff60208601866128b0565b83818110610c0f57610c0f61287a565b905060200201359050896001600160a01b0316856000016020810190610c3591906120ef565b6001600160a01b0316636352211e836040518263ffffffff1660e01b8152600401610c6291815260200190565b602060405180830381865afa158015610c7f573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610ca39190612946565b6001600160a01b031614610cf2578b610cbf60208701876120ef565b60405163da8c7bc760e01b81526001600160a01b039283166004820152911660248201526044810182905260640161070c565b6001600160a01b038c1660009081526009602090815260408220908290610d1b908901896120ef565b6001600160a01b031681526020808201929092526040908101600090812085825290925290205460ff169050801515600103610d92578c610d5f60208801886120ef565b60405163a93f299b60e01b81526001600160a01b039283166004820152911660248201526044810183905260640161070c565b5050600101610be8565b508154610dd0908b908a9084906001600160501b03811690600160c01b810460ff1690600160f01b900461ffff168f611a46565b836001019350505050610ad5565b50803414610e0857604051630d35e92160e01b81523460048201526024810182905260440161070c565b505050505050565b6001600160a01b0381166000908152600660209081526040918290208054835181840281018401909452808452606093928301828280156108fd576020028201919060005260206000209081546001600160a01b031681526001909101906020018083116108df5750505050509050919050565b6040516301ffc9a760e01b815233906301ffc9a790610eae9063290d607b60e21b9060040161259d565b602060405180830381865afa158015610ecb573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610eef91906125b2565b610f0e5760405163104fa0ef60e11b815233600482015260240161070c565b3360008181526004602090815260408083206001600160a01b0387168085529252808320805460ff19168615159081179091559051909391927f6486c31f9d664e241acf94ec2541d328f6b9e97257ae16a1d887f296f879719f91a45050565b6040516301ffc9a760e01b815233906301ffc9a790610f989063290d607b60e21b9060040161259d565b602060405180830381865afa158015610fb5573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610fd991906125b2565b610ff85760405163104fa0ef60e11b815233600482015260240161070c565b336000908152600660209081526040808320805482518185028101850190935280835290939284919083018282801561105a57602002820191906000526020600020905b81546001600160a01b0316815260019091019060200180831161103c575b505033600090815260066020526040812094955061107d9493509150611fcb9050565b60005b838110156110e8578285858381811061109b5761109b61287a565b90506020020160208101906110b091906120ef565b815460018082018455600093845260209093200180546001600160a01b0319166001600160a01b039290921691909117905501611080565b50336000908152600560205260408120905b825181101561115457600082600085848151811061111a5761111a61287a565b6020908102919091018101516001600160a01b03168252810191909152604001600020805460ff19169115159190911790556001016110fa565b5060005b848110156111bc5760018260008888858181106111775761117761287a565b905060200201602081019061118c91906120ef565b6001600160a01b031681526020810191909152604001600020805460ff1916911515919091179055600101611158565b50336001600160a01b03167ff91188cded8aeb0b400eb42edd175d6dc8d5e5a9f457adfc27e0be7bd612caab8387876040516111fa93929190612963565b60405180910390a25050505050565b6001600160a01b0381166000908152600160205260409020805460609190611230906129c6565b80601f016020809104026020016040519081016040528092919081815260200182805461125c906129c6565b80156108fd5780601f1061127e576101008083540402835291602001916108fd565b820191906000526020600020905b81548152906001019060200180831161128c5750939695505050505050565b6040516301ffc9a760e01b815233906301ffc9a7906112d59063290d607b60e21b9060040161259d565b602060405180830381865afa1580156112f2573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061131691906125b2565b6113355760405163104fa0ef60e11b815233600482015260240161070c565b33600090815260016020526040902061134f828483612a56565b50336001600160a01b03167fa0295608d25b3033c2e2c41cbac8746c2d08767bcfde6d47fae1ed7ba1d32150838360405161138b929190612b3e565b60405180910390a25050565b6040516301ffc9a760e01b815233906301ffc9a7906113c19063290d607b60e21b9060040161259d565b602060405180830381865afa1580156113de573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061140291906125b2565b6114215760405163104fa0ef60e11b815233600482015260240161070c565b6001600160a01b03808416600090815260076020908152604080832093861683529290522081906114528282612b97565b50600090506114676040830160208401612d13565b61ffff161115611537576000805b6001600160a01b0385166000908152600860205260409020548110156114eb576001600160a01b038581166000908152600860205260409020805491861691839081106114c4576114c461287a565b6000918252602090912001546001600160a01b0316036114e357600191505b600101611475565b50801515600003611535576001600160a01b0384811660009081526008602090815260408220805460018101825590835291200180546001600160a01b0319169185169190911790555b505b816001600160a01b0316836001600160a01b03167f5ddb09355563c3e5d9f8472902701579edb3aca9bc1c2d53a316eafd91df9ebb8360405161157a9190612d46565b60405180910390a3505050565b61159983604001358460600135611b1e565b6115a484843561183d565b6115b987868686602001358760a0013561187e565b6115cf8787610951610100870160e088016125cf565b600061190160f01b7fc5a5659b242a6663b12840058c7853f303b645b3c900c86203f2e12030167f177f5d2d2748b9e0b62f8bb884fa4fa88030198735648d300a06521feab7a3d6552d888760405160200161162d93929190612e06565b60408051601f198184030181529082905280516020918201206001600160f01b0319909416908201526022810191909152604281019190915260620160405160208183030381529060405280519060200120905060006116c584848080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152508693925050611b939050565b6001600160a01b03808b1660009081526005602090815260408083209385168352929052205490915060ff1661171957604051633615713d60e21b81526001600160a01b038216600482015260240161070c565b611731898888883560808a013560c08b01358e611a46565b505050505050505050565b6040516301ffc9a760e01b815233906301ffc9a7906117669063290d607b60e21b9060040161259d565b602060405180830381865afa158015611783573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906117a791906125b2565b6117c65760405163104fa0ef60e11b815233600482015260240161070c565b33600081815260036020908152604090912080548435918290559290918391907fefcd7e019bc8b47d27881fd59e2619280ca5894f285950f10ab049870652efa590611814908701876128b0565b6118216040890189612e2a565b6040516118319493929190612e70565b60405180910390a45050565b611847818361290f565b341461187a5734611858828461290f565b604051630d35e92160e01b81526004810192909252602482015260440161070c565b5050565b604051632103857560e21b81526001600160a01b038581166004830152600091829182919089169063840e15d490602401606060405180830381865afa1580156118cc573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906118f09190612f21565b9194509250905084611902848861292e565b111561193657611912838761292e565b60405163edc0127360e01b815260048101919091526024810186905260440161070c565b80611941838861292e565b111561197557611951828761292e565b60405163384b48c560e21b815260048101919091526024810182905260440161070c565b83156119ba5783611986838861292e565b11156119ba57611996828761292e565b604051635cc6d5f560e11b815260048101919091526024810185905260440161070c565b5050505050505050565b6001600160a01b0382166119eb57604051635136e8d560e01b815260040160405180910390fd5b6001811515148015611a2357506001600160a01b0380841660009081526004602090815260408083209386168352929052205460ff16155b15611a415760405163f477d26f60e01b815260040160405180910390fd5b505050565b611a51878284611bb7565b6040516364869dad60e01b81526001600160a01b038781166004830152602482018790528816906364869dad90604401600060405180830381600087803b158015611a9b57600080fd5b505af1158015611aaf573d6000803e3d6000fd5b5050604080513381526020810189905290810187905260608101859052608081018690526001600160a01b03808516935089811692508a16907fe90cf9cc0a552cf52ea6ff74ece0f1c8ae8cc9ad630d3181f55ac43ca076b7d69060a00160405180910390a450505050505050565b81421080611b2b57508042115b1561187a576040516309ed117960e11b8152426004820152602481018390526044810182905260640161070c565b60008315611b8b578360051b8501855b803580851160051b94855260209485185260406000209301818110611b695750505b501492915050565b6000806000611ba28585611c2d565b91509150611baf81611c9b565b509392505050565b6001600160a01b038084166000908152600260205260409020541680611bf057604051633f00976960e01b815260040160405180910390fd5b6000612710611bff843461290f565b611c099190612f4f565b90506000611c178234612f71565b9050611c238583611e54565b610e088382611e54565b6000808251604103611c635760208301516040840151606085015160001a611c5787828585611ea5565b94509450505050611c94565b8251604003611c8c5760208301516040840151611c81868383611f92565b935093505050611c94565b506000905060025b9250929050565b6000816004811115611caf57611caf612f88565b03611cb75750565b6001816004811115611ccb57611ccb612f88565b03611d185760405162461bcd60e51b815260206004820152601860248201527f45434453413a20696e76616c6964207369676e61747572650000000000000000604482015260640161070c565b6002816004811115611d2c57611d2c612f88565b03611d795760405162461bcd60e51b815260206004820152601f60248201527f45434453413a20696e76616c6964207369676e6174757265206c656e67746800604482015260640161070c565b6003816004811115611d8d57611d8d612f88565b03611de55760405162461bcd60e51b815260206004820152602260248201527f45434453413a20696e76616c6964207369676e6174757265202773272076616c604482015261756560f01b606482015260840161070c565b6004816004811115611df957611df9612f88565b03611e515760405162461bcd60e51b815260206004820152602260248201527f45434453413a20696e76616c6964207369676e6174757265202776272076616c604482015261756560f01b606482015260840161070c565b50565b600080600080600085875af1905080611a415760405162461bcd60e51b815260206004820152601360248201527211551217d514905394d1915497d19052531151606a1b604482015260640161070c565b6000807f7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a0831115611edc5750600090506003611f89565b8460ff16601b14158015611ef457508460ff16601c14155b15611f055750600090506004611f89565b6040805160008082526020820180845289905260ff881692820192909252606081018690526080810185905260019060a0016020604051602081039080840390855afa158015611f59573d6000803e3d6000fd5b5050604051601f1901519150506001600160a01b038116611f8257600060019250925050611f89565b9150600090505b94509492505050565b6000806001600160ff1b03831681611faf60ff86901c601b61292e565b9050611fbd87828885611ea5565b935093505050935093915050565b5080546000825590600052602060002090810190611e5191905b80821115611ff95760008155600101611fe5565b5090565b6001600160a01b0381168114611e5157600080fd5b6000806040838503121561202557600080fd5b823561203081611ffd565b9150602083013561204081611ffd565b809150509250929050565b6000610100820190506001600160501b03835116825261ffff6020840151166020830152604083015165ffffffffffff8082166040850152806060860151166060850152505060808301516120a5608084018260ff169052565b5060a08301516120be60a084018264ffffffffff169052565b5060c08301516120d460c084018261ffff169052565b5060e08301516120e860e084018215159052565b5092915050565b60006020828403121561210157600080fd5b813561210c81611ffd565b9392505050565b6000806000806080858703121561212957600080fd5b843561213481611ffd565b9350602085013561214481611ffd565b9250604085013561215481611ffd565b9396929550929360600135925050565b600081518084526020808501945080840160005b8381101561219d5781516001600160a01b031687529582019590820190600101612178565b509495945050505050565b60208152600061210c6020830184612164565b600061010082840312156121ce57600080fd5b50919050565b60008083601f8401126121e657600080fd5b5081356001600160401b038111156121fd57600080fd5b6020830191508360208260051b8501011115611c9457600080fd5b60008060008060008060006101a0888a03121561223457600080fd5b873561223f81611ffd565b9650602088013561224f81611ffd565b9550604088013561225f81611ffd565b9450606088013593506122758960808a016121bb565b92506101808801356001600160401b0381111561229157600080fd5b61229d8a828b016121d4565b989b979a50959850939692959293505050565b600060a082840312156121ce57600080fd5b6000806000806000608086880312156122da57600080fd5b85356122e581611ffd565b945060208601356122f581611ffd565b9350604086013561230581611ffd565b925060608601356001600160401b0381111561232057600080fd5b61232c888289016121d4565b969995985093965092949392505050565b8015158114611e5157600080fd5b80356123568161233d565b919050565b6000806040838503121561236e57600080fd5b823561237981611ffd565b915060208301356120408161233d565b6000806020838503121561239c57600080fd5b82356001600160401b038111156123b257600080fd5b6123be858286016121d4565b90969095509350505050565b600060208083528351808285015260005b818110156123f7578581018301518582016040015282016123db565b81811115612409576000604083870101525b50601f01601f1916929092016040019392505050565b60008083601f84011261243157600080fd5b5081356001600160401b0381111561244857600080fd5b602083019150836020828501011115611c9457600080fd5b6000806020838503121561247357600080fd5b82356001600160401b0381111561248957600080fd5b6123be8582860161241f565b600080600061014084860312156124ab57600080fd5b83356124b681611ffd565b925060208401356124c681611ffd565b91506124d585604086016121bb565b90509250925092565b60008060008060008060006101a0888a0312156124fa57600080fd5b873561250581611ffd565b9650602088013561251581611ffd565b9550604088013561252581611ffd565b94506060880135935061253b8960808a016121bb565b92506101808801356001600160401b0381111561255757600080fd5b61229d8a828b0161241f565b60006020828403121561257557600080fd5b81356001600160401b0381111561258b57600080fd5b82016060818503121561210c57600080fd5b6001600160e01b031991909116815260200190565b6000602082840312156125c457600080fd5b815161210c8161233d565b6000602082840312156125e157600080fd5b813561210c8161233d565b803582526020810135602083015260408101356040830152606081013560608301526080810135608083015260a081013560a083015260c081013560c083015260e081013561263a8161233d565b80151560e0840152505050565b6001600160a01b0383168152610120810161210c60208301846125ec565b6001600160501b0381168114611e5157600080fd5b6001600160401b0381168114611e5157600080fd5b64ffffffffff81168114611e5157600080fd5b600081356126af8161268f565b92915050565b61ffff81168114611e5157600080fd5b600081356126af816126b5565b600081356126af8161233d565b81356126ea81612665565b815469ffffffffffffffffffff19166001600160501b0382161782555060208201356127158161267a565b815467ffffffffffffffff60501b19811660509290921b67ffffffffffffffff60501b169182178355604084013561274c8161268f565b64ffffffffff60901b60909190911b166cffffffffffffffffffffffffff60501b1982168317811784556060850135612784816126b5565b6effffffffffffffffffffffffffffff60501b19929092169092179190911760b89190911b61ffff60b81b1617815561187a6127c2608084016126d2565b82805460ff60c81b191691151560c81b60ff60c81b16919091179055565b80356123568161268f565b8035612356816126b5565b60a08101823561280581612665565b6001600160501b03168252602083013561281e8161267a565b6001600160401b03166020830152604083013561283a8161268f565b64ffffffffff1660408301526060830135612854816126b5565b61ffff166060830152608083013561286b8161233d565b80151560808401525092915050565b634e487b7160e01b600052603260045260246000fd5b60008235603e198336030181126128a657600080fd5b9190910192915050565b6000808335601e198436030181126128c757600080fd5b8301803591506001600160401b038211156128e157600080fd5b6020019150600581901b3603821315611c9457600080fd5b634e487b7160e01b600052601160045260246000fd5b6000816000190483118215151615612929576129296128f9565b500290565b60008219821115612941576129416128f9565b500190565b60006020828403121561295857600080fd5b815161210c81611ffd565b6040815260006129766040830186612164565b8281036020848101919091528482528591810160005b868110156129ba57833561299f81611ffd565b6001600160a01b03168252928201929082019060010161298c565b50979650505050505050565b600181811c908216806129da57607f821691505b6020821081036121ce57634e487b7160e01b600052602260045260246000fd5b634e487b7160e01b600052604160045260246000fd5b601f821115611a4157600081815260208120601f850160051c81016020861015612a375750805b601f850160051c820191505b81811015610e0857828155600101612a43565b6001600160401b03831115612a6d57612a6d6129fa565b612a8183612a7b83546129c6565b83612a10565b6000601f841160018114612ab55760008515612a9d5750838201355b600019600387901b1c1916600186901b17835561088c565b600083815260209020601f19861690835b82811015612ae65786850135825560209485019460019092019101612ac6565b5086821015612b035760001960f88860031b161c19848701351681555b505060018560011b0183555050505050565b81835281816020850137506000828201602090810191909152601f909101601f19169091010190565b602081526000612b52602083018486612b15565b949350505050565b65ffffffffffff81168114611e5157600080fd5b600081356126af81612b5a565b60ff81168114611e5157600080fd5b600081356126af81612b7b565b8135612ba281612665565b815469ffffffffffffffffffff19166001600160501b038216178255506020820135612bcd816126b5565b815461ffff60501b19811660509290921b61ffff60501b1691821783556040840135612bf881612b5a565b67ffffffffffffffff60501b1991909116909117606091821b65ffffffffffff60601b16178255612c5690612c2e908401612b6e565b82805465ffffffffffff60901b191660909290921b65ffffffffffff60901b16919091179055565b612c83612c6560808401612b8a565b82805460ff60c01b191660c09290921b60ff60c01b16919091179055565b612cb8612c9260a084016126a2565b82805464ffffffffff60c81b191660c89290921b64ffffffffff60c81b16919091179055565b612ceb612cc760c084016126c5565b8280546001600160f01b031660f09290921b6001600160f01b031916919091179055565b61187a612cfa60e084016126d2565b6001830160ff1981541660ff8315151681178255505050565b600060208284031215612d2557600080fd5b813561210c816126b5565b803561235681612b5a565b803561235681612b7b565b61010081018235612d5681612665565b6001600160501b031682526020830135612d6f816126b5565b61ffff1660208301526040830135612d8681612b5a565b65ffffffffffff166040830152612d9f60608401612d30565b65ffffffffffff166060830152612db860808401612d3b565b60ff166080830152612dcc60a084016127e0565b64ffffffffff1660a0830152612de460c084016127eb565b61ffff1660c0830152612df960e0840161234b565b80151560e08401526120e8565b8381526001600160a01b03831660208201526101408101612b5260408301846125ec565b6000808335601e19843603018112612e4157600080fd5b8301803591506001600160401b03821115612e5b57600080fd5b602001915036819003821315611c9457600080fd5b6040808252810184905260006060600586901b8301810190830187835b88811015612f0057858403605f190183528135368b9003601e19018112612eb357600080fd5b8a0160208181019135906001600160401b03821115612ed157600080fd5b813603831315612ee057600080fd5b612eeb878385612b15565b96509485019493909301925050600101612e8d565b5050508281036020840152612f16818587612b15565b979650505050505050565b600080600060608486031215612f3657600080fd5b8351925060208401519150604084015190509250925092565b600082612f6c57634e487b7160e01b600052601260045260246000fd5b500490565b600082821015612f8357612f836128f9565b500390565b634e487b7160e01b600052602160045260246000fdfea26469706673582212206e73effaf3764248c116ee3a72258ca43e9caeab94b45b782b420433b05beeb864736f6c634300080f0033

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.