pragma solidity ^0.8.0;






contract Shinsekai is Ownable, ERC721A, ReentrancyGuard {
    uint256 public immutable amountForTeam;
    uint private constant MAX_SUPPLY = 10000;
    uint private constant MAX_BATCH = 5;
    bytes32 public portalAccessRoot;
    bytes32 public reserveCorpRoot;
    bytes32 public publicRegistrationRoot;
    mapping(address => uint256) public portalAccessClaimed;
    mapping(address => bool) public portalAccessClaimedChecker;
    mapping(address => uint256) public reserveCorpClaimed;
    mapping(address => bool) public reserveCorpClaimedChecker;
    mapping(address => uint256) public publicRegistrationClaimed;
    mapping(address => bool) public publicRegistrationChecker;
    

    struct SaleConfig {
        uint32 publicSaleStartTime;
        uint32 publicSaleKey;
        uint64 portalAccessPrice;
        uint64 reserveCorpPrice;
        uint64 publicRegistrationPrice;
        uint64 publicPrice;
        
    }

    SaleConfig public saleConfig;

    constructor(
        uint256 amountForTeam_
    ) ERC721A("Shinsekai", "SSK") {
        
        amountForTeam = amountForTeam_;
        require(
            amountForTeam_ <= MAX_SUPPLY,
            "larger collection size needed"
        );
    }

    modifier callerIsUser() {
        require(tx.origin == msg.sender, "The caller is another contract");
        _;
    }

    function gift(address[] calldata receivers) external onlyOwner {
        require(totalSupply() + receivers.length <= MAX_SUPPLY, "MAX_MINT");
        for (uint256 i = 0; i < receivers.length; i++) {
            _safeMint(receivers[i], 1);
        }
    }

    function reserveGiveaway(uint256 num, address walletAddress)
        public
        onlyOwner
    {
        require(totalSupply() + num <= MAX_SUPPLY, "Exceeds total supply");
        _safeMint(walletAddress, num);
    }

    function portalAccess(bytes32[] calldata _merkleProof, uint256 quantity) external payable callerIsUser {
        bytes32 leaf = keccak256(abi.encodePacked(msg.sender));
        require(MerkleProof.verify(_merkleProof, portalAccessRoot, leaf),
            "Invalid Merkle Proof.");
        uint256 price = uint256(saleConfig.portalAccessPrice);
        require(price != 0, "whitelist sale has not begun yet");
        require(totalSupply() + quantity <= 8000, "Exceeds total supply");
        if (!portalAccessClaimedChecker[msg.sender]) {
            portalAccessClaimedChecker[msg.sender] = true;
            portalAccessClaimed[msg.sender] = 2;
        }
        require(portalAccessClaimed[msg.sender] - quantity >= 0, "Address already minted num of tokens allowed");
        portalAccessClaimed[msg.sender] = portalAccessClaimed[msg.sender] - quantity;
        _safeMint(msg.sender, quantity);
        refundIfOver(price * quantity);
    }


    function reserveCorp(bytes32[] calldata _merkleProof, uint256 quantity) external payable callerIsUser {
        bytes32 leaf = keccak256(abi.encodePacked(msg.sender));
        require(MerkleProof.verify(_merkleProof, reserveCorpRoot, leaf),
            "Invalid Merkle Proof.");
        uint256 price = uint256(saleConfig.reserveCorpPrice);
        require(price != 0, "whitelist sale has not begun yet");
        require(totalSupply() + quantity <= 9000, "Exceeds total supply");
        if (!reserveCorpClaimedChecker[msg.sender]) {
            reserveCorpClaimedChecker[msg.sender] = true;
            reserveCorpClaimed[msg.sender] = 2;
        }
        require(reserveCorpClaimed[msg.sender] - quantity >= 0, "Address already minted num of tokens allowed");
        reserveCorpClaimed[msg.sender] = reserveCorpClaimed[msg.sender] - quantity;
        _safeMint(msg.sender, quantity);
        refundIfOver(price * quantity);
    }

    function publicRegistration(bytes32[] calldata _merkleProof, uint256 quantity) external payable callerIsUser {
        bytes32 leaf = keccak256(abi.encodePacked(msg.sender));
        require(MerkleProof.verify(_merkleProof, publicRegistrationRoot, leaf),
            "Invalid Merkle Proof.");
        uint256 price = uint256(saleConfig.publicRegistrationPrice);
        require(price != 0, "whitelist sale has not begun yet");
        require(totalSupply() + quantity <= MAX_SUPPLY, "Exceeds total supply");
        if (!publicRegistrationChecker[msg.sender]) {
            publicRegistrationChecker[msg.sender] = true;
            publicRegistrationClaimed[msg.sender] = 2;
        }
        require(publicRegistrationClaimed[msg.sender] - quantity >= 0, "Address already minted num of tokens allowed");
        publicRegistrationClaimed[msg.sender] = publicRegistrationClaimed[msg.sender] - quantity;
        _safeMint(msg.sender, quantity);
        refundIfOver(price * quantity);
    }

    function publicSaleMint(uint256 quantity, uint256 callerPublicSaleKey)
        external
        payable
        callerIsUser
    {
        SaleConfig memory config = saleConfig;
        uint256 publicSaleKey = uint256(config.publicSaleKey);
        uint256 publicPrice = uint256(config.publicPrice);
        uint256 publicSaleStartTime = uint256(config.publicSaleStartTime);
        require(
            publicSaleKey == callerPublicSaleKey,
            "called with incorrect public sale key"
        );

        require(
            isPublicSaleOn(publicPrice, publicSaleKey, publicSaleStartTime),
            "public sale has not begun yet"
        );
        require(
            totalSupply() + quantity <= MAX_SUPPLY,
            "reached max supply"
        );
       
        _safeMint(msg.sender, quantity);
        refundIfOver(publicPrice * quantity);
    }

    function refundIfOver(uint256 price) private {
        require(msg.value >= price, "Need to send more ETH.");
        if (msg.value > price) {
            payable(msg.sender).transfer(msg.value - price);
        }
    }

    function isPublicSaleOn(
        uint256 publicPriceWei,
        uint256 publicSaleKey,
        uint256 publicSaleStartTime
    ) public view returns (bool) {
        return
            publicPriceWei != 0 &&
            publicSaleKey != 0 &&
            block.timestamp >= publicSaleStartTime;
    }

    function setupSaleInfo(
        uint64 whitelistPriceWei2,
        uint64 whitelistPriceWei1,
        uint64 publicRegistrationPrice,
        uint64 publicPriceWei,
        uint32 publicSaleStartTime
    ) external onlyOwner {
        saleConfig = SaleConfig(
            publicSaleStartTime,
            saleConfig.publicSaleKey,
            whitelistPriceWei1,
            whitelistPriceWei2,
            publicRegistrationPrice,
            publicPriceWei
        );
    }

    function setPublicSaleKey(uint32 key) external onlyOwner {
        saleConfig.publicSaleKey = key;
    }

    function setPublicPrice(uint64 price) external onlyOwner {
        saleConfig.publicPrice = price;
    }

    function setPublicRegistrationPrice(uint64 price) external onlyOwner {
        saleConfig.publicRegistrationPrice = price;
    }

    function setPortalAccessPrice(uint64 price) external onlyOwner {
        saleConfig.portalAccessPrice = price;
    }

    function setReserveCorpPrice(uint64 price) external onlyOwner {
        saleConfig.reserveCorpPrice = price;
    }

    function setPublicSaleStartTime(uint32 time) external onlyOwner {
        saleConfig.publicSaleStartTime = time;
    }

    function setPortalAccessRoot(bytes32 merkle_root) external onlyOwner {
        portalAccessRoot = merkle_root;
    }

    function setPublicRegistrationRoot(bytes32 merkle_root) external onlyOwner {
        publicRegistrationRoot = merkle_root;
    }

    function setReserveCorpRoot(bytes32 merkle_root) external onlyOwner {
        reserveCorpRoot = merkle_root;
    }

    function teamMint(uint256 quantity, address walletAddress)
        external
        onlyOwner
    {
        require(
            totalSupply() + quantity <= amountForTeam,
            "too many already minted for team mint"
        );
        require(
            quantity % MAX_BATCH == 0,
            "can only mint a multiple of the MAX_BATCH"
        );
        uint256 numChunks = quantity / MAX_BATCH;
        for (uint256 i = 0; i < numChunks; i++) {
            _safeMint(walletAddress, MAX_BATCH);
        }
    }

    string private _baseTokenURI;

    function _baseURI() internal view virtual override returns (string memory) {
        return _baseTokenURI;
    }

    function setBaseURI(string calldata baseURI) external onlyOwner {
        _baseTokenURI = baseURI;
    }

    function withdraw() external onlyOwner nonReentrant {
        uint256 balance = address(this).balance;

        payable(0xa6Fc099Cc057b647B1E01562305D3e8150ADf847).transfer(
            balance
        );
    }

    function withdrawAmount(uint _amount) external onlyOwner nonReentrant {
        payable(0xa6Fc099Cc057b647B1E01562305D3e8150ADf847).transfer(_amount);
    }

    

    function numberMinted(address owner) public view returns (uint256) {
        return _numberMinted(owner);
    }

    function getOwnershipData(uint256 tokenId)
        external
        view
        returns (TokenOwnership memory)
    {
        return ownershipOf(tokenId);
    }
}
[{"inputs":[{"internalType":"uint256","name":"amountForTeam_","type":"uint256"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"ApprovalCallerNotOwnerNorApproved","type":"error"},{"inputs":[],"name":"ApprovalQueryForNonexistentToken","type":"error"},{"inputs":[],"name":"ApprovalToCurrentOwner","type":"error"},{"inputs":[],"name":"ApproveToCaller","type":"error"},{"inputs":[],"name":"BalanceQueryForZeroAddress","type":"error"},{"inputs":[],"name":"MintToZeroAddress","type":"error"},{"inputs":[],"name":"MintZeroQuantity","type":"error"},{"inputs":[],"name":"MintedQueryForZeroAddress","type":"error"},{"inputs":[],"name":"OwnerQueryForNonexistentToken","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":"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":"amountForTeam","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"approve","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"getApproved","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"getOwnershipData","outputs":[{"components":[{"internalType":"address","name":"addr","type":"address"},{"internalType":"uint64","name":"startTimestamp","type":"uint64"},{"internalType":"bool","name":"burned","type":"bool"}],"internalType":"struct ERC721A.TokenOwnership","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address[]","name":"receivers","type":"address[]"}],"name":"gift","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"operator","type":"address"}],"name":"isApprovedForAll","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"publicPriceWei","type":"uint256"},{"internalType":"uint256","name":"publicSaleKey","type":"uint256"},{"internalType":"uint256","name":"publicSaleStartTime","type":"uint256"}],"name":"isPublicSaleOn","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"numberMinted","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"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":[{"internalType":"bytes32[]","name":"_merkleProof","type":"bytes32[]"},{"internalType":"uint256","name":"quantity","type":"uint256"}],"name":"portalAccess","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"portalAccessClaimed","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"portalAccessClaimedChecker","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"portalAccessRoot","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32[]","name":"_merkleProof","type":"bytes32[]"},{"internalType":"uint256","name":"quantity","type":"uint256"}],"name":"publicRegistration","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"publicRegistrationChecker","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"publicRegistrationClaimed","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"publicRegistrationRoot","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"quantity","type":"uint256"},{"internalType":"uint256","name":"callerPublicSaleKey","type":"uint256"}],"name":"publicSaleMint","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32[]","name":"_merkleProof","type":"bytes32[]"},{"internalType":"uint256","name":"quantity","type":"uint256"}],"name":"reserveCorp","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"reserveCorpClaimed","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"reserveCorpClaimedChecker","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"reserveCorpRoot","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"num","type":"uint256"},{"internalType":"address","name":"walletAddress","type":"address"}],"name":"reserveGiveaway","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"safeTransferFrom","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"bytes","name":"_data","type":"bytes"}],"name":"safeTransferFrom","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"saleConfig","outputs":[{"internalType":"uint32","name":"publicSaleStartTime","type":"uint32"},{"internalType":"uint32","name":"publicSaleKey","type":"uint32"},{"internalType":"uint64","name":"portalAccessPrice","type":"uint64"},{"internalType":"uint64","name":"reserveCorpPrice","type":"uint64"},{"internalType":"uint64","name":"publicRegistrationPrice","type":"uint64"},{"internalType":"uint64","name":"publicPrice","type":"uint64"}],"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":"string","name":"baseURI","type":"string"}],"name":"setBaseURI","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint64","name":"price","type":"uint64"}],"name":"setPortalAccessPrice","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"merkle_root","type":"bytes32"}],"name":"setPortalAccessRoot","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint64","name":"price","type":"uint64"}],"name":"setPublicPrice","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint64","name":"price","type":"uint64"}],"name":"setPublicRegistrationPrice","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"merkle_root","type":"bytes32"}],"name":"setPublicRegistrationRoot","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint32","name":"key","type":"uint32"}],"name":"setPublicSaleKey","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint32","name":"time","type":"uint32"}],"name":"setPublicSaleStartTime","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint64","name":"price","type":"uint64"}],"name":"setReserveCorpPrice","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"merkle_root","type":"bytes32"}],"name":"setReserveCorpRoot","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint64","name":"whitelistPriceWei2","type":"uint64"},{"internalType":"uint64","name":"whitelistPriceWei1","type":"uint64"},{"internalType":"uint64","name":"publicRegistrationPrice","type":"uint64"},{"internalType":"uint64","name":"publicPriceWei","type":"uint64"},{"internalType":"uint32","name":"publicSaleStartTime","type":"uint32"}],"name":"setupSaleInfo","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":"quantity","type":"uint256"},{"internalType":"address","name":"walletAddress","type":"address"}],"name":"teamMint","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"tokenURI","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"transferFrom","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"withdraw","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"withdrawAmount","outputs":[],"stateMutability":"nonpayable","type":"function"}]