From 0918f954219199cb6ee5efd29a296c1f412b2888 Mon Sep 17 00:00:00 2001 From: Amir Bandeali Date: Sun, 19 Aug 2018 20:03:59 -0700 Subject: Don't throw if ERC721 token isn't owned --- .../extensions/OrderValidator/OrderValidator.sol | 52 +++++++++++++++++++++- 1 file changed, 51 insertions(+), 1 deletion(-) (limited to 'packages') diff --git a/packages/contracts/src/2.0.0/extensions/OrderValidator/OrderValidator.sol b/packages/contracts/src/2.0.0/extensions/OrderValidator/OrderValidator.sol index dcbbd7fa5..1c9cc6978 100644 --- a/packages/contracts/src/2.0.0/extensions/OrderValidator/OrderValidator.sol +++ b/packages/contracts/src/2.0.0/extensions/OrderValidator/OrderValidator.sol @@ -124,17 +124,67 @@ contract OrderValidator { address assetProxy = EXCHANGE.getAssetProxy(assetProxyId); if (assetProxyId == ERC20_DATA_ID) { + // Query balance balance = IERC20Token(token).balanceOf(target); + + // Query allowance allowance = IERC20Token(token).allowance(target, assetProxy); } else if (assetProxyId == ERC721_DATA_ID) { uint256 tokenId = assetData.readUint256(36); - address owner = IERC721Token(token).ownerOf(tokenId); + + // Query owner of tokenId + address owner = getERC721TokenOwner(token, tokenId); + + // Set balance to 1 if tokenId is owned by target balance = target == owner ? 1 : 0; + + // Check if ERC721Proxy is approved to spend tokenId bool isApproved = IERC721Token(token).isApprovedForAll(target, assetProxy) || IERC721Token(token).getApproved(tokenId) == assetProxy; + + // Set alowance to 1 if ERC721Proxy is approved to spend tokenId allowance = isApproved ? 1 : 0; } else { revert("UNSUPPORTED_ASSET_PROXY"); } return (balance, allowance); } + + /// @dev Calls `token.ownerOf(tokenId)`, but returns a null owner instead of reverting on an unowned token. + /// @param token Address of ERC721 token. + /// @param tokenId The identifier for the specific NFT. + /// @return Owner of tokenId or null address if unowned. + function getERC721TokenOwner(address token, uint256 tokenId) + public + view + returns (address owner) + { + assembly { + // load free memory pointer + let cdStart := mload(64) + + // bytes4(keccak256(ownerOf(uint256))) = 0x6352211e + mstore(cdStart, 0x6352211e00000000000000000000000000000000000000000000000000000000) + mstore(add(cdStart, 4), tokenId) + + // staticcall `ownerOf(tokenId)` + // `ownerOf` will revert if tokenId is not owned + let success := staticcall( + gas, // forward all gas + token, // call token contract + cdStart, // start of calldata + 36, // length of input is 36 bytes + cdStart, // write output over input + 32 // size of output is 32 bytes + ) + + // Success implies that tokenId is owned + // Copy owner from return data if successful + if success { + owner := mload(cdStart) + } + } + + // Owner initialized to address(0), no need to modify if call is unsuccessful + return owner; + } } -- cgit v1.2.3