Ghosthood

0xc1172b138d01c7753ad5d926389c30212bf6f7ca

Verification
Verified
v0.8.24+commit.e11b9ed9
Type
Contract
13,804 bytes
ABI entries
115
42 read · 26 write
License
none

Contract information

Address
0xc1172b138d01c7753ad5d926389c30212bf6f7ca
Chain
Robinhood Chain (4663)
Compiler
v0.8.24+commit.e11b9ed9
Optimization
Enabled
Creation tx
0x71c7ab3dd1…78a7df9033

Token

Name
Ghosthood
Symbol
GHOOD
Decimals
Holders
1,410

Read contract (42)

DEFAULT_TRANSFER_VALIDATOR()address
TOTAL_SUPPLY()uint256
authorizedMinter()address
autoApproveTransfersFromValidator()bool
balanceOf(address)uint256
freeBps()uint256
freeMintActive()bool
freeMinted()uint256
freeMintedByWallet(address)uint256
getApproved(uint256)address
getTransferValidationFunction()bytes4, bool
getTransferValidator()address
heldFor(uint256)uint256
holdAnchorOf(uint256)uint64
isApprovedForAll(address, address)bool
lastMintAt(address)uint256
maxFreePerWallet()uint256
maxFreeSupply()uint256
maxMintsPerWallet()uint256
maxQtyPerTx()uint256
mintCooldown()uint256
mintedPerWallet(address)uint256
name()string
nextTokenId()uint256
owner()address
ownerOf(uint256)address
price()uint256
quoteMint(address, uint256)uint256, uint256, uint256
renderer()address
renounceOwnership()
royaltyBps()uint256
royaltyInfo(uint256, uint256)address, uint256
saleActive()bool
supportsInterface(bytes4)bool
symbol()string
tokenURI(uint256)string
totalMinted()uint256
totalSupply()uint256
traitsOf(uint256)uint256
v2Threshold()uint64
v3Threshold()uint64
variantOf(uint256)uint8

Events (21)

ApprovalApprovalForAllAuthorizedMinterUpdatedAutomaticApprovalOfTransferValidatorSetConfigUpdatedConsecutiveTransferEvolutionThresholdsUpdatedMaxMintsPerWalletUpdatedMaxQtyPerTxUpdatedMetadataUpdateMintCooldownUpdatedMintedMinterMintedOwnershipTransferredPriceUpdatedRefundedRendererUpdatedRoyaltyBpsUpdatedSaleStatusUpdatedTransferTransferValidatorUpdated

ABI

