// SPDX-License-Identifier: UNLICENSED pragma solidity =0.8.18; import "@openzeppelin/contracts-upgradeable/token/ERC721/extensions/ERC721EnumerableUpgradeable.sol"; import "@openzeppelin/contracts/utils/Address.sol"; // temp files which will come from SwapV3 folder after merged to master import "./temp/libraries/ChainId.sol"; import "./temp/interfaces/external/IERC1271.sol"; import "./IERC721PermitUpgradeable.sol"; /** * @notice Implementation of the {IERC721PermitUpgradeable} interface. */ abstract contract ERC721PermitUpgradeable is IERC721PermitUpgradeable, ERC721EnumerableUpgradeable { /** * @dev Gets the current nonce for a token ID and then increments it, returning the original value. */ function _getAndIncrementNonce(uint256 tokenId) internal virtual returns (uint256); /** * @dev The hash of the name used in the permit signature verification. */ bytes32 private nameHash; /** * @dev The hash of the version string used in the permit signature verification. */ bytes32 private versionHash; /** * @notice Initializer for extended contracts. */ function __ERC721PermitUpgradeable_init(string memory name_, string memory symbol_, string memory version_) internal onlyInitializing { __ERC721Enumerable_init_unchained(); __ERC721_init_unchained(name_, symbol_); __ERC721PermitUpgradeable_init_unchained(name_, version_); } /** * @notice Unchained initializer. */ function __ERC721PermitUpgradeable_init_unchained(string memory name_, string memory version_) internal onlyInitializing { nameHash = keccak256(bytes(name_)); versionHash = keccak256(bytes(version_)); } /** * @dev See {IERC165-supportsInterface}. */ function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) { return interfaceId == type(IERC721PermitUpgradeable).interfaceId || super.supportsInterface(interfaceId); } /** * @inheritdoc IERC721PermitUpgradeable */ function DOMAIN_SEPARATOR() public view override returns (bytes32) { return keccak256( abi.encode( // keccak256('EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)') 0x8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f, nameHash, versionHash, ChainId.get(), address(this) ) ); } /** * @inheritdoc IERC721PermitUpgradeable */ bytes32 public constant override PERMIT_TYPEHASH = 0x49ecf333e5b8c95c40fdafc95c1ad136e8914a8fb55e9dc8bb01eaa83a2df9ad; /** * @inheritdoc IERC721PermitUpgradeable */ function permit(address spender, uint256 tokenId, uint256 deadline, uint8 v, bytes32 r, bytes32 s) external payable override { require(block.timestamp <= deadline, "Permit expired"); bytes32 digest = keccak256( abi.encodePacked( "\x19\x01", DOMAIN_SEPARATOR(), keccak256(abi.encode(PERMIT_TYPEHASH, spender, tokenId, _getAndIncrementNonce(tokenId), deadline)) ) ); address owner = ownerOf(tokenId); require(spender != owner, "ERC721PermitUpgradeable: approval to current owner"); if (Address.isContract(owner)) { require(IERC1271(owner).isValidSignature(digest, abi.encodePacked(r, s, v)) == 0x1626ba7e, "Unauthorized"); } else { address recoveredAddress = ecrecover(digest, v, r, s); require(recoveredAddress != address(0), "Invalid signature"); require(recoveredAddress == owner, "Unauthorized"); } _approve(spender, tokenId); } }