ScanHoodNFTFactory

0x08418af2299d4ec3a2598dbce5f6dcfb1e64a733

Verification
Verified
v0.8.30+commit.73712a01
Type
Contract
15,630 bytes
ABI entries
30
8 read · 7 write
License
mit

Contract information

Address
0x08418af2299d4ec3a2598dbce5f6dcfb1e64a733
Chain
Robinhood Chain (4663)
Compiler
v0.8.30+commit.73712a01
Optimization
Enabled
Creation tx
0xe4c90a7943…deca476da3

Token

Not a token

This contract does not expose ERC-20 metadata.

Read contract (8)

allCollections(uint256)address
collectionsCount()uint256
feeWallet()address
isScanHoodCollection(address)bool
launchFee()uint256
owner()address
pendingOwner()address
platformFeeBps()uint16

Events (6)

CollectionLaunchedFeeWalletUpdatedLaunchFeeUpdatedOwnershipTransferStartedOwnershipTransferredPlatformFeeBpsUpdated

ABI

[
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "initialOwner",
        "type": "address"
      },
      {
        "internalType": "address",
        "name": "feeWallet_",
        "type": "address"
      },
      {
        "internalType": "uint256",
        "name": "initialLaunchFee",
        "type": "uint256"
      },
      {
        "internalType": "uint16",
        "name": "initialPlatformFeeBps",
        "type": "uint16"
      }
    ],
    "stateMutability": "nonpayable",
    "type": "constructor"
  },
  {
    "inputs": [],
    "name": "InvalidBasisPoints",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "InvalidMaxSupply",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "InvalidRoyaltyBasisPoints",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "LaunchFeeNotPaid",
    "type": "error"
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "owner",
        "type": "address"
      }
    ],
    "name": "OwnableInvalidOwner",
    "type": "error"
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "account",
        "type": "address"
      }
    ],
    "name": "OwnableUnauthorizedAccount",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "ReentrancyGuardReentrantCall",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "ZeroAddress",
    "type": "error"
  },
  {
    "anonymous": false,
    "inputs": [
      {
        "indexed": true,
        "internalType": "address",
        "name": "collection",
        "type": "address"
      },
      {
        "indexed": true,
        "internalType": "address",
        "name": "creator",
        "type": "address"
      },
      {
        "indexed": false,
        "internalType": "string",
        "name": "name",
        "type": "string"
      },
      {
        "indexed": false,
        "internalType": "string",
        "name": "symbol",
        "type": "string"
      },
      {
        "indexed": false,
        "internalType": "uint256",
        "name": "maxSupply",
        "type": "uint256"
      },
      {
        "indexed": false,
        "internalType": "uint256",
        "name": "mintPrice",
        "type": "uint256"
      },
      {
        "indexed": false,
        "internalType": "uint256",
        "name": "mintStart",
        "type": "uint256"
      }
    ],
    "name": "CollectionLaunched",
    "type": "event"
  },
  {
    "anonymous": false,
    "inputs": [
      {
        "indexed": false,
        "internalType": "address",
        "name": "feeWallet",
        "type": "address"
      }
    ],
    "name": "FeeWalletUpdated",
    "type": "event"
  },
  {
    "anonymous": false,
    "inputs": [
      {
        "indexed": false,
        "internalType": "uint256",
        "name": "launchFee",
        "type": "uint256"
      }
    ],
    "name": "LaunchFeeUpdated",
    "type": "event"
  },
  {
    "anonymous": false,
    "inputs": [
      {
        "indexed": true,
        "internalType": "address",
        "name": "previousOwner",
        "type": "address"
      },
      {
        "indexed": true,
        "internalType": "address",
        "name": "newOwner",
        "type": "address"
      }
    ],
    "name": "OwnershipTransferStarted",
    "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": "uint16",
        "name": "platformFeeBps",
        "type": "uint16"
      }
    ],
    "name": "PlatformFeeBpsUpdated",
    "type": "event"
  },
  {
    "inputs": [],
    "name": "acceptOwnership",
    "outputs": [],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "uint256",
        "name": "",
        "type": "uint256"
      }
    ],
    "name": "allCollections",
    "outputs": [
      {
        "internalType": "address",
        "name": "",
        "type": "address"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "collectionsCount",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "",
        "type": "uint256"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "feeWallet",
    "outputs": [
      {
        "internalType": "address",
        "name": "",
        "type": "address"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "collection",
        "type": "address"
      }
    ],
    "name": "isScanHoodCollection",
    "outputs": [
      {
        "internalType": "bool",
        "name": "isOurs",
        "type": "bool"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [
      {
        "components": [
          {
            "internalType": "string",
            "name": "name",
            "type": "string"
          },
          {
            "internalType": "string",
            "name": "symbol",
            "type": "string"
          },
          {
            "internalType": "string",
            "name": "baseURI",
            "type": "string"
          },
          {
            "internalType": "string",
            "name": "logo",
            "type": "string"
          },
          {
            "internalType": "string",
            "name": "description",
            "type": "string"
          },
          {
            "components": [
              {
                "internalType": "string",
                "name": "twitter",
                "type": "string"
              },
              {
                "internalType": "string",
                "name": "telegram",
                "type": "string"
              },
              {
                "internalType": "string",
                "name": "discord",
                "type": "string"
              },
              {
                "internalType": "string",
                "name": "website",
                "type": "string"
              },
              {
                "internalType": "string",
                "name": "farcaster",
                "type": "string"
              }
            ],
            "internalType": "struct ScanHoodNFTCollection.Socials",
            "name": "socials",
            "type": "tuple"
          },
          {
            "internalType": "uint256",
            "name": "maxSupply",
            "type": "uint256"
          },
          {
            "internalType": "uint256",
            "name": "reserveSupply",
            "type": "uint256"
          },
          {
            "internalType": "uint256",
            "name": "mintPrice",
            "type": "uint256"
          },
          {
            "internalType": "uint256",
            "name": "walletLimit",
            "type": "uint256"
          },
          {
            "internalType": "uint256",
            "name": "mintStart",
            "type": "uint256"
          },
          {
            "internalType": "uint96",
            "name": "royaltyBps",
            "type": "uint96"
          }
        ],
        "internalType": "struct ScanHoodNFTFactory.CollectionParams",
        "name": "params",
        "type": "tuple"
      }
    ],
    "name": "launchCollection",
    "outputs": [
      {
        "internalType": "address",
        "name": "collection",
        "type": "address"
      }
    ],
    "stateMutability": "payable",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "launchFee",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "",
        "type": "uint256"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "owner",
    "outputs": [
      {
        "internalType": "address",
        "name": "",
        "type": "address"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "pendingOwner",
    "outputs": [
      {
        "internalType": "address",
        "name": "",
        "type": "address"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "platformFeeBps",
    "outputs": [
      {
        "internalType": "uint16",
        "name": "",
        "type": "uint16"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "renounceOwnership",
    "outputs": [],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "newFeeWallet",
        "type": "address"
      }
    ],
    "name": "setFeeWallet",
    "outputs": [],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "uint256",
        "name": "newLaunchFee",
        "type": "uint256"
      }
    ],
    "name": "setLaunchFee",
    "outputs": [],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "uint16",
        "name": "newPlatformFeeBps",
        "type": "uint16"
      }
    ],
    "name": "setPlatformFeeBps",
    "outputs": [],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "newOwner",
        "type": "address"
      }
    ],
    "name": "transferOwnership",
    "outputs": [],
    "stateMutability": "nonpayable",
    "type": "function"
  }
]