[
  {
    "inputs": [],
    "stateMutability": "nonpayable",
    "type": "constructor"
  },
  {
    "inputs": [],
    "name": "ApprovalCallerNotOwnerNorApproved",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "ApprovalQueryForNonexistentToken",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "BalanceQueryForZeroAddress",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "CreatorTokenBase__InvalidTransferValidatorContract",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "MintERC2309QuantityExceedsLimit",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "MintToZeroAddress",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "MintZeroQuantity",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "NotCompatibleWithSpotMints",
    "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": "OwnerQueryForNonexistentToken",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "OwnershipNotInitializedForExtraData",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "ReentrancyGuardReentrantCall",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "SequentialMintExceedsLimit",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "SequentialUpToTooSmall",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "ShouldNotMintToBurnAddress",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "SpotMintTokenIdTooSmall",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "TokenAlreadyExists",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "TokenIdsNotStrictlyAscending",
    "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": "oldMinter",
        "type": "address"
      },
      {
        "indexed": true,
        "internalType": "address",
        "name": "newMinter",
        "type": "address"
      }
    ],
    "name": "AuthorizedMinterUpdated",
    "type": "event"
  },
  {
    "anonymous": false,
    "inputs": [
      {
        "indexed": false,
        "internalType": "bool",
        "name": "autoApproved",
        "type": "bool"
      }
    ],
    "name": "AutomaticApprovalOfTransferValidatorSet",
    "type": "event"
  },
  {
    "anonymous": false,
    "inputs": [
      {
        "indexed": true,
        "internalType": "bytes32",
        "name": "key",
        "type": "bytes32"
      },
      {
        "indexed": false,
        "internalType": "uint256",
        "name": "value",
        "type": "uint256"
      }
    ],
    "name": "ConfigUpdated",
    "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": false,
        "internalType": "uint64",
        "name": "v2Seconds",
        "type": "uint64"
      },
      {
        "indexed": false,
        "internalType": "uint64",
        "name": "v3Seconds",
        "type": "uint64"
      }
    ],
    "name": "EvolutionThresholdsUpdated",
    "type": "event"
  },
  {
    "anonymous": false,
    "inputs": [
      {
        "indexed": false,
        "internalType": "uint256",
        "name": "newMax",
        "type": "uint256"
      }
    ],
    "name": "MaxMintsPerWalletUpdated",
    "type": "event"
  },
  {
    "anonymous": false,
    "inputs": [
      {
        "indexed": false,
        "internalType": "uint256",
        "name": "newMax",
        "type": "uint256"
      }
    ],
    "name": "MaxQtyPerTxUpdated",
    "type": "event"
  },
  {
    "anonymous": false,
    "inputs": [
      {
        "indexed": false,
        "internalType": "uint256",
        "name": "_tokenId",
        "type": "uint256"
      }
    ],
    "name": "MetadataUpdate",
    "type": "event"
  },
  {
    "anonymous": false,
    "inputs": [
      {
        "indexed": false,
        "internalType": "uint256",
        "name": "newCooldownSeconds",
        "type": "uint256"
      }
    ],
    "name": "MintCooldownUpdated",
    "type": "event"
  },
  {
    "anonymous": false,
    "inputs": [
      {
        "indexed": true,
        "internalType": "address",
        "name": "minter",
        "type": "address"
      },
      {
        "indexed": true,
        "internalType": "uint256",
        "name": "tokenId",
        "type": "uint256"
      },
      {
        "indexed": false,
        "internalType": "bool",
        "name": "free",
        "type": "bool"
      },
      {
        "indexed": false,
        "internalType": "uint256",
        "name": "traits",
        "type": "uint256"
      }
    ],
    "name": "Minted",
    "type": "event"
  },
  {
    "anonymous": false,
    "inputs": [
      {
        "indexed": true,
        "internalType": "address",
        "name": "minter",
        "type": "address"
      },
      {
        "indexed": true,
        "internalType": "address",
        "name": "to",
        "type": "address"
      },
      {
        "indexed": true,
        "internalType": "uint256",
        "name": "tokenId",
        "type": "uint256"
      },
      {
        "indexed": false,
        "internalType": "uint256",
        "name": "traits",
        "type": "uint256"
      }
    ],
    "name": "MinterMinted",
    "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": false,
        "internalType": "uint256",
        "name": "newPriceWei",
        "type": "uint256"
      }
    ],
    "name": "PriceUpdated",
    "type": "event"
  },
  {
    "anonymous": false,
    "inputs": [
      {
        "indexed": true,
        "internalType": "address",
        "name": "minter",
        "type": "address"
      },
      {
        "indexed": false,
        "internalType": "uint256",
        "name": "amount",
        "type": "uint256"
      }
    ],
    "name": "Refunded",
    "type": "event"
  },
  {
    "anonymous": false,
    "inputs": [
      {
        "indexed": false,
        "internalType": "address",
        "name": "newRenderer",
        "type": "address"
      }
    ],
    "name": "RendererUpdated",
    "type": "event"
  },
  {
    "anonymous": false,
    "inputs": [
      {
        "indexed": false,
        "internalType": "uint256",
        "name": "newRoyaltyBps",
        "type": "uint256"
      }
    ],
    "name": "RoyaltyBpsUpdated",
    "type": "event"
  },
  {
    "anonymous": false,
    "inputs": [
      {
        "indexed": false,
        "internalType": "bool",
        "name": "active",
        "type": "bool"
      }
    ],
    "name": "SaleStatusUpdated",
    "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": "oldValidator",
        "type": "address"
      },
      {
        "indexed": false,
        "internalType": "address",
        "name": "newValidator",
        "type": "address"
      }
    ],
    "name": "TransferValidatorUpdated",
    "type": "event"
  },
  {
    "inputs": [],
    "name": "DEFAULT_TRANSFER_VALIDATOR",
    "outputs": [
      {
        "internalType": "address",
        "name": "",
        "type": "address"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "TOTAL_SUPPLY",
    "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": "payable",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "authorizedMinter",
    "outputs": [
      {
        "internalType": "address",
        "name": "",
        "type": "address"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "autoApproveTransfersFromValidator",
    "outputs": [
      {
        "internalType": "bool",
        "name": "",
        "type": "bool"
      }
    ],
    "stateMutability": "view",
    "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": "burn",
    "outputs": [],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "to",
        "type": "address"
      },
      {
        "internalType": "uint16",
        "name": "quantity",
        "type": "uint16"
      }
    ],
    "name": "devMint",
    "outputs": [],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "freeBps",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "",
        "type": "uint256"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "freeMintActive",
    "outputs": [
      {
        "internalType": "bool",
        "name": "",
        "type": "bool"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "freeMinted",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "",
        "type": "uint256"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "",
        "type": "address"
      }
    ],
    "name": "freeMintedByWallet",
    "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": [],
    "name": "getTransferValidationFunction",
    "outputs": [
      {
        "internalType": "bytes4",
        "name": "functionSignature",
        "type": "bytes4"
      },
      {
        "internalType": "bool",
        "name": "isViewFunction",
        "type": "bool"
      }
    ],
    "stateMutability": "pure",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "getTransferValidator",
    "outputs": [
      {
        "internalType": "address",
        "name": "validator",
        "type": "address"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "uint256",
        "name": "tokenId",
        "type": "uint256"
      }
    ],
    "name": "heldFor",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "",
        "type": "uint256"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "uint256",
        "name": "",
        "type": "uint256"
      }
    ],
    "name": "holdAnchorOf",
    "outputs": [
      {
        "internalType": "uint64",
        "name": "",
        "type": "uint64"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "owner",
        "type": "address"
      },
      {
        "internalType": "address",
        "name": "operator",
        "type": "address"
      }
    ],
    "name": "isApprovedForAll",
    "outputs": [
      {
        "internalType": "bool",
        "name": "isApproved",
        "type": "bool"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "",
        "type": "address"
      }
    ],
    "name": "lastMintAt",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "",
        "type": "uint256"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "maxFreePerWallet",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "",
        "type": "uint256"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "maxFreeSupply",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "",
        "type": "uint256"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "maxMintsPerWallet",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "",
        "type": "uint256"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "maxQtyPerTx",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "",
        "type": "uint256"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "to",
        "type": "address"
      },
      {
        "internalType": "uint256",
        "name": "traits",
        "type": "uint256"
      }
    ],
    "name": "mintByMinter",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "",
        "type": "uint256"
      }
    ],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "mintCooldown",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "",
        "type": "uint256"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "",
        "type": "address"
      }
    ],
    "name": "mintedPerWallet",
    "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": "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": "price",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "",
        "type": "uint256"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "uint256",
        "name": "quantity",
        "type": "uint256"
      }
    ],
    "name": "publicMint",
    "outputs": [],
    "stateMutability": "payable",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "minter",
        "type": "address"
      },
      {
        "internalType": "uint256",
        "name": "quantity",
        "type": "uint256"
      }
    ],
    "name": "quoteMint",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "requiredPrice",
        "type": "uint256"
      },
      {
        "internalType": "uint256",
        "name": "freeCount",
        "type": "uint256"
      },
      {
        "internalType": "uint256",
        "name": "paidCount",
        "type": "uint256"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "renderer",
    "outputs": [
      {
        "internalType": "address",
        "name": "",
        "type": "address"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "renounceOwnership",
    "outputs": [],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "royaltyBps",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "",
        "type": "uint256"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "uint256",
        "name": "",
        "type": "uint256"
      },
      {
        "internalType": "uint256",
        "name": "salePrice",
        "type": "uint256"
      }
    ],
    "name": "royaltyInfo",
    "outputs": [
      {
        "internalType": "address",
        "name": "receiver",
        "type": "address"
      },
      {
        "internalType": "uint256",
        "name": "royaltyAmount",
        "type": "uint256"
      }
    ],
    "stateMutability": "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": "saleActive",
    "outputs": [
      {
        "internalType": "bool",
        "name": "",
        "type": "bool"
      }
    ],
    "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": "address",
        "name": "minter",
        "type": "address"
      }
    ],
    "name": "setAuthorizedMinter",
    "outputs": [],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "bool",
        "name": "autoApprove",
        "type": "bool"
      }
    ],
    "name": "setAutomaticApprovalOfTransfersFromValidator",
    "outputs": [],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "uint64",
        "name": "v2Seconds",
        "type": "uint64"
      },
      {
        "internalType": "uint64",
        "name": "v3Seconds",
        "type": "uint64"
      }
    ],
    "name": "setEvolutionThresholds",
    "outputs": [],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "uint256",
        "name": "newFreeBps",
        "type": "uint256"
      }
    ],
    "name": "setFreeBps",
    "outputs": [],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "bool",
        "name": "active",
        "type": "bool"
      }
    ],
    "name": "setFreeMintActive",
    "outputs": [],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "uint256",
        "name": "newMax",
        "type": "uint256"
      }
    ],
    "name": "setMaxFreePerWallet",
    "outputs": [],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "uint256",
        "name": "newMax",
        "type": "uint256"
      }
    ],
    "name": "setMaxFreeSupply",
    "outputs": [],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "uint256",
        "name": "newMax",
        "type": "uint256"
      }
    ],
    "name": "setMaxMintsPerWallet",
    "outputs": [],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "uint256",
        "name": "newMax",
        "type": "uint256"
      }
    ],
    "name": "setMaxQtyPerTx",
    "outputs": [],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "uint256",
        "name": "newCooldownSeconds",
        "type": "uint256"
      }
    ],
    "name": "setMintCooldown",
    "outputs": [],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "uint256",
        "name": "newPriceWei",
        "type": "uint256"
      }
    ],
    "name": "setPrice",
    "outputs": [],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "newRenderer",
        "type": "address"
      }
    ],
    "name": "setRenderer",
    "outputs": [],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "uint256",
        "name": "newRoyaltyBps",
        "type": "uint256"
      }
    ],
    "name": "setRoyaltyBps",
    "outputs": [],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "bool",
        "name": "active",
        "type": "bool"
      }
    ],
    "name": "setSaleActive",
    "outputs": [],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "transferValidator_",
        "type": "address"
      }
    ],
    "name": "setTransferValidator",
    "outputs": [],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "bytes4",
        "name": "interfaceId",
        "type": "bytes4"
      }
    ],
    "name": "supportsInterface",
    "outputs": [
      {
        "internalType": "bool",
        "name": "",
        "type": "bool"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "symbol",
    "outputs": [
      {
        "internalType": "string",
        "name": "",
        "type": "string"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "uint256",
        "name": "tokenId",
        "type": "uint256"
      }
    ],
    "name": "tokenURI",
    "outputs": [
      {
        "internalType": "string",
        "name": "",
        "type": "string"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "totalMinted",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "",
        "type": "uint256"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "totalSupply",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "result",
        "type": "uint256"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "uint256",
        "name": "",
        "type": "uint256"
      }
    ],
    "name": "traitsOf",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "",
        "type": "uint256"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "from",
        "type": "address"
      },
      {
        "internalType": "address",
        "name": "to",
        "type": "address"
      },
      {
        "internalType": "uint256",
        "name": "tokenId",
        "type": "uint256"
      }
    ],
    "name": "transferFrom",
    "outputs": [],
    "stateMutability": "payable",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "newOwner",
        "type": "address"
      }
    ],
    "name": "transferOwnership",
    "outputs": [],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "v2Threshold",
    "outputs": [
      {
        "internalType": "uint64",
        "name": "",
        "type": "uint64"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "v3Threshold",
    "outputs": [
      {
        "internalType": "uint64",
        "name": "",
        "type": "uint64"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "uint256",
        "name": "tokenId",
        "type": "uint256"
      }
    ],
    "name": "variantOf",
    "outputs": [
      {
        "internalType": "uint8",
        "name": "",
        "type": "uint8"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "address payable",
        "name": "to",
        "type": "address"
      }
    ],
    "name": "withdraw",
    "outputs": [],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "stateMutability": "payable",
    "type": "receive"
  }
]

