如何让MetaMask的eth_signTypedData_v4以可读格式显示日期与价格
问题描述
使用eth_signTypedData_v4方法签署链下NFT挂单请求时,合约要求价格与日期值以uint256类型传入,但MetaMask向用户展示签名消息时,会将这些值显示为原始数字(比如wei值、时间戳),希望能将这些数字转换为更易读的格式,例如价格以ETH单位展示、日期转换为易懂的日期格式。
当前签名请求代码如下:
const metamaskProvider = new Web3Provider(window.ethereum); // Get the signer and account address const signer = metamaskProvider.getSigner(); const marketPlaceContractName = "NFTMarketplace"; const listingDomain = { name: marketPlaceContractName, version: "1", chainId: chainId, // or whatever chain ID you're using verifyingContract: marketPlaceAddress, // address of the contract }; const listingTypes = { NftListing: [ { name: "seller", type: "address" }, { name: "nftAddress", type: "address" }, { name: "nftId", type: "uint256" }, { name: "startPrice", type: "uint256" }, { name: "endPrice", type: "uint256" }, { name: "startedAt", type: "uint256" }, { name: "duration", type: "uint256" }, ], }; const listing = { seller: account, nftAddress: process.env.REACT_APP_CONTRACT_ADDRESS, nftId: nftId, startPrice: BigNumber.from(parseEther("0.02")).toString(), endPrice: BigNumber.from(parseEther("0.01")).toString(), startedAt: timestampBefore, duration: 5 * 60, }; const listSignature = await signer._signTypedData( listingDomain, listingTypes, listing );
解决方案
MetaMask支持EIP-712的扩展display属性,可以在定义typed data的字段时,为uint256类型的价格、日期字段添加自定义展示格式,不会影响签名的哈希计算(合约验证签名逻辑无需改动),仅在用户签名弹窗中显示易读的转换后值。
具体实现步骤:
- 价格字段:将wei值转换为ETH单位的字符串,添加到字段的
display属性中 - 日期/时长字段:将时间戳转换为ISO日期格式,时长转换为分钟/小时等易读文本,添加到
display属性中
修改后的代码示例
const metamaskProvider = new Web3Provider(window.ethereum); const signer = metamaskProvider.getSigner(); const marketPlaceContractName = "NFTMarketplace"; const listingDomain = { name: marketPlaceContractName, version: "1", chainId: chainId, verifyingContract: marketPlaceAddress, }; // 计算易读的展示值 const ethStartPrice = "0.02"; const ethEndPrice = "0.01"; const startPriceWei = BigNumber.from(parseEther(ethStartPrice)).toString(); const endPriceWei = BigNumber.from(parseEther(ethEndPrice)).toString(); // 将时间戳转换为ISO日期字符串 const startedAtDate = new Date(timestampBefore * 1000).toISOString(); // 将秒数转换为分钟文本 const durationText = `${Math.floor(duration / 60)}分钟`; const listingTypes = { NftListing: [ { name: "seller", type: "address" }, { name: "nftAddress", type: "address" }, { name: "nftId", type: "uint256" }, { name: "startPrice", type: "uint256", // 添加价格的展示格式 display: { type: "string", value: `${ethStartPrice} ETH` } }, { name: "endPrice", type: "uint256", display: { type: "string", value: `${ethEndPrice} ETH` } }, { name: "startedAt", type: "uint256", // 添加日期的展示格式(MetaMask会自动解析为友好日期) display: { type: "date-time", value: startedAtDate } }, { name: "duration", type: "uint256", // 添加时长的展示文本 display: { type: "string", value: durationText } }, ], }; const listing = { seller: account, nftAddress: process.env.REACT_APP_CONTRACT_ADDRESS, nftId: nftId, startPrice: startPriceWei, endPrice: endPriceWei, startedAt: timestampBefore, duration: 5 * 60, }; const listSignature = await signer._signTypedData( listingDomain, listingTypes, listing );
关键说明
display属性是MetaMask等主流钱包支持的EIP-712扩展,不会改变签名的核心逻辑,合约端仍可正常验证uint256类型的原始值- 对于日期字段,使用
display.type: "date-time"时,MetaMask会自动将ISO字符串转换为用户本地时区的友好日期格式 - 所有展示值仅用于用户界面显示,签名哈希计算只依赖字段的
name和type以及原始uint256值
内容的提问来源于stack exchange,提问作者Mohammed Abo-zaid
相关产品推荐
相关产品推荐

