HOODTONE
0xc2f06d6ad19e64dd272ef140ca66df30c247c940
Verification
Verified
v0.8.35+commit.47b9dedd
Type
Contract
8,400 bytes
ABI entries
54
26 read · 17 write
License
none
Contract information
- Address
- 0xc2f06d6ad19e64dd272ef140ca66df30c247c940
- Chain
- Robinhood Chain (4663)
- Compiler
- v0.8.35+commit.47b9dedd
- Optimization
- Enabled
- Creator
- 0x5b82919dBA…9CC9a67088
- Creation tx
- 0xa0efbf95c7…4caf786672
Token
- Name
- HOODTONE
- Symbol
- HTONE
- Decimals
- —
- Holders
- 2,256
- Market
- View token page →
Read contract (26)
DEFAULT_TRANSFER_VALIDATOR() → address
MAX_PER_TX() → uint256
MAX_SUPPLY() → uint256
REVEAL_DELAY() → uint256
autoApproveTransfersFromValidator() → bool
balanceOf(address) → uint256
claimedFree(address) → bool
commitBlock(uint256) → uint256
getApproved(uint256) → address
getTransferValidationFunction() → bytes4, bool
getTransferValidator() → address
isApprovedForAll(address, address) → bool
mintActive() → bool
name() → string
owner() → address
ownerOf(uint256) → address
price() → uint256
renderer() → address
revealed(uint256) → bool
royaltyInfo(uint256, uint256) → address, uint256
supportsInterface(bytes4) → bool
symbol() → string
tokenSeed(uint256) → uint256
tokenURI(uint256) → string
totalMinted() → uint256
totalSupply() → uint256
Events (8)
ApprovalApprovalForAllAutomaticApprovalOfTransferValidatorSetDefaultRoyaltySetOwnershipTransferredTokenRoyaltySetTransferTransferValidatorUpdated
ABI
[
{
"inputs": [
{
"internalType": "address",
"name": "royaltyReceiver",
"type": "address"
},
{
"internalType": "uint96",
"name": "royaltyFeeNumerator",
"type": "uint96"
}
],
"stateMutability": "nonpayable",
"type": "constructor"
},
{
"inputs": [],
"name": "CreatorTokenBase__InvalidTransferValidatorContract",
"type": "error"
},
{
"inputs": [],
"name": "ShouldNotMintToBurnAddress",
"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": false,
"internalType": "bool",
"name": "autoApproved",
"type": "bool"
}
],
"name": "AutomaticApprovalOfTransferValidatorSet",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "address",
"name": "receiver",
"type": "address"
},
{
"indexed": false,
"internalType": "uint96",
"name": "feeNumerator",
"type": "uint96"
}
],
"name": "DefaultRoyaltySet",
"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": "uint256",
"name": "tokenId",
"type": "uint256"
},
{
"indexed": true,
"internalType": "address",
"name": "receiver",
"type": "address"
},
{
"indexed": false,
"internalType": "uint96",
"name": "feeNumerator",
"type": "uint96"
}
],
"name": "TokenRoyaltySet",
"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": "MAX_PER_TX",
"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": [],
"name": "REVEAL_DELAY",
"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": [],
"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": "address",
"name": "",
"type": "address"
}
],
"name": "claimedFree",
"outputs": [
{
"internalType": "bool",
"name": "",
"type": "bool"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"name": "commitBlock",
"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": "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": "uint256",
"name": "qty",
"type": "uint256"
}
],
"name": "mint",
"outputs": [],
"stateMutability": "payable",
"type": "function"
},
{
"inputs": [],
"name": "mintActive",
"outputs": [
{
"internalType": "bool",
"name": "",
"type": "bool"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "name",
"outputs": [
{
"internalType": "string",
"name": "",
"type": "string"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "owner",
"outputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "to",
"type": "address"
},
{
"internalType": "uint256",
"name": "qty",
"type": "uint256"
}
],
"name": "ownerMint",
"outputs": [],
"stateMutability": "nonpayable",
"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": [],
"name": "renderer",
"outputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "renounceOwnership",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "uint256",
"name": "tokenId",
"type": "uint256"
}
],
"name": "reveal",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"name": "revealed",
"outputs": [
{
"internalType": "bool",
"name": "",
"type": "bool"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "uint256",
"name": "_tokenId",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "_salePrice",
"type": "uint256"
}
],
"name": "royaltyInfo",
"outputs": [
{
"internalType": "address",
"name": "",
"type": "address"
},
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "from",
"type": "address"
},
{
"internalType": "address",
"name": "to",
"type": "address"
},
{
"internalType": "uint256",
"name": "tokenId",
"type": "uint256"
}
],
"name": "safeTransferFrom",
"outputs": [],
"stateMutability": "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": [
{
"internalType": "address",
"name": "operator",
"type": "address"
},
{
"internalType": "bool",
"name": "approved",
"type": "bool"
}
],
"name": "setApprovalForAll",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "bool",
"name": "autoApprove",
"type": "bool"
}
],
"name": "setAutomaticApprovalOfTransfersFromValidator",
"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": "bool",
"name": "active",
"type": "bool"
}
],
"name": "setMintActive",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "uint256",
"name": "newPrice",
"type": "uint256"
}
],
"name": "setPrice",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "_renderer",
"type": "address"
}
],
"name": "setRenderer",
"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": "",
"type": "uint256"
}
],
"name": "tokenSeed",
"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": [],
"name": "totalMinted",
"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": "withdraw",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
}
]Source code
// SPDX-License-Identifier: MIT
pragma solidity 0.8.35;
import "@limitbreak/creator-token-standards/src/erc721c/ERC721C.sol";
import "@limitbreak/creator-token-standards/src/access/OwnableBasic.sol";
import "@limitbreak/creator-token-standards/src/programmable-royalties/BasicRoyalties.sol";
import "@openzeppelin/contracts/security/ReentrancyGuard.sol";
interface IHoodtoneRenderer {
function tokenURI(uint256 tokenId, uint256 seed) external view returns (string memory);
}
/// @title HOODTONE
/// @notice ERC721-C (Limit Break Creator Token, built-in transfer validator enforcement) + ERC2981 royalties.
/// Traits are resolved via commit-reveal using blockhash, NOT block.prevrandao (constant on
/// Robinhood Chain per their official docs: "block.prevrandao / block.difficulty return a
/// constant value... never use them for randomness"). The seed is only known after reveal()
/// is called, REVEAL_DELAY blocks later -- this prevents simulating trait outcomes before minting.
contract HOODTONE is OwnableBasic, ERC721C, BasicRoyalties, ReentrancyGuard {
uint256 public constant MAX_SUPPLY = 4444;
uint256 public constant MAX_PER_TX = 10;
uint256 public constant REVEAL_DELAY = 3; // blocks to wait before reveal() can be called
uint256 public totalMinted;
uint256 public price = 0.0002 ether;
bool public mintActive;
address public renderer;
mapping(address => bool) public claimedFree;
mapping(uint256 => uint256) public commitBlock;
mapping(uint256 => uint256) public tokenSeed;
mapping(uint256 => bool) public revealed;
constructor(address royaltyReceiver, uint96 royaltyFeeNumerator)
ERC721OpenZeppelin("HOODTONE", "HTONE")
BasicRoyalties(royaltyReceiver, royaltyFeeNumerator)
{
// Note: BasicRoyalties (OZ ERC2981) already rejects royaltyReceiver == address(0)
// internally ("ERC2981: invalid receiver") -- base constructors run before this body,
// so no need to duplicate the check here.
}
/// @notice Combined mint: first unit per wallet is free, remaining units cost `price` each.
function mint(uint256 qty) external payable nonReentrant {
require(mintActive, "Mint not active");
require(qty > 0 && qty <= MAX_PER_TX, "Bad quantity");
require(totalMinted + qty <= MAX_SUPPLY, "Sold out");
uint256 payableQty = qty;
if (!claimedFree[msg.sender]) {
claimedFree[msg.sender] = true;
payableQty -= 1;
}
uint256 cost = payableQty * price;
require(msg.value >= cost, "Insufficient ETH");
_mintBatch(msg.sender, qty);
if (msg.value > cost) {
(bool ok, ) = msg.sender.call{value: msg.value - cost}("");
require(ok, "Refund failed");
}
}
/// @notice Reserve/airdrop mint by the owner. Goes through the same commit-reveal flow for consistency.
function ownerMint(address to, uint256 qty) external {
_requireCallerIsContractOwner();
require(totalMinted + qty <= MAX_SUPPLY, "Sold out");
_mintBatch(to, qty);
}
function _mintBatch(address to, uint256 qty) private {
uint256 target = block.number + REVEAL_DELAY;
for (uint256 i = 0; i < qty; i++) {
uint256 id = ++totalMinted;
commitBlock[id] = target;
_safeMint(to, id);
}
}
/// @notice Resolves a token's seed using the blockhash of the block committed at mint time
/// (a block that didn't exist yet at mint time, so it can't be simulated/sniped).
/// Callable by anyone, any time after REVEAL_DELAY blocks have passed.
function reveal(uint256 tokenId) external {
require(_ownerOf(tokenId) != address(0), "Nonexistent token");
require(!revealed[tokenId], "Already revealed");
uint256 target = commitBlock[tokenId];
require(block.number > target, "Too early");
bytes32 bh = blockhash(target);
if (bh == bytes32(0)) {
// more than 256 blocks have passed, the original hash is gone from the EVM --
// fall back to the immediately preceding block instead
bh = blockhash(block.number - 1);
}
tokenSeed[tokenId] = uint256(keccak256(abi.encodePacked(bh, tokenId, address(this))));
revealed[tokenId] = true;
}
function setMintActive(bool active) external {
_requireCallerIsContractOwner();
mintActive = active;
}
function setPrice(uint256 newPrice) external {
_requireCallerIsContractOwner();
price = newPrice;
}
function setRenderer(address _renderer) external {
_requireCallerIsContractOwner();
renderer = _renderer;
}
function setDefaultRoyalty(address receiver, uint96 feeNumerator) external {
_requireCallerIsContractOwner();
_setDefaultRoyalty(receiver, feeNumerator);
}
function totalSupply() external view returns (uint256) {
return totalMinted;
}
function tokenURI(uint256 tokenId) public view override returns (string memory) {
require(_ownerOf(tokenId) != address(0), "Nonexistent token");
require(revealed[tokenId], "Not revealed yet");
return IHoodtoneRenderer(renderer).tokenURI(tokenId, tokenSeed[tokenId]);
}
function withdraw() external {
_requireCallerIsContractOwner();
(bool ok, ) = owner().call{value: address(this).balance}("");
require(ok, "Withdraw failed");
}
function supportsInterface(bytes4 interfaceId) public view virtual override(ERC721C, ERC2981) returns (bool) {
return ERC721C.supportsInterface(interfaceId) || ERC2981.supportsInterface(interfaceId);
}
}
Chain explorer4540msChain node98ms