Source code

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

import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol";
import {Ownable2Step} from "@openzeppelin/contracts/access/Ownable2Step.sol";
import {ReentrancyGuard} from "@openzeppelin/contracts/utils/ReentrancyGuard.sol";
import {ScanHoodNFTCollection} from "./ScanHoodNFTCollection.sol";

/**
 * @title ScanHoodNFTFactory
 * @notice Deploys ScanHoodNFTCollection contracts (fixed-supply ERC-721, locked
 * royalty, capped mint) for a flat native-token launch fee. Mirrors the posture
 * of the token launchpad: fork audited primitives (OpenZeppelin ERC-721/2981),
 * add only the orchestration and no-rug guarantees on top. No marketplace or
 * escrow here — trading happens on OpenSea; this only handles fair launch.
 */
contract ScanHoodNFTFactory is Ownable2Step, ReentrancyGuard {
    error LaunchFeeNotPaid();
    error ZeroAddress();
    error InvalidMaxSupply();
    error InvalidBasisPoints();
    error InvalidRoyaltyBasisPoints();

    event CollectionLaunched(
        address indexed collection,
        address indexed creator,
        string name,
        string symbol,
        uint256 maxSupply,
        uint256 mintPrice,
        uint256 mintStart
    );
    event LaunchFeeUpdated(uint256 launchFee);
    event FeeWalletUpdated(address feeWallet);
    event PlatformFeeBpsUpdated(uint16 platformFeeBps);

    uint256 private constant BASIS_POINTS = 10_000;
    uint96 private constant MAX_ROYALTY_BPS = 1_000; // 10% cap, matches OpenSea/EIP-2981 marketplace norms

    uint256 public launchFee;
    address public feeWallet;
    uint16 public platformFeeBps;

    address[] public allCollections;
    mapping(address collection => bool isOurs) public isScanHoodCollection;

    struct CollectionParams {
        string name;
        string symbol;
        string baseURI;
        string logo;
        string description;
        ScanHoodNFTCollection.Socials socials;
        uint256 maxSupply;
        uint256 reserveSupply;
        uint256 mintPrice;
        uint256 walletLimit;
        uint256 mintStart;
        uint96 royaltyBps;
    }

    constructor(address initialOwner, address feeWallet_, uint256 initialLaunchFee, uint16 initialPlatformFeeBps)
        Ownable(initialOwner)
    {
        if (feeWallet_ == address(0)) revert ZeroAddress();
        if (initialPlatformFeeBps > BASIS_POINTS) revert InvalidBasisPoints();
        feeWallet = feeWallet_;
        launchFee = initialLaunchFee;
        platformFeeBps = initialPlatformFeeBps;
    }

    /// @notice Deploys and returns a new fixed-supply collection owned by the caller.
    function launchCollection(CollectionParams calldata params)
        external
        payable
        nonReentrant
        returns (address collection)
    {
        if (msg.value < launchFee) revert LaunchFeeNotPaid();
        if (params.maxSupply == 0) revert InvalidMaxSupply();
        if (params.royaltyBps > MAX_ROYALTY_BPS) revert InvalidRoyaltyBasisPoints();

        collection = address(
            new ScanHoodNFTCollection(
                params.name,
                params.symbol,
                params.baseURI,
                params.logo,
                params.description,
                params.socials,
                msg.sender,
                feeWallet,
                platformFeeBps,
                params.maxSupply,
                params.reserveSupply,
                params.mintPrice,
                params.walletLimit,
                params.mintStart,
                params.royaltyBps
            )
        );

        allCollections.push(collection);
        isScanHoodCollection[collection] = true;

        emit CollectionLaunched(
            collection, msg.sender, params.name, params.symbol, params.maxSupply, params.mintPrice, params.mintStart
        );

        if (launchFee > 0) {
            (bool sent,) = payable(feeWallet).call{value: launchFee}("");
            if (!sent) revert LaunchFeeNotPaid();
        }
        uint256 refund = msg.value - launchFee;
        if (refund > 0) {
            (bool sentRefund,) = payable(msg.sender).call{value: refund}("");
            if (!sentRefund) revert LaunchFeeNotPaid();
        }
    }

    function collectionsCount() external view returns (uint256) {
        return allCollections.length;
    }

    function setLaunchFee(uint256 newLaunchFee) external onlyOwner {
        launchFee = newLaunchFee;
        emit LaunchFeeUpdated(newLaunchFee);
    }

    function setFeeWallet(address newFeeWallet) external onlyOwner {
        if (newFeeWallet == address(0)) revert ZeroAddress();
        feeWallet = newFeeWallet;
        emit FeeWalletUpdated(newFeeWallet);
    }

    function setPlatformFeeBps(uint16 newPlatformFeeBps) external onlyOwner {
        if (newPlatformFeeBps > BASIS_POINTS) revert InvalidBasisPoints();
        platformFeeBps = newPlatformFeeBps;
        emit PlatformFeeBpsUpdated(newPlatformFeeBps);
    }
}
Chain explorer4203msChain node82ms