HoodsCreatorNFT

0x0a19f9a844eccc0a49e08bcabd54d3d541ab1b4a

Verification
Unverified
v0.8.26+commit.8a97fa7a
Type
Contract
4,535 bytes
ABI entries
33
13 read · 12 write
License
none

Contract information

Address
0x0a19f9a844eccc0a49e08bcabd54d3d541ab1b4a
Chain
Robinhood Chain (4663)
Compiler
v0.8.26+commit.8a97fa7a
Optimization
Enabled
Creation tx
0x6fa6238991…f4e28d2e8f

Token

Name
hoods.meme Creator
Symbol
HOODC
Decimals
Holders
1

Read contract (13)

CTO_DELAY()uint256
balanceOf(address)uint256
deployer()address
factory()address
getApproved(uint256)address
guardian()address
isApprovedForAll(address, address)bool
name()string
ownerOf(uint256)address
pendingCto(uint256)address, uint64
splitter()address
supportsInterface(bytes4)bool
symbol()string

Events (7)

ApprovalApprovalForAllCTOTransferCtoCancelledCtoQueuedGuardianChangedTransfer

ABI

[
  {
    "inputs": [],
    "stateMutability": "nonpayable",
    "type": "constructor"
  },
  {
    "anonymous": false,
    "inputs": [
      {
        "indexed": true,
        "internalType": "address",
        "name": "owner",
        "type": "address"
      },
      {
        "indexed": true,
        "internalType": "address",
        "name": "approved",
        "type": "address"
      },
      {
        "indexed": true,
        "internalType": "uint256",
        "name": "tokenId",
        "type": "uint256"
      }
    ],
    "name": "Approval",
    "type": "event"
  },
  {
    "anonymous": false,
    "inputs": [
      {
        "indexed": true,
        "internalType": "address",
        "name": "owner",
        "type": "address"
      },
      {
        "indexed": true,
        "internalType": "address",
        "name": "operator",
        "type": "address"
      },
      {
        "indexed": false,
        "internalType": "bool",
        "name": "approved",
        "type": "bool"
      }
    ],
    "name": "ApprovalForAll",
    "type": "event"
  },
  {
    "anonymous": false,
    "inputs": [
      {
        "indexed": true,
        "internalType": "uint256",
        "name": "tokenId",
        "type": "uint256"
      },
      {
        "indexed": true,
        "internalType": "address",
        "name": "from",
        "type": "address"
      },
      {
        "indexed": true,
        "internalType": "address",
        "name": "to",
        "type": "address"
      }
    ],
    "name": "CTOTransfer",
    "type": "event"
  },
  {
    "anonymous": false,
    "inputs": [
      {
        "indexed": true,
        "internalType": "uint256",
        "name": "tokenId",
        "type": "uint256"
      }
    ],
    "name": "CtoCancelled",
    "type": "event"
  },
  {
    "anonymous": false,
    "inputs": [
      {
        "indexed": true,
        "internalType": "uint256",
        "name": "tokenId",
        "type": "uint256"
      },
      {
        "indexed": true,
        "internalType": "address",
        "name": "to",
        "type": "address"
      },
      {
        "indexed": false,
        "internalType": "uint64",
        "name": "eta",
        "type": "uint64"
      }
    ],
    "name": "CtoQueued",
    "type": "event"
  },
  {
    "anonymous": false,
    "inputs": [
      {
        "indexed": true,
        "internalType": "address",
        "name": "previousGuardian",
        "type": "address"
      },
      {
        "indexed": true,
        "internalType": "address",
        "name": "newGuardian",
        "type": "address"
      }
    ],
    "name": "GuardianChanged",
    "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": "CTO_DELAY",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "",
        "type": "uint256"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "spender",
        "type": "address"
      },
      {
        "internalType": "uint256",
        "name": "tokenId",
        "type": "uint256"
      }
    ],
    "name": "approve",
    "outputs": [],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "",
        "type": "address"
      }
    ],
    "name": "balanceOf",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "",
        "type": "uint256"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "uint256",
        "name": "tokenId",
        "type": "uint256"
      }
    ],
    "name": "cancelCtoTransfer",
    "outputs": [],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "deployer",
    "outputs": [
      {
        "internalType": "address",
        "name": "",
        "type": "address"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "uint256",
        "name": "tokenId",
        "type": "uint256"
      }
    ],
    "name": "executeCtoTransfer",
    "outputs": [],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "factory",
    "outputs": [
      {
        "internalType": "address",
        "name": "",
        "type": "address"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "uint256",
        "name": "",
        "type": "uint256"
      }
    ],
    "name": "getApproved",
    "outputs": [
      {
        "internalType": "address",
        "name": "",
        "type": "address"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "guardian",
    "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": [
      {
        "internalType": "address",
        "name": "to",
        "type": "address"
      },
      {
        "internalType": "uint256",
        "name": "tokenId",
        "type": "uint256"
      }
    ],
    "name": "mint",
    "outputs": [],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "name",
    "outputs": [
      {
        "internalType": "string",
        "name": "",
        "type": "string"
      }
    ],
    "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": "uint256",
        "name": "",
        "type": "uint256"
      }
    ],
    "name": "pendingCto",
    "outputs": [
      {
        "internalType": "address",
        "name": "to",
        "type": "address"
      },
      {
        "internalType": "uint64",
        "name": "eta",
        "type": "uint64"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "uint256",
        "name": "tokenId",
        "type": "uint256"
      },
      {
        "internalType": "address",
        "name": "to",
        "type": "address"
      }
    ],
    "name": "queueCtoTransfer",
    "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": "address",
        "name": "factory_",
        "type": "address"
      }
    ],
    "name": "setFactory",
    "outputs": [],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "newGuardian",
        "type": "address"
      }
    ],
    "name": "setGuardian",
    "outputs": [],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "splitter_",
        "type": "address"
      }
    ],
    "name": "setSplitter",
    "outputs": [],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "splitter",
    "outputs": [
      {
        "internalType": "address",
        "name": "",
        "type": "address"
      }
    ],
    "stateMutability": "view",
    "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": "address",
        "name": "from",
        "type": "address"
      },
      {
        "internalType": "address",
        "name": "to",
        "type": "address"
      },
      {
        "internalType": "uint256",
        "name": "tokenId",
        "type": "uint256"
      }
    ],
    "name": "transferFrom",
    "outputs": [],
    "stateMutability": "nonpayable",
    "type": "function"
  }
]

