RobinWoodPlank

0x327ceaaedbbcf55f40d6f1abc71bd9bc8adcb156

Verification
Verified
v0.8.27+commit.40a35a09
Type
Contract
12,350 bytes
ABI entries
119
46 read · 21 write
License
none

Contract information

Address
0x327ceaaedbbcf55f40d6f1abc71bd9bc8adcb156
Chain
Robinhood Chain (4663)
Compiler
v0.8.27+commit.40a35a09
Optimization
Enabled
Creation tx
0x072c01ae10…9360c62169

Token

Name
RobinWood
Symbol
PLANK
Decimals
Holders
348

Read contract (46)

COMMUNITY_POOL()uint256
INITIAL_NON_COMMUNITY_ALLOCATION()uint256
MAX_ALLOWLIST_PER_WALLET()uint256
MAX_FREE_PER_WALLET()uint256
MAX_PAID_PER_TRANSACTION()uint256
MAX_PAID_PER_WALLET()uint256
MAX_SUPPLY()uint256
allowlistMintsByWallet(address)uint256
allowlistMintsClaimed()uint256
balanceOf(address)uint256
communityMintsClaimed()uint256
communitySupplyReleased()bool
contractBalance()uint256
currentNonCommunityCap()uint256
freeMintsByWallet(address)uint256
freeMintsClaimed()uint256
getApproved(uint256)address
isAllowlisted(address, bytes32[])bool
isApprovedForAll(address, address)bool
merkleRoot()bytes32
mintPrice()uint256
name()string
nextTokenId()uint256
nonCommunityMintsClaimed()uint256
owner()address
ownerMintsClaimed()uint256
ownerOf(uint256)address
paidMintsByWallet(address)uint256
paidMintsClaimed()uint256
paused()bool
publicMintCost(uint256)uint256
remainingAllowlistMintsForWallet(address)uint256
remainingCommunitySupply()uint256
remainingFreeMintsForWallet(address)uint256
remainingNonCommunitySupply()uint256
remainingPaidMintsForWallet(address)uint256
remainingTotalSupply()uint256
royaltyInfo(uint256, uint256)address, uint256
salePhase()uint8
supportsInterface(bytes4)bool
symbol()string
tokenByIndex(uint256)uint256
tokenOfOwnerByIndex(address, uint256)uint256
tokenURI(uint256)string
totalCommunityMintsForWallet(address)uint256
totalSupply()uint256

Events (16)

AllowlistMintCompletedApprovalApprovalForAllBaseURIUpdatedCommunitySupplyReleasedFreeMintCompletedMerkleRootUpdatedMintPriceUpdatedOwnerMintCompletedOwnershipTransferredPaidMintCompletedPausedSalePhaseUpdatedTransferUnpausedWithdrawal

ABI

