EthereumEthereum
0x5c...cd85
DBY Club Pass

DBY Club Pass

DBYPASS

收藏品
大小
418
收藏品
所有者
218
52% 独特的所有者
此合同的源代码已经过验证!
合同元数据
编译器
0.8.18+commit.87f61d96
语言
Solidity
合同源代码
文件 1 的 15:Context.sol
合同源代码
文件 2 的 15:DBYClubPass.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.7;

import "https://github.com/chiru-labs/ERC721A/blob/main/contracts/extensions/ERC721AQueryable.sol";
import "https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/access/Ownable.sol";
import "https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/utils/cryptography/ECDSA.sol";
import "https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/utils/Strings.sol";
import "https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v4.8.2/contracts/token/common/ERC2981.sol";

enum SalePhase {
    CLOSED,
    FREE,
    PAID,
    OPEN
}

error HashAlreadyUsed(bytes32 messageHash);
error HashDoesNotMatch(bytes32 messageHash);
error InsufficientPayment(uint256 weiSent, uint256 weiRequired, uint256 quantity);
error MaxSupplyReached();
error RejectZeroAddress();
error SaleNotActive(SalePhase salePhase, SalePhase attemptedPhase);
error SignerDoesNotMatchServer();
error InvalidQuantity(uint256 quantity);

