RPunksNFT
0xb01e3f72f2f4031bee0429e3c267f28db7decef2
Verification
Verified
v0.8.28+commit.7893614a
Type
Contract
6,276 bytes
ABI entries
41
14 read · 9 write
License
mit
Contract information
- Address
- 0xb01e3f72f2f4031bee0429e3c267f28db7decef2
- Chain
- Robinhood Chain (4663)
- Compiler
- v0.8.28+commit.7893614a
- Optimization
- Enabled
- Creator
- 0x1A0ed77C45…5C129B214c
- Creation tx
- 0x307bcd09be…50d916a89e
Token
- Name
- rPunks
- Symbol
- PUNKS
- Decimals
- —
- Holders
- 16
- Market
- View token page →
Read contract (14)
balanceOf(address) → uint256
certOf(uint256) → uint256
getApproved(uint256) → address
isApprovedForAll(address, address) → bool
name() → string
nextId() → uint256
ownedIds(address) → uint32[]
owner() → address
ownerOf(uint256) → address
renderer() → address
supportsInterface(bytes4) → bool
symbol() → string
token() → address
tokenURI(uint256) → string
Events (5)
ApprovalApprovalForAllBornOwnershipTransferredTransfer
ABI
[
{
"inputs": [
{
"internalType": "address",
"name": "renderer_",
"type": "address"
},
{
"internalType": "address",
"name": "token_",
"type": "address"
}
],
"stateMutability": "nonpayable",
"type": "constructor"
},
{
"inputs": [
{
"internalType": "address",
"name": "sender",
"type": "address"
},
{
"internalType": "uint256",
"name": "tokenId",
"type": "uint256"
},
{
"internalType": "address",
"name": "owner",
"type": "address"
}
],
"name": "ERC721IncorrectOwner",
"type": "error"
},
{
"inputs": [
{
"internalType": "address",
"name": "operator",
"type": "address"
},
{
"internalType": "uint256",
"name": "tokenId",
"type": "uint256"
}
],
"name": "ERC721InsufficientApproval",
"type": "error"
},
{
"inputs": [
{
"internalType": "address",
"name": "approver",
"type": "address"
}
],
"name": "ERC721InvalidApprover",
"type": "error"
},
{
"inputs": [
{
"internalType": "address",
"name": "operator",
"type": "address"
}
],
"name": "ERC721InvalidOperator",
"type": "error"
},
{
"inputs": [
{
"internalType": "address",
"name": "owner",
"type": "address"
}
],
"name": "ERC721InvalidOwner",
"type": "error"
},
{
"inputs": [
{
"internalType": "address",
"name": "receiver",
"type": "address"
}
],
"name": "ERC721InvalidReceiver",
"type": "error"
},
{
"inputs": [
{
"internalType": "address",
"name": "sender",
"type": "address"
}
],
"name": "ERC721InvalidSender",
"type": "error"
},
{
"inputs": [
{
"internalType": "uint256",
"name": "tokenId",
"type": "uint256"
}
],
"name": "ERC721NonexistentToken",
"type": "error"
},
{
"inputs": [],
"name": "EmptyStack",
"type": "error"
},
{
"inputs": [],
"name": "NotToken",
"type": "error"
},
{
"inputs": [
{
"internalType": "address",
"name": "owner",
"type": "address"
}
],
"name": "OwnableInvalidOwner",
"type": "error"
},
{
"inputs": [
{
"internalType": "address",
"name": "account",
"type": "address"
}
],
"name": "OwnableUnauthorizedAccount",
"type": "error"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "address",
"name": "owner",
"type": "address"
},
{
"indexed": true,
"internalType": "address",
"name": "approved",
"type": "address"
},
{
"indexed": true,
"internalType": "uint256",
"name": "tokenId",
"type": "uint256"
}
],
"name": "Approval",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "address",
"name": "owner",
"type": "address"
},
{
"indexed": true,
"internalType": "address",
"name": "operator",
"type": "address"
},
{
"indexed": false,
"internalType": "bool",
"name": "approved",
"type": "bool"
}
],
"name": "ApprovalForAll",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "uint256",
"name": "tokenId",
"type": "uint256"
},
{
"indexed": true,
"internalType": "address",
"name": "to",
"type": "address"
},
{
"indexed": false,
"internalType": "uint256",
"name": "cert",
"type": "uint256"
}
],
"name": "Born",
"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": [
{
"internalType": "address",
"name": "to",
"type": "address"
},
{
"internalType": "uint256",
"name": "tokenId",
"type": "uint256"
}
],
"name": "approve",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "owner",
"type": "address"
}
],
"name": "balanceOf",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "from",
"type": "address"
},
{
"internalType": "uint256",
"name": "count",
"type": "uint256"
}
],
"name": "burnLIFO",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"name": "certOf",
"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": [
{
"internalType": "address",
"name": "owner",
"type": "address"
},
{
"internalType": "address",
"name": "operator",
"type": "address"
}
],
"name": "isApprovedForAll",
"outputs": [
{
"internalType": "bool",
"name": "",
"type": "bool"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "to",
"type": "address"
},
{
"internalType": "uint256",
"name": "count",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "certBase",
"type": "uint256"
}
],
"name": "mintSynced",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [],
"name": "name",
"outputs": [
{
"internalType": "string",
"name": "",
"type": "string"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "nextId",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "account",
"type": "address"
}
],
"name": "ownedIds",
"outputs": [
{
"internalType": "uint32[]",
"name": "",
"type": "uint32[]"
}
],
"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": "renderer",
"outputs": [
{
"internalType": "contract RPunksRenderer",
"name": "",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "renounceOwnership",
"outputs": [],
"stateMutability": "nonpayable",
"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": "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": [],
"name": "token",
"outputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "uint256",
"name": "tokenId",
"type": "uint256"
}
],
"name": "tokenURI",
"outputs": [
{
"internalType": "string",
"name": "",
"type": "string"
}
],
"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
//
// ▄▄▄▄▄▄▄▄
// █▀▀▀▀▀▀▀▀█
// █ ██ ██ █ r P U N K S
// █ ▄▄ █ every punk is born from a trade
// █ ▀▀▀▀ █
// ▀▀▀▀▀▀▀▀▀▀
//
// website: https://rpunks.xyz
// twitter: https://x.com/rPunks_
// telegram: https://t.me/rpunksxyz
//
pragma solidity ^0.8.28;
import {ERC721} from "@openzeppelin/contracts/token/ERC721/ERC721.sol";
import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol";
import {RPunksRenderer} from "./RPunksRenderer.sol";
import {RPunksToken} from "./RPunksToken.sol";
/// @notice rPunks mirror NFT. Punks are born and burned by the token's
/// balance mirror — never minted directly. A wallet-to-wallet NFT
/// transfer carries exactly one whole token with it. tokenURI is
/// fully on-chain.
contract RPunksNFT is ERC721, Ownable {
error NotToken();
error EmptyStack();
/// @notice Emitted on every birth with the punk's locked BirthCert.
event Born(uint256 indexed tokenId, address indexed to, uint256 cert);
uint256 private constant SEED_MASK = (1 << 160) - 1;
RPunksRenderer public immutable renderer;
address public immutable token;
uint256 public nextId = 1;
/// tokenId -> BirthCert (locked at birth, immutable afterwards)
mapping(uint256 => uint256) public certOf;
mapping(address => uint32[]) private _owned;
/// tokenId -> position in the owner's stack + 1 (0 = absent)
mapping(uint256 => uint256) private _ownedPos;
constructor(address renderer_, address token_)
ERC721("rPunks", "PUNKS")
Ownable(msg.sender)
{
renderer = RPunksRenderer(renderer_);
token = token_;
}
modifier onlyToken() {
if (msg.sender != token) revert NotToken();
_;
}
/// @notice Births driven by the balance mirror.
function mintSynced(address to, uint256 count, uint256 certBase)
external onlyToken
{
for (uint256 i; i < count; ++i) {
uint256 id = nextId++;
uint256 cert;
if (certBase == 0) {
uint256 seed = uint256(keccak256(abi.encodePacked(
to, block.timestamp, block.number, id
))) & SEED_MASK;
cert = seed | ((block.number & 0xffffffff) << 180);
} else {
uint256 seed = uint256(
keccak256(abi.encodePacked(certBase, id))
) & SEED_MASK;
cert = seed | (certBase & ~SEED_MASK);
}
certOf[id] = cert;
_mint(to, id);
emit Born(id, to, cert);
}
}
/// @notice Last born dies first; burns as many punks as the balance lost.
function burnLIFO(address from, uint256 count) external onlyToken {
uint32[] storage stack = _owned[from];
for (uint256 i; i < count; ++i) {
if (stack.length == 0) revert EmptyStack();
uint256 id = stack[stack.length - 1];
_burn(id);
delete certOf[id];
}
}
function ownedIds(address account)
external view returns (uint32[] memory)
{
return _owned[account];
}
function _update(address to, uint256 tokenId, address auth)
internal override returns (address from)
{
from = super._update(to, tokenId, auth);
if (from != address(0)) {
uint32[] storage s = _owned[from];
uint256 pos = _ownedPos[tokenId] - 1;
uint256 last = s.length - 1;
if (pos != last) {
uint32 moved = s[last];
s[pos] = moved;
_ownedPos[moved] = pos + 1;
}
s.pop();
delete _ownedPos[tokenId];
}
if (to != address(0)) {
_owned[to].push(uint32(tokenId));
_ownedPos[tokenId] = _owned[to].length;
}
if (from != address(0) && to != address(0)) {
RPunksToken(token).mirrorTransfer(from, to, 1e18);
}
}
function tokenURI(uint256 tokenId)
public view override returns (string memory)
{
_requireOwned(tokenId);
return renderer.tokenURI(tokenId, certOf[tokenId]);
}
}
Chain explorer4241msChain node82ms