@@ -155,9 +155,16 @@ abstract contract ERC721 is Context, ERC165, IERC721, IERC721Metadata, IERC721Er
155155 if (to == address (0 )) {
156156 revert ERC721InvalidReceiver (address (0 ));
157157 }
158- address owner = _update (to, tokenId, _constraintApprovedOrOwner);
159- if (owner != from) {
160- revert ERC721IncorrectOwner (from, tokenId, owner);
158+
159+ _checkApproved (from, _msgSender (), tokenId);
160+ address previousOwner = _update (to, tokenId);
161+
162+ if (previousOwner != from) {
163+ if (previousOwner == address (0 )) {
164+ revert ERC721NonexistentToken (tokenId);
165+ } else {
166+ revert ERC721IncorrectOwner (from, tokenId, previousOwner);
167+ }
161168 }
162169 }
163170
@@ -223,17 +230,42 @@ abstract contract ERC721 is Context, ERC165, IERC721, IERC721Metadata, IERC721Er
223230 }
224231
225232 /**
226- * @dev Returns whether `spender` is allowed to manage `tokenId`.
233+ * @dev Returns whether `spender` is allowed to manage `tokenId` owned by `owner` .
227234 *
228235 * Requirements:
229236 *
230237 * - `tokenId` must exist.
231238 */
232- function _isApprovedOrOwner (address spender , uint256 tokenId ) internal view virtual returns (bool ) {
233- address owner = ownerOf (tokenId);
239+ function _isApprovedOrOwner (address owner , address spender , uint256 tokenId ) internal view virtual returns (bool ) {
234240 return (spender == owner || isApprovedForAll (owner, spender) || getApproved (tokenId) == spender);
235241 }
236242
243+ /**
244+ * @dev Alternative version of {_isApprovedOrOwner} that will fetch the owner from storage.
245+ * This is not virtual, overrides should be performed on the other version of {_isApprovedOrOwner}
246+ */
247+ function _isApprovedOrOwner (address spender , uint256 tokenId ) internal view returns (bool ) {
248+ return _isApprovedOrOwner (_ownerOf (tokenId), spender, tokenId);
249+ }
250+
251+ /**
252+ * @dev Check that spender is approved or owner, and revert if that is not the case.
253+ *
254+ * NOTE: If the approval is correct, the owner is not checked. If the `owner` parameter is not directly comming
255+ * from a call to `_ownerOf`, it should be checked. {_update} returns the previousOwner, which should match the
256+ * `owner` for this function. See {transferFrom}.
257+ */
258+ function _checkApproved (address owner , address spender , uint256 tokenId ) internal view virtual {
259+ if (! _isApprovedOrOwner (owner, spender, tokenId)) {
260+ address actualOwner = _ownerOf (tokenId);
261+ if (owner == actualOwner) {
262+ revert ERC721InsufficientApproval (spender, tokenId);
263+ } else {
264+ revert ERC721IncorrectOwner (owner, tokenId, actualOwner);
265+ }
266+ }
267+ }
268+
237269 /**
238270 * @dev Safely mints `tokenId` and transfers it to `to`.
239271 *
@@ -270,15 +302,9 @@ abstract contract ERC721 is Context, ERC165, IERC721, IERC721Metadata, IERC721Er
270302 *
271303 * Emits a {Transfer} event.
272304 */
273- function _update (
274- address to ,
275- uint256 tokenId ,
276- function (address , address , uint256 ) view constraints
277- ) internal virtual returns (address ) {
305+ function _update (address to , uint256 tokenId ) internal virtual returns (address previousOwner ) {
278306 address from = _ownerOf (tokenId);
279307
280- constraints (from, to, tokenId);
281-
282308 if (from != address (0 )) {
283309 delete _tokenApprovals[tokenId];
284310 unchecked {
@@ -315,7 +341,10 @@ abstract contract ERC721 is Context, ERC165, IERC721, IERC721Metadata, IERC721Er
315341 if (to == address (0 )) {
316342 revert ERC721InvalidReceiver (address (0 ));
317343 }
318- _update (to, tokenId, _constraintNotMinted);
344+ address previousOwner = _update (to, tokenId);
345+ if (previousOwner != address (0 )) {
346+ revert ERC721InvalidSender (address (0 ));
347+ }
319348 }
320349
321350 /**
@@ -330,7 +359,10 @@ abstract contract ERC721 is Context, ERC165, IERC721, IERC721Metadata, IERC721Er
330359 * Emits a {Transfer} event.
331360 */
332361 function _burn (uint256 tokenId ) internal {
333- _update (address (0 ), tokenId, _constraintMinted);
362+ address previousOwner = _update (address (0 ), tokenId);
363+ if (previousOwner == address (0 )) {
364+ revert ERC721NonexistentToken (tokenId);
365+ }
334366 }
335367
336368 /**
@@ -348,9 +380,9 @@ abstract contract ERC721 is Context, ERC165, IERC721, IERC721Metadata, IERC721Er
348380 if (to == address (0 )) {
349381 revert ERC721InvalidReceiver (address (0 ));
350382 }
351- address owner = _update (to, tokenId, _constraintMinted );
352- if (owner != from) {
353- revert ERC721IncorrectOwner (from, tokenId, owner );
383+ address previousOwner = _update (to, tokenId);
384+ if (previousOwner != from) {
385+ revert ERC721IncorrectOwner (from, tokenId, previousOwner );
354386 }
355387 }
356388
@@ -386,34 +418,6 @@ abstract contract ERC721 is Context, ERC165, IERC721, IERC721Metadata, IERC721Er
386418 }
387419 }
388420
389- /**
390- * @dev Constraint: revert if token is already minted
391- */
392- function _constraintNotMinted (address from , address , uint256 ) internal pure {
393- if (from != address (0 )) {
394- revert ERC721InvalidSender (address (0 ));
395- }
396- }
397-
398- /**
399- * @dev Constraint: revert if token is not yet minted
400- */
401- function _constraintMinted (address from , address , uint256 tokenId ) internal pure {
402- if (from == address (0 )) {
403- revert ERC721NonexistentToken (tokenId);
404- }
405- }
406-
407- /**
408- * @dev Constraint: check that the caller is the current owner, or approved by the current owner
409- */
410- function _constraintApprovedOrOwner (address owner , address , uint256 tokenId ) internal view {
411- address spender = _msgSender ();
412- if (spender != owner && ! isApprovedForAll (owner, spender) && getApproved (tokenId) != spender) {
413- revert ERC721InsufficientApproval (_msgSender (), tokenId);
414- }
415- }
416-
417421 /**
418422 * @dev Private function to invoke {IERC721Receiver-onERC721Received} on a target address.
419423 * The call is not executed if the target address is not a contract.
0 commit comments