[
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "initialOwner",
        "type": "address"
      },
      {
        "internalType": "string",
        "name": "initialBaseURI",
        "type": "string"
      },
      {
        "internalType": "bytes32",
        "name": "initialMerkleRoot",
        "type": "bytes32"
      },
      {
        "internalType": "address",
        "name": "royaltyReceiver",
        "type": "address"
      },
      {
        "internalType": "uint96",
        "name": "royaltyFeeNumerator",
        "type": "uint96"
      }
    ],
    "stateMutability": "nonpayable",
    "type": "constructor"
  },
  {
    "inputs": [],
    "name": "AllowlistWalletLimitExceeded",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "CommunityMintPermanentlyClosed",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "CommunityPoolExceeded",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "CommunitySupplyAlreadyReleased",
    "type": "error"
  },
  {
    "inputs": [
      {
        "internalType": "uint256",
        "name": "numerator",
        "type": "uint256"
      },
      {
        "internalType": "uint256",
        "name": "denominator",
        "type": "uint256"
      }
    ],
    "name": "ERC2981InvalidDefaultRoyalty",
    "type": "error"
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "receiver",
        "type": "address"
      }
    ],
    "name": "ERC2981InvalidDefaultRoyaltyReceiver",
    "type": "error"
  },
  {
    "inputs": [
      {
        "internalType": "uint256",
        "name": "tokenId",
        "type": "uint256"
      },
      {
        "internalType": "uint256",
        "name": "numerator",
        "type": "uint256"
      },
      {
        "internalType": "uint256",
        "name": "denominator",
        "type": "uint256"
      }
    ],
    "name": "ERC2981InvalidTokenRoyalty",
    "type": "error"
  },
  {
    "inputs": [
      {
        "internalType": "uint256",
        "name": "tokenId",
        "type": "uint256"
      },
      {
        "internalType": "address",
        "name": "receiver",
        "type": "address"
      }
    ],
    "name": "ERC2981InvalidTokenRoyaltyReceiver",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "ERC721EnumerableForbiddenBatchMint",
    "type": "error"
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "sender",
        "type": "address"
      },
      {
        "internalType": "uint256",
        "name": "tokenId",
        "type": "uint256"
      },
      {
        "internalType": "address",
        "name": "owner",
        "type": "address"
      }
    ],
    "name": "ERC721IncorrectOwner",
    "type": "error"
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "operator",
        "type": "address"
      },
      {
        "internalType": "uint256",
        "name": "tokenId",
        "type": "uint256"
      }
    ],
    "name": "ERC721InsufficientApproval",
    "type": "error"
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "approver",
        "type": "address"
      }
    ],
    "name": "ERC721InvalidApprover",
    "type": "error"
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "operator",
        "type": "address"
      }
    ],
    "name": "ERC721InvalidOperator",
    "type": "error"
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "owner",
        "type": "address"
      }
    ],
    "name": "ERC721InvalidOwner",
    "type": "error"
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "receiver",
        "type": "address"
      }
    ],
    "name": "ERC721InvalidReceiver",
    "type": "error"
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "sender",
        "type": "address"
      }
    ],
    "name": "ERC721InvalidSender",
    "type": "error"
  },
  {
    "inputs": [
      {
        "internalType": "uint256",
        "name": "tokenId",
        "type": "uint256"
      }
    ],
    "name": "ERC721NonexistentToken",
    "type": "error"
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "owner",
        "type": "address"
      },
      {
        "internalType": "uint256",
        "name": "index",
        "type": "uint256"
      }
    ],
    "name": "ERC721OutOfBoundsIndex",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "EnforcedPause",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "ExpectedPause",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "FreeWalletLimitExceeded",
    "type": "error"
  },
  {
    "inputs": [
      {
        "internalType": "uint256",
        "name": "requiredPayment",
        "type": "uint256"
      },
      {
        "internalType": "uint256",
        "name": "suppliedPayment",
        "type": "uint256"
      }
    ],
    "name": "IncorrectPayment",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "InvalidAllowlistProof",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "InvalidQuantity",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "MaxSupplyExceeded",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "NonCommunityAllocationExceeded",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "NothingToWithdraw",
    "type": "error"
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "owner",
        "type": "address"
      }
    ],
    "name": "OwnableInvalidOwner",
    "type": "error"
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "account",
        "type": "address"
      }
    ],
    "name": "OwnableUnauthorizedAccount",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "PaidWalletLimitExceeded",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "ReentrancyGuardReentrantCall",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "SaleMustBeClosed",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "WithdrawalFailed",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "WrongSalePhase",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "ZeroAddress",
    "type": "error"
  },
  {
    "anonymous": false,
    "inputs": [
      {
        "indexed": true,
        "internalType": "address",
        "name": "wallet",
        "type": "address"
      },
      {
        "indexed": false,
        "internalType": "uint256",
        "name": "quantity",
        "type": "uint256"
      },
      {
        "indexed": false,
        "internalType": "uint256",
        "name": "firstTokenId",
        "type": "uint256"
      }
    ],
    "name": "AllowlistMintCompleted",
    "type": "event"
  },
  {
    "anonymous": false,
    "inputs": [
      {
        "indexed": true,
        "internalType": "address",
        "name": "owner",
        "type": "address"
      },
      {
        "indexed": true,
        "internalType": "address",
        "name": "approved",
        "type": "address"
      },
      {
        "indexed": true,
        "internalType": "uint256",
        "name": "tokenId",
        "type": "uint256"
      }
    ],
    "name": "Approval",
    "type": "event"
  },
  {
    "anonymous": false,
    "inputs": [
      {
        "indexed": true,
        "internalType": "address",
        "name": "owner",
        "type": "address"
      },
      {
        "indexed": true,
        "internalType": "address",
        "name": "operator",
        "type": "address"
      },
      {
        "indexed": false,
        "internalType": "bool",
        "name": "approved",
        "type": "bool"
      }
    ],
    "name": "ApprovalForAll",
    "type": "event"
  },
  {
    "anonymous": false,
    "inputs": [
      {
        "indexed": false,
        "internalType": "string",
        "name": "previousBaseURI",
        "type": "string"
      },
      {
        "indexed": false,
        "internalType": "string",
        "name": "newBaseURI",
        "type": "string"
      }
    ],
    "name": "BaseURIUpdated",
    "type": "event"
  },
  {
    "anonymous": false,
    "inputs": [
      {
        "indexed": false,
        "internalType": "uint256",
        "name": "communityMinted",
        "type": "uint256"
      },
      {
        "indexed": false,
        "internalType": "uint256",
        "name": "unusedCommunitySupply",
        "type": "uint256"
      },
      {
        "indexed": false,
        "internalType": "uint256",
        "name": "finalNonCommunityCap",
        "type": "uint256"
      }
    ],
    "name": "CommunitySupplyReleased",
    "type": "event"
  },
  {
    "anonymous": false,
    "inputs": [
      {
        "indexed": true,
        "internalType": "address",
        "name": "wallet",
        "type": "address"
      },
      {
        "indexed": false,
        "internalType": "uint256",
        "name": "quantity",
        "type": "uint256"
      },
      {
        "indexed": false,
        "internalType": "uint256",
        "name": "firstTokenId",
        "type": "uint256"
      }
    ],
    "name": "FreeMintCompleted",
    "type": "event"
  },
  {
    "anonymous": false,
    "inputs": [
      {
        "indexed": true,
        "internalType": "bytes32",
        "name": "previousRoot",
        "type": "bytes32"
      },
      {
        "indexed": true,
        "internalType": "bytes32",
        "name": "newRoot",
        "type": "bytes32"
      }
    ],
    "name": "MerkleRootUpdated",
    "type": "event"
  },
  {
    "anonymous": false,
    "inputs": [
      {
        "indexed": false,
        "internalType": "uint256",
        "name": "previousPrice",
        "type": "uint256"
      },
      {
        "indexed": false,
        "internalType": "uint256",
        "name": "newPrice",
        "type": "uint256"
      }
    ],
    "name": "MintPriceUpdated",
    "type": "event"
  },
  {
    "anonymous": false,
    "inputs": [
      {
        "indexed": true,
        "internalType": "address",
        "name": "recipient",
        "type": "address"
      },
      {
        "indexed": false,
        "internalType": "uint256",
        "name": "quantity",
        "type": "uint256"
      },
      {
        "indexed": false,
        "internalType": "uint256",
        "name": "firstTokenId",
        "type": "uint256"
      }
    ],
    "name": "OwnerMintCompleted",
    "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": "wallet",
        "type": "address"
      },
      {
        "indexed": false,
        "internalType": "uint256",
        "name": "quantity",
        "type": "uint256"
      },
      {
        "indexed": false,
        "internalType": "uint256",
        "name": "firstTokenId",
        "type": "uint256"
      },
      {
        "indexed": false,
        "internalType": "uint256",
        "name": "amountPaid",
        "type": "uint256"
      }
    ],
    "name": "PaidMintCompleted",
    "type": "event"
  },
  {
    "anonymous": false,
    "inputs": [
      {
        "indexed": false,
        "internalType": "address",
        "name": "account",
        "type": "address"
      }
    ],
    "name": "Paused",
    "type": "event"
  },
  {
    "anonymous": false,
    "inputs": [
      {
        "indexed": true,
        "internalType": "enum RobinWoodPlank.SalePhase",
        "name": "previousPhase",
        "type": "uint8"
      },
      {
        "indexed": true,
        "internalType": "enum RobinWoodPlank.SalePhase",
        "name": "newPhase",
        "type": "uint8"
      }
    ],
    "name": "SalePhaseUpdated",
    "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"
  },
  {
    "anonymous": false,
    "inputs": [
      {
        "indexed": false,
        "internalType": "address",
        "name": "account",
        "type": "address"
      }
    ],
    "name": "Unpaused",
    "type": "event"
  },
  {
    "anonymous": false,
    "inputs": [
      {
        "indexed": true,
        "internalType": "address",
        "name": "recipient",
        "type": "address"
      },
      {
        "indexed": false,
        "internalType": "uint256",
        "name": "amount",
        "type": "uint256"
      }
    ],
    "name": "Withdrawal",
    "type": "event"
  },
  {
    "inputs": [],
    "name": "COMMUNITY_POOL",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "",
        "type": "uint256"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "INITIAL_NON_COMMUNITY_ALLOCATION",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "",
        "type": "uint256"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "MAX_ALLOWLIST_PER_WALLET",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "",
        "type": "uint256"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "MAX_FREE_PER_WALLET",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "",
        "type": "uint256"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "MAX_PAID_PER_TRANSACTION",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "",
        "type": "uint256"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "MAX_PAID_PER_WALLET",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "",
        "type": "uint256"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "MAX_SUPPLY",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "",
        "type": "uint256"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "uint256",
        "name": "quantity",
        "type": "uint256"
      },
      {
        "internalType": "bytes32[]",
        "name": "merkleProof",
        "type": "bytes32[]"
      }
    ],
    "name": "allowlistMint",
    "outputs": [],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "wallet",
        "type": "address"
      }
    ],
    "name": "allowlistMintsByWallet",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "quantity",
        "type": "uint256"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "allowlistMintsClaimed",
    "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": [],
    "name": "communityMintsClaimed",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "",
        "type": "uint256"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "communitySupplyReleased",
    "outputs": [
      {
        "internalType": "bool",
        "name": "",
        "type": "bool"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "contractBalance",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "",
        "type": "uint256"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "currentNonCommunityCap",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "",
        "type": "uint256"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "deleteDefaultRoyalty",
    "outputs": [],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "uint256",
        "name": "quantity",
        "type": "uint256"
      }
    ],
    "name": "freeMint",
    "outputs": [],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "wallet",
        "type": "address"
      }
    ],
    "name": "freeMintsByWallet",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "quantity",
        "type": "uint256"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "freeMintsClaimed",
    "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": "address",
        "name": "wallet",
        "type": "address"
      },
      {
        "internalType": "bytes32[]",
        "name": "merkleProof",
        "type": "bytes32[]"
      }
    ],
    "name": "isAllowlisted",
    "outputs": [
      {
        "internalType": "bool",
        "name": "",
        "type": "bool"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "owner",
        "type": "address"
      },
      {
        "internalType": "address",
        "name": "operator",
        "type": "address"
      }
    ],
    "name": "isApprovedForAll",
    "outputs": [
      {
        "internalType": "bool",
        "name": "",
        "type": "bool"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "merkleRoot",
    "outputs": [
      {
        "internalType": "bytes32",
        "name": "",
        "type": "bytes32"
      }
    ],
    "stateMutability": "view",
    "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": "nextTokenId",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "",
        "type": "uint256"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "nonCommunityMintsClaimed",
    "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": "address",
        "name": "recipient",
        "type": "address"
      },
      {
        "internalType": "uint256",
        "name": "quantity",
        "type": "uint256"
      }
    ],
    "name": "ownerMint",
    "outputs": [],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "ownerMintsClaimed",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "",
        "type": "uint256"
      }
    ],
    "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": "address",
        "name": "wallet",
        "type": "address"
      }
    ],
    "name": "paidMintsByWallet",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "quantity",
        "type": "uint256"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "paidMintsClaimed",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "",
        "type": "uint256"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "pause",
    "outputs": [],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "paused",
    "outputs": [
      {
        "internalType": "bool",
        "name": "",
        "type": "bool"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "uint256",
        "name": "quantity",
        "type": "uint256"
      }
    ],
    "name": "publicMint",
    "outputs": [],
    "stateMutability": "payable",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "uint256",
        "name": "quantity",
        "type": "uint256"
      }
    ],
    "name": "publicMintCost",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "",
        "type": "uint256"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "releaseUnusedCommunitySupply",
    "outputs": [],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "wallet",
        "type": "address"
      }
    ],
    "name": "remainingAllowlistMintsForWallet",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "",
        "type": "uint256"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "remainingCommunitySupply",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "",
        "type": "uint256"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "wallet",
        "type": "address"
      }
    ],
    "name": "remainingFreeMintsForWallet",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "",
        "type": "uint256"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "remainingNonCommunitySupply",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "",
        "type": "uint256"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "wallet",
        "type": "address"
      }
    ],
    "name": "remainingPaidMintsForWallet",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "",
        "type": "uint256"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "remainingTotalSupply",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "",
        "type": "uint256"
      }
    ],
    "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": "receiver",
        "type": "address"
      },
      {
        "internalType": "uint256",
        "name": "amount",
        "type": "uint256"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "from",
        "type": "address"
      },
      {
        "internalType": "address",
        "name": "to",
        "type": "address"
      },
      {
        "internalType": "uint256",
        "name": "tokenId",
        "type": "uint256"
      }
    ],
    "name": "safeTransferFrom",
    "outputs": [],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "from",
        "type": "address"
      },
      {
        "internalType": "address",
        "name": "to",
        "type": "address"
      },
      {
        "internalType": "uint256",
        "name": "tokenId",
        "type": "uint256"
      },
      {
        "internalType": "bytes",
        "name": "data",
        "type": "bytes"
      }
    ],
    "name": "safeTransferFrom",
    "outputs": [],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "salePhase",
    "outputs": [
      {
        "internalType": "enum RobinWoodPlank.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": "string",
        "name": "newBaseURI",
        "type": "string"
      }
    ],
    "name": "setBaseURI",
    "outputs": [],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "receiver",
        "type": "address"
      },
      {
        "internalType": "uint96",
        "name": "feeNumerator",
        "type": "uint96"
      }
    ],
    "name": "setDefaultRoyalty",
    "outputs": [],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "bytes32",
        "name": "newMerkleRoot",
        "type": "bytes32"
      }
    ],
    "name": "setMerkleRoot",
    "outputs": [],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "uint256",
        "name": "newMintPrice",
        "type": "uint256"
      }
    ],
    "name": "setMintPrice",
    "outputs": [],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "enum RobinWoodPlank.SalePhase",
        "name": "newPhase",
        "type": "uint8"
      }
    ],
    "name": "setSalePhase",
    "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": "index",
        "type": "uint256"
      }
    ],
    "name": "tokenByIndex",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "",
        "type": "uint256"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "owner",
        "type": "address"
      },
      {
        "internalType": "uint256",
        "name": "index",
        "type": "uint256"
      }
    ],
    "name": "tokenOfOwnerByIndex",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "",
        "type": "uint256"
      }
    ],
    "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": "wallet",
        "type": "address"
      }
    ],
    "name": "totalCommunityMintsForWallet",
    "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": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "newOwner",
        "type": "address"
      }
    ],
    "name": "transferOwnership",
    "outputs": [],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "unpause",
    "outputs": [],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "address payable",
        "name": "recipient",
        "type": "address"
      }
    ],
    "name": "withdraw",
    "outputs": [],
    "stateMutability": "nonpayable",
    "type": "function"
  }
]

