HoodPunks

0xceeb3ddfbfdc1bfc485468371093eb851f90aa6a

Verification
Verified
v0.8.24+commit.e11b9ed9
Type
Contract
16,020 bytes
ABI entries
66
33 read · 22 write
License
none

Contract information

Address
0xceeb3ddfbfdc1bfc485468371093eb851f90aa6a
Chain
Robinhood Chain (4663)
Compiler
v0.8.24+commit.e11b9ed9
Optimization
Enabled
Creation tx
0x6b61d3c226…97f3dd9493

Token

Name
Robinhood Punks
Symbol
HOODPUNKS
Decimals
Holders
1,470

Read contract (33)

DEV_RESERVE_MAX()uint256
MAX_SUPPLY()uint256
ROYALTY_BPS()uint256
balanceOf(address)uint256
bidsPage(uint256, uint256)bool[], uint256[], address[]
claimAllowance(address)uint256
claimedCount(address)uint256
claimsLeft(address)uint256
claimsOpen()bool
coin()address
coinLocked()bool
coinUnit()uint256
data()address
devReserved()uint256
exists(uint256)bool
getApproved(uint256)address
isApprovedForAll(address, address)bool
migrationLocked()bool
name()string
offersPage(uint256, uint256)bool[], uint256[], address[]
owner()address
ownerOf(uint256)address
ownersPage(uint256, uint256)address[]
pendingWithdrawals(address)uint256
punkBids(uint256)bool, address, uint256
punkOffers(uint256)bool, address, uint256, address
punkSvg(uint256)string
royaltyInfo(uint256, uint256)address, uint256
royaltyReceiver()address
supportsInterface(bytes4)bool
symbol()string
tokenURI(uint256)string
totalSupply()uint256

Events (10)

ApprovalApprovalForAllClaimsOpenSetPunkBidEnteredPunkBidWithdrawnPunkBoughtPunkClaimedPunkNoLongerForSalePunkOfferedTransfer

ABI

