diff --git a/src/token/erc721/ERC721.sol b/src/token/erc721/ERC721.sol index 9c69324..a356f3e 100644 --- a/src/token/erc721/ERC721.sol +++ b/src/token/erc721/ERC721.sol @@ -153,6 +153,14 @@ contract ERC721 is IERC721 { INTERNAL CONSTANT FUNCTIONS //////////////////////////////////////////////////////////////////////////*/ + /// @notice Returns whether `spender` is allowed to manage `tokenId`. + /// @param spender The address to make the check. + /// @param tokenId The identifier for the NFT. + function isApprovedOrOwner(address spender, uint256 tokenId) internal view virtual returns (bool) { + address owner = owners[tokenId]; + return (spender == owner || isApprovedForAll(owner, spender) || getApproved(tokenId) == spender); + } + /// @dev Internal function to invoke {IERC721Receiver-onERC721Received} on a target address. /// The call is not executed if the target address is not a contract. /// @param from The address that represents the previous owner of the given `tokenId`. @@ -224,8 +232,8 @@ contract ERC721 is IERC721 { /// /// Requirements: /// - /// - `tokenId` must not exist. /// - `to` cannot be the zero address. + /// - `tokenId` must not exist. function mintInternal(address to, uint256 tokenId) internal virtual { if (to == address(0)) { revert ERC721__MintZeroAddress(); @@ -250,9 +258,9 @@ contract ERC721 is IERC721 { /// /// Requirements: /// - /// - `tokenId` must not exist. /// - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, /// which is called upon a safe transfer. + /// - `tokenId` must not exist. function safeMintInternal(address to, uint256 tokenId) internal virtual { safeMintInternal(to, tokenId, ""); } @@ -303,13 +311,18 @@ contract ERC721 is IERC721 { address to, uint256 tokenId ) internal virtual { + // Checks: the `from` is not the zero address. if (from != ownerOf(tokenId)) { revert ERC721__TransferInvalidFrom(from); } + + // Checks: the `to` is not the zero address. if (to == address(0)) { revert ERC721__TransferToZeroAddress(); } - if (msg.sender != from && !isApprovedForAll(from, msg.sender) && getApproved(tokenId) != msg.sender) { + + // Checks: the caller of the function is authorized to make the transfer. + if (!isApprovedOrOwner(msg.sender, tokenId)) { revert ERC721__UnauthorizedSender(msg.sender); } diff --git a/test/unit/token/erc721/ERC721UnitTest.t.sol b/test/unit/token/erc721/ERC721UnitTest.t.sol index 255fa36..fed7b34 100644 --- a/test/unit/token/erc721/ERC721UnitTest.t.sol +++ b/test/unit/token/erc721/ERC721UnitTest.t.sol @@ -116,7 +116,7 @@ contract ERC721Recipient is IERC721Receiver { } } -// A contract with no `onERC721Received` method implementation that will be +// A contract with no `onERC721Received` method implementation that will be // used for the `safeMint` and `safeTransferFrom` failing tests. contract NonERC721ReceiverImplementer {