Description:
Non-Fungible Token (NFT) contract following ERC721 standard.
Blockchain: Ethereum
Source Code: View Code On The Blockchain
Solidity Source Code:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
/// @title ShleepyNFT — single-file ERC721 with MINTER_ROLE + per-token URI (no imports)
/// @notice Minimal ERC721 + Metadata + ERC165. Roles: owner + MINTER_ROLE. BaseURI + per-token override.
contract ShleepyNFT {
function supportsInterface(bytes4 interfaceId) public pure returns (bool) {
return interfaceId == 0x01ffc9a7 || interfaceId == 0x80ac58cd || interfaceId == 0x5b5e139f;
}
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 OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
event BaseURIChanged(string oldBase, string newBase);
event RoleGranted(bytes32 indexed role, address indexed account);
event RoleRevoked(bytes32 indexed role, address indexed account);
address public owner; modifier onlyOwner() { require(msg.sender == owner, "NOT_OWNER"); _; }
mapping(bytes32 => mapping(address => bool)) private _roles;
function MINTER_ROLE() public pure returns (bytes32) { return keccak256("MINTER_ROLE"); }
modifier onlyMinter() { require(_roles[MINTER_ROLE()][msg.sender] || msg.sender == owner, "NOT_MINTER"); _; }
string public name; string public symbol; string private _baseURI_;
uint256 private _totalSupply; uint256 private _nextId;
mapping(uint256 => address) private _ownerOf; mapping(address => uint256) private _balanceOf;
mapping(uint256 => address) private _tokenApproval; mapping(address => mapping(address => bool)) private _operatorApproval;
mapping(uint256 => string) private _tokenURIs;
constructor(string memory _name, string memory _symbol, string memory baseURI_, address initialOwner) {
require(initialOwner != address(0), "ZERO_OWNER"); name = _name; symbol = _symbol; _baseURI_ = baseURI_;
owner = initialOwner; _nextId = 1; emit OwnershipTransferred(address(0), initialOwner);
}
function grantRole(bytes32 role, address account) external onlyOwner { _roles[role][account] = true; emit RoleGranted(role, account); }
function revokeRole(bytes32 role, address account) external onlyOwner { _roles[role][account] = false; emit RoleRevoked(role, account); }
function hasRole(bytes32 role, address account) external view returns (bool) { return _roles[role][account]; }
function transferOwnership(address newOwner) external onlyOwner { require(newOwner != address(0), "ZERO_OWNER"); emit OwnershipTransferred(owner, newOwner); owner = newOwner; }
function setBaseURI(string calldata newBase) external onlyOwner { emit BaseURIChanged(_baseURI_, newBase); _baseURI_ = newBase; }
function setTokenURI(uint256 tokenId, string calldata uri) external onlyMinter { require(_exists(tokenId), "NO_TOKEN"); require(bytes(_tokenURIs[tokenId]).length == 0, "FROZEN"); _tokenURIs[tokenId] = uri; }
function totalSupply() external view returns (uint256) { return _totalSupply; }
function balanceOf(address account) public view returns (uint256) { require(account != address(0), "ZERO_ADDR"); return _balanceOf[account]; }
function ownerOf(uint256 tokenId) public view returns (address) { address o = _ownerOf[tokenId]; require(o != address(0), "NO_TOKEN"); return o; }
function getApproved(uint256 tokenId) public view returns (address) { require(_exists(tokenId), "NO_TOKEN"); return _tokenApproval[tokenId]; }
function isApprovedForAll(address holder, address operator) public view returns (bool) { return _operatorApproval[holder][operator]; }
function tokenURI(uint256 tokenId) external view returns (string memory) { require(_exists(tokenId), "NO_TOKEN"); bytes memory u = bytes(_tokenURIs[tokenId]); return u.length > 0 ? string(u) : string(abi.encodePacked(_baseURI_, _toString(tokenId))); }
function approve(address spender, uint256 tokenId) external { address o = ownerOf(tokenId); require(spender != o, "APPROVE_SELF"); require(msg.sender == o || isApprovedForAll(o, msg.sender), "NOT_AUTH"); _tokenApproval[tokenId] = spender; emit Approval(o, spender, tokenId); }
function setApprovalForAll(address operator, bool approved) external { require(operator != msg.sender, "APPROVE_SELF"); _operatorApproval[msg.sender][operator] = approved; emit ApprovalForAll(msg.sender, operator, approved); }
function transferFrom(address from, address to, uint256 tokenId) public { require(_isApprovedOrOwner(msg.sender, tokenId), "NOT_AUTH"); require(ownerOf(tokenId) == from, "NOT_OWNER_OF"); require(to != address(0), "ZERO_TO"); _approve(address(0), tokenId); _balanceOf[from] -= 1; _balanceOf[to] += 1; _ownerOf[tokenId] = to; emit Transfer(from, to, tokenId); }
function safeTransferFrom(address from, address to, uint256 tokenId) external { transferFrom(from, to, tokenId); }
function safeTransferFrom(address from, address to, uint256 tokenId, bytes calldata) external { transferFrom(from, to, tokenId); }
function mint(address to) external onlyMinter returns (uint256 tokenId) { require(to != address(0), "ZERO_TO"); tokenId = _nextId++; _mintUnchecked(to, tokenId); return tokenId; }
function mint(address to, uint256 tokenId) external onlyMinter { require(to != address(0), "ZERO_TO"); require(!_exists(tokenId), "ALREADY_MINTED"); _mintUnchecked(to, tokenId); if (tokenId >= _nextId) _nextId = tokenId + 1; }
function _mintUnchecked(address to, uint256 tokenId) internal { _ownerOf[tokenId] = to; _balanceOf[to] += 1; _totalSupply += 1; emit Transfer(address(0), to, tokenId); }
function _approve(address spender, uint256 tokenId) internal { _tokenApproval[tokenId] = spender; emit Approval(ownerOf(tokenId), spender, tokenId); }
function _exists(uint256 tokenId) internal view returns (bool) { return _ownerOf[tokenId] != address(0); }
function _isApprovedOrOwner(address spender, uint256 tokenId) internal view returns (bool) { address o = ownerOf(tokenId); return (spender == o || spender == getApproved(tokenId) || isApprovedForAll(o, spender)); }
function _toString(uint256 value) internal pure returns (string memory) { if (value == 0) return "0"; uint256 temp = value; uint256 digits; while (temp != 0) { digits++; temp /= 10; } bytes memory buffer = new bytes(digits); while (value != 0) { digits -= 1; buffer[digits] = bytes1(uint8(48 + uint256(value % 10))); value /= 10; } return string(buffer); }
}
Submitted on: 2025-09-29 18:31:13
Comments
Log in to comment.
No comments yet.