1+ // SPDX-License-Identifier: MIT
2+ // by 0xAA
3+ pragma solidity ^ 0.8.21 ;
4+
5+ import "./IERC165.sol " ;
6+ import "./IERC721.sol " ;
7+ import "./IERC721Receiver.sol " ;
8+ import "./IERC721Metadata.sol " ;
9+ import "./String.sol " ;
10+
11+ contract ERC721 is IERC721 , IERC721Metadata {
12+ using Strings for uint256 ; // Stringsライブラリを使用
13+
14+ // トークン名
15+ string public override name;
16+ // トークンシンボル
17+ string public override symbol;
18+ // tokenId から owner address への所有者マッピング
19+ mapping (uint => address ) private _owners;
20+ // address から保有数量への保有量マッピング
21+ mapping (address => uint ) private _balances;
22+ // tokenID から承認アドレスへの承認マッピング
23+ mapping (uint => address ) private _tokenApprovals;
24+ // ownerアドレス から operatorアドレスへの一括承認マッピング
25+ mapping (address => mapping (address => bool )) private _operatorApprovals;
26+
27+ // エラー 無効な受信者
28+ error ERC721InvalidReceiver (address receiver );
29+
30+ /**
31+ * コンストラクタ、`name` と`symbol` を初期化 .
32+ */
33+ constructor (string memory name_ , string memory symbol_ ) {
34+ name = name_;
35+ symbol = symbol_;
36+ }
37+
38+ // IERC165インターフェースsupportsInterfaceを実装
39+ function supportsInterface (bytes4 interfaceId )
40+ external
41+ pure
42+ override
43+ returns (bool )
44+ {
45+ return
46+ interfaceId == type (IERC721 ).interfaceId ||
47+ interfaceId == type (IERC165 ).interfaceId ||
48+ interfaceId == type (IERC721Metadata ).interfaceId;
49+ }
50+
51+ // IERC721のbalanceOfを実装、_balances変数を使用してownerアドレスのbalanceをクエリ。
52+ function balanceOf (address owner ) external view override returns (uint ) {
53+ require (owner != address (0 ), "owner = zero address " );
54+ return _balances[owner];
55+ }
56+
57+ // IERC721のownerOfを実装、_owners変数を使用してtokenIdのownerをクエリ。
58+ function ownerOf (uint tokenId ) public view override returns (address owner ) {
59+ owner = _owners[tokenId];
60+ require (owner != address (0 ), "token doesn't exist " );
61+ }
62+
63+ // IERC721のisApprovedForAllを実装、_operatorApprovals変数を使用してownerアドレスが保有するNFTをoperatorアドレスに一括承認しているかをクエリ。
64+ function isApprovedForAll (address owner , address operator )
65+ external
66+ view
67+ override
68+ returns (bool )
69+ {
70+ return _operatorApprovals[owner][operator];
71+ }
72+
73+ // IERC721のsetApprovalForAllを実装、保有トークンを全てoperatorアドレスに承認。_setApprovalForAll関数を呼び出し。
74+ function setApprovalForAll (address operator , bool approved ) external override {
75+ _operatorApprovals[msg .sender ][operator] = approved;
76+ emit ApprovalForAll (msg .sender , operator, approved);
77+ }
78+
79+ // IERC721のgetApprovedを実装、_tokenApprovals変数を使用してtokenIdの承認アドレスをクエリ。
80+ function getApproved (uint tokenId ) external view override returns (address ) {
81+ require (_owners[tokenId] != address (0 ), "token doesn't exist " );
82+ return _tokenApprovals[tokenId];
83+ }
84+
85+ // 承認関数。_tokenApprovalsを調整して、to アドレスに tokenId の操作を承認し、同時にApprovalイベントを発行。
86+ function _approve (
87+ address owner ,
88+ address to ,
89+ uint tokenId
90+ ) private {
91+ _tokenApprovals[tokenId] = to;
92+ emit Approval (owner, to, tokenId);
93+ }
94+
95+ // IERC721のapproveを実装、tokenIdを to アドレスに承認。条件:toはownerではなく、msg.senderはownerまたは承認アドレス。_approve関数を呼び出し。
96+ function approve (address to , uint tokenId ) external override {
97+ address owner = _owners[tokenId];
98+ require (
99+ msg .sender == owner || _operatorApprovals[owner][msg .sender ],
100+ "not owner nor approved for all "
101+ );
102+ _approve (owner, to, tokenId);
103+ }
104+
105+ // spenderアドレスがtokenIdを使用できるかをクエリ(ownerまたは承認アドレスである必要がある)
106+ function _isApprovedOrOwner (
107+ address owner ,
108+ address spender ,
109+ uint tokenId
110+ ) private view returns (bool ) {
111+ return (spender == owner ||
112+ _tokenApprovals[tokenId] == spender ||
113+ _operatorApprovals[owner][spender]);
114+ }
115+
116+ /*
117+ * 転送関数。_balancesと_owner変数を調整して tokenId を from から to に転送し、同時にTransferイベントを発行。
118+ * 条件:
119+ * 1. tokenId が from によって所有されている
120+ * 2. to が0アドレスではない
121+ */
122+ function _transfer (
123+ address owner ,
124+ address from ,
125+ address to ,
126+ uint tokenId
127+ ) private {
128+ require (from == owner, "not owner " );
129+ require (to != address (0 ), "transfer to the zero address " );
130+
131+ _approve (owner, address (0 ), tokenId);
132+
133+ _balances[from] -= 1 ;
134+ _balances[to] += 1 ;
135+ _owners[tokenId] = to;
136+
137+ emit Transfer (from, to, tokenId);
138+ }
139+
140+ // IERC721のtransferFromを実装、非安全転送、推奨されません。_transfer関数を呼び出し
141+ function transferFrom (
142+ address from ,
143+ address to ,
144+ uint tokenId
145+ ) external override {
146+ address owner = ownerOf (tokenId);
147+ require (
148+ _isApprovedOrOwner (owner, msg .sender , tokenId),
149+ "not owner nor approved "
150+ );
151+ _transfer (owner, from, to, tokenId);
152+ }
153+
154+ /**
155+ * 安全転送、tokenId トークンを from から to に安全に転送し、コントラクト受信者がERC721プロトコルを理解しているかをチェックしてトークンが永続的にロックされることを防止。_transfer関数と_checkOnERC721Received関数を呼び出し。条件:
156+ * from は0アドレスではない.
157+ * to は0アドレスではない.
158+ * tokenId トークンが存在し、from によって所有されている.
159+ * to がスマートコントラクトの場合、IERC721Receiver-onERC721Receivedをサポートする必要がある.
160+ */
161+ function _safeTransfer (
162+ address owner ,
163+ address from ,
164+ address to ,
165+ uint tokenId ,
166+ bytes memory _data
167+ ) private {
168+ _transfer (owner, from, to, tokenId);
169+ _checkOnERC721Received (from, to, tokenId, _data);
170+ }
171+
172+ /**
173+ * IERC721のsafeTransferFromを実装、安全転送、_safeTransfer関数を呼び出し。
174+ */
175+ function safeTransferFrom (
176+ address from ,
177+ address to ,
178+ uint tokenId ,
179+ bytes memory _data
180+ ) public override {
181+ address owner = ownerOf (tokenId);
182+ require (
183+ _isApprovedOrOwner (owner, msg .sender , tokenId),
184+ "not owner nor approved "
185+ );
186+ _safeTransfer (owner, from, to, tokenId, _data);
187+ }
188+
189+ // safeTransferFromオーバーロード関数
190+ function safeTransferFrom (
191+ address from ,
192+ address to ,
193+ uint tokenId
194+ ) external override {
195+ safeTransferFrom (from, to, tokenId, "" );
196+ }
197+
198+ /**
199+ * ミント関数。_balancesと_owners変数を調整してtokenIdをミントし、to に転送、同時にTransferイベントを発行。ミント関数。_balancesと_owners変数を調整してtokenIdをミントし、to に転送、同時にTransferイベントを発行。
200+ * このmint関数は誰でも呼び出すことができ、実際の使用では開発者が書き直して条件を追加する必要があります。
201+ * 条件:
202+ * 1. tokenIdがまだ存在しない。
203+ * 2. toが0アドレスではない.
204+ */
205+ function _mint (address to , uint tokenId ) internal virtual {
206+ require (to != address (0 ), "mint to zero address " );
207+ require (_owners[tokenId] == address (0 ), "token already minted " );
208+
209+ _balances[to] += 1 ;
210+ _owners[tokenId] = to;
211+
212+ emit Transfer (address (0 ), to, tokenId);
213+ }
214+
215+ // バーン関数、_balancesと_owners変数を調整してtokenIdを破棄し、同時にTransferイベントを発行。条件:tokenIdが存在する。
216+ function _burn (uint tokenId ) internal virtual {
217+ address owner = ownerOf (tokenId);
218+ require (msg .sender == owner, "not owner of token " );
219+
220+ _approve (owner, address (0 ), tokenId);
221+
222+ _balances[owner] -= 1 ;
223+ delete _owners[tokenId];
224+
225+ emit Transfer (owner, address (0 ), tokenId);
226+ }
227+
228+ // _checkOnERC721Received:関数、to がコントラクトの場合にIERC721Receiver-onERC721Receivedを呼び出し、tokenId が誤ってブラックホールに転送されることを防ぐ。
229+ function _checkOnERC721Received (address from , address to , uint256 tokenId , bytes memory data ) private {
230+ if (to.code.length > 0 ) {
231+ try IERC721Receiver (to).onERC721Received (msg .sender , from, tokenId, data) returns (bytes4 retval ) {
232+ if (retval != IERC721Receiver .onERC721Received.selector ) {
233+ revert ERC721InvalidReceiver (to);
234+ }
235+ } catch (bytes memory reason ) {
236+ if (reason.length == 0 ) {
237+ revert ERC721InvalidReceiver (to);
238+ } else {
239+ /// @solidity memory-safe-assembly
240+ assembly {
241+ revert (add (32 , reason), mload (reason))
242+ }
243+ }
244+ }
245+ }
246+ }
247+
248+ /**
249+ * IERC721MetadataのtokenURI関数を実装、metadataをクエリ。
250+ */
251+ function tokenURI (uint256 tokenId ) public view virtual override returns (string memory ) {
252+ require (_owners[tokenId] != address (0 ), "Token Not Exist " );
253+
254+ string memory baseURI = _baseURI ();
255+ return bytes (baseURI).length > 0 ? string (abi.encodePacked (baseURI, tokenId.toString ())) : "" ;
256+ }
257+
258+ /**
259+ * {tokenURI}のBaseURIを計算、tokenURIはbaseURIとtokenIdを連結したもので、開発者が書き直す必要がある。
260+ * BAYCのbaseURIは ipfs://QmeSjSinHpPnmXmspMjwiXyN6zS4E9zccariGR3jxcaWtq/
261+ */
262+ function _baseURI () internal view virtual returns (string memory ) {
263+ return "" ;
264+ }
265+ }
0 commit comments