Hoodyo
0xfdc0c0576adfa867103d0a552e1671c5486703cd
Verification
Verified
v0.8.24+commit.e11b9ed9
Type
Contract
8,117 bytes
ABI entries
50
28 read · 14 write
License
none
Contract information
- Address
- 0xfdc0c0576adfa867103d0a552e1671c5486703cd
- Chain
- Robinhood Chain (4663)
- Compiler
- v0.8.24+commit.e11b9ed9
- Optimization
- Enabled
- Creator
- 0x4B2115D365…C523185De8
- Creation tx
- 0x46becf1f7b…5e724b591d
Token
- Name
- Hoodyo
- Symbol
- HOODYO
- Decimals
- —
- Holders
- 1,339
- Market
- View token page →
Read contract (28)
IMAGES_CID() → string
MAX_SUPPLY() → uint256
METADATA_CID() → string
ROYALTY_BPS() → uint96
TIER1_COINS() → uint256
TIER2_COINS() → uint256
TIER3_COINS() → uint256
allowanceOf(address) → uint256
balanceOf(address) → uint256
baseURI() → string
claimableOf(address) → uint256
claimed(address) → uint256
coin() → address
coinDecimals() → uint8
coinLocked() → bool
getApproved(uint256) → address
isApprovedForAll(address, address) → bool
metadataLocked() → bool
mintOpen() → bool
name() → string
owner() → address
ownerOf(uint256) → address
royaltyInfo(uint256, uint256) → address, uint256
royaltyReceiver() → address
supportsInterface(bytes4) → bool
symbol() → string
tokenURI(uint256) → string
totalSupply() → uint256
Events (7)
ApprovalApprovalForAllClaimedCoinSetMintOpenSetOwnershipTransferredTransfer
ABI
[
{
"inputs": [
{
"internalType": "string",
"name": "baseURI_",
"type": "string"
}
],
"stateMutability": "nonpayable",
"type": "constructor"
},
{
"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": "holder",
"type": "address"
},
{
"indexed": false,
"internalType": "uint256",
"name": "count",
"type": "uint256"
},
{
"indexed": false,
"internalType": "uint256",
"name": "firstId",
"type": "uint256"
}
],
"name": "Claimed",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "address",
"name": "coin",
"type": "address"
},
{
"indexed": false,
"internalType": "uint8",
"name": "decimals",
"type": "uint8"
}
],
"name": "CoinSet",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": false,
"internalType": "bool",
"name": "open",
"type": "bool"
}
],
"name": "MintOpenSet",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "address",
"name": "previousOwner",
"type": "address"
},
{
"indexed": true,
"internalType": "address",
"name": "newOwner",
"type": "address"
}
],
"name": "OwnershipTransferred",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "address",
"name": "from",
"type": "address"
},
{
"indexed": true,
"internalType": "address",
"name": "to",
"type": "address"
},
{
"indexed": true,
"internalType": "uint256",
"name": "tokenId",
"type": "uint256"
}
],
"name": "Transfer",
"type": "event"
},
{
"inputs": [],
"name": "IMAGES_CID",
"outputs": [
{
"internalType": "string",
"name": "",
"type": "string"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "MAX_SUPPLY",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "METADATA_CID",
"outputs": [
{
"internalType": "string",
"name": "",
"type": "string"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "ROYALTY_BPS",
"outputs": [
{
"internalType": "uint96",
"name": "",
"type": "uint96"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "TIER1_COINS",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "TIER2_COINS",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "TIER3_COINS",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "holder",
"type": "address"
}
],
"name": "allowanceOf",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "to",
"type": "address"
},
{
"internalType": "uint256",
"name": "tokenId",
"type": "uint256"
}
],
"name": "approve",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "holder",
"type": "address"
}
],
"name": "balanceOf",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "baseURI",
"outputs": [
{
"internalType": "string",
"name": "",
"type": "string"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "uint256",
"name": "count",
"type": "uint256"
}
],
"name": "claim",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "holder",
"type": "address"
}
],
"name": "claimableOf",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"name": "claimed",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "coin",
"outputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "coinDecimals",
"outputs": [
{
"internalType": "uint8",
"name": "",
"type": "uint8"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "coinLocked",
"outputs": [
{
"internalType": "bool",
"name": "",
"type": "bool"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"name": "getApproved",
"outputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "",
"type": "address"
},
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"name": "isApprovedForAll",
"outputs": [
{
"internalType": "bool",
"name": "",
"type": "bool"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "lockCoin",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [],
"name": "lockMetadata",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [],
"name": "metadataLocked",
"outputs": [
{
"internalType": "bool",
"name": "",
"type": "bool"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "mintOpen",
"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": "count",
"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": [
{
"internalType": "uint256",
"name": "",
"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": [],
"name": "royaltyReceiver",
"outputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"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": "string",
"name": "baseURI_",
"type": "string"
}
],
"name": "setBaseURI",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "coin_",
"type": "address"
}
],
"name": "setCoin",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "bool",
"name": "open",
"type": "bool"
}
],
"name": "setMintOpen",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "receiver",
"type": "address"
}
],
"name": "setRoyaltyReceiver",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "bytes4",
"name": "interfaceId",
"type": "bytes4"
}
],
"name": "supportsInterface",
"outputs": [
{
"internalType": "bool",
"name": "",
"type": "bool"
}
],
"stateMutability": "pure",
"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": "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"
}
]Source code
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;
/// @title Hoodyo
/// @notice The culture coin and pfp of Robinhood Chain. 2000 Hoodyos, minted in
/// order starting at #1, art revealed instantly from IPFS.
/// Free claim for $Hoodyo coin holders:
/// hold at least 1 coin -> mint 1
/// hold at least 2,500,000 -> mint 3
/// hold at least 5,000,000 -> mint 5
/// 5% royalty on all secondary sales via ERC2981.
interface IERC20Like {
function balanceOf(address account) external view returns (uint256);
function decimals() external view returns (uint8);
}
interface IERC721TokenReceiver {
function onERC721Received(address operator, address from, uint256 tokenId, bytes calldata data)
external
returns (bytes4);
}
contract Hoodyo {
// ------------------------------------------------------------------
// Collection constants
// ------------------------------------------------------------------
string public constant name = "Hoodyo";
string public constant symbol = "HOODYO";
uint256 public constant MAX_SUPPLY = 2000;
uint96 public constant ROYALTY_BPS = 500; // 5%
/// @notice On chain provenance seal. IPFS CIDs are content hashes: these
/// constants commit this contract, forever, to the exact bytes of
/// all 2000 images and metadata files. Change one pixel anywhere
/// and the hash no longer matches. Once lockMetadata() is called
/// the pointer can never move either. The chain holds the
/// fingerprint of every Hoodyo, permanently.
string public constant IMAGES_CID = "bafybeidgooynpbednxqqifrxqiicbxnji4qp7toerefxcphf7shbpi3gd4";
string public constant METADATA_CID = "bafybeibebfg5utq6wsxdcocwmg7rsz2ktfbg72efs7zvhouojty2eteiki";
// Claim tiers, denominated in whole coins (scaled by coin decimals when checked)
uint256 public constant TIER1_COINS = 1;
uint256 public constant TIER2_COINS = 2_500_000;
uint256 public constant TIER3_COINS = 5_000_000;
// ------------------------------------------------------------------
// State
// ------------------------------------------------------------------
address public owner;
address public royaltyReceiver;
string public baseURI;
bool public metadataLocked;
address public coin; // $Hoodyo ERC20, set once known
uint8 public coinDecimals;
bool public coinLocked;
bool public mintOpen;
uint256 public totalSupply; // also the last minted token id (sequential from 1)
mapping(address => uint256) public claimed;
mapping(uint256 => address) private _ownerOf;
mapping(address => uint256) private _balanceOf;
mapping(uint256 => address) public getApproved;
mapping(address => mapping(address => bool)) public isApprovedForAll;
// ------------------------------------------------------------------
// Events
// ------------------------------------------------------------------
event Transfer(address indexed from, address indexed to, uint256 indexed tokenId);
event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId);
event ApprovalForAll(address indexed owner, address indexed operator, bool approved);
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
event CoinSet(address indexed coin, uint8 decimals);
event MintOpenSet(bool open);
event Claimed(address indexed holder, uint256 count, uint256 firstId);
modifier onlyOwner() {
require(msg.sender == owner, "not owner");
_;
}
constructor(string memory baseURI_) {
owner = msg.sender;
royaltyReceiver = msg.sender;
baseURI = baseURI_;
}
// ------------------------------------------------------------------
// Claim
// ------------------------------------------------------------------
/// @notice How many total mints a wallet qualifies for at its live coin balance.
function allowanceOf(address holder) public view returns (uint256) {
if (coin == address(0)) return 0;
uint256 bal = IERC20Like(coin).balanceOf(holder);
uint256 unit = 10 ** uint256(coinDecimals);
if (bal >= TIER3_COINS * unit) return 5;
if (bal >= TIER2_COINS * unit) return 3;
if (bal >= TIER1_COINS * unit) return 1;
return 0;
}
/// @notice Mints remaining for a wallet right now.
function claimableOf(address holder) external view returns (uint256) {
uint256 allowed = allowanceOf(holder);
uint256 used = claimed[holder];
if (used >= allowed) return 0;
uint256 left = allowed - used;
uint256 supplyLeft = MAX_SUPPLY - totalSupply;
return left < supplyLeft ? left : supplyLeft;
}
/// @notice Free claim. Holder pays only gas. Mints sequentially from #1.
function claim(uint256 count) external {
require(mintOpen, "mint not open");
require(count > 0, "zero count");
require(totalSupply + count <= MAX_SUPPLY, "sold out");
uint256 allowed = allowanceOf(msg.sender);
require(claimed[msg.sender] + count <= allowed, "exceeds allowance");
claimed[msg.sender] += count;
uint256 firstId = totalSupply + 1;
for (uint256 i = 0; i < count; i++) {
uint256 id = firstId + i;
_ownerOf[id] = msg.sender;
emit Transfer(address(0), msg.sender, id);
}
_balanceOf[msg.sender] += count;
totalSupply += count;
emit Claimed(msg.sender, count, firstId);
}
/// @notice Owner mint for treasury / marketing, obeys max supply.
function ownerMint(address to, uint256 count) external onlyOwner {
require(to != address(0), "zero address");
require(count > 0, "zero count");
require(totalSupply + count <= MAX_SUPPLY, "sold out");
uint256 firstId = totalSupply + 1;
for (uint256 i = 0; i < count; i++) {
uint256 id = firstId + i;
_ownerOf[id] = to;
emit Transfer(address(0), to, id);
}
_balanceOf[to] += count;
totalSupply += count;
}
// ------------------------------------------------------------------
// Admin
// ------------------------------------------------------------------
function setCoin(address coin_) external onlyOwner {
require(!coinLocked, "coin locked");
require(coin_ != address(0), "zero address");
coin = coin_;
uint8 dec = 18;
try IERC20Like(coin_).decimals() returns (uint8 d) {
dec = d;
} catch {}
coinDecimals = dec;
emit CoinSet(coin_, dec);
}
function lockCoin() external onlyOwner {
require(coin != address(0), "coin not set");
coinLocked = true;
}
function setMintOpen(bool open) external onlyOwner {
mintOpen = open;
emit MintOpenSet(open);
}
function setBaseURI(string calldata baseURI_) external onlyOwner {
require(!metadataLocked, "metadata locked");
baseURI = baseURI_;
}
function lockMetadata() external onlyOwner {
metadataLocked = true;
}
function setRoyaltyReceiver(address receiver) external onlyOwner {
require(receiver != address(0), "zero address");
royaltyReceiver = receiver;
}
function transferOwnership(address newOwner) external onlyOwner {
require(newOwner != address(0), "zero address");
emit OwnershipTransferred(owner, newOwner);
owner = newOwner;
}
// ------------------------------------------------------------------
// ERC721
// ------------------------------------------------------------------
function ownerOf(uint256 tokenId) public view returns (address) {
address holder = _ownerOf[tokenId];
require(holder != address(0), "nonexistent token");
return holder;
}
function balanceOf(address holder) external view returns (uint256) {
require(holder != address(0), "zero address");
return _balanceOf[holder];
}
function tokenURI(uint256 tokenId) external view returns (string memory) {
require(_ownerOf[tokenId] != address(0), "nonexistent token");
return string(abi.encodePacked(baseURI, _toString(tokenId), ".json"));
}
function approve(address to, uint256 tokenId) external {
address holder = ownerOf(tokenId);
require(msg.sender == holder || isApprovedForAll[holder][msg.sender], "not authorized");
getApproved[tokenId] = to;
emit Approval(holder, to, tokenId);
}
function setApprovalForAll(address operator, bool approved) external {
isApprovedForAll[msg.sender][operator] = approved;
emit ApprovalForAll(msg.sender, operator, approved);
}
function transferFrom(address from, address to, uint256 tokenId) public {
address holder = ownerOf(tokenId);
require(holder == from, "wrong from");
require(to != address(0), "zero address");
require(
msg.sender == holder || msg.sender == getApproved[tokenId] || isApprovedForAll[holder][msg.sender],
"not authorized"
);
delete getApproved[tokenId];
_ownerOf[tokenId] = to;
_balanceOf[from] -= 1;
_balanceOf[to] += 1;
emit Transfer(from, to, tokenId);
}
function safeTransferFrom(address from, address to, uint256 tokenId) external {
safeTransferFrom(from, to, tokenId, "");
}
function safeTransferFrom(address from, address to, uint256 tokenId, bytes memory data) public {
transferFrom(from, to, tokenId);
if (to.code.length > 0) {
require(
IERC721TokenReceiver(to).onERC721Received(msg.sender, from, tokenId, data)
== IERC721TokenReceiver.onERC721Received.selector,
"unsafe recipient"
);
}
}
// ------------------------------------------------------------------
// ERC2981 royalties: 5% on every secondary sale
// ------------------------------------------------------------------
function royaltyInfo(uint256, uint256 salePrice) external view returns (address, uint256) {
return (royaltyReceiver, (salePrice * ROYALTY_BPS) / 10_000);
}
// ------------------------------------------------------------------
// ERC165
// ------------------------------------------------------------------
function supportsInterface(bytes4 interfaceId) external pure returns (bool) {
return interfaceId == 0x01ffc9a7 // ERC165
|| interfaceId == 0x80ac58cd // ERC721
|| interfaceId == 0x5b5e139f // ERC721Metadata
|| interfaceId == 0x2a55205a; // ERC2981
}
// ------------------------------------------------------------------
// Internals
// ------------------------------------------------------------------
function _toString(uint256 value) internal pure returns (string memory) {
if (value == 0) return "0";
uint256 temp = value;
uint256 digits;
while (temp != 0) {
digits++;
temp /= 10;
}
bytes memory buffer = new bytes(digits);
while (value != 0) {
digits -= 1;
buffer[digits] = bytes1(uint8(48 + (value % 10)));
value /= 10;
}
return string(buffer);
}
}
Chain explorer4558msChain node79ms