[
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "dataContract",
        "type": "address"
      },
      {
        "internalType": "address",
        "name": "coinAddress",
        "type": "address"
      }
    ],
    "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": false,
        "internalType": "bool",
        "name": "open",
        "type": "bool"
      }
    ],
    "name": "ClaimsOpenSet",
    "type": "event"
  },
  {
    "anonymous": false,
    "inputs": [
      {
        "indexed": true,
        "internalType": "uint256",
        "name": "punkId",
        "type": "uint256"
      },
      {
        "indexed": false,
        "internalType": "uint256",
        "name": "value",
        "type": "uint256"
      },
      {
        "indexed": true,
        "internalType": "address",
        "name": "fromAddress",
        "type": "address"
      }
    ],
    "name": "PunkBidEntered",
    "type": "event"
  },
  {
    "anonymous": false,
    "inputs": [
      {
        "indexed": true,
        "internalType": "uint256",
        "name": "punkId",
        "type": "uint256"
      },
      {
        "indexed": false,
        "internalType": "uint256",
        "name": "value",
        "type": "uint256"
      },
      {
        "indexed": true,
        "internalType": "address",
        "name": "fromAddress",
        "type": "address"
      }
    ],
    "name": "PunkBidWithdrawn",
    "type": "event"
  },
  {
    "anonymous": false,
    "inputs": [
      {
        "indexed": true,
        "internalType": "uint256",
        "name": "punkId",
        "type": "uint256"
      },
      {
        "indexed": false,
        "internalType": "uint256",
        "name": "value",
        "type": "uint256"
      },
      {
        "indexed": true,
        "internalType": "address",
        "name": "fromAddress",
        "type": "address"
      },
      {
        "indexed": true,
        "internalType": "address",
        "name": "toAddress",
        "type": "address"
      }
    ],
    "name": "PunkBought",
    "type": "event"
  },
  {
    "anonymous": false,
    "inputs": [
      {
        "indexed": true,
        "internalType": "uint256",
        "name": "punkId",
        "type": "uint256"
      },
      {
        "indexed": true,
        "internalType": "address",
        "name": "claimer",
        "type": "address"
      }
    ],
    "name": "PunkClaimed",
    "type": "event"
  },
  {
    "anonymous": false,
    "inputs": [
      {
        "indexed": true,
        "internalType": "uint256",
        "name": "punkId",
        "type": "uint256"
      }
    ],
    "name": "PunkNoLongerForSale",
    "type": "event"
  },
  {
    "anonymous": false,
    "inputs": [
      {
        "indexed": true,
        "internalType": "uint256",
        "name": "punkId",
        "type": "uint256"
      },
      {
        "indexed": false,
        "internalType": "uint256",
        "name": "minValue",
        "type": "uint256"
      },
      {
        "indexed": true,
        "internalType": "address",
        "name": "toAddress",
        "type": "address"
      }
    ],
    "name": "PunkOffered",
    "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": "DEV_RESERVE_MAX",
    "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": "ROYALTY_BPS",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "",
        "type": "uint256"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "uint256",
        "name": "punkId",
        "type": "uint256"
      },
      {
        "internalType": "uint256",
        "name": "minPrice",
        "type": "uint256"
      }
    ],
    "name": "acceptBidForPunk",
    "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": [
      {
        "internalType": "address",
        "name": "account",
        "type": "address"
      }
    ],
    "name": "balanceOf",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "",
        "type": "uint256"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "uint256",
        "name": "start",
        "type": "uint256"
      },
      {
        "internalType": "uint256",
        "name": "count",
        "type": "uint256"
      }
    ],
    "name": "bidsPage",
    "outputs": [
      {
        "internalType": "bool[]",
        "name": "hasBid",
        "type": "bool[]"
      },
      {
        "internalType": "uint256[]",
        "name": "values",
        "type": "uint256[]"
      },
      {
        "internalType": "address[]",
        "name": "bidders",
        "type": "address[]"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "uint256",
        "name": "punkId",
        "type": "uint256"
      }
    ],
    "name": "buyPunk",
    "outputs": [],
    "stateMutability": "payable",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "uint16[]",
        "name": "punkIds",
        "type": "uint16[]"
      }
    ],
    "name": "claim",
    "outputs": [],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "wallet",
        "type": "address"
      }
    ],
    "name": "claimAllowance",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "",
        "type": "uint256"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "",
        "type": "address"
      }
    ],
    "name": "claimedCount",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "",
        "type": "uint256"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "wallet",
        "type": "address"
      }
    ],
    "name": "claimsLeft",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "",
        "type": "uint256"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "claimsOpen",
    "outputs": [
      {
        "internalType": "bool",
        "name": "",
        "type": "bool"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "coin",
    "outputs": [
      {
        "internalType": "contract IERC20Like",
        "name": "",
        "type": "address"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "coinLocked",
    "outputs": [
      {
        "internalType": "bool",
        "name": "",
        "type": "bool"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "coinUnit",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "",
        "type": "uint256"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "data",
    "outputs": [
      {
        "internalType": "contract HoodPunksData",
        "name": "",
        "type": "address"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "devReserved",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "",
        "type": "uint256"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "uint256",
        "name": "punkId",
        "type": "uint256"
      }
    ],
    "name": "enterBidForPunk",
    "outputs": [],
    "stateMutability": "payable",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "uint256",
        "name": "tokenId",
        "type": "uint256"
      }
    ],
    "name": "exists",
    "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": "lockMigration",
    "outputs": [],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "uint16[]",
        "name": "punkIds",
        "type": "uint16[]"
      },
      {
        "internalType": "address[]",
        "name": "owners_",
        "type": "address[]"
      }
    ],
    "name": "migrateClaims",
    "outputs": [],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "migrationLocked",
    "outputs": [
      {
        "internalType": "bool",
        "name": "",
        "type": "bool"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "name",
    "outputs": [
      {
        "internalType": "string",
        "name": "",
        "type": "string"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "uint256",
        "name": "punkId",
        "type": "uint256"
      },
      {
        "internalType": "uint256",
        "name": "minSalePriceInWei",
        "type": "uint256"
      }
    ],
    "name": "offerPunkForSale",
    "outputs": [],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "uint256",
        "name": "punkId",
        "type": "uint256"
      },
      {
        "internalType": "uint256",
        "name": "minSalePriceInWei",
        "type": "uint256"
      },
      {
        "internalType": "address",
        "name": "toAddress",
        "type": "address"
      }
    ],
    "name": "offerPunkForSaleTo",
    "outputs": [],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "uint256",
        "name": "start",
        "type": "uint256"
      },
      {
        "internalType": "uint256",
        "name": "count",
        "type": "uint256"
      }
    ],
    "name": "offersPage",
    "outputs": [
      {
        "internalType": "bool[]",
        "name": "forSale",
        "type": "bool[]"
      },
      {
        "internalType": "uint256[]",
        "name": "prices",
        "type": "uint256[]"
      },
      {
        "internalType": "address[]",
        "name": "sellers",
        "type": "address[]"
      }
    ],
    "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": "o",
        "type": "address"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "uint16[]",
        "name": "punkIds",
        "type": "uint16[]"
      }
    ],
    "name": "ownerReserve",
    "outputs": [],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "uint256",
        "name": "start",
        "type": "uint256"
      },
      {
        "internalType": "uint256",
        "name": "count",
        "type": "uint256"
      }
    ],
    "name": "ownersPage",
    "outputs": [
      {
        "internalType": "address[]",
        "name": "out",
        "type": "address[]"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "",
        "type": "address"
      }
    ],
    "name": "pendingWithdrawals",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "",
        "type": "uint256"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "uint256",
        "name": "",
        "type": "uint256"
      }
    ],
    "name": "punkBids",
    "outputs": [
      {
        "internalType": "bool",
        "name": "hasBid",
        "type": "bool"
      },
      {
        "internalType": "address",
        "name": "bidder",
        "type": "address"
      },
      {
        "internalType": "uint256",
        "name": "value",
        "type": "uint256"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "uint256",
        "name": "punkId",
        "type": "uint256"
      }
    ],
    "name": "punkNoLongerForSale",
    "outputs": [],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "uint256",
        "name": "",
        "type": "uint256"
      }
    ],
    "name": "punkOffers",
    "outputs": [
      {
        "internalType": "bool",
        "name": "isForSale",
        "type": "bool"
      },
      {
        "internalType": "address",
        "name": "seller",
        "type": "address"
      },
      {
        "internalType": "uint256",
        "name": "minValue",
        "type": "uint256"
      },
      {
        "internalType": "address",
        "name": "onlySellTo",
        "type": "address"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "uint256",
        "name": "punkId",
        "type": "uint256"
      }
    ],
    "name": "punkSvg",
    "outputs": [
      {
        "internalType": "string",
        "name": "",
        "type": "string"
      }
    ],
    "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": "dataArg",
        "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": "open",
        "type": "bool"
      }
    ],
    "name": "setClaimsOpen",
    "outputs": [],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "coinAddress",
        "type": "address"
      }
    ],
    "name": "setCoin",
    "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"
  },
  {
    "inputs": [],
    "name": "withdraw",
    "outputs": [],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "uint256",
        "name": "punkId",
        "type": "uint256"
      }
    ],
    "name": "withdrawBidForPunk",
    "outputs": [],
    "stateMutability": "nonpayable",
    "type": "function"
  }
]

