// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;
contract SimpleNFT {
string public name;
string public symbol;
// Token ID counter
uint256 private _tokenIdCounter;
// Mapping from token ID to owner address
mapping(uint256 => address) private _owners;
// Mapping from owner to number of owned tokens
mapping(address => uint256) private _balances;
// Mapping from token ID to approved address
mapping(uint256 => address) private _tokenApprovals;
// Mapping from owner to operator approvals
mapping(address => mapping(address => bool)) private _operatorApprovals;
// Mapping from token ID to token URI
mapping(uint256 => string) private _tokenURIs;
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);
constructor(string memory _name, string memory _symbol) {
name = _name;
symbol = _symbol;
_tokenIdCounter = 0;
}
function mint(address to, string memory tokenURI) public returns (uint256) {
uint256 tokenId = _tokenIdCounter;
_tokenIdCounter++;
_owners[tokenId] = to;
_balances[to] += 1;
_tokenURIs[tokenId] = tokenURI;
emit Transfer(address(0), to, tokenId);
return tokenId;
}
function balanceOf(address owner) public view returns (uint256) {
require(owner != address(0), "ERC721: balance query for the zero address");
return _balances[owner];
}
function ownerOf(uint256 tokenId) public view returns (address) {
address owner = _owners[tokenId];
require(owner != address(0), "ERC721: owner query for nonexistent token");
return owner;
}
function tokenURI(uint256 tokenId) public view returns (string memory) {
require(_owners[tokenId] != address(0), "ERC721: URI query for nonexistent token");
return _tokenURIs[tokenId];
}
function approve(address to, uint256 tokenId) public {
address owner = _owners[tokenId];
require(to != owner, "ERC721: approval to current owner");
require(msg.sender == owner || isApprovedForAll(owner, msg.sender),
"ERC721: approve caller is not owner nor approved for all");
_tokenApprovals[tokenId] = to;
emit Approval(owner, to, tokenId);
}
function getApproved(uint256 tokenId) public view returns (address) {
require(_owners[tokenId] != address(0), "ERC721: approved query for nonexistent token");
return _tokenApprovals[tokenId];
}
function setApprovalForAll(address operator, bool approved) public {
require(operator != msg.sender, "ERC721: approve to caller");
_operatorApprovals[msg.sender][operator] = approved;
emit ApprovalForAll(msg.sender, operator, approved);
}
function isApprovedForAll(address owner, address operator) public view returns (bool) {
return _operatorApprovals[owner][operator];
}
function transferFrom(address from, address to, uint256 tokenId) public {
require(_isApprovedOrOwner(msg.sender, tokenId), "ERC721: transfer caller is not owner nor approved");
_transfer(from, to, tokenId);
}
function safeTransferFrom(address from, address to, uint256 tokenId, bytes memory _data) public {
require(_isApprovedOrOwner(msg.sender, tokenId), "ERC721: transfer caller is not owner nor approved");
_transfer(from, to, tokenId);
// We don't check for ERC721 receiver interface in this simplified contract
}
function safeTransferFrom(address from, address to, uint256 tokenId) public {
safeTransferFrom(from, to, tokenId, "");
}
function _isApprovedOrOwner(address spender, uint256 tokenId) internal view returns (bool) {
address owner = _owners[tokenId];
require(owner != address(0), "ERC721: operator query for nonexistent token");
return (spender == owner || getApproved(tokenId) == spender || isApprovedForAll(owner, spender));
}
function _transfer(address from, address to, uint256 tokenId) internal {
require(_owners[tokenId] == from, "ERC721: transfer of token that is not own");
require(to != address(0), "ERC721: transfer to the zero address");
// Clear approvals
_tokenApprovals[tokenId] = address(0);
// Update balances
_balances[from] -= 1;
_balances[to] += 1;
// Update ownership
_owners[tokenId] = to;
emit Transfer(from, to, tokenId);
}
}