You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何实现EIP-712签名并验证签名者是否为NFT所有者?

如何实现EIP-712签名验证并确认签名者为NFT所有者

一、合约层面完善方案

1. 引入ERC721接口

首先定义标准ERC721接口,用于获取NFT的所有者地址:

interface IERC721 {
    function ownerOf(uint256 tokenId) external view returns (address);
}

2. 完善verify函数

原verify函数仅恢复签名者地址,未验证其是否为NFT所有者。修改后实现签名有效性+所有者匹配双重验证:

function verify(NFT calldata nft, bytes calldata signature) public view returns (bool) {
    // 从签名中恢复出签名者地址
    address signer = _hashTypedDataV4(
        keccak256(abi.encode(TYPEHASH, nft.nftcontract, nft.tokenId))
    ).recover(signature);

    // 排除无效签名(恢复出零地址)
    if (signer == address(0)) return false;

    // 安全获取NFT所有者(捕获tokenId不存在的异常)
    address nftOwner;
    try IERC721(nft.nftcontract).ownerOf(nft.tokenId) {
        nftOwner = IERC721(nft.nftcontract).ownerOf(nft.tokenId);
    } catch {
        // tokenId不存在时直接返回验证失败
        return false;
    }

    // 验证签名者是否为NFT所有者
    return signer == nftOwner;
}

3. 修正depositNFT函数逻辑

原函数未正确赋值返回值,调整后确保返回验证结果并添加验证失败的回滚条件:

function depositNFT(NFT calldata nft, bytes calldata signature) public override returns (bool signerIsOwner)
{
    signerIsOwner = verify(nft, signature);
    // 验证失败则回滚交易
    require(signerIsOwner, "Signer is not the NFT owner");

    uint256 tokenId = _tokenIdCounter.current();
    userInfo[msg.sender] = tokenId;
    _tokenIdCounter.increment();

    return signerIsOwner;
}

二、前端MetaMask签名实现

通过MetaMask完成EIP-712签名,需构造符合规范的签名数据,示例代码基于ethers.js:

async function signNFT(nftContractAddress, tokenId, yourContractAddress) {
    const provider = new ethers.providers.Web3Provider(window.ethereum);
    const signer = provider.getSigner();
    const chainId = (await provider.getNetwork()).chainId;

    // 定义EIP-712域名信息,需与合约构造函数参数完全一致
    const domain = {
        name: "aaa",
        version: "1.0.0",
        chainId: chainId,
        verifyingContract: yourContractAddress
    };

    // 定义消息类型,需与合约中TYPEHASH对应
    const types = {
        NFT: [
            { name: "nftcontract", type: "address" },
            { name: "tokenId", type: "uint256" }
        ]
    };

    // 构造待签名的消息内容
    const message = {
        nftcontract: nftContractAddress,
        tokenId: tokenId
    };

    // 调用MetaMask完成EIP-712签名
    const signature = await signer._signTypedData(domain, types, message);
    return signature;
}

关键注意事项

  • 确保NFT合约遵循标准ERC721规范,实现ownerOf方法;
  • 前端构造的domain信息(name、version、chainId)必须与合约中EIP712构造函数参数完全一致,否则签名验证会失败;
  • 若需防止签名重放,可在消息中添加唯一nonce和过期时间字段,提升安全性。

内容的提问来源于stack exchange,提问作者Software14

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.13 06:50:28