Source code

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;

import {ERC721AC} from "@limitbreak/creator-token-standards/erc721c/ERC721AC.sol";
import {IERC2981} from "@openzeppelin/contracts/interfaces/IERC2981.sol";
import {IERC165} from "@openzeppelin/contracts/utils/introspection/IERC165.sol";
import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol";
import {ReentrancyGuard} from "@openzeppelin/contracts/utils/ReentrancyGuard.sol";

import {IGhosthoodRenderer} from "./GhosthoodRenderer.sol";

contract Ghosthood is ERC721AC, IERC2981, Ownable, ReentrancyGuard {
    uint256 public constant TOTAL_SUPPLY = 6969;

    mapping(uint256 => uint256) public traitsOf;

    mapping(uint256 => uint64) public holdAnchorOf;
    uint64 public v2Threshold = 30 days;
    uint64 public v3Threshold = 90 days;

    uint256 public mintCooldown = 0;
    mapping(address => uint256) public lastMintAt;

    uint256 public totalMinted;
    mapping(address => uint256) public mintedPerWallet;
    uint256 public maxMintsPerWallet = 10;

    uint256 public maxQtyPerTx = 5;

    uint256 public freeBps = 5000;
    uint256 public maxFreeSupply = 3484;
    uint256 public maxFreePerWallet = 2;
    bool public freeMintActive = true;
    uint256 public freeMinted;
    mapping(address => uint256) public freeMintedByWallet;

    uint256 public price = 0;
    uint256 public royaltyBps = 1000;
    bool public saleActive;

    address public renderer;

    address public authorizedMinter;

    bytes32 private immutable _collectionSeed;
    uint256 private _rngCounter;

    event Minted(address indexed minter, uint256 indexed tokenId, bool free, uint256 traits);
    event MinterMinted(address indexed minter, address indexed to, uint256 indexed tokenId, uint256 traits);
    event SaleStatusUpdated(bool active);
    event PriceUpdated(uint256 newPriceWei);
    event RoyaltyBpsUpdated(uint256 newRoyaltyBps);
    event MaxMintsPerWalletUpdated(uint256 newMax);
    event MaxQtyPerTxUpdated(uint256 newMax);
    event MintCooldownUpdated(uint256 newCooldownSeconds);
    event EvolutionThresholdsUpdated(uint64 v2Seconds, uint64 v3Seconds);
    event RendererUpdated(address newRenderer);
    event AuthorizedMinterUpdated(address indexed oldMinter, address indexed newMinter);
    event ConfigUpdated(bytes32 indexed key, uint256 value);
    event Refunded(address indexed minter, uint256 amount);

    event MetadataUpdate(uint256 _tokenId);

    constructor() ERC721AC("Ghosthood", "GHOOD") Ownable(msg.sender) {
        _collectionSeed = keccak256(abi.encodePacked(block.prevrandao, block.timestamp, msg.sender, address(this)));
    }

    function _startTokenId() internal pure override returns (uint256) {
        return 1;
    }

    function _requireCallerIsContractOwner() internal view override {
        _checkOwner();
    }

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

    function quoteMint(address minter, uint256 quantity)
        public
        view
        returns (uint256 requiredPrice, uint256 freeCount, uint256 paidCount)
    {
        require(quantity > 0 && quantity <= maxQtyPerTx, "bad quantity");
        require(totalMinted + quantity <= TOTAL_SUPPLY, "sold out");
        require(mintedPerWallet[minter] + quantity <= maxMintsPerWallet, "wallet cap");

        freeCount = _freeCountFor(minter, quantity);
        paidCount = quantity - freeCount;
        requiredPrice = price * paidCount;
    }

    function _freeCountFor(address minter, uint256 quantity) internal view returns (uint256) {
        if (!freeMintActive) return 0;
        uint256 globalCap = (TOTAL_SUPPLY * freeBps) / 10_000;
        if (maxFreeSupply < globalCap) globalCap = maxFreeSupply;
        if (freeMinted >= globalCap) return 0;
        uint256 globalLeft = globalCap - freeMinted;

        uint256 walletFree = freeMintedByWallet[minter];
        if (walletFree >= maxFreePerWallet) return 0;
        uint256 walletLeft = maxFreePerWallet - walletFree;

        uint256 freeCount = quantity;
        if (walletLeft < freeCount) freeCount = walletLeft;
        if (globalLeft < freeCount) freeCount = globalLeft;
        return freeCount;
    }

    function publicMint(uint256 quantity) external payable nonReentrant {
        require(saleActive, "sale closed");
        require(block.timestamp >= lastMintAt[msg.sender] + mintCooldown, "cooldown");

        (uint256 requiredPrice, uint256 freeCount,) = quoteMint(msg.sender, quantity);
        require(msg.value >= requiredPrice, "underpaid");

        lastMintAt[msg.sender] = block.timestamp;
        mintedPerWallet[msg.sender] += quantity;
        totalMinted += quantity;
        if (freeCount > 0) {
            freeMinted += freeCount;
            freeMintedByWallet[msg.sender] += freeCount;
        }

        _batchMintMixed(msg.sender, quantity, freeCount);

        if (msg.value > requiredPrice) {
            _safeRefund(msg.sender, msg.value - requiredPrice);
        }
    }

    function _safeRefund(address to, uint256 amount) internal {
        (bool ok,) = payable(to).call{value: amount}("");
        require(ok, "refund failed");
        emit Refunded(to, amount);
    }

    function _batchMintMixed(address to, uint256 quantity, uint256 freeCount) internal {
        uint256 startId = _nextTokenId();
        for (uint256 i = 0; i < quantity; i++) {
            uint256 tokenId = startId + i;
            uint256 seed = uint256(
                keccak256(abi.encodePacked(_collectionSeed, tokenId, to, ++_rngCounter, block.prevrandao))
            );
            traitsOf[tokenId] = _rollTraits(seed);
        }
        _safeMint(to, quantity);
        for (uint256 i = 0; i < quantity; i++) {
            uint256 tokenId = startId + i;
            emit Minted(to, tokenId, i < freeCount, traitsOf[tokenId]);
        }
    }

    function _batchMint(address to, uint256 quantity, bool free) internal {
        uint256 startId = _nextTokenId();
        for (uint256 i = 0; i < quantity; i++) {
            uint256 tokenId = startId + i;
            uint256 seed = uint256(
                keccak256(abi.encodePacked(_collectionSeed, tokenId, to, ++_rngCounter, block.prevrandao))
            );
            traitsOf[tokenId] = _rollTraits(seed);
        }
        _safeMint(to, quantity);
        for (uint256 i = 0; i < quantity; i++) {
            uint256 tokenId = startId + i;
            emit Minted(to, tokenId, free, traitsOf[tokenId]);
        }
    }

    function _rollTraits(uint256 seed) internal pure returns (uint256) {
        uint256 entity;
        {
            uint256 r = seed % 1000;
            if (r < 820) entity = (seed >> 8) % 11;
            else if (r < 870) entity = 11;
            else if (r < 920) entity = 12;
            else if (r < 960) entity = 13;
            else if (r < 990) entity = 14;
            else entity = 15;
        }
        uint256 artifact;
        {
            uint256 ar = (seed >> 16) % 100;
            if (ar < 25) artifact = 10;
            else if (ar < 32) artifact = 8 + ((seed >> 23) & 1);
            else artifact = (seed >> 24) % 8;
        }
        uint256 palette;
        {
            uint256 pr = (seed >> 32) % 100;
            if (pr < 12) palette = 8 + ((seed >> 39) & 1);
            else palette = (seed >> 40) % 8;
        }
        uint256 field = (seed >> 48) % 6;
        uint256 corrupted = ((seed >> 56) % 100) < 10 ? 1 : 0;
        return entity | (artifact << 8) | (palette << 16) | (field << 24) | (corrupted << 32);
    }

    function burn(uint256 tokenId) external {
        _burn(tokenId, true);
    }

    function variantOf(uint256 tokenId) public view returns (uint8) {
        require(_exists(tokenId), "nonexistent token");
        uint256 held = block.timestamp - holdAnchorOf[tokenId];
        if (held >= v3Threshold) return 2;
        if (held >= v2Threshold) return 1;
        return 0;
    }

    function heldFor(uint256 tokenId) external view returns (uint256) {
        require(_exists(tokenId), "nonexistent token");
        return block.timestamp - holdAnchorOf[tokenId];
    }

    function setEvolutionThresholds(uint64 v2Seconds, uint64 v3Seconds) external onlyOwner {
        require(v2Seconds > 0 && v3Seconds > v2Seconds, "bad thresholds");
        v2Threshold = v2Seconds;
        v3Threshold = v3Seconds;
        emit EvolutionThresholdsUpdated(v2Seconds, v3Seconds);
    }

    modifier onlyAuthorizedMinter() {
        require(authorizedMinter != address(0) && msg.sender == authorizedMinter, "not minter");
        _;
    }

    function mintByMinter(address to, uint256 traits) external onlyAuthorizedMinter nonReentrant returns (uint256) {
        require(to != address(0), "zero addr");
        uint256 tokenId = _nextTokenId();
        traitsOf[tokenId] = traits;
        _safeMint(to, 1);
        emit MinterMinted(msg.sender, to, tokenId, traits);
        return tokenId;
    }

    function setAuthorizedMinter(address minter) external onlyOwner {
        address old = authorizedMinter;
        authorizedMinter = minter;
        emit AuthorizedMinterUpdated(old, minter);
    }

    function tokenURI(uint256 tokenId) public view override returns (string memory) {
        require(_exists(tokenId), "nonexistent token");
        require(renderer != address(0), "renderer unset");
        return IGhosthoodRenderer(renderer).tokenURI(tokenId, traitsOf[tokenId], variantOf(tokenId));
    }

    function royaltyInfo(uint256, uint256 salePrice)
        external
        view
        override
        returns (address receiver, uint256 royaltyAmount)
    {
        receiver = owner();
        royaltyAmount = (salePrice * royaltyBps) / 10_000;
    }

    function _afterTokenTransfers(address from, address to, uint256 startTokenId, uint256 quantity)
        internal
        override
    {
        super._afterTokenTransfers(from, to, startTokenId, quantity);
        for (uint256 i = 0; i < quantity; i++) {
            uint256 tokenId = startTokenId + i;
            if (to != address(0)) {
                holdAnchorOf[tokenId] = uint64(block.timestamp);
            }
            if (from != address(0) && to != address(0)) {
                emit MetadataUpdate(tokenId);
            }
        }
    }

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

    function setSaleActive(bool active) external onlyOwner {
        saleActive = active;
        emit SaleStatusUpdated(active);
        emit ConfigUpdated("saleActive", active ? 1 : 0);
    }

    function setPrice(uint256 newPriceWei) external onlyOwner {
        price = newPriceWei;
        emit PriceUpdated(newPriceWei);
        emit ConfigUpdated("price", newPriceWei);
    }

    function setFreeBps(uint256 newFreeBps) external onlyOwner {
        require(newFreeBps <= 10_000, "bps max 10000");
        freeBps = newFreeBps;
        emit ConfigUpdated("freeBps", newFreeBps);
    }

    function setMaxFreeSupply(uint256 newMax) external onlyOwner {
        require(newMax <= TOTAL_SUPPLY, "exceeds supply");
        maxFreeSupply = newMax;
        emit ConfigUpdated("maxFreeSupply", newMax);
    }

    function setMaxFreePerWallet(uint256 newMax) external onlyOwner {
        require(newMax <= maxMintsPerWallet, "exceeds wallet cap");
        maxFreePerWallet = newMax;
        emit ConfigUpdated("maxFreePerWallet", newMax);
    }

    function setFreeMintActive(bool active) external onlyOwner {
        freeMintActive = active;
        emit ConfigUpdated("freeMintActive", active ? 1 : 0);
    }

    function setRoyaltyBps(uint256 newRoyaltyBps) external onlyOwner {
        require(newRoyaltyBps <= 1000, "royalty max 10%");
        royaltyBps = newRoyaltyBps;
        emit RoyaltyBpsUpdated(newRoyaltyBps);
        emit ConfigUpdated("royaltyBps", newRoyaltyBps);
    }

    function setMaxMintsPerWallet(uint256 newMax) external onlyOwner {
        require(newMax > 0, "max=0");
        require(newMax >= maxFreePerWallet, "below free cap");
        maxMintsPerWallet = newMax;
        emit MaxMintsPerWalletUpdated(newMax);
        emit ConfigUpdated("maxMintsPerWallet", newMax);
    }

    function setMaxQtyPerTx(uint256 newMax) external onlyOwner {
        require(newMax > 0, "max=0");
        maxQtyPerTx = newMax;
        emit MaxQtyPerTxUpdated(newMax);
        emit ConfigUpdated("maxQtyPerTx", newMax);
    }

    function setMintCooldown(uint256 newCooldownSeconds) external onlyOwner {
        mintCooldown = newCooldownSeconds;
        emit MintCooldownUpdated(newCooldownSeconds);
        emit ConfigUpdated("mintCooldown", newCooldownSeconds);
    }

    function setRenderer(address newRenderer) external onlyOwner {
        renderer = newRenderer;
        emit RendererUpdated(newRenderer);
    }

    function devMint(address to, uint16 quantity) external onlyOwner nonReentrant {
        require(to != address(0), "zero addr");
        require(quantity > 0, "qty=0");
        require(totalMinted + quantity <= TOTAL_SUPPLY, "exceeds supply");
        totalMinted += quantity;
        _batchMint(to, quantity, true);
    }

    function withdraw(address payable to) external onlyOwner nonReentrant {
        require(to != address(0), "zero addr");
        uint256 bal = address(this).balance;
        require(bal > 0, "nothing to withdraw");
        (bool ok,) = to.call{value: bal}("");
        require(ok, "withdraw failed");
    }

    function renounceOwnership() public view override onlyOwner {
        revert("renounce disabled");
    }

    receive() external payable {}
}
Chain explorer3751msChain node87ms