-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathERC-721.sol
More file actions
158 lines (130 loc) · 5.76 KB
/
ERC-721.sol
File metadata and controls
158 lines (130 loc) · 5.76 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;
interface IERC721Receiver {
function onERC721Received(
address operator,
address from,
uint256 tokenId,
bytes calldata data
) external returns (bytes4);
}
contract MyERC721 {
// ERC721 storage
mapping(uint256 => address) private _owners;
mapping(address => uint256) private _balances;
mapping(uint256 => address) private _tokenApprovals;
mapping(address => mapping(address => bool)) private _operatorApprovals;
// Ownership
address public owner;
modifier onlyOwner() { require(msg.sender == owner, "Not owner"); _; }
// Metadata
string private _name = "MyNFT";
string private _symbol = "MNFT";
string private _baseTokenURI = "https://my-nft-api.com/metadata/";
// Events
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 sets deployer as owner
constructor() {
owner = msg.sender;
}
// ERC721 Core
function balanceOf(address owner_) public view returns (uint256) {
require(owner_ != address(0), "Zero address");
return _balances[owner_];
}
function ownerOf(uint256 tokenId) public view returns (address) {
address tokenOwner = _owners[tokenId];
require(tokenOwner != address(0), "Nonexistent token");
return tokenOwner;
}
function approve(address to, uint256 tokenId) public {
address tokenOwner = ownerOf(tokenId);
require(msg.sender == tokenOwner || isApprovedForAll(tokenOwner, msg.sender), "Not owner nor approved");
require(to != tokenOwner, "Cannot approve self");
_tokenApprovals[tokenId] = to;
emit Approval(tokenOwner, to, tokenId);
}
function getApproved(uint256 tokenId) public view returns (address) {
require(_owners[tokenId] != address(0), "Nonexistent token");
return _tokenApprovals[tokenId];
}
function setApprovalForAll(address operator, bool approved) public {
require(operator != msg.sender, "Cannot approve self");
_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];
}
// ERC721 transfer
function transferFrom(address from, address to, uint256 tokenId) public {
_transfer(from, to, tokenId);
}
function safeTransferFrom(address from, address to, uint256 tokenId) public {
safeTransferFrom(from, to, tokenId, "");
}
function safeTransferFrom(address from, address to, uint256 tokenId, bytes memory data) public {
_transfer(from, to, tokenId);
require(_checkOnERC721Received(msg.sender, from, to, tokenId, data), "Non ERC721Receiver implementer");
}
function _transfer(address from, address to, uint256 tokenId) internal {
require(ownerOf(tokenId) == from, "Not token owner");
require(msg.sender == from || getApproved(tokenId) == msg.sender || isApprovedForAll(from, msg.sender), "Not owner nor approved");
require(to != address(0), "Zero address");
_owners[tokenId] = to;
_balances[from] -= 1;
_balances[to] += 1;
_tokenApprovals[tokenId] = address(0);
emit Transfer(from, to, tokenId);
}
function _checkOnERC721Received(address operator, address from, address to, uint256 tokenId, bytes memory data) internal returns (bool) {
if (to.code.length == 0) return true;
try IERC721Receiver(to).onERC721Received(operator, from, tokenId, data) returns (bytes4 retval) {
return retval == IERC721Receiver.onERC721Received.selector;
} catch {
return false;
}
}
// Mint & Burn
function mint(address to, uint256 tokenId) external onlyOwner {
require(to != address(0), "Zero address");
require(_owners[tokenId] == address(0), "Token exists");
_owners[tokenId] = to;
_balances[to] += 1;
emit Transfer(address(0), to, tokenId);
}
function burn(uint256 tokenId) external {
address tokenOwner = ownerOf(tokenId);
require(msg.sender == tokenOwner || isApprovedForAll(tokenOwner, msg.sender), "Not owner nor approved");
_balances[tokenOwner] -= 1;
delete _owners[tokenId];
delete _tokenApprovals[tokenId];
emit Transfer(tokenOwner, address(0), tokenId);
}
// Metadata
function name() external view returns (string memory) { return _name; }
function symbol() external view returns (string memory) { return _symbol; }
function setBaseURI(string memory newURI) external onlyOwner { _baseTokenURI = newURI; }
function tokenURI(uint256 tokenId) external view returns (string memory) {
require(_owners[tokenId] != address(0), "Nonexistent token");
return string(abi.encodePacked(_baseTokenURI, _uint2str(tokenId), ".json"));
}
// ERC165 support
function supportsInterface(bytes4 interfaceId) external pure returns (bool) {
return interfaceId == 0x80ac58cd || interfaceId == 0x01ffc9a7;
}
// Helper: uint -> string
function _uint2str(uint256 _i) internal pure returns (string memory str) {
if (_i == 0) return "0";
uint256 temp = _i;
uint256 len;
while (temp != 0) { len++; temp /= 10; }
bytes memory bstr = new bytes(len);
uint256 k = len - 1;
temp = _i;
while (temp != 0) { bstr[k--] = bytes1(uint8(48 + temp % 10)); temp /= 10; }
str = string(bstr);
}
}