HoodMaps

0x36373fff6312e2fc72aab77667d6db5db62526ab

Verification
Verified
v0.8.33+commit.64118f21
Type
Contract
16,043 bytes
ABI entries
84
30 read · 22 write
License
none

Contract information

Address
0x36373fff6312e2fc72aab77667d6db5db62526ab
Chain
Robinhood Chain (4663)
Compiler
v0.8.33+commit.64118f21
Optimization
Enabled
Creation tx
0xf4b682b0d2…12e231fead

Token

Name
HoodMaps
Symbol
HOOD
Decimals
Holders
1,090

Read contract (30)

DEFAULT_TRANSFER_VALIDATOR()address
MAX_SUPPLY()uint256
ROYALTY_BPS()uint96
autoApproveTransfersFromValidator()bool
balanceOf(address)uint256
dataChunks(uint256)address
description()string
genesisSupply()uint256
getApproved(uint256)address
getTransferValidationFunction()bytes4, bool
getTransferValidator()address
isApprovedForAll(address, address)bool
isMinted(uint256)bool
merkleRoot()bytes32
mintedBy(address)uint256
name()string
owner()address
ownerOf(uint256)address
paused()bool
phase()uint8
plotsStored()uint256
publicMintsPerWallet()uint256
publicStart()uint64
royaltyInfo(uint256, uint256)address, uint256
supportsInterface(bytes4)bool
symbol()string
tokenURI(uint256)string
totalSupply()uint256
wlMintsPerWallet()uint256
wlStart()uint64

Events (15)

AirdroppedApprovalApprovalForAllAutomaticApprovalOfTransferValidatorSetDataChunkAddedDefaultRoyaltySetMerkleRootSetOwnershipTransferredPausedSetPhasesSetPlotMintedTokenRoyaltySetTransferTransferValidatorUpdatedWithdrawn

ABI

