Contract detail
NeonNodes
NeonNodes is a verified Robinhood Chain smart contract at 0xb3ac2c2c…a645eac7 with 17 indexed transactions.
- Address type
- Smart contract
- Transactions
- 17
- First indexed block
- #28455229
- Latest indexed block
- #28503744
- Funding transaction
- 0x817c4341…ffbc64d8 in block #28461300
- Emitted events
- 54
- Token tracker
- testing mods
- Verification
- Verified
- Compiler
- v0.8.27+commit.40a35a09
- Verification match
- full
- Creation transaction
- 0xc8e7cd42…46304ef4
- Created at block
- #28455229
- Language
- solidity
- EVM version
- cancun
- License
- none
- Compiler optimization
- Enabled · 200 runs
Related activity
Transactions
Verified contract
Contract source code
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.27;
import "erc721a/contracts/ERC721A.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts/utils/cryptography/ECDSA.sol";
import "@openzeppelin/contracts/utils/cryptography/MessageHashUtils.sol";
import "@openzeppelin/contracts/token/common/ERC2981.sol";
interface ITransferValidator {
function validateTransfer(address caller, address from, address to, uint256 tokenId) external;
}
contract NeonNodes is ERC721A, Ownable, ERC2981 {
using ECDSA for bytes32;
using MessageHashUtils for bytes32;
// Testing deployment — 50 tokens, matches /prod/testing_42 image set.
// Bump to 5555 for the real production deploy.
uint256 public constant MAX_SUPPLY = 50;
uint256 public constant MAX_PER_WALLET = 25;
uint8 public constant MAX_BATCH = 5;
uint256 public mintPrice = 0.00075 ether;
address public signer;
address public fundsReceiver;
string private _baseTokenURI;
string private _contractMetadataURI;
bool public mintActive;
mapping(bytes32 => bool) public usedNonces;
// ─── ICreatorToken (LimitBreak) transfer validator ────────────────────
address private _transferValidator;
event TransferValidatorUpdated(address oldValidator, address newValidator);
error MintNotActive();
error MaxSupplyReached();
error WalletLimitReached();
error InsufficientPayment();
error InvalidSignature();
error NonceAlreadyUsed();
error TransferFailed();
error InvalidQuantity();
constructor(
address _signer,
address _royaltyReceiver,
address _fundsReceiver,
string memory baseURI_,
string memory contractURI_
) ERC721A("testing mods", "testing mods") Ownable(msg.sender) {
signer = _signer;
fundsReceiver = _fundsReceiver;
_baseTokenURI = baseURI_;
_contractMetadataURI = contractURI_;
_setDefaultRoyalty(_royaltyReceiver, 500); // 5% (500 basis points)
}
function mint(
bytes32 nonce,
bytes calldata signature
) external payable {
if (!mintActive) revert MintNotActive();
if (_totalMinted() + 1 > MAX_SUPPLY) revert MaxSupplyReached();
if (_numberMinted(msg.sender) + 1 > MAX_PER_WALLET) revert WalletLimitReached();
if (msg.value < mintPrice) revert InsufficientPayment();
if (usedNonces[nonce]) revert NonceAlreadyUsed();
bytes32 messageHash = keccak256(
abi.encodePacked(msg.sender, nonce, address(this), block.chainid)
);
bytes32 ethSignedHash = messageHash.toEthSignedMessageHash();
if (ethSignedHash.recover(signature) != signer) revert InvalidSignature();
usedNonces[nonce] = true;
_mint(msg.sender, 1);
(bool success, ) = payable(fundsReceiver).call{value: msg.value}("");
if (!success) revert TransferFailed();
}
function mintBatch(
uint8 quantity,
bytes32 nonce,
bytes calldata signature
) external payable {
if (!mintActive) revert MintNotActive();
if (quantity == 0 || quantity > MAX_BATCH) revert InvalidQuantity();
if (_totalMinted() + quantity > MAX_SUPPLY) revert MaxSupplyReached();
if (_numberMinted(msg.sender) + quantity > MAX_PER_WALLET) revert WalletLimitReached();
if (msg.value < mintPrice * quantity) revert InsufficientPayment();
if (usedNonces[nonce]) revert NonceAlreadyUsed();
bytes32 messageHash = keccak256(
abi.encodePacked(msg.sender, nonce, quantity, address(this), block.chainid)
);
bytes32 ethSignedHash = messageHash.toEthSignedMessageHash();
if (ethSignedHash.recover(signature) != signer) revert InvalidSignature();
usedNonces[nonce] = true;
_mint(msg.sender, quantity);
(bool success, ) = payable(fundsReceiver).call{value: msg.value}("");
if (!success) revert TransferFailed();
}
function totalMinted() external view returns (uint256) {
return _totalMinted();
}
function numberMinted(address wallet) external view returns (uint256) {
return _numberMinted(wallet);
}
function _startTokenId() internal pure override returns (uint256) {
return 1;
}
function _baseURI() internal view override returns (string memory) {
return _baseTokenURI;
}
function tokenURI(uint256 tokenId) public view override returns (string memory) {
if (!_exists(tokenId)) revert URIQueryForNonexistentToken();
string memory base = _baseURI();
return bytes(base).length > 0
? string(abi.encodePacked(base, _toString(tokenId)))
: "";
}
function contractURI() external view returns (string memory) {
return _contractMetadataURI;
}
function setBaseURI(string calldata baseURI) external onlyOwner {
_baseTokenURI = baseURI;
}
function setContractURI(string calldata uri) external onlyOwner {
_contractMetadataURI = uri;
}
function setSigner(address _signer) external onlyOwner {
signer = _signer;
}
function setMintPrice(uint256 _price) external onlyOwner {
mintPrice = _price;
}
function setMintActive(bool _active) external onlyOwner {
mintActive = _active;
}
function setFundsReceiver(address _fundsReceiver) external onlyOwner {
fundsReceiver = _fundsReceiver;
}
function withdraw() external onlyOwner {
(bool success, ) = payable(fundsReceiver).call{value: address(this).balance}("");
if (!success) revert TransferFailed();
}
// ─── ICreatorToken (LimitBreak) — enforces royalties via marketplace whitelist ─
function getTransferValidator() external view returns (address) {
return _transferValidator;
}
function setTransferValidator(address validator) external onlyOwner {
address old = _transferValidator;
_transferValidator = validator;
emit TransferValidatorUpdated(old, validator);
}
function getTransferValidationFunction() external pure returns (bytes4 functionSignature, bool isViewFunction) {
// 0xcaee23ea = validateTransfer(address,address,address,uint256)
return (0xcaee23ea, false);
}
function _beforeTokenTransfers(
address from,
address to,
uint256 startTokenId,
uint256 quantity
) internal virtual override {
super._beforeTokenTransfers(from, to, startTokenId, quantity);
address v = _transferValidator;
// Skip on mint (from == 0) so bootstrapping and initial mints are never gated.
if (v != address(0) && from != address(0)) {
for (uint256 i = 0; i < quantity; i++) {
ITransferValidator(v).validateTransfer(msg.sender, from, to, startTokenId + i);
}
}
}
function supportsInterface(
bytes4 interfaceId
) public view override(ERC721A, ERC2981) returns (bool) {
return interfaceId == 0xad0d7f6c // ICreatorToken
|| interfaceId == 0xe8a3d485 // contractURI()
|| ERC721A.supportsInterface(interfaceId)
|| ERC2981.supportsInterface(interfaceId);
}
}