如何实现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
相关产品推荐
相关产品推荐