[
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "royaltyReceiver_",
        "type": "address"
      }
    ],
    "stateMutability": "nonpayable",
    "type": "constructor"
  },
  {
    "inputs": [],
    "name": "BadChunk",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "CreatorTokenBase__InvalidTransferValidatorContract",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "DataMissing",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "MintPaused",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "NonexistentToken",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "NotWhitelisted",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "NothingToWithdraw",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "OutOfRange",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "PublicNotStarted",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "ShouldNotMintToBurnAddress",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "SoldOut",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "WLNotStarted",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "WalletLimitReached",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "WithdrawFailed",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "ZeroAddress",
    "type": "error"
  },
  {
    "anonymous": false,
    "inputs": [
      {
        "indexed": false,
        "internalType": "uint256",
        "name": "count",
        "type": "uint256"
      },
      {
        "indexed": false,
        "internalType": "uint256",
        "name": "newTotal",
        "type": "uint256"
      }
    ],
    "name": "Airdropped",
    "type": "event"
  },
  {
    "anonymous": false,
    "inputs": [
      {
        "indexed": true,
        "internalType": "address",
        "name": "owner",
        "type": "address"
      },
      {
        "indexed": true,
        "internalType": "address",
        "name": "approved",
        "type": "address"
      },
      {
        "indexed": true,
        "internalType": "uint256",
        "name": "tokenId",
        "type": "uint256"
      }
    ],
    "name": "Approval",
    "type": "event"
  },
  {
    "anonymous": false,
    "inputs": [
      {
        "indexed": true,
        "internalType": "address",
        "name": "owner",
        "type": "address"
      },
      {
        "indexed": true,
        "internalType": "address",
        "name": "operator",
        "type": "address"
      },
      {
        "indexed": false,
        "internalType": "bool",
        "name": "approved",
        "type": "bool"
      }
    ],
    "name": "ApprovalForAll",
    "type": "event"
  },
  {
    "anonymous": false,
    "inputs": [
      {
        "indexed": false,
        "internalType": "bool",
        "name": "autoApproved",
        "type": "bool"
      }
    ],
    "name": "AutomaticApprovalOfTransferValidatorSet",
    "type": "event"
  },
  {
    "anonymous": false,
    "inputs": [
      {
        "indexed": false,
        "internalType": "uint256",
        "name": "chunkIndex",
        "type": "uint256"
      },
      {
        "indexed": false,
        "internalType": "uint256",
        "name": "plotsInChunk",
        "type": "uint256"
      },
      {
        "indexed": false,
        "internalType": "uint256",
        "name": "plotsStored",
        "type": "uint256"
      }
    ],
    "name": "DataChunkAdded",
    "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": false,
        "internalType": "bytes32",
        "name": "root",
        "type": "bytes32"
      }
    ],
    "name": "MerkleRootSet",
    "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": "bool",
        "name": "paused",
        "type": "bool"
      }
    ],
    "name": "PausedSet",
    "type": "event"
  },
  {
    "anonymous": false,
    "inputs": [
      {
        "indexed": false,
        "internalType": "uint64",
        "name": "wlStart",
        "type": "uint64"
      },
      {
        "indexed": false,
        "internalType": "uint64",
        "name": "publicStart",
        "type": "uint64"
      }
    ],
    "name": "PhasesSet",
    "type": "event"
  },
  {
    "anonymous": false,
    "inputs": [
      {
        "indexed": true,
        "internalType": "address",
        "name": "to",
        "type": "address"
      },
      {
        "indexed": true,
        "internalType": "uint256",
        "name": "tokenId",
        "type": "uint256"
      }
    ],
    "name": "PlotMinted",
    "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"
  },
  {
    "anonymous": false,
    "inputs": [
      {
        "indexed": true,
        "internalType": "address",
        "name": "to",
        "type": "address"
      },
      {
        "indexed": false,
        "internalType": "uint256",
        "name": "amount",
        "type": "uint256"
      }
    ],
    "name": "Withdrawn",
    "type": "event"
  },
  {
    "inputs": [],
    "name": "DEFAULT_TRANSFER_VALIDATOR",
    "outputs": [
      {
        "internalType": "address",
        "name": "",
        "type": "address"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "MAX_SUPPLY",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "",
        "type": "uint256"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "ROYALTY_BPS",
    "outputs": [
      {
        "internalType": "uint96",
        "name": "",
        "type": "uint96"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "bytes",
        "name": "packedPlots",
        "type": "bytes"
      }
    ],
    "name": "addDataChunk",
    "outputs": [],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "address[]",
        "name": "recipients",
        "type": "address[]"
      }
    ],
    "name": "airdrop",
    "outputs": [],
    "stateMutability": "nonpayable",
    "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": "uint256",
        "name": "",
        "type": "uint256"
      }
    ],
    "name": "dataChunks",
    "outputs": [
      {
        "internalType": "address",
        "name": "",
        "type": "address"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "description",
    "outputs": [
      {
        "internalType": "string",
        "name": "",
        "type": "string"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "genesisSupply",
    "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": "tokenId",
        "type": "uint256"
      }
    ],
    "name": "isMinted",
    "outputs": [
      {
        "internalType": "bool",
        "name": "",
        "type": "bool"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "merkleRoot",
    "outputs": [
      {
        "internalType": "bytes32",
        "name": "",
        "type": "bytes32"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "uint256",
        "name": "quantity",
        "type": "uint256"
      }
    ],
    "name": "mint",
    "outputs": [],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "",
        "type": "address"
      }
    ],
    "name": "mintedBy",
    "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": "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": "paused",
    "outputs": [
      {
        "internalType": "bool",
        "name": "",
        "type": "bool"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "phase",
    "outputs": [
      {
        "internalType": "uint8",
        "name": "",
        "type": "uint8"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "plotsStored",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "",
        "type": "uint256"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "publicMintsPerWallet",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "",
        "type": "uint256"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "publicStart",
    "outputs": [
      {
        "internalType": "uint64",
        "name": "",
        "type": "uint64"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "renounceOwnership",
    "outputs": [],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "uint256",
        "name": "_tokenId",
        "type": "uint256"
      },
      {
        "internalType": "uint256",
        "name": "_salePrice",
        "type": "uint256"
      }
    ],
    "name": "royaltyInfo",
    "outputs": [
      {
        "internalType": "address",
        "name": "",
        "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": "string",
        "name": "d",
        "type": "string"
      }
    ],
    "name": "setDescription",
    "outputs": [],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "uint256",
        "name": "n",
        "type": "uint256"
      }
    ],
    "name": "setGenesisSupply",
    "outputs": [],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "bytes32",
        "name": "root",
        "type": "bytes32"
      }
    ],
    "name": "setMerkleRoot",
    "outputs": [],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "bool",
        "name": "p",
        "type": "bool"
      }
    ],
    "name": "setPaused",
    "outputs": [],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "uint64",
        "name": "wlStart_",
        "type": "uint64"
      },
      {
        "internalType": "uint64",
        "name": "publicStart_",
        "type": "uint64"
      }
    ],
    "name": "setPhases",
    "outputs": [],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "r",
        "type": "address"
      },
      {
        "internalType": "uint96",
        "name": "fee",
        "type": "uint96"
      }
    ],
    "name": "setRoyaltyInfo",
    "outputs": [],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "transferValidator_",
        "type": "address"
      }
    ],
    "name": "setTransferValidator",
    "outputs": [],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "uint256",
        "name": "wlCap",
        "type": "uint256"
      },
      {
        "internalType": "uint256",
        "name": "publicCap",
        "type": "uint256"
      }
    ],
    "name": "setWalletCaps",
    "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": "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": [
      {
        "internalType": "uint256",
        "name": "quantity",
        "type": "uint256"
      },
      {
        "internalType": "bytes32[]",
        "name": "proof",
        "type": "bytes32[]"
      }
    ],
    "name": "whitelistMint",
    "outputs": [],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "withdraw",
    "outputs": [],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "to",
        "type": "address"
      }
    ],
    "name": "withdrawTo",
    "outputs": [],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "wlMintsPerWallet",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "",
        "type": "uint256"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "wlStart",
    "outputs": [
      {
        "internalType": "uint64",
        "name": "",
        "type": "uint64"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "stateMutability": "payable",
    "type": "receive"
  }
]

Source code

// SPDX-License-Identifier: MIT
pragma solidity 0.8.33;

// ============================================================
// HOODMAPS — ERC-721C + SSTORE2  (WL/Public relaunch)
// 21,000 cap | genesis 3,323 | sequential mint | on-chain pixel SVG
// Phase 1: whitelist (merkle-gated, free, 1hr) -> Phase 2: public (free)
// owner airdrop(address[]) | 5% royalties | data via SSTORE2
// ============================================================
// Compiler: 0.8.33, optimizer ON (runs 50), EVM: paris
// ============================================================

import { OwnableBasic } from "@limitbreak/creator-token-standards/src/access/OwnableBasic.sol";
import { ERC721C } from "@limitbreak/creator-token-standards/src/erc721c/ERC721C.sol";
import { ERC721OpenZeppelin } from "@limitbreak/creator-token-standards/src/token/erc721/ERC721OpenZeppelin.sol";
import { BasicRoyalties, ERC2981 } from "@limitbreak/creator-token-standards/src/programmable-royalties/BasicRoyalties.sol";
import "@openzeppelin/contracts@4.8.3/utils/Strings.sol";
import "@openzeppelin/contracts@4.8.3/utils/Base64.sol";
import "@openzeppelin/contracts@4.8.3/security/ReentrancyGuard.sol";
import "@openzeppelin/contracts@4.8.3/utils/cryptography/MerkleProof.sol";

/// @dev Minimal SSTORE2 (based on the well-audited Solmate/0xSequence pattern).
///      Stores `data` as the runtime code of a tiny contract, prefixed with a STOP (0x00)
///      byte so the stored data can never be executed. Read back via extcodecopy.
library SSTORE2 {
    // Creation code that returns (STOP ++ data) as runtime bytecode:
    //   0x60_0B  PUSH1 11      (offset where runtime begins in this creation code)
    //   0x59     MSIZE         -> 0
    //   0x81     DUP2
    //   0x38     CODESIZE
    //   0x03     SUB           -> runtime length
    //   0x80     DUP1
    //   0x92     SWAP3
    //   0x59     MSIZE -> 0
    //   0x39     CODECOPY
    //   0x59     MSIZE -> 0
    //   0xF3     RETURN
    //   0x00     STOP          (first runtime byte; guarantees data is non-executable)
    function write(bytes memory data) internal returns (address ptr) {
        bytes memory code = abi.encodePacked(hex"600B5981380380925939F300", data);
        assembly { ptr := create(0, add(code, 0x20), mload(code)) }
        require(ptr != address(0), "SSTORE2: write failed");
    }
    function read(address ptr, uint256 start, uint256 end) internal view returns (bytes memory data) {
        uint256 len = end - start;
        data = new bytes(len);
        assembly { extcodecopy(ptr, add(data, 0x20), add(start, 1), len) } // +1 skips STOP byte
    }
    function codeLen(address ptr) internal view returns (uint256) {
        return ptr.code.length - 1; // minus the STOP byte
    }
}

contract HoodMaps is ERC721C, BasicRoyalties, OwnableBasic, ReentrancyGuard {
    using Strings for uint256;

    // ===================== CONSTANTS =====================
    uint256 public constant MAX_SUPPLY = 21000;
    uint96  public constant ROYALTY_BPS = 500;
    uint256 internal constant PLOT_BYTES = 20; // packed bytes per plot (see _pack)

    // ===================== SUPPLY =====================
    uint256 public genesisSupply = 3323;       // mintable genesis pool (<= MAX_SUPPLY)
    uint256 public totalSupply;                // number minted so far (== last tokenId)

    // SSTORE2 data chunks, appended in order. Each holds N plots' packed bytes.
    address[] public dataChunks;
    uint256 public plotsStored;                // how many plots have data uploaded

    // ===================== PHASES / MINT CONTROLS =====================
    // Phase gating by timestamp:  wlStart <= now < publicStart => WL only
    //                             now >= publicStart          => public
    uint64  public wlStart;                    // whitelist phase start (unix)
    uint64  public publicStart;                // public phase start (unix)
    bool    public paused = true;              // hard kill-switch over everything

    bytes32 public merkleRoot;                 // whitelist allowlist root
    uint256 public wlMintsPerWallet = 1;       // cap during WL (1 per wallet)
    uint256 public publicMintsPerWallet = 2;   // cap during public

    mapping(address => uint256) public mintedBy; // lifetime minted per wallet

    string public description =
        "HoodMaps is a fully on-chain collection mapping the genesis activity of Robinhood Chain. Each plot is a real slice of the chain, rendered on-chain from its own transaction data.";

    // ===================== EVENTS =====================
    event PlotMinted(address indexed to, uint256 indexed tokenId);
    event DataChunkAdded(uint256 chunkIndex, uint256 plotsInChunk, uint256 plotsStored);
    event PhasesSet(uint64 wlStart, uint64 publicStart);
    event MerkleRootSet(bytes32 root);
    event PausedSet(bool paused);
    event Airdropped(uint256 count, uint256 newTotal);
    event Withdrawn(address indexed to, uint256 amount);

    // ===================== ERRORS =====================
    error MintPaused();
    error OutOfRange();
    error SoldOut();
    error WalletLimitReached();
    error NotWhitelisted();
    error WLNotStarted();
    error PublicNotStarted();
    error ZeroAddress();
    error NothingToWithdraw();
    error WithdrawFailed();
    error NonexistentToken();
    error DataMissing();
    error BadChunk();

    constructor(address royaltyReceiver_)
        ERC721OpenZeppelin("HoodMaps", "HOOD")
        OwnableBasic()
        BasicRoyalties(royaltyReceiver_, ROYALTY_BPS)
    {}

    // ================================================================
    //                          PUBLIC MINT
    // ================================================================

    /// @dev current phase: 0 = closed, 1 = whitelist, 2 = public
    function phase() public view returns (uint8) {
        if (paused) return 0;
        if (wlStart == 0) return 0;                                   // phases not configured yet
        if (publicStart != 0 && block.timestamp >= publicStart) return 2;
        if (block.timestamp >= wlStart) return 1;
        return 0;
    }

    /// @notice Whitelist mint (free). Requires a valid merkle proof for msg.sender.
    function whitelistMint(uint256 quantity, bytes32[] calldata proof) external nonReentrant {
        if (phase() != 1) revert WLNotStarted();
        bytes32 leaf = keccak256(abi.encodePacked(msg.sender));
        if (!MerkleProof.verifyCalldata(proof, merkleRoot, leaf)) revert NotWhitelisted();
        if (mintedBy[msg.sender] + quantity > wlMintsPerWallet) revert WalletLimitReached();
        _mintMany(msg.sender, quantity);
    }

    /// @notice Public mint (free).
    function mint(uint256 quantity) external nonReentrant {
        if (phase() != 2) revert PublicNotStarted();
        if (mintedBy[msg.sender] + quantity > publicMintsPerWallet) revert WalletLimitReached();
        _mintMany(msg.sender, quantity);
    }

    function _mintMany(address to, uint256 quantity) internal {
        if (quantity == 0) revert OutOfRange();
        if (totalSupply + quantity > genesisSupply) revert SoldOut();
        mintedBy[to] += quantity;
        for (uint256 i = 0; i < quantity; i++) {
            uint256 id = ++totalSupply;   // sequential 1,2,3...
            _mint(to, id);
            emit PlotMinted(to, id);
        }
    }

    // ================================================================
    //                     DATA READ (SSTORE2)
    // ================================================================
    // Plot i (0-indexed = tokenId-1) is stored at global byte offset i*PLOT_BYTES,
    // spread across dataChunks in order. Each chunk holds a whole number of plots.
    function _readPlot(uint256 tokenId) internal view returns (bytes memory) {
        uint256 gi = tokenId - 1;                 // global plot index
        if (gi >= plotsStored) revert DataMissing();
        uint256 seen = 0;
        for (uint256 c = 0; c < dataChunks.length; c++) {
            uint256 plotsHere = SSTORE2.codeLen(dataChunks[c]) / PLOT_BYTES;
            if (gi < seen + plotsHere) {
                uint256 local = gi - seen;
                return SSTORE2.read(dataChunks[c], local * PLOT_BYTES, local * PLOT_BYTES + PLOT_BYTES);
            }
            seen += plotsHere;
        }
        revert DataMissing();
    }

    // unpack 20-byte record: tx(4) blocks(2) maxTx(2) gasK(4) firstBlock(4) lastBlock(4)
    function _fields(uint256 tokenId) internal view returns (
        uint256 tx_, uint256 blocks_, uint256 maxTx_, uint256 gasK, uint256 fb, uint256 lb
    ) {
        bytes memory b = _readPlot(tokenId);
        tx_     = _u(b, 0, 4);
        blocks_ = _u(b, 4, 2);
        maxTx_  = _u(b, 6, 2);
        gasK    = _u(b, 8, 4);
        fb      = _u(b, 12, 4);
        lb      = _u(b, 16, 4);
    }
    function _u(bytes memory b, uint256 off, uint256 n) internal pure returns (uint256 v) {
        for (uint256 i = 0; i < n; i++) { v = (v << 8) | uint8(b[off + i]); }
    }

    // ================================================================
    //                        METADATA
    // ================================================================
    function tokenURI(uint256 tokenId) public view override returns (string memory) {
        if (!_exists(tokenId)) revert NonexistentToken();
        (uint256 tx_, uint256 blocks_, uint256 maxTx_, , , ) = _fields(tokenId);
        string memory image = string(abi.encodePacked(
            "data:image/svg+xml;base64,", Base64.encode(bytes(_renderSVG(tokenId, tx_, blocks_, maxTx_)))
        ));
        string memory json = string(abi.encodePacked(
            '{"name":"HoodMap #', tokenId.toString(),
            '","description":"', description,
            '","attributes":[', _attributes(tokenId),
            '],"image":"', image, '"}'
        ));
        return string(abi.encodePacked("data:application/json;base64,", Base64.encode(bytes(json))));
    }

    function _attributes(uint256 tokenId) internal view returns (string memory) {
        (uint256 tx_, uint256 blocks_, uint256 maxTx_, uint256 gasK, uint256 fb, uint256 lb) = _fields(tokenId);
        return string(abi.encodePacked(
            '{"trait_type":"Transactions","value":', tx_.toString(), '},',
            '{"trait_type":"Blocks","value":', blocks_.toString(), '},',
            '{"trait_type":"Busiest Block","value":', maxTx_.toString(), '},',
            _attributes2(gasK, fb, lb)
        ));
    }
    function _attributes2(uint256 gasK, uint256 fb, uint256 lb) internal pure returns (string memory) {
        return string(abi.encodePacked(
            '{"trait_type":"Gas (k)","value":', gasK.toString(), '},',
            '{"trait_type":"First Block","value":', fb.toString(), '},',
            '{"trait_type":"Last Block","value":', lb.toString(), '}'
        ));
    }

    // ================================================================
    //                     ON-CHAIN PIXEL RENDER
    // ================================================================
    function _renderSVG(uint256 id, uint256 tx_, uint256 blocks_, uint256 maxTx_)
        internal pure returns (string memory)
    {
        blocks_; maxTx_;
        uint256 density = tx_ >= 380 ? 100 : (tx_ * 100) / 380;
        uint256 bigChance = (density * 60) / 100;
        bool[256] memory covered;
        string memory cells = "";
        for (uint256 gy = 0; gy < 16; gy++) {
            for (uint256 gx = 0; gx < 16; gx++) {
                if (covered[gy * 16 + gx]) continue;
                uint256 hsh = uint256(keccak256(abi.encodePacked(id, gy * 16 + gx)));
                if (hsh % 100 > (density * 85) / 100) continue;
                uint256 span = _pickSpan(hsh, gx, gy, bigChance);
                if (!_spanFree(covered, gx, gy, span)) span = 1;
                _mark(covered, gx, gy, span);
                cells = string(abi.encodePacked(cells, _rect(hsh, gx, gy, span)));
            }
        }
        return string(abi.encodePacked(
            '<svg xmlns="http://www.w3.org/2000/svg" width="500" height="500" viewBox="0 0 500 500">',
            '<rect width="500" height="500" rx="26" fill="#000000"/>', cells, '</svg>'
        ));
    }
    function _pickSpan(uint256 hsh, uint256 gx, uint256 gy, uint256 bigChance) internal pure returns (uint256) {
        uint256 r3 = (hsh >> 16) % 100;
        if (r3 < bigChance && gx < 14 && gy < 14) return 3;
        if (r3 < bigChance + 22 && gx < 15 && gy < 15) return 2;
        return 1;
    }
    function _spanFree(bool[256] memory covered, uint256 gx, uint256 gy, uint256 span) internal pure returns (bool) {
        for (uint256 dy = 0; dy < span; dy++)
            for (uint256 dx = 0; dx < span; dx++)
                if (covered[(gy + dy) * 16 + (gx + dx)]) return false;
        return true;
    }
    function _mark(bool[256] memory covered, uint256 gx, uint256 gy, uint256 span) internal pure {
        for (uint256 dy = 0; dy < span; dy++)
            for (uint256 dx = 0; dx < span; dx++)
                covered[(gy + dy) * 16 + (gx + dx)] = true;
    }
    function _rect(uint256 hsh, uint256 gx, uint256 gy, uint256 span) internal pure returns (string memory) {
        uint256 r2 = (hsh >> 8) % 100;
        string memory fill; string memory op;
        if (r2 > 90 && (hsh % 9 == 0)) { fill = "#faffe8"; op = "0.95"; }
        else {
            fill = "#ccff00";
            if (span >= 3) op = "0.92";
            else if (span >= 2) op = (r2 > 50) ? "0.8" : "0.55";
            else op = (r2 > 60) ? "0.7" : ((r2 > 30) ? "0.45" : "0.28");
        }
        return string(abi.encodePacked(
            '<rect x="', (10 + gx * 30).toString(), '" y="', (10 + gy * 30).toString(),
            '" width="', (span * 30 - 2).toString(), '" height="', (span * 30 - 2).toString(),
            '" rx="', (span >= 2 ? "4" : "2"), '" fill="', fill, '" fill-opacity="', op, '"/>'
        ));
    }

    // ================================================================
    //                        INTERFACE
    // ================================================================
    function supportsInterface(bytes4 interfaceId)
        public view virtual override(ERC721C, ERC2981) returns (bool)
    {
        return ERC721C.supportsInterface(interfaceId) || ERC2981.supportsInterface(interfaceId);
    }

    // ================================================================
    //                      VIEWS
    // ================================================================
    function isMinted(uint256 tokenId) external view returns (bool) { return _exists(tokenId); }

    // ================================================================
    //                      OWNER — DATA
    // ================================================================
    /// @notice Append one SSTORE2 chunk of packed plot bytes (multiple of PLOT_BYTES).
    function addDataChunk(bytes calldata packedPlots) external onlyOwner {
        if (packedPlots.length == 0 || packedPlots.length % PLOT_BYTES != 0) revert BadChunk();
        address ptr = SSTORE2.write(packedPlots);
        dataChunks.push(ptr);
        plotsStored += packedPlots.length / PLOT_BYTES;
        emit DataChunkAdded(dataChunks.length - 1, packedPlots.length / PLOT_BYTES, plotsStored);
    }

    // ================================================================
    //                      OWNER — AIRDROP
    // ================================================================
    /// @notice Airdrop one token each to `recipients`, sequentially. Call in batches
    ///         (e.g. ~80 per tx) to stay under the block gas limit. Counts toward supply.
    function airdrop(address[] calldata recipients) external onlyOwner {
        uint256 n = recipients.length;
        if (totalSupply + n > genesisSupply) revert SoldOut();
        for (uint256 i = 0; i < n; i++) {
            address to = recipients[i];
            if (to == address(0)) revert ZeroAddress();
            uint256 id = ++totalSupply;
            _mint(to, id);
            emit PlotMinted(to, id);
        }
        emit Airdropped(n, totalSupply);
    }

    // ================================================================
    //                      OWNER — PHASES / SETTINGS
    // ================================================================
    function setPhases(uint64 wlStart_, uint64 publicStart_) external onlyOwner {
        wlStart = wlStart_;
        publicStart = publicStart_;
        emit PhasesSet(wlStart_, publicStart_);
    }
    function setMerkleRoot(bytes32 root) external onlyOwner {
        merkleRoot = root;
        emit MerkleRootSet(root);
    }
    function setWalletCaps(uint256 wlCap, uint256 publicCap) external onlyOwner {
        wlMintsPerWallet = wlCap;
        publicMintsPerWallet = publicCap;
    }
    function setGenesisSupply(uint256 n) external onlyOwner {
        require(n >= totalSupply && n <= MAX_SUPPLY, "bad supply");
        genesisSupply = n;
    }
    function setPaused(bool p) external onlyOwner { paused = p; emit PausedSet(p); }
    function setDescription(string calldata d) external onlyOwner { description = d; }
    function setRoyaltyInfo(address r, uint96 fee) external onlyOwner { _setDefaultRoyalty(r, fee); }

    function withdraw() external onlyOwner nonReentrant {
        uint256 bal = address(this).balance;
        if (bal == 0) revert NothingToWithdraw();
        (bool ok, ) = payable(owner()).call{ value: bal }("");
        if (!ok) revert WithdrawFailed();
        emit Withdrawn(owner(), bal);
    }
    function withdrawTo(address to) external onlyOwner nonReentrant {
        if (to == address(0)) revert ZeroAddress();
        uint256 bal = address(this).balance;
        if (bal == 0) revert NothingToWithdraw();
        (bool ok, ) = payable(to).call{ value: bal }("");
        if (!ok) revert WithdrawFailed();
        emit Withdrawn(to, bal);
    }

    receive() external payable {}
}
Chain explorer1875msChain node106ms