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
- Creator
- 0xCEE1631a25…46Ac11fEbD
- Creation tx
- 0x71c7ab3dd1…78a7df9033
Token
- Name
- Ghosthood
- Symbol
- GHOOD
- Decimals
- —
- Holders
- 1,410
- Market
- View token page →
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