diff --git a/34_ERC721/Address.sol b/34_ERC721/Address.sol deleted file mode 100644 index 0861a44c..00000000 --- a/34_ERC721/Address.sol +++ /dev/null @@ -1,14 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.1; - -// Address库 -library Address { - // 利用extcodesize判断一个地址是否为合约地址 - function isContract(address account) internal view returns (bool) { - uint size; - assembly { - size := extcodesize(account) - } - return size > 0; - } -} diff --git a/34_ERC721/ERC721.sol b/34_ERC721/ERC721.sol index 82492819..d43b3351 100644 --- a/34_ERC721/ERC721.sol +++ b/34_ERC721/ERC721.sol @@ -6,11 +6,9 @@ import "./IERC165.sol"; import "./IERC721.sol"; import "./IERC721Receiver.sol"; import "./IERC721Metadata.sol"; -import "./Address.sol"; import "./String.sol"; contract ERC721 is IERC721, IERC721Metadata{ - using Address for address; // 使用Address库,用isContract来判断地址是否为合约 using Strings for uint256; // 使用String库, // Token名称 @@ -26,6 +24,9 @@ contract ERC721 is IERC721, IERC721Metadata{ // owner地址。到operator地址 的批量授权映射 mapping(address => mapping(address => bool)) private _operatorApprovals; + // 错误 无效的接收者 + error ERC721InvalidReceiver(address receiver); + /** * 构造函数,初始化`name` 和`symbol` . */ @@ -165,7 +166,7 @@ contract ERC721 is IERC721, IERC721Metadata{ bytes memory _data ) private { _transfer(owner, from, to, tokenId); - require(_checkOnERC721Received(from, to, tokenId, _data), "not ERC721Receiver"); + _checkOnERC721Received(from, to, tokenId, _data); } /** @@ -225,22 +226,22 @@ contract ERC721 is IERC721, IERC721Metadata{ } // _checkOnERC721Received:函数,用于在 to 为合约的时候调用IERC721Receiver-onERC721Received, 以防 tokenId 被不小心转入黑洞。 - function _checkOnERC721Received( - address from, - address to, - uint tokenId, - bytes memory _data - ) private returns (bool) { - if (to.isContract()) { - return - IERC721Receiver(to).onERC721Received( - msg.sender, - from, - tokenId, - _data - ) == IERC721Receiver.onERC721Received.selector; - } else { - return true; + function _checkOnERC721Received(address from, address to, uint256 tokenId, bytes memory data) private { + if (to.code.length > 0) { + try IERC721Receiver(to).onERC721Received(msg.sender, 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)) + } + } + } } } diff --git a/34_ERC721/readme.md b/34_ERC721/readme.md index 54b960dd..49d07071 100644 --- a/34_ERC721/readme.md +++ b/34_ERC721/readme.md @@ -156,7 +156,7 @@ interface IERC721Receiver { uint tokenId, bytes memory _data ) private returns (bool) { - if (to.isContract()) { + if (to.code.length > 0) { return IERC721Receiver(to).onERC721Received( msg.sender, @@ -199,11 +199,9 @@ import "./IERC165.sol"; import "./IERC721.sol"; import "./IERC721Receiver.sol"; import "./IERC721Metadata.sol"; -import "./Address.sol"; import "./String.sol"; contract ERC721 is IERC721, IERC721Metadata{ - using Address for address; // 使用Address库,用isContract来判断地址是否为合约 using Strings for uint256; // 使用String库, // Token名称 @@ -219,6 +217,9 @@ contract ERC721 is IERC721, IERC721Metadata{ // owner地址。到operator地址 的批量授权映射 mapping(address => mapping(address => bool)) private _operatorApprovals; + // 错误 无效的接收者 + error ERC721InvalidReceiver(address receiver); + /** * 构造函数,初始化`name` 和`symbol` . */ @@ -294,7 +295,7 @@ contract ERC721 is IERC721, IERC721Metadata{ _approve(owner, to, tokenId); } - // 查询 spender地址是否可以使用tokenId(他是owner或被授权地址)。 + // 查询 spender地址是否可以使用tokenId(需要是owner或被授权地址) function _isApprovedOrOwner( address owner, address spender, @@ -358,7 +359,7 @@ contract ERC721 is IERC721, IERC721Metadata{ bytes memory _data ) private { _transfer(owner, from, to, tokenId); - require(_checkOnERC721Received(from, to, tokenId, _data), "not ERC721Receiver"); + _checkOnERC721Received(from, to, tokenId, _data); } /** @@ -418,22 +419,22 @@ contract ERC721 is IERC721, IERC721Metadata{ } // _checkOnERC721Received:函数,用于在 to 为合约的时候调用IERC721Receiver-onERC721Received, 以防 tokenId 被不小心转入黑洞。 - function _checkOnERC721Received( - address from, - address to, - uint tokenId, - bytes memory _data - ) private returns (bool) { - if (to.isContract()) { - return - IERC721Receiver(to).onERC721Received( - msg.sender, - from, - tokenId, - _data - ) == IERC721Receiver.onERC721Received.selector; - } else { - return true; + function _checkOnERC721Received(address from, address to, uint256 tokenId, bytes memory data) private { + if (to.code.length > 0) { + try IERC721Receiver(to).onERC721Received(msg.sender, 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)) + } + } + } } } @@ -455,6 +456,7 @@ contract ERC721 is IERC721, IERC721Metadata{ return ""; } } + ``` ## 写一个免费铸造的APE