Source code

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

/*
█   █  ███   ███  ████   ████     █   █ █████ █   █ █████
█   █ █   █ █   █ █   █ █         ██ ██ █     ██ ██ █
█████ █   █ █   █ █   █  ███      █ █ █ ████  █ █ █ ████
█   █ █   █ █   █ █   █     █  █  █   █ █     █   █ █
█   █  ███   ███  ████  ████   █  █   █ █████ █   █ █████

   The most rewarding place to launch and trade.
   Fixed supply · LP burned forever · fees flow to creators.

   web  https://hoods.meme
   x    https://x.com/hoodsmeme
   tg   https://t.me/hoods_meme
*/


import { IERC721Receiver } from "./common/Interfaces.sol";

interface IHoodsSplitterSettle {
    function settleTo(address token, address to) external;
}

/// @title CreatorNFT — creator-fee ownership indirection (spec §2.4)
/// @notice One NFT per launched token (tokenId = uint160(token address)), minted by the factory
/// to the launch creator. The CreatorSplitter always pays the CURRENT holder — transferring this
/// NFT transfers the fee stream (creator-fee sales, CTO handovers).
///
/// TRUST MODEL — stated plainly: a platform `guardian` can reassign any Creator NFT via
/// `ctoTransfer` (community-takeover handovers when a dev abandons a project). This is deliberate,
/// platform-controlled custody of the FEE STREAM ONLY — it cannot touch the token, the pool, the
/// locked LP, or anyone's funds. Guardian mechanics (multisig/timelock/veto) may be layered on later
/// by rotating `guardian` to a stricter controller.
/// Minimal, self-contained ERC-721; no enumerable/metadata extensions beyond tokenURI-less name.
contract HoodsCreatorNFT {
    string public constant name = "hoods.meme Creator";
    string public constant symbol = "HOODC";

    address public immutable deployer; // only address allowed to wire factory/splitter (anti front-run, M-1)
    address public factory; // set once at deploy wiring (breaks the factory↔nft ctor cycle)
    address public splitter; // set-once; lets ctoTransfer settle the OUTGOING holder's fees (CON-2)
    address public guardian; // platform CTO authority (see trust-model note above)

    mapping(uint256 => address) private _owner;
    mapping(address => uint256) public balanceOf;
    mapping(uint256 => address) public getApproved;
    mapping(address => mapping(address => bool)) public isApprovedForAll;

    // CTO handovers are TIMELOCKED: the guardian queues one, and only after CTO_DELAY can anyone execute
    // it. The delay is a fixed safety window (not tunable) — a public interval in which a compromised
    // guardian can be rotated (setGuardian) or the move cancelled, so no single key can seize a creator's
    // fee stream instantly. Pair with a multisig guardian at deploy for defense in depth.
    uint256 public constant CTO_DELAY = 48 hours;
    struct PendingCto {
        address to;
        uint64 eta;
    }
    mapping(uint256 => PendingCto) public pendingCto;

    event Transfer(address indexed from, address indexed to, uint256 indexed tokenId);
    event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId);
    event ApprovalForAll(address indexed owner, address indexed operator, bool approved);
    event GuardianChanged(address indexed previousGuardian, address indexed newGuardian);
    event CTOTransfer(uint256 indexed tokenId, address indexed from, address indexed to);
    event CtoQueued(uint256 indexed tokenId, address indexed to, uint64 eta);
    event CtoCancelled(uint256 indexed tokenId);

    constructor() {
        deployer = msg.sender;
        guardian = msg.sender; // deployer; rotate later via setGuardian (e.g. to a multisig)
        emit GuardianChanged(address(0), msg.sender);
    }

    /// One-shot wiring: the factory is the only minter. Deployer-only so it can't be front-run into an
    /// attacker address (which would let them pre-mint NFTs / brick the real factory, M-1).
    function setFactory(address factory_) external {
        require(msg.sender == deployer, "not deployer");
        require(factory == address(0) && factory_ != address(0), "factory set");
        factory = factory_;
    }

    /// One-shot wiring: the splitter used to settle a departing holder's fees on ctoTransfer. Deployer-only.
    function setSplitter(address splitter_) external {
        require(msg.sender == deployer, "not deployer");
        require(splitter == address(0) && splitter_ != address(0), "splitter set");
        splitter = splitter_;
    }

    /// Rotate the platform CTO authority (guardian-only). Set to a multisig/timelock as governance
    /// matures; setting to address(0) permanently renounces CTO power.
    function setGuardian(address newGuardian) external {
        require(msg.sender == guardian, "not guardian");
        emit GuardianChanged(guardian, newGuardian);
        guardian = newGuardian;
    }

    /// Queue a platform CTO handover (guardian-only). Takes effect after CTO_DELAY. Queuing again for
    /// the same token overwrites the pending entry (and restarts the clock).
    function queueCtoTransfer(uint256 tokenId, address to) external {
        require(msg.sender == guardian, "not guardian");
        require(to != address(0), "zero to");
        ownerOf(tokenId); // reverts if the token doesn't exist
        uint64 eta = uint64(block.timestamp + CTO_DELAY);
        pendingCto[tokenId] = PendingCto({ to: to, eta: eta });
        emit CtoQueued(tokenId, to, eta);
    }

    /// Cancel a queued handover (guardian-only) — e.g. after rotating a compromised guardian.
    function cancelCtoTransfer(uint256 tokenId) external {
        require(msg.sender == guardian, "not guardian");
        require(pendingCto[tokenId].to != address(0), "none queued");
        delete pendingCto[tokenId];
        emit CtoCancelled(tokenId);
    }

    /// Execute a queued CTO handover once its delay has elapsed. PERMISSIONLESS — the timelock is the
    /// safeguard, not the caller. Reassigns the fee-stream NFT and settles the OUTGOING holder's earned
    /// fees to them first (CON-2), so a takeover never captures accrued fees.
    function executeCtoTransfer(uint256 tokenId) external {
        PendingCto memory p = pendingCto[tokenId];
        require(p.to != address(0), "none queued");
        require(block.timestamp >= p.eta, "timelocked");
        delete pendingCto[tokenId]; // effect before the reassign + external call (CEI)

        address from = ownerOf(tokenId);
        delete getApproved[tokenId];
        unchecked {
            balanceOf[from]--;
            balanceOf[p.to]++;
        }
        _owner[tokenId] = p.to;
        emit Transfer(from, p.to, tokenId);
        emit CTOTransfer(tokenId, from, p.to);
        address sp = splitter;
        if (sp != address(0)) IHoodsSplitterSettle(sp).settleTo(address(uint160(tokenId)), from);
    }

    function ownerOf(uint256 tokenId) public view returns (address o) {
        o = _owner[tokenId];
        require(o != address(0), "no token");
    }

    /// Factory-only, once per launch (tokenId = the launched token's address).
    function mint(address to, uint256 tokenId) external {
        require(msg.sender == factory, "not factory");
        require(_owner[tokenId] == address(0), "exists");
        _owner[tokenId] = to;
        unchecked {
            balanceOf[to]++;
        }
        emit Transfer(address(0), to, tokenId);
    }

    function approve(address spender, uint256 tokenId) external {
        address o = ownerOf(tokenId);
        require(msg.sender == o || isApprovedForAll[o][msg.sender], "not authorized");
        getApproved[tokenId] = spender;
        emit Approval(o, spender, 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 to");
        require(msg.sender == o || msg.sender == getApproved[tokenId] || isApprovedForAll[o][msg.sender], "not authorized");
        delete getApproved[tokenId];
        unchecked {
            balanceOf[from]--;
            balanceOf[to]++;
        }
        _owner[tokenId] = to;
        emit Transfer(from, to, tokenId);
    }

    function safeTransferFrom(address from, address to, uint256 tokenId) external {
        safeTransferFrom(from, to, tokenId, "");
    }

    function safeTransferFrom(address from, address to, uint256 tokenId, bytes memory data) public {
        transferFrom(from, to, tokenId);
        if (to.code.length > 0) {
            require(
                IERC721Receiver(to).onERC721Received(msg.sender, from, tokenId, data)
                    == IERC721Receiver.onERC721Received.selector,
                "unsafe recipient"
            );
        }
    }

    function supportsInterface(bytes4 interfaceId) external pure returns (bool) {
        return interfaceId == 0x80ac58cd || interfaceId == 0x01ffc9a7; // ERC721 + ERC165
    }
}
Chain explorer3979msChain node90ms