diff --git a/.gas-snapshot b/.gas-snapshot index 1e54ee4..14862d8 100644 --- a/.gas-snapshot +++ b/.gas-snapshot @@ -1,10 +1,10 @@ -DN404Test:testBurnOnTransfer(uint32,address,address) (runs: 256, μ: 277323, ~: 279111) -DN404Test:testInitialize(uint32,address) (runs: 256, μ: 97313, ~: 112344) -DN404Test:testMintOnTransfer(uint32,address,address) (runs: 256, μ: 254835, ~: 256235) -DN404Test:testNameAndSymbol(string,string) (runs: 256, μ: 206378, ~: 206719) -DN404Test:testRegisterAndResolveAlias(address,address) (runs: 256, μ: 120543, ~: 120621) +DN404Test:testBurnOnTransfer(uint32,address,address) (runs: 256, μ: 277814, ~: 279525) +DN404Test:testInitialize(uint32,address) (runs: 256, μ: 98557, ~: 112410) +DN404Test:testMintOnTransfer(uint32,address,address) (runs: 256, μ: 255465, ~: 256709) +DN404Test:testNameAndSymbol(string,string) (runs: 256, μ: 206444, ~: 206785) +DN404Test:testRegisterAndResolveAlias(address,address) (runs: 256, μ: 120621, ~: 120621) DN404Test:testSetAndGetOperatorApprovals(address,address,bool) (runs: 256, μ: 129145, ~: 120210) -DN404Test:testTokenURI(string,uint256) (runs: 256, μ: 157275, ~: 134976) -DN404Test:test__codesize() (gas: 20274) +DN404Test:testTokenURI(string,uint256) (runs: 256, μ: 157297, ~: 134998) +DN404Test:test__codesize() (gas: 20772) SoladyTest:test__codesize() (gas: 1075) TestPlus:test__codesize() (gas: 398) \ No newline at end of file diff --git a/src/DN404.sol b/src/DN404.sol index 26354a1..a1a2884 100644 --- a/src/DN404.sol +++ b/src/DN404.sol @@ -139,6 +139,10 @@ abstract contract DN404 { return true; } + function transfer(address to, uint256 amount) public virtual returns (bool) { + return _transfer(msg.sender, to, amount); + } + function transferFrom(address from, address to, uint256 amount) external virtual { DN404Storage storage $ = _getDN404Storage(); @@ -234,10 +238,6 @@ abstract contract DN404 { return addressAlias; } - function transfer(address to, uint256 amount) public virtual returns (bool) { - return _transfer(msg.sender, to, amount); - } - function _transfer(address from, address to, uint256 amount) internal returns (bool) { if (to == address(0)) revert TransferToZeroAddress(); @@ -322,6 +322,25 @@ abstract contract DN404 { } } + function _ownerAt(uint256 id) internal view virtual returns (address result) { + DN404Storage storage $ = _getDN404Storage(); + result = $.aliasToAddress[$.ownerships.get(id)]; + } + + function _ownerOf(uint256 id) internal view virtual returns (address result) { + if (!_exists(id)) revert TokenDoesNotExist(); + result = _ownerAt(id); + } + + function _exists(uint256 id) internal view virtual returns (bool result) { + result = _ownerAt(id) != address(0); + } + + function _getApproved(uint256 id) internal view returns (address result) { + if (!_exists(id)) revert TokenDoesNotExist(); + result = _getDN404Storage().tokenApprovals[id]; + } + function sisterERC721() public view returns (address sister) { return _getDN404Storage().sisterERC721; } @@ -379,12 +398,10 @@ abstract contract DN404 { if (msg.data.length < 0x24) revert(); uint256 id = _calldataload(0x04); - address owner = $.aliasToAddress[$.ownerships.get(id)]; - if (owner == address(0)) revert TokenDoesNotExist(); - _return(uint160(owner)); + _return(uint160(_ownerOf(id))); } - // `_transferFromNFT(address,address,uint256,address)`. + // `transferFromNFT(address,address,uint256,address)`. if (fnSelector == 0xe5eb36c8) { if (msg.sender != $.sisterERC721) revert Unauthorized(); if (msg.data.length < 0x84) revert(); @@ -426,10 +443,8 @@ abstract contract DN404 { if (msg.data.length < 0x24) revert(); uint256 id = _calldataload(0x04); - address owner = $.aliasToAddress[$.ownerships.get(id)]; - if (owner == address(0)) revert TokenDoesNotExist(); - _return(uint160($.tokenApprovals[id])); + _return(uint160(_getApproved(id))); } // `implementsDN404()`. if (fnSelector == 0xb7a94eb8) { diff --git a/src/DN404NonFungibleShadow.sol b/src/DN404NonFungibleShadow.sol index 47fc0a3..4c0248e 100644 --- a/src/DN404NonFungibleShadow.sol +++ b/src/DN404NonFungibleShadow.sol @@ -282,6 +282,22 @@ contract DN404NonFungibleShadow { if (_hasCode(to)) _checkOnERC721Received(from, to, id, data); } + /// @dev Returns true if this contract implements the interface defined by + /// `interfaceId`. See the corresponding + /// [EIP section](https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified) + /// to learn more about how these ids are created. + /// + /// This function call must use less than 30000 gas. + function supportsInterface(bytes4 interfaceId) public view virtual returns (bool) { + // The interface IDs are constants representing the first 4 bytes + // of the XOR of all function selectors in the interface. + // See: [ERC165](https://eips.ethereum.org/EIPS/eip-165) + // (e.g. `bytes4(i.functionA.selector ^ i.functionB.selector ^ ...)`) + return interfaceId == 0x01ffc9a7 // ERC165 interface ID for ERC165. + || interfaceId == 0x80ac58cd // ERC165 interface ID for ERC721. + || interfaceId == 0x5b5e139f; // ERC165 interface ID for ERC721Metadata. + } + /// @dev Returns if `a` has bytecode of non-zero length. function _hasCode(address a) private view returns (bool result) { /// @solidity memory-safe-assembly