Validating NFT ownership through EIP-712 signature verification in smart contracts

I’m working on a smart contract where users need to prove they own an NFT before depositing it. The process should work like this: when someone calls the deposit function, they provide a signature created with their wallet (like MetaMask), and the contract checks if that signature proves they actually own the NFT.

import "@openzeppelin/contracts/utils/Counters.sol";
import "@openzeppelin/contracts/utils/cryptography/ECDSA.sol";

using Counters for Counters.Counter;
using ECDSA for bytes32;

Counters.Counter private _depositCounter;
mapping(address => uint256) private deposits;

bytes32 private constant MESSAGE_TYPEHASH =
    keccak256("TokenOwnership(address contractAddr,uint256 id)");

constructor() EIP712("MyNFTValidator", "2.0.0") {}

function stakeNFT(TokenData calldata data, bytes calldata sig) external returns (bool isValidOwner) {
    require(validateOwnership(data, sig));
    
    uint256 depositId = _depositCounter.current();
    deposits[msg.sender] = depositId;
    _depositCounter.increment();
    
    return isValidOwner;
}

function validateOwnership(TokenData calldata data, bytes calldata sig) public view returns (bool) {
    address recovered = _hashTypedDataV4(
        keccak256(abi.encode(MESSAGE_TYPEHASH, data.contractAddr, data.id))
    ).recover(sig);
    
    return true;
}

Right now my verification function always returns true, but I need it to actually check if the person who signed the message is the real owner of the NFT. How can I properly implement this ownership verification?

hmm interesting approach! i’m curious about a few things though.

first - you recover the signer from the signature but don’t actually use that address for anything. you just return true regardless. shouldn’t you check if that recovered address owns the nft?

once you get the recovered address, don’t you need to call IERC721(data.contractAddr).ownerOf(data.id) to see who owns that token? then compare it with the recovered signer?

also confused about the flow - users sign a message proving they own an nft before depositing it? what stops someone from copying someone else’s signature if they know the contract address and token id?

another thing - you’re missing the TokenData struct definition. what fields does that have?

seems like timing issues too. what if ownership changes between signing the message and calling the deposit function? that’d create problems right?

just curious how you’re handling these edge cases. thinking of adding timestamps to prevent replay attacks or signature reuse?

you’re missing the ownership check in your validateOwnership function. after recovering the signer address, you need to verify they actually own the nft by calling the ERC721 contract. try return recovered == IERC721(data.contractAddr).ownerOf(data.id); instead of just returning true. right now anyone can sign any message and bypass validation.