Source code

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

import {HoodPunksData} from "./HoodPunksData.sol";

interface IERC20Like {
    function balanceOf(address account) external view returns (uint256);
    function decimals() external view returns (uint8);
}

interface IERC721Receiver {
    function onERC721Received(address operator, address from, uint256 tokenId, bytes calldata data)
        external returns (bytes4);
}

library Base64 {
    bytes internal constant TABLE = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";

    function encode(bytes memory data) internal pure returns (string memory) {
        if (data.length == 0) return "";
        string memory table = string(TABLE);
        uint256 encodedLen = 4 * ((data.length + 2) / 3);
        string memory result = new string(encodedLen + 32);
        assembly {
            let tablePtr := add(table, 1)
            let resultPtr := add(result, 32)
            for { let i := 0 } lt(i, mload(data)) { i := add(i, 3) } {
                let input := and(mload(add(data, add(32, i))), 0xffffff0000000000000000000000000000000000000000000000000000000000)
                input := shr(232, input)
                mstore8(resultPtr, mload(add(tablePtr, and(shr(18, input), 0x3F))))
                resultPtr := add(resultPtr, 1)
                mstore8(resultPtr, mload(add(tablePtr, and(shr(12, input), 0x3F))))
                resultPtr := add(resultPtr, 1)
                mstore8(resultPtr, mload(add(tablePtr, and(shr(6, input), 0x3F))))
                resultPtr := add(resultPtr, 1)
                mstore8(resultPtr, mload(add(tablePtr, and(input, 0x3F))))
                resultPtr := add(resultPtr, 1)
            }
            switch mod(mload(data), 3)
            case 1 {
                mstore8(sub(resultPtr, 1), 0x3d)
                mstore8(sub(resultPtr, 2), 0x3d)
            }
            case 2 { mstore8(sub(resultPtr, 1), 0x3d) }
            mstore(result, encodedLen)
        }
        return result;
    }
}