Source code

// SPDX-License-Identifier: MIT
// Compatible with OpenZeppelin Contracts ^5.6.0
pragma solidity ^0.8.27;

import {Ownable} from
    "@openzeppelin/contracts/access/Ownable.sol";

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

import {ERC721Enumerable} from
    "@openzeppelin/contracts/token/ERC721/extensions/ERC721Enumerable.sol";

import {ERC721Pausable} from
    "@openzeppelin/contracts/token/ERC721/extensions/ERC721Pausable.sol";

import {ERC721Royalty} from
    "@openzeppelin/contracts/token/ERC721/extensions/ERC721Royalty.sol";

import {MerkleProof} from
    "@openzeppelin/contracts/utils/cryptography/MerkleProof.sol";

import {ReentrancyGuard} from
    "@openzeppelin/contracts/utils/ReentrancyGuard.sol";


contract RobinWoodPlank is
    ERC721,
    ERC721Enumerable,
    ERC721Pausable,
    ERC721Royalty,
    Ownable,
    ReentrancyGuard
{
    // =============================================================
    //                         SUPPLY RULES
    // =============================================================

    uint256 public constant MAX_SUPPLY = 1_542;

    // Free + allowlist mints share this allocation.
    uint256 public constant COMMUNITY_POOL = 777;

    // Guaranteed paid/owner allocation before community release.
    uint256 public constant INITIAL_NON_COMMUNITY_ALLOCATION =
        MAX_SUPPLY - COMMUNITY_POOL; // 765

    uint256 public constant MAX_FREE_PER_WALLET = 2;
    uint256 public constant MAX_ALLOWLIST_PER_WALLET = 2;

    uint256 public constant MAX_PAID_PER_TRANSACTION = 33;
    uint256 public constant MAX_PAID_PER_WALLET = 33;

    uint256 public mintPrice = 0.01 ether;

    // =============================================================
    //                          SALE PHASES
    // =============================================================

    enum SalePhase {
        Closed,     // 0
        Free,       // 1
        Allowlist,  // 2
        Paid        // 3
    }

    SalePhase public salePhase = SalePhase.Closed;

    // =============================================================
    //                         MINT TRACKING
    // =============================================================

    // Token IDs begin at 1.
    uint256 private _nextTokenId = 1;

    // Combined total from freeMint and allowlistMint.
    uint256 public communityMintsClaimed;

    uint256 public freeMintsClaimed;
    uint256 public allowlistMintsClaimed;
    uint256 public paidMintsClaimed;
    uint256 public ownerMintsClaimed;

    mapping(address wallet => uint256 quantity)
        public freeMintsByWallet;

    mapping(address wallet => uint256 quantity)
        public allowlistMintsByWallet;

    mapping(address wallet => uint256 quantity)
        public paidMintsByWallet;

    // =============================================================
    //                         ALLOWLIST DATA
    // =============================================================

    bytes32 public merkleRoot;

    // =============================================================
    //                      COMMUNITY RELEASE
    // =============================================================

    /**
     * Once true:
     * - Free and allowlist minting are permanently disabled.
     * - Unused community tokens become available for paid/owner mint.
     */
    bool public communitySupplyReleased;

    // =============================================================
    //                           METADATA
    // =============================================================

    string private _baseTokenURI;

    // =============================================================
    //                            ERRORS
    // =============================================================

    error WrongSalePhase();
    error InvalidQuantity();

    error FreeWalletLimitExceeded();
    error AllowlistWalletLimitExceeded();
    error PaidWalletLimitExceeded();

    error InvalidAllowlistProof();
    error CommunityPoolExceeded();
    error NonCommunityAllocationExceeded();
    error MaxSupplyExceeded();

    error CommunityMintPermanentlyClosed();
    error CommunitySupplyAlreadyReleased();
    error SaleMustBeClosed();

    error IncorrectPayment(
        uint256 requiredPayment,
        uint256 suppliedPayment
    );

    error ZeroAddress();
    error NothingToWithdraw();
    error WithdrawalFailed();

    // =============================================================
    //                            EVENTS
    // =============================================================

    event SalePhaseUpdated(
        SalePhase indexed previousPhase,
        SalePhase indexed newPhase
    );

    event FreeMintCompleted(
        address indexed wallet,
        uint256 quantity,
        uint256 firstTokenId
    );

    event AllowlistMintCompleted(
        address indexed wallet,
        uint256 quantity,
        uint256 firstTokenId
    );

    event PaidMintCompleted(
        address indexed wallet,
        uint256 quantity,
        uint256 firstTokenId,
        uint256 amountPaid
    );

    event OwnerMintCompleted(
        address indexed recipient,
        uint256 quantity,
        uint256 firstTokenId
    );

    event CommunitySupplyReleased(
        uint256 communityMinted,
        uint256 unusedCommunitySupply,
        uint256 finalNonCommunityCap
    );

    event MerkleRootUpdated(
        bytes32 indexed previousRoot,
        bytes32 indexed newRoot
    );

    event MintPriceUpdated(
        uint256 previousPrice,
        uint256 newPrice
    );

    event BaseURIUpdated(
        string previousBaseURI,
        string newBaseURI
    );

    event Withdrawal(
        address indexed recipient,
        uint256 amount
    );

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

    /**
     * royaltyFeeNumerator uses basis points:
     *
     * 500  = 5%
     * 750  = 7.5%
     * 1000 = 10%
     */
    constructor(
        address initialOwner,
        string memory initialBaseURI,
        bytes32 initialMerkleRoot,
        address royaltyReceiver,
        uint96 royaltyFeeNumerator
    )
        ERC721("RobinWood", "PLANK")
        Ownable(initialOwner)
    {
        if (
            initialOwner == address(0) ||
            royaltyReceiver == address(0)
        ) {
            revert ZeroAddress();
        }

        _baseTokenURI = initialBaseURI;
        merkleRoot = initialMerkleRoot;

        _setDefaultRoyalty(
            royaltyReceiver,
            royaltyFeeNumerator
        );
    }

    // =============================================================
    //                         FREE MINT
    // =============================================================

    /**
     * Public free mint.
     *
     * Maximum:
     * - 2 per wallet
     * - Shared 777-token community pool
     *
     * No ETH should be sent.
     */
    function freeMint(uint256 quantity)
        external
        nonReentrant
        whenNotPaused
    {
        if (salePhase != SalePhase.Free) {
            revert WrongSalePhase();
        }

        if (
            quantity == 0 ||
            quantity > MAX_FREE_PER_WALLET
        ) {
            revert InvalidQuantity();
        }

        if (
            freeMintsByWallet[msg.sender] + quantity >
            MAX_FREE_PER_WALLET
        ) {
            revert FreeWalletLimitExceeded();
        }

        _enforceCommunityAllocation(quantity);

        uint256 firstTokenId = _nextTokenId;

        // Update state before safe-mint callbacks.
        freeMintsByWallet[msg.sender] += quantity;
        freeMintsClaimed += quantity;
        communityMintsClaimed += quantity;

        _mintMultiple(msg.sender, quantity);

        emit FreeMintCompleted(
            msg.sender,
            quantity,
            firstTokenId
        );
    }

    // =============================================================
    //                       ALLOWLIST MINT
    // =============================================================

    /**
     * Free allowlist mint.
     *
     * Maximum:
     * - 2 per allowlisted wallet
     * - Shared 777-token community pool
     *
     * A wallet that already minted 2 through freeMint may still mint
     * 2 through allowlistMint.
     */
    function allowlistMint(
        uint256 quantity,
        bytes32[] calldata merkleProof
    )
        external
        nonReentrant
        whenNotPaused
    {
        if (salePhase != SalePhase.Allowlist) {
            revert WrongSalePhase();
        }

        if (
            quantity == 0 ||
            quantity > MAX_ALLOWLIST_PER_WALLET
        ) {
            revert InvalidQuantity();
        }

        if (
            allowlistMintsByWallet[msg.sender] + quantity >
            MAX_ALLOWLIST_PER_WALLET
        ) {
            revert AllowlistWalletLimitExceeded();
        }

        bytes32 leaf = _allowlistLeaf(msg.sender);

        if (
            !MerkleProof.verifyCalldata(
                merkleProof,
                merkleRoot,
                leaf
            )
        ) {
            revert InvalidAllowlistProof();
        }

        _enforceCommunityAllocation(quantity);

        uint256 firstTokenId = _nextTokenId;

        // Update state before safe-mint callbacks.
        allowlistMintsByWallet[msg.sender] += quantity;
        allowlistMintsClaimed += quantity;
        communityMintsClaimed += quantity;

        _mintMultiple(msg.sender, quantity);

        emit AllowlistMintCompleted(
            msg.sender,
            quantity,
            firstTokenId
        );
    }

    // =============================================================
    //                          PAID MINT
    // =============================================================

    function publicMint(uint256 quantity)
        external
        payable
        nonReentrant
        whenNotPaused
    {
        if (salePhase != SalePhase.Paid) {
            revert WrongSalePhase();
        }

        if (
            quantity == 0 ||
            quantity > MAX_PAID_PER_TRANSACTION
        ) {
            revert InvalidQuantity();
        }

        if (
            paidMintsByWallet[msg.sender] + quantity >
            MAX_PAID_PER_WALLET
        ) {
            revert PaidWalletLimitExceeded();
        }

        _enforceNonCommunityAllocation(quantity);

        uint256 requiredPayment = mintPrice * quantity;

        if (msg.value != requiredPayment) {
            revert IncorrectPayment(
                requiredPayment,
                msg.value
            );
        }

        uint256 firstTokenId = _nextTokenId;

        // Update state before safe-mint callbacks.
        paidMintsByWallet[msg.sender] += quantity;
        paidMintsClaimed += quantity;

        _mintMultiple(msg.sender, quantity);

        emit PaidMintCompleted(
            msg.sender,
            quantity,
            firstTokenId,
            msg.value
        );
    }

    // =============================================================
    //                          OWNER MINT
    // =============================================================

    /**
     * Owner mint for team, giveaways, or reserves.
     *
     * Owner mints consume the same supply allocation as paid mint.
     * They cannot consume the protected community allocation before
     * that allocation has been released.
     */
    function ownerMint(
        address recipient,
        uint256 quantity
    )
        external
        onlyOwner
        nonReentrant
        whenNotPaused
    {
        if (recipient == address(0)) {
            revert ZeroAddress();
        }

        if (quantity == 0) {
            revert InvalidQuantity();
        }

        _enforceNonCommunityAllocation(quantity);

        uint256 firstTokenId = _nextTokenId;

        ownerMintsClaimed += quantity;

        _mintMultiple(recipient, quantity);

        emit OwnerMintCompleted(
            recipient,
            quantity,
            firstTokenId
        );
    }

    // =============================================================
    //                    ALLOCATION ENFORCEMENT
    // =============================================================

    function _enforceCommunityAllocation(
        uint256 quantity
    )
        internal
        view
    {
        if (communitySupplyReleased) {
            revert CommunityMintPermanentlyClosed();
        }

        if (
            communityMintsClaimed + quantity >
            COMMUNITY_POOL
        ) {
            revert CommunityPoolExceeded();
        }

        if (totalSupply() + quantity > MAX_SUPPLY) {
            revert MaxSupplyExceeded();
        }
    }

    function _enforceNonCommunityAllocation(
        uint256 quantity
    )
        internal
        view
    {
        uint256 nonCommunityMinted =
            paidMintsClaimed + ownerMintsClaimed;

        if (
            nonCommunityMinted + quantity >
            currentNonCommunityCap()
        ) {
            revert NonCommunityAllocationExceeded();
        }

        if (totalSupply() + quantity > MAX_SUPPLY) {
            revert MaxSupplyExceeded();
        }
    }

    /**
     * Before release:
     *     Paid + owner cap = 765
     *
     * After release:
     *     Paid + owner cap =
     *     1,542 minus community tokens already minted
     */
    function currentNonCommunityCap()
        public
        view
        returns (uint256)
    {
        if (!communitySupplyReleased) {
            return INITIAL_NON_COMMUNITY_ALLOCATION;
        }

        return MAX_SUPPLY - communityMintsClaimed;
    }

    /**
     * Permanently ends free and allowlist minting.
     *
     * Any unused portion of the 777 community pool becomes available
     * for paid and owner minting.
     *
     * The sale must first be Closed.
     */
    function releaseUnusedCommunitySupply()
        external
        onlyOwner
    {
        if (communitySupplyReleased) {
            revert CommunitySupplyAlreadyReleased();
        }

        if (salePhase != SalePhase.Closed) {
            revert SaleMustBeClosed();
        }

        communitySupplyReleased = true;

        uint256 unusedCommunitySupply =
            COMMUNITY_POOL - communityMintsClaimed;

        emit CommunitySupplyReleased(
            communityMintsClaimed,
            unusedCommunitySupply,
            currentNonCommunityCap()
        );
    }

    // =============================================================
    //                      INTERNAL MINT LOGIC
    // =============================================================

    function _mintOne(
        address recipient
    )
        internal
        returns (uint256 tokenId)
    {
        tokenId = _nextTokenId;

        unchecked {
            ++_nextTokenId;
        }

        _safeMint(recipient, tokenId);
    }

    function _mintMultiple(
        address recipient,
        uint256 quantity
    )
        internal
    {
        for (uint256 i; i < quantity; ) {
            _mintOne(recipient);

            unchecked {
                ++i;
            }
        }
    }

    // =============================================================
    //                       OWNER SETTINGS
    // =============================================================

    /**
     * Remix phase values:
     *
     * 0 = Closed
     * 1 = Free
     * 2 = Allowlist
     * 3 = Paid
     */
    function setSalePhase(
        SalePhase newPhase
    )
        external
        onlyOwner
    {
        if (
            communitySupplyReleased &&
            (
                newPhase == SalePhase.Free ||
                newPhase == SalePhase.Allowlist
            )
        ) {
            revert CommunityMintPermanentlyClosed();
        }

        SalePhase previousPhase = salePhase;
        salePhase = newPhase;

        emit SalePhaseUpdated(
            previousPhase,
            newPhase
        );
    }

    function setMerkleRoot(
        bytes32 newMerkleRoot
    )
        external
        onlyOwner
    {
        bytes32 previousRoot = merkleRoot;
        merkleRoot = newMerkleRoot;

        emit MerkleRootUpdated(
            previousRoot,
            newMerkleRoot
        );
    }

    function setMintPrice(
        uint256 newMintPrice
    )
        external
        onlyOwner
    {
        uint256 previousPrice = mintPrice;
        mintPrice = newMintPrice;

        emit MintPriceUpdated(
            previousPrice,
            newMintPrice
        );
    }

    function setBaseURI(
        string calldata newBaseURI
    )
        external
        onlyOwner
    {
        string memory previousBaseURI = _baseTokenURI;
        _baseTokenURI = newBaseURI;

        emit BaseURIUpdated(
            previousBaseURI,
            newBaseURI
        );
    }

    function setDefaultRoyalty(
        address receiver,
        uint96 feeNumerator
    )
        external
        onlyOwner
    {
        if (receiver == address(0)) {
            revert ZeroAddress();
        }

        _setDefaultRoyalty(
            receiver,
            feeNumerator
        );
    }

    function deleteDefaultRoyalty()
        external
        onlyOwner
    {
        _deleteDefaultRoyalty();
    }

    function pause()
        external
        onlyOwner
    {
        _pause();
    }

    function unpause()
        external
        onlyOwner
    {
        _unpause();
    }

    // =============================================================
    //                          WITHDRAWAL
    // =============================================================

    function withdraw(
        address payable recipient
    )
        external
        onlyOwner
        nonReentrant
    {
        if (recipient == address(0)) {
            revert ZeroAddress();
        }

        uint256 amount = address(this).balance;

        if (amount == 0) {
            revert NothingToWithdraw();
        }

        (bool success, ) = recipient.call{
            value: amount
        }("");

        if (!success) {
            revert WithdrawalFailed();
        }

        emit Withdrawal(recipient, amount);
    }

    // =============================================================
    //                         READ FUNCTIONS
    // =============================================================

    function remainingTotalSupply()
        external
        view
        returns (uint256)
    {
        return MAX_SUPPLY - totalSupply();
    }

    function remainingCommunitySupply()
        external
        view
        returns (uint256)
    {
        if (communitySupplyReleased) {
            return 0;
        }

        return COMMUNITY_POOL - communityMintsClaimed;
    }

    function nonCommunityMintsClaimed()
        public
        view
        returns (uint256)
    {
        return paidMintsClaimed + ownerMintsClaimed;
    }

    function remainingNonCommunitySupply()
        external
        view
        returns (uint256)
    {
        return
            currentNonCommunityCap() -
            nonCommunityMintsClaimed();
    }

    function totalCommunityMintsForWallet(
        address wallet
    )
        external
        view
        returns (uint256)
    {
        return
            freeMintsByWallet[wallet] +
            allowlistMintsByWallet[wallet];
    }

    function remainingFreeMintsForWallet(
        address wallet
    )
        external
        view
        returns (uint256)
    {
        return
            MAX_FREE_PER_WALLET -
            freeMintsByWallet[wallet];
    }

    function remainingAllowlistMintsForWallet(
        address wallet
    )
        external
        view
        returns (uint256)
    {
        return
            MAX_ALLOWLIST_PER_WALLET -
            allowlistMintsByWallet[wallet];
    }

    function remainingPaidMintsForWallet(
        address wallet
    )
        external
        view
        returns (uint256)
    {
        return
            MAX_PAID_PER_WALLET -
            paidMintsByWallet[wallet];
    }

    function publicMintCost(
        uint256 quantity
    )
        external
        view
        returns (uint256)
    {
        return mintPrice * quantity;
    }

    function isAllowlisted(
        address wallet,
        bytes32[] calldata merkleProof
    )
        external
        view
        returns (bool)
    {
        return MerkleProof.verifyCalldata(
            merkleProof,
            merkleRoot,
            _allowlistLeaf(wallet)
        );
    }

    function nextTokenId()
        external
        view
        returns (uint256)
    {
        return _nextTokenId;
    }

    function contractBalance()
        external
        view
        returns (uint256)
    {
        return address(this).balance;
    }

    function _allowlistLeaf(
        address wallet
    )
        internal
        pure
        returns (bytes32)
    {
        // Compatible with OpenZeppelin StandardMerkleTree
        // using ["address"] entries.
        return keccak256(
            bytes.concat(
                keccak256(
                    abi.encode(wallet)
                )
            )
        );
    }

    // =============================================================
    //                           METADATA
    // =============================================================

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

    // =============================================================
    //                    REQUIRED OZ OVERRIDES
    // =============================================================

    function _update(
        address to,
        uint256 tokenId,
        address auth
    )
        internal
        override(
            ERC721,
            ERC721Enumerable,
            ERC721Pausable
        )
        returns (address)
    {
        return super._update(
            to,
            tokenId,
            auth
        );
    }

    function _increaseBalance(
        address account,
        uint128 value
    )
        internal
        override(
            ERC721,
            ERC721Enumerable
        )
    {
        super._increaseBalance(
            account,
            value
        );
    }

    function supportsInterface(
        bytes4 interfaceId
    )
        public
        view
        override(
            ERC721,
            ERC721Enumerable,
            ERC721Royalty
        )
        returns (bool)
    {
        return super.supportsInterface(
            interfaceId
        );
    }
}
Chain explorer4114msChain node80ms