보통 NFT 컨트랙트는 Klaytnscope에서 NFT라고 표기가 되는 것 같은데 제 컨트랙트는 그렇지 않네요.
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
import "./NFTCore.sol";
import "@openzeppelin/contracts/token/ERC721/IERC721.sol";
contract NFTSampleis is Context, ERC165, IERC721, IERC721Metadata, IERC721Errors {
address private _thisAddr;
NFTCore private _nftCore;
uint256 private _tokenId = 0;
constructor(string memory _name, string memory _symbol, address _nftCoreAddress) {
_thisAddr = address(this);
_nftCore = NFTCore(_nftCoreAddress);
_nftCore.init(_thisAddr, _name, _symbol);
}
function supportsInterface(bytes4 interfaceId) public view virtual override(ERC165, IERC165) returns (bool) {
return
interfaceId == type(IERC721).interfaceId ||
interfaceId == type(IERC721Metadata).interfaceId ||
super.supportsInterface(interfaceId);
}
function balanceOf(address owner) external view returns (uint256 balance) {
return _nftCore.balanceOf(_thisAddr, owner);
}
function ownerOf(uint256 tokenId) external view returns (address owner) {
return _nftCore.ownerOf(_thisAddr, tokenId);
}
function safeTransferFrom(address from, address to, uint256 tokenId, bytes calldata data) external {
_nftCore.safeTransferFrom(_thisAddr, from, to, tokenId, data);
}
function safeTransferFrom(address from, address to, uint256 tokenId) external {
_nftCore.safeTransferFrom(_thisAddr, from, to, tokenId);
}
function transferFrom(address from, address to, uint256 tokenId) external {
_nftCore.transferFrom(_thisAddr, from, to, tokenId);
}
function approve(address to, uint256 tokenId) external {
_nftCore.approve(_thisAddr, to, tokenId);
}
function setApprovalForAll(address operator, bool approved) external {
_nftCore.setApprovalForAll(_thisAddr, operator, approved);
}
function getApproved(uint256 tokenId) external view returns (address operator) {
return _nftCore.getApproved(_thisAddr, tokenId);
}
function isApprovedForAll(address owner, address operator) external view returns (bool) {
return _nftCore.isApprovedForAll(_thisAddr, owner, operator);
}
function mint(address to) public {
_nftCore._mint(_thisAddr, to, _tokenId++);
}
function name() external view returns (string memory) {
return _nftCore.name(_thisAddr);
}
function symbol() external view returns (string memory) {
return _nftCore.symbol(_thisAddr);
}
function tokenURI(uint256 tokenId) external view returns (string memory) {
return _nftCore.tokenURI(_thisAddr,tokenId);
}
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
import "@openzeppelin/contracts/token/ERC721/extensions/ERC721URIStorage.sol";
import "@openzeppelin/contracts/token/ERC721/ERC721.sol";
contract NFTCore is Context, IERC721Errors, ERC165 {
//contract NFTCore is Context, ERC165, IERC721, IERC721Metadata, IERC721Errors {
using Strings for uint256;
mapping (address => string) __name;
mapping (address => string) __symbol;
mapping (address => mapping(uint256 tokenId => address)) __owners;
mapping (address => mapping(address owner => uint256)) __balances;
mapping (address => mapping(uint256 tokenId => address)) __tokenApprovals;
mapping (address => mapping(address owner => mapping(address operator => bool))) __operatorApprovals;
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);
function init(address contractAddr, string memory _name, string memory _symbol) public{
__name[contractAddr] = _name;
__symbol[contractAddr] = _symbol;
}
function balanceOf(address contractAddr, address owner) public view returns (uint256) {
if (owner == address(0)) {
revert ERC721InvalidOwner(address(0));
}
return __balances[contractAddr][owner];
}
function ownerOf(address contractAddr, uint256 tokenId) public view returns (address) {
return _requireOwned(contractAddr, tokenId);
}
function name(address contractAddr) public view returns (string memory) {
return __name[contractAddr];
}
function symbol(address contractAddr) public view returns (string memory) {
return __symbol[contractAddr];
}
function tokenURI(address contractAddr, uint256 tokenId) public view virtual returns (string memory) {
_requireOwned(contractAddr, tokenId);
string memory baseURI = _baseURI(contractAddr);
return bytes(baseURI).length > 0 ? string.concat(baseURI, tokenId.toString()) : "";
}
function _baseURI(address contractAddr) internal view virtual returns (string memory) {
return "";
}
function getApproved(address contractAddr, uint256 tokenId) public view virtual returns (address) {
_requireOwned(contractAddr, tokenId);
return _getApproved(contractAddr, tokenId);
}
function approve(address contractAddr, address to, uint256 tokenId) public virtual {
_approve(contractAddr, to, tokenId, _msgSender());
}
function _approve(address contractAddr, address to, uint256 tokenId, address auth) internal {
_approve(contractAddr, to, tokenId, auth, true);
}
function _approve(address contractAddr, address to, uint256 tokenId, address auth, bool emitEvent) internal virtual {
// Avoid reading the owner unless necessary
if (emitEvent || auth != address(0)) {
address owner = _requireOwned(contractAddr, tokenId);
// We do not use _isAuthorized because single-token approvals should not be able to call approve
if (auth != address(0) && owner != auth && !isApprovedForAll(contractAddr, owner, auth)) {
revert ERC721InvalidApprover(auth);
}
if (emitEvent) {
emit Approval(owner, to, tokenId);
}
}
__tokenApprovals[contractAddr][tokenId] = to;
}
function isApprovedForAll(address contractAddr, address owner, address operator) public view virtual returns (bool) {
return __operatorApprovals[contractAddr][owner][operator];
}
function _requireOwned(address contractAddr, uint256 tokenId) internal view returns (address) {
address owner = _ownerOf(contractAddr, tokenId);
if (owner == address(0)) {
revert ERC721NonexistentToken(tokenId);
}
return owner;
}
function _ownerOf(address contractAddr, uint256 tokenId) internal view virtual returns (address) {
return __owners[contractAddr][tokenId];
}
function transferFrom(address contractAddr, address from, address to, uint256 tokenId) public virtual {
if (to == address(0)) {
revert ERC721InvalidReceiver(address(0));
}
// Setting an "auth" arguments enables the `_isAuthorized` check which verifies that the token exists
// (from != 0). Therefore, it is not needed to verify that the return value is not 0 here.
address previousOwner = _update(contractAddr, to, tokenId, _msgSender());
if (previousOwner != from) {
revert ERC721IncorrectOwner(from, tokenId, previousOwner);
}
}
function safeTransferFrom(address contractAddr, address from, address to, uint256 tokenId) public {
safeTransferFrom(contractAddr, from, to, tokenId, "");
}
function safeTransferFrom(address contractAddr, address from, address to, uint256 tokenId, bytes memory data) public virtual {
transferFrom(contractAddr, from, to, tokenId);
_checkOnERC721Received(contractAddr, from, to, tokenId, data);
}
function _update(address contractAddr, address to, uint256 tokenId, address auth) internal virtual returns (address) {
address from = _ownerOf(contractAddr, tokenId);
// Perform (optional) operator check
if (auth != address(0)) {
_checkAuthorized(contractAddr, from, auth, tokenId);
}
// Execute the update
if (from != address(0)) {
// Clear approval. No need to re-authorize or emit the Approval event
_approve(contractAddr, address(0), tokenId, address(0), false);
unchecked {
__balances[contractAddr][from] -= 1;
}
}
if (to != address(0)) {
unchecked {
__balances[contractAddr][to] += 1;
}
}
__owners[contractAddr][tokenId] = to;
emit Transfer(from, to, tokenId);
return from;
}
function _checkAuthorized(address contractAddr, address owner, address spender, uint256 tokenId) internal view virtual {
if (!_isAuthorized(contractAddr, owner, spender, tokenId)) {
if (owner == address(0)) {
revert ERC721NonexistentToken(tokenId);
} else {
revert ERC721InsufficientApproval(spender, tokenId);
}
}
}
function _isAuthorized(address contractAddr, address owner, address spender, uint256 tokenId) internal view virtual returns (bool) {
return
spender != address(0) &&
(owner == spender || isApprovedForAll(contractAddr, owner, spender) || _getApproved(contractAddr, tokenId) == spender);
}
function _getApproved(address contractAddr, uint256 tokenId) internal view virtual returns (address) {
return __tokenApprovals[contractAddr][tokenId];
}
function setApprovalForAll(address contractAddr,address operator, bool approved) public virtual {
_setApprovalForAll(contractAddr, _msgSender(), operator, approved);
}
function _setApprovalForAll(address contractAddr, address owner, address operator, bool approved) internal virtual {
if (operator == address(0)) {
revert ERC721InvalidOperator(operator);
}
__operatorApprovals[contractAddr][owner][operator] = approved;
emit ApprovalForAll(owner, operator, approved);
}
function _increaseBalance(address contractAddr, address account, uint128 value) internal virtual {
unchecked {
__balances[contractAddr][account] += value;
}
}
function _mint(address contractAddr,address to, uint256 tokenId) public {
if (to == address(0)) {
revert ERC721InvalidReceiver(address(0));
}
address previousOwner = _update(contractAddr, to, tokenId, address(0));
if (previousOwner != address(0)) {
revert ERC721InvalidSender(address(0));
}
}
function _safeMint(address contractAddr,address to, uint256 tokenId) internal {
_safeMint(contractAddr, to, tokenId, "");
}
function _safeMint(address contractAddr, address to, uint256 tokenId, bytes memory data) internal virtual {
_mint(contractAddr, to, tokenId);
_checkOnERC721Received(contractAddr, address(0), to, tokenId, data);
}
function _transfer(address contractAddr, address from, address to, uint256 tokenId) internal {
if (to == address(0)) {
revert ERC721InvalidReceiver(address(0));
}
address previousOwner = _update(contractAddr, to, tokenId, address(0));
if (previousOwner == address(0)) {
revert ERC721NonexistentToken(tokenId);
} else if (previousOwner != from) {
revert ERC721IncorrectOwner(from, tokenId, previousOwner);
}
}
function _safeTransfer(address contractAddr, address from, address to, uint256 tokenId) internal {
_safeTransfer(contractAddr, from, to, tokenId, "");
}
function _safeTransfer(address contractAddr, address from, address to, uint256 tokenId, bytes memory data) internal virtual {
_transfer(contractAddr, from, to, tokenId);
_checkOnERC721Received(contractAddr, from, to, tokenId, data);
}
function _checkOnERC721Received(address contractAddr, address from, address to, uint256 tokenId, bytes memory data) private {
if (to.code.length > 0) {
try IERC721Receiver(to).onERC721Received(_msgSender(), from, tokenId, data) returns (bytes4 retval) {
if (retval != IERC721Receiver.onERC721Received.selector) {
revert ERC721InvalidReceiver(to);
}
} catch (bytes memory reason) {
if (reason.length == 0) {
revert ERC721InvalidReceiver(to);
} else {
/// @solidity memory-safe-assembly
assembly {
revert(add(32, reason), mload(reason))
}
}
}
}
}
function _burn(address contractAddr, uint256 tokenId) internal {
address previousOwner = _update(contractAddr, address(0), tokenId, address(0));
if (previousOwner == address(0)) {
revert ERC721NonexistentToken(tokenId);
}
}
}
저는 NFTCore와 NFTSample이라는 컨트랙트를 만들었습니다.
NFTCore 컨트랙트는 실제 ERC721 인터페이스 구현체 입니다.
NFTSample는 NFTCore의 주소를 받아 NFT 기능은 NFTCore에 의해서 작동됩니다.
제 생각엔 ERC721을 준수하기 때문에 정상적으로 오픈시에서 NFT가 보여야 할 것 같은데 그렇지 않네요.
제 컨트랙트에 문제가 있는 걸까요?