contract DBYClubPass is ERC2981, ERC721AQueryable, Ownable {
    uint16 public constant MAX_SUPPLY = 15000;
    uint256 public constant RESERVED_SUPPLY = 2200;

    string public constant TOKEN_NAME = "DBY Club Pass";
    string public constant TOKEN_SYMBOL = "DBYPASS";

    string private tokenBaseURI;
    address private serverAddress;
    address private withdrawalAddress;

    mapping(bytes32 => bool) public usedHashes;

    SalePhase public salePhase = SalePhase.CLOSED;
    uint256 public mintPrice = 0.025 ether;

    // Require externally-owned accounts
    modifier onlyEOA() {
        require(msg.sender == tx.origin, "Not externally owned account");
        _;
    }

    constructor(string memory tokenBaseURI_, address serverAddress_, address withdrawalAddress_)
        ERC721A(TOKEN_NAME, TOKEN_SYMBOL)
    {
        tokenBaseURI = tokenBaseURI_;
        serverAddress = serverAddress_;
        withdrawalAddress = withdrawalAddress_;

        _mintERC2309(withdrawalAddress_, RESERVED_SUPPLY);
        _setDefaultRoyalty(withdrawalAddress_, 500);
    }

    /// @notice Set the token URI
    /// @param newTokenBaseURI New URI
    function setTokenBaseURI(string memory newTokenBaseURI) external onlyOwner {
        tokenBaseURI = newTokenBaseURI;
    }

    /// @dev View function used in ERC721A's 'tokenURI()' function
    function _baseURI() internal view override returns (string memory) {
        return tokenBaseURI;
    }

    /// @notice Set the current sale phase
    /// @param phase New sale phase
    function setSalePhase(SalePhase phase) external onlyOwner {
        salePhase = phase;
    }

    /// @notice Set the fee numerator for default royalty
    /// @param feeNumerator New fee numerator
    function setDefaultRoyalty(uint96 feeNumerator) external onlyOwner {
        _setDefaultRoyalty(withdrawalAddress, feeNumerator);
    }

    /// @notice Set new royalties
    /// @param price New mint price in wei
    function setMintPrice(uint256 price) external onlyOwner {
        mintPrice = price;
    }

    /// @notice Get whether or not address has minted
    /// @param owner Address to check
    function hasMinted(address owner) external view returns (bool) {
        return _numberMinted(owner) > 0;
    }

    function batchMint(uint256 quantity) external onlyOwner {
        _safeMint(msg.sender, quantity);
    }

    /// @notice Free mint
    /// @param v Parity of the y-coordinate of r
    /// @param r X-coordinate of r
    /// @param s S value of the signature
    /// @param msgLen Length of the unhashed message
    function freeMint(bytes32 messageHash, uint8 v, bytes32 r, bytes32 s, uint256 msgLen)
        external
        onlyEOA
    {
        if (salePhase != SalePhase.FREE && salePhase != SalePhase.OPEN) {
            revert SaleNotActive(salePhase, SalePhase.FREE);
        }
        if (totalSupply() + 1 > MAX_SUPPLY) {
            revert MaxSupplyReached();
        }
        if (!verifySignature(messageHash, v, r, s, msgLen, true)) {
            revert HashDoesNotMatch(messageHash);
        }
        if (usedHashes[messageHash]) {
            revert HashAlreadyUsed(messageHash);
        }
        usedHashes[messageHash] = true;
        _mint(msg.sender, 1);
    }

    /// @notice Paid mint
    /// @param v Parity of the y-coordinate of r
    /// @param r X-coordinate of r
    /// @param s S value of the signature
    /// @param msgLen Length of the unhashed message
    function mint(
        bytes32 messageHash,
        uint8 v,
        bytes32 r,
        bytes32 s,
        uint256 msgLen,
        uint256 quantity
    ) external payable onlyEOA {
        if (salePhase != SalePhase.PAID && salePhase != SalePhase.OPEN) {
            revert SaleNotActive(salePhase, SalePhase.PAID);
        }
        if (quantity != 1 && quantity != 2) {
            revert InvalidQuantity(quantity);
        }
        if (totalSupply() + quantity > MAX_SUPPLY) {
            revert MaxSupplyReached();
        }
        if (msg.value != mintPrice * quantity) {
            revert InsufficientPayment(msg.value, mintPrice, quantity);
        }
        if (!verifySignature(messageHash, v, r, s, msgLen, false)) {
            revert HashDoesNotMatch(messageHash);
        }
        if (usedHashes[messageHash]) {
            revert HashAlreadyUsed(messageHash);
        }
        usedHashes[messageHash] = true;
        _mint(msg.sender, quantity);
    }

    /// @notice Set the withdrawal address and set royalties to go to new withdrawal address
    /// @param _withdrawalAddress New address to send withdrawals
    function setWithdrawalAddress(address _withdrawalAddress) external onlyOwner {
        if (_withdrawalAddress == address(0)) {
            revert RejectZeroAddress();
        }
        withdrawalAddress = _withdrawalAddress;
        _setDefaultRoyalty(_withdrawalAddress, 500);
    }

    /// @notice Withdraw the ETH from the contract
    function withdrawETH() external onlyOwner {
        (bool sent,) = payable(withdrawalAddress).call{value: address(this).balance}("");
        require(sent, "Withdraw failed");
    }

    /// @notice Verify the incoming hash from the server
    function verifySignature(
        bytes32 messageHash,
        uint8 v,
        bytes32 r,
        bytes32 s,
        uint256 msgLen,
        bool isFree
    ) private view returns (bool) {
        bytes memory prefix = "\x19Ethereum Signed Message:\n";
        bytes32 contractHash = keccak256(
            abi.encodePacked(
                prefix,
                Strings.toString(msgLen),
                string.concat(
                    Strings.toHexString(uint256(uint160(msg.sender)), 20), isFree ? "free" : "paid"
                )
            )
        );

        address signer = ecrecover(contractHash, v, r, s);
        if (signer != serverAddress) {
            revert SignerDoesNotMatchServer();
        }
        return contractHash == messageHash;
    }

    function supportsInterface(bytes4 interfaceId)
        public
        view
        virtual
        override(IERC721A, ERC721A, ERC2981)
        returns (bool)
    {
        return super.supportsInterface(interfaceId);
    }
    
    /// @notice Set server address
    /// @param _serverAddress New server address
    function setServerAddress(address _serverAddress) external onlyOwner {
        serverAddress = _serverAddress;
    }
}
合同源代码
文件 3 的 15:ECDSA.sol
合同源代码
文件 4 的 15:ERC165.sol
合同源代码
文件 5 的 15:ERC2981.sol
合同源代码
文件 6 的 15:ERC721A.sol
合同源代码
文件 7 的 15:ERC721AQueryable.sol
合同源代码
文件 8 的 15:IERC165.sol
合同源代码
文件 9 的 15:IERC2981.sol
合同源代码
文件 10 的 15:IERC721A.sol
合同源代码
文件 11 的 15:IERC721AQueryable.sol
合同源代码
文件 12 的 15:Math.sol
合同源代码
文件 13 的 15:Ownable.sol
合同源代码
文件 14 的 15:SignedMath.sol
合同源代码
文件 15 的 15:Strings.sol
设置
{
  "compilationTarget": {
    "src/DBYClubPass.sol": "DBYClubPass"
  },
  "evmVersion": "paris",
  "libraries": {},
  "metadata": {
    "bytecodeHash": "ipfs"
  },
  "optimizer": {
    "enabled": false,
    "runs": 200
  },
  "remappings": []
}
ABI
[{"inputs":[{"internalType":"string","name":"tokenBaseURI_","type":"string"},{"internalType":"address","name":"serverAddress_","type":"address"},{"internalType":"address","name":"withdrawalAddress_","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"ApprovalCallerNotOwnerNorApproved","type":"error"},{"inputs":[],"name":"ApprovalQueryForNonexistentToken","type":"error"},{"inputs":[],"name":"BalanceQueryForZeroAddress","type":"error"},{"inputs":[{"internalType":"bytes32","name":"messageHash","type":"bytes32"}],"name":"HashAlreadyUsed","type":"error"},{"inputs":[{"internalType":"bytes32","name":"messageHash","type":"bytes32"}],"name":"HashDoesNotMatch","type":"error"},{"inputs":[{"internalType":"uint256","name":"weiSent","type":"uint256"},{"internalType":"uint256","name":"weiRequired","type":"uint256"},{"internalType":"uint256","name":"quantity","type":"uint256"}],"name":"InsufficientPayment","type":"error"},{"inputs":[{"internalType":"uint256","name":"quantity","type":"uint256"}],"name":"InvalidQuantity","type":"error"},{"inputs":[],"name":"InvalidQueryRange","type":"error"},{"inputs":[],"name":"MaxSupplyReached","type":"error"},{"inputs":[],"name":"MintERC2309QuantityExceedsLimit","type":"error"},{"inputs":[],"name":"MintToZeroAddress","type":"error"},{"inputs":[],"name":"MintZeroQuantity","type":"error"},{"inputs":[],"name":"OwnerQueryForNonexistentToken","type":"error"},{"inputs":[],"name":"OwnershipNotInitializedForExtraData","type":"error"},{"inputs":[],"name":"RejectZeroAddress","type":"error"},{"inputs":[{"internalType":"enum SalePhase","name":"salePhase","type":"uint8"},{"internalType":"enum SalePhase","name":"attemptedPhase","type":"uint8"}],"name":"SaleNotActive","type":"error"},{"inputs":[],"name":"SignerDoesNotMatchServer","type":"error"},{"inputs":[],"name":"TransferCallerNotOwnerNorApproved","type":"error"},{"inputs":[],"name":"TransferFromIncorrectOwner","type":"error"},{"inputs":[],"name":"TransferToNonERC721ReceiverImplementer","type":"error"},{"inputs":[],"name":"TransferToZeroAddress","type":"error"},{"inputs":[],"name":"URIQueryForNonexistentToken","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"approved","type":"address"},{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"operator","type":"address"},{"indexed":false,"internalType":"bool","name":"approved","type":"bool"}],"name":"ApprovalForAll","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"fromTokenId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"toTokenId","type":"uint256"},{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"}],"name":"ConsecutiveTransfer","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"Transfer","type":"event"},{"inputs":[],"name":"MAX_SUPPLY","outputs":[{"internalType":"uint16","name":"","type":"uint16"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"RESERVED_SUPPLY","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"TOKEN_NAME","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"TOKEN_SYMBOL","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"approve","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"quantity","type":"uint256"}],"name":"batchMint","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"explicitOwnershipOf","outputs":[{"components":[{"internalType":"address","name":"addr","type":"address"},{"internalType":"uint64","name":"startTimestamp","type":"uint64"},{"internalType":"bool","name":"burned","type":"bool"},{"internalType":"uint24","name":"extraData","type":"uint24"}],"internalType":"struct IERC721A.TokenOwnership","name":"ownership","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256[]","name":"tokenIds","type":"uint256[]"}],"name":"explicitOwnershipsOf","outputs":[{"components":[{"internalType":"address","name":"addr","type":"address"},{"internalType":"uint64","name":"startTimestamp","type":"uint64"},{"internalType":"bool","name":"burned","type":"bool"},{"internalType":"uint24","name":"extraData","type":"uint24"}],"internalType":"struct IERC721A.TokenOwnership[]","name":"","type":"tuple[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"messageHash","type":"bytes32"},{"internalType":"uint8","name":"v","type":"uint8"},{"internalType":"bytes32","name":"r","type":"bytes32"},{"internalType":"bytes32","name":"s","type":"bytes32"},{"internalType":"uint256","name":"msgLen","type":"uint256"}],"name":"freeMint","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"getApproved","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"hasMinted","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"operator","type":"address"}],"name":"isApprovedForAll","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"messageHash","type":"bytes32"},{"internalType":"uint8","name":"v","type":"uint8"},{"internalType":"bytes32","name":"r","type":"bytes32"},{"internalType":"bytes32","name":"s","type":"bytes32"},{"internalType":"uint256","name":"msgLen","type":"uint256"},{"internalType":"uint256","name":"quantity","type":"uint256"}],"name":"mint","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"mintPrice","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"ownerOf","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_tokenId","type":"uint256"},{"internalType":"uint256","name":"_salePrice","type":"uint256"}],"name":"royaltyInfo","outputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"safeTransferFrom","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"bytes","name":"_data","type":"bytes"}],"name":"safeTransferFrom","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"salePhase","outputs":[{"internalType":"enum SalePhase","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"operator","type":"address"},{"internalType":"bool","name":"approved","type":"bool"}],"name":"setApprovalForAll","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint96","name":"feeNumerator","type":"uint96"}],"name":"setDefaultRoyalty","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"price","type":"uint256"}],"name":"setMintPrice","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"enum SalePhase","name":"phase","type":"uint8"}],"name":"setSalePhase","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_serverAddress","type":"address"}],"name":"setServerAddress","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string","name":"newTokenBaseURI","type":"string"}],"name":"setTokenBaseURI","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_withdrawalAddress","type":"address"}],"name":"setWithdrawalAddress","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes4","name":"interfaceId","type":"bytes4"}],"name":"supportsInterface","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"tokenURI","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"tokensOfOwner","outputs":[{"internalType":"uint256[]","name":"","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"uint256","name":"start","type":"uint256"},{"internalType":"uint256","name":"stop","type":"uint256"}],"name":"tokensOfOwnerIn","outputs":[{"internalType":"uint256[]","name":"","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"transferFrom","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"name":"usedHashes","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"withdrawETH","outputs":[],"stateMutability":"nonpayable","type":"function"}]