/// @title HoodPunks
/// @notice 2000 fully on-chain pixel punks on Robinhood Chain. Free claim,
///         gated by coin holdings, with a CryptoPunks style native
///         marketplace (offers, bids, buys in ETH) built into the token.
contract HoodPunks {
    // ------------------------------------------------------------------
    // ERC721 core
    // ------------------------------------------------------------------
    string public constant name = "Robinhood Punks";
    string public constant symbol = "HOODPUNKS";
    uint256 public constant MAX_SUPPLY = 2000;

    mapping(uint256 => address) internal _owners;
    mapping(address => uint256) internal _balances;
    mapping(uint256 => address) public getApproved;
    mapping(address => mapping(address => bool)) public isApprovedForAll;
    uint256 public totalSupply;

    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);

    // ------------------------------------------------------------------
    // Project state
    // ------------------------------------------------------------------
    HoodPunksData public immutable data;
    IERC20Like public coin;
    uint256 public coinUnit; // 10 ** coin decimals
    address public owner;
    bool public claimsOpen;
    bool public coinLocked;

    uint256 public constant DEV_RESERVE_MAX = 200;
    uint256 public devReserved;

    /// 2% creator royalty on native marketplace sales, ERC2981 for external ones.
    uint256 public constant ROYALTY_BPS = 200;
    address public royaltyReceiver = 0x2dDb1f1fC31b4495dA195F422b215053c6D62924;

    /// one time migration hatch to honor claims made on a prior contract
    bool public migrationLocked;

    mapping(address => uint256) public claimedCount;

    event PunkClaimed(uint256 indexed punkId, address indexed claimer);
    event ClaimsOpenSet(bool open);

    // ------------------------------------------------------------------
    // Native marketplace (CryptoPunks style, pull payments)
    // ------------------------------------------------------------------
    struct Offer {
        bool isForSale;
        address seller;
        uint256 minValue;      // in wei (ETH is the gas coin on Robinhood Chain)
        address onlySellTo;    // zero address = anyone
    }
    struct Bid {
        bool hasBid;
        address bidder;
        uint256 value;
    }

    mapping(uint256 => Offer) public punkOffers;
    mapping(uint256 => Bid) public punkBids;
    mapping(address => uint256) public pendingWithdrawals;

    event PunkOffered(uint256 indexed punkId, uint256 minValue, address indexed toAddress);
    event PunkNoLongerForSale(uint256 indexed punkId);
    event PunkBidEntered(uint256 indexed punkId, uint256 value, address indexed fromAddress);
    event PunkBidWithdrawn(uint256 indexed punkId, uint256 value, address indexed fromAddress);
    event PunkBought(uint256 indexed punkId, uint256 value, address indexed fromAddress, address indexed toAddress);

    uint256 private _reentrancy = 1;
    modifier nonReentrant() {
        require(_reentrancy == 1, "reentrant");
        _reentrancy = 2;
        _;
        _reentrancy = 1;
    }
    modifier onlyOwner() { require(msg.sender == owner, "not owner"); _; }

    constructor(address dataContract, address coinAddress) {
        owner = msg.sender;
        data = HoodPunksData(dataContract);
        if (coinAddress != address(0)) _setCoin(coinAddress);
    }

    // ------------------------------------------------------------------
    // Admin
    // ------------------------------------------------------------------
    function transferOwnership(address newOwner) external onlyOwner { owner = newOwner; }

    /// @notice Point at the coin used for claim gating. Lock it once live.
    function setCoin(address coinAddress) external onlyOwner {
        require(!coinLocked, "coin locked");
        _setCoin(coinAddress);
    }

    function lockCoin() external onlyOwner {
        require(address(coin) != address(0), "no coin");
        coinLocked = true;
    }

    function _setCoin(address coinAddress) internal {
        coin = IERC20Like(coinAddress);
        uint8 dec = 18;
        (bool ok, bytes memory ret) =
            coinAddress.staticcall(abi.encodeWithSignature("decimals()"));
        if (ok && ret.length >= 32) dec = uint8(abi.decode(ret, (uint256)));
        coinUnit = 10 ** dec;
    }

    function setClaimsOpen(bool open) external onlyOwner {
        require(address(coin) != address(0), "no coin");
        claimsOpen = open;
        emit ClaimsOpenSet(open);
    }

    function setRoyaltyReceiver(address receiver) external onlyOwner {
        require(receiver != address(0), "zero address");
        royaltyReceiver = receiver;
    }

    /// @notice Re-mint punks claimed on a prior contract to their rightful
    ///         owners, crediting their claim counts. Locked before claims open.
    function migrateClaims(uint16[] calldata punkIds, address[] calldata owners_) external onlyOwner {
        require(!migrationLocked, "migration locked");
        require(punkIds.length == owners_.length, "length mismatch");
        for (uint256 i = 0; i < punkIds.length; i++) {
            claimedCount[owners_[i]] += 1;
            emit PunkClaimed(punkIds[i], owners_[i]);
            _mint(owners_[i], punkIds[i]);
        }
    }

    function lockMigration() external onlyOwner { migrationLocked = true; }

    /// @notice Team reserve, hard capped at DEV_RESERVE_MAX punks.
    function ownerReserve(uint16[] calldata punkIds) external onlyOwner {
        require(devReserved + punkIds.length <= DEV_RESERVE_MAX, "reserve cap");
        devReserved += punkIds.length;
        for (uint256 i = 0; i < punkIds.length; i++) _mint(msg.sender, punkIds[i]);
    }

    // ------------------------------------------------------------------
    // Claim: hold the coin, pick your punks
    // ------------------------------------------------------------------

    /// @notice How many punks a wallet may claim in total, from live coin balance.
    ///         100k => 1, 500k => 2, 1.5M => 3, 3M => 4, 5M+ => 5.
    function claimAllowance(address wallet) public view returns (uint256) {
        if (address(coin) == address(0)) return 0;
        uint256 bal = coin.balanceOf(wallet);
        if (bal >= 5_000_000 * coinUnit) return 5;
        if (bal >= 3_000_000 * coinUnit) return 4;
        if (bal >= 1_500_000 * coinUnit) return 3;
        if (bal >= 500_000 * coinUnit) return 2;
        if (bal >= 100_000 * coinUnit) return 1;
        return 0;
    }

    function claimsLeft(address wallet) public view returns (uint256) {
        uint256 allowance = claimAllowance(wallet);
        uint256 used = claimedCount[wallet];
        return allowance > used ? allowance - used : 0;
    }

    /// @notice Claim specific punks by id. Free, gas only.
    function claim(uint16[] calldata punkIds) external {
        require(claimsOpen, "claims closed");
        require(punkIds.length > 0, "no punks");
        require(punkIds.length <= claimsLeft(msg.sender), "over allowance");
        claimedCount[msg.sender] += punkIds.length;
        for (uint256 i = 0; i < punkIds.length; i++) {
            emit PunkClaimed(punkIds[i], msg.sender);
            _mint(msg.sender, punkIds[i]);
        }
    }

    // ------------------------------------------------------------------
    // ERC721 logic
    // ------------------------------------------------------------------
    function ownerOf(uint256 tokenId) public view returns (address o) {
        o = _owners[tokenId];
        require(o != address(0), "not minted");
    }

    function balanceOf(address account) external view returns (uint256) {
        require(account != address(0), "zero address");
        return _balances[account];
    }

    function exists(uint256 tokenId) public view returns (bool) {
        return _owners[tokenId] != address(0);
    }

    function _mint(address to, uint256 tokenId) internal {
        require(tokenId < MAX_SUPPLY, "bad punk");
        require(_owners[tokenId] == address(0), "already minted");
        _owners[tokenId] = to;
        _balances[to] += 1;
        totalSupply += 1;
        emit Transfer(address(0), to, tokenId);
    }

    function approve(address to, uint256 tokenId) external {
        address o = ownerOf(tokenId);
        require(msg.sender == o || isApprovedForAll[o][msg.sender], "not authorized");
        getApproved[tokenId] = to;
        emit Approval(o, 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 o = ownerOf(tokenId);
        require(o == from, "wrong from");
        require(to != address(0), "zero address");
        require(
            msg.sender == o || msg.sender == getApproved[tokenId] || isApprovedForAll[o][msg.sender],
            "not authorized"
        );
        _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 dataArg) public {
        transferFrom(from, to, tokenId);
        require(_checkOnERC721Received(from, to, tokenId, dataArg), "unsafe receiver");
    }

    function _transfer(address from, address to, uint256 tokenId) internal {
        delete getApproved[tokenId];
        // an offer made by the previous owner is no longer valid
        if (punkOffers[tokenId].isForSale) {
            punkOffers[tokenId] = Offer(false, address(0), 0, address(0));
            emit PunkNoLongerForSale(tokenId);
        }
        _owners[tokenId] = to;
        _balances[from] -= 1;
        _balances[to] += 1;
        emit Transfer(from, to, tokenId);
        // if the new owner had a live bid on this punk, refund it to them
        Bid memory bid = punkBids[tokenId];
        if (bid.hasBid && bid.bidder == to) {
            punkBids[tokenId] = Bid(false, address(0), 0);
            pendingWithdrawals[to] += bid.value;
        }
    }

    function _checkOnERC721Received(address from, address to, uint256 tokenId, bytes memory dataArg)
        private returns (bool)
    {
        if (to.code.length == 0) return true;
        try IERC721Receiver(to).onERC721Received(msg.sender, from, tokenId, dataArg) returns (bytes4 ret) {
            return ret == IERC721Receiver.onERC721Received.selector;
        } catch {
            return false;
        }
    }

    function supportsInterface(bytes4 interfaceId) external pure returns (bool) {
        return interfaceId == 0x80ac58cd  // ERC721
            || interfaceId == 0x5b5e139f  // ERC721Metadata
            || interfaceId == 0x2a55205a  // ERC2981 royalties
            || interfaceId == 0x01ffc9a7; // ERC165
    }

    /// @notice ERC2981: external marketplaces honor the same 2% royalty.
    function royaltyInfo(uint256, uint256 salePrice) external view returns (address, uint256) {
        return (royaltyReceiver, (salePrice * ROYALTY_BPS) / 10_000);
    }

    // ------------------------------------------------------------------
    // Marketplace
    // ------------------------------------------------------------------
    function offerPunkForSale(uint256 punkId, uint256 minSalePriceInWei) external {
        require(ownerOf(punkId) == msg.sender, "not your punk");
        punkOffers[punkId] = Offer(true, msg.sender, minSalePriceInWei, address(0));
        emit PunkOffered(punkId, minSalePriceInWei, address(0));
    }

    function offerPunkForSaleTo(uint256 punkId, uint256 minSalePriceInWei, address toAddress) external {
        require(ownerOf(punkId) == msg.sender, "not your punk");
        punkOffers[punkId] = Offer(true, msg.sender, minSalePriceInWei, toAddress);
        emit PunkOffered(punkId, minSalePriceInWei, toAddress);
    }

    function punkNoLongerForSale(uint256 punkId) external {
        require(ownerOf(punkId) == msg.sender, "not your punk");
        punkOffers[punkId] = Offer(false, address(0), 0, address(0));
        emit PunkNoLongerForSale(punkId);
    }

    function buyPunk(uint256 punkId) external payable nonReentrant {
        Offer memory offer = punkOffers[punkId];
        require(offer.isForSale, "not for sale");
        require(offer.onlySellTo == address(0) || offer.onlySellTo == msg.sender, "reserved sale");
        require(msg.value >= offer.minValue, "underpaid");
        address seller = offer.seller;
        require(seller == ownerOf(punkId), "stale offer");
        require(msg.sender != seller, "own punk");

        _transfer(seller, msg.sender, punkId);
        uint256 royalty = (msg.value * ROYALTY_BPS) / 10_000;
        pendingWithdrawals[royaltyReceiver] += royalty;
        pendingWithdrawals[seller] += msg.value - royalty;
        emit PunkBought(punkId, msg.value, seller, msg.sender);
    }

    function enterBidForPunk(uint256 punkId) external payable nonReentrant {
        require(exists(punkId), "not minted");
        require(ownerOf(punkId) != msg.sender, "own punk");
        require(msg.value > 0, "zero bid");
        Bid memory existing = punkBids[punkId];
        require(msg.value > existing.value, "bid too low");
        if (existing.value > 0) {
            pendingWithdrawals[existing.bidder] += existing.value;
        }
        punkBids[punkId] = Bid(true, msg.sender, msg.value);
        emit PunkBidEntered(punkId, msg.value, msg.sender);
    }

    function withdrawBidForPunk(uint256 punkId) external nonReentrant {
        Bid memory bid = punkBids[punkId];
        require(bid.hasBid && bid.bidder == msg.sender, "no bid");
        punkBids[punkId] = Bid(false, address(0), 0);
        emit PunkBidWithdrawn(punkId, bid.value, msg.sender);
        (bool sent, ) = msg.sender.call{value: bid.value}("");
        require(sent, "send failed");
    }

    function acceptBidForPunk(uint256 punkId, uint256 minPrice) external nonReentrant {
        require(ownerOf(punkId) == msg.sender, "not your punk");
        Bid memory bid = punkBids[punkId];
        require(bid.hasBid && bid.value > 0, "no bid");
        require(bid.value >= minPrice, "bid below min");
        punkBids[punkId] = Bid(false, address(0), 0);
        _transfer(msg.sender, bid.bidder, punkId);
        uint256 royalty = (bid.value * ROYALTY_BPS) / 10_000;
        pendingWithdrawals[royaltyReceiver] += royalty;
        pendingWithdrawals[msg.sender] += bid.value - royalty;
        emit PunkBought(punkId, bid.value, msg.sender, bid.bidder);
    }

    function withdraw() external nonReentrant {
        uint256 amount = pendingWithdrawals[msg.sender];
        require(amount > 0, "nothing to withdraw");
        pendingWithdrawals[msg.sender] = 0;
        (bool sent, ) = msg.sender.call{value: amount}("");
        require(sent, "send failed");
    }

    // ------------------------------------------------------------------
    // Metadata, fully on-chain
    // ------------------------------------------------------------------
    function punkSvg(uint256 punkId) external view returns (string memory) {
        return data.punkSvg(punkId);
    }

    function tokenURI(uint256 tokenId) external view returns (string memory) {
        require(tokenId < MAX_SUPPLY, "bad punk");
        string memory image = Base64.encode(bytes(data.punkSvg(tokenId)));
        bytes memory json = abi.encodePacked(
            '{"name":"Robinhood Punk #', _u2s(tokenId),
            '","description":"Robinhood Punks: 2000 pixel punks living fully on chain on Robinhood Chain, claimed free by holders of $PUNKS. Art, metadata and marketplace all inside the contract.",',
            '"attributes":', data.punkAttributesJson(tokenId),
            ',"image":"data:image/svg+xml;base64,', image, '"}'
        );
        return string(abi.encodePacked("data:application/json;base64,", Base64.encode(json)));
    }

    function _u2s(uint256 v) internal pure returns (bytes memory) {
        if (v == 0) return "0";
        uint256 t = v; uint256 len;
        while (t > 0) { len++; t /= 10; }
        bytes memory b = new bytes(len);
        while (v > 0) { len--; b[len] = bytes1(uint8(48 + (v % 10))); v /= 10; }
        return b;
    }

    // ------------------------------------------------------------------
    // Batched read views for the website
    // ------------------------------------------------------------------
    function ownersPage(uint256 start, uint256 count) external view returns (address[] memory out) {
        out = new address[](count);
        for (uint256 i = 0; i < count; i++) {
            uint256 id = start + i;
            if (id < MAX_SUPPLY) out[i] = _owners[id];
        }
    }

    function offersPage(uint256 start, uint256 count)
        external view
        returns (bool[] memory forSale, uint256[] memory prices, address[] memory sellers)
    {
        forSale = new bool[](count);
        prices = new uint256[](count);
        sellers = new address[](count);
        for (uint256 i = 0; i < count; i++) {
            uint256 id = start + i;
            if (id >= MAX_SUPPLY) continue;
            Offer memory o = punkOffers[id];
            // only surface offers that are still valid for the current owner
            if (o.isForSale && o.seller == _owners[id]) {
                forSale[i] = true;
                prices[i] = o.minValue;
                sellers[i] = o.seller;
            }
        }
    }

    function bidsPage(uint256 start, uint256 count)
        external view
        returns (bool[] memory hasBid, uint256[] memory values, address[] memory bidders)
    {
        hasBid = new bool[](count);
        values = new uint256[](count);
        bidders = new address[](count);
        for (uint256 i = 0; i < count; i++) {
            uint256 id = start + i;
            if (id >= MAX_SUPPLY) continue;
            Bid memory b = punkBids[id];
            if (b.hasBid) {
                hasBid[i] = true;
                values[i] = b.value;
                bidders[i] = b.bidder;
            }
        }
    }
}
Chain explorer2111msChain node91ms