调用USDC合约transferWithAuthorization时出现无效签名错误求助
我在实现了EIP-3009的Goerli USDC合约上调用transferWithAuthorization方法时,先签署交易数据再执行交易,却触发错误:Fail with error 'FiatTokenV2: invalid signature'。以下是问题原因及修正方案:
核心问题分析
1. Domain参数与合约要求不匹配
EIP-712签名的Domain字段必须严格匹配目标合约的配置:
- 你代码中使用自定义的
name: 'testapp'和version: '1',但Goerli上的USDC合约实际name()返回值为"USD Coin",version()返回值为"2" chainId应传入数字类型5,而非字符串'5',类型不匹配会导致签名验证逻辑失败
2. Nonce使用错误
EIP-3009的nonce是地址专属的递增计数器,用于防止交易重放,必须调用合约的nonces(from)方法获取当前有效nonce,不能自行生成随机32字节值。
3. 签名上下文不统一
虽然你的类型结构与合约定义一致,但Domain和Nonce的错误会直接导致签名与合约验证逻辑不匹配,这是触发无效签名错误的主要原因。
修正后的关键代码片段
const { ethers } = require('ethers'); const abi = require('./abi'); async function main() { /* 初始化连接与签名准备 */ const sendingWallet = ethers.Wallet.createRandom(); const sendingAddress = await sendingWallet.getAddress(); const connection = new ethers.providers.InfuraProvider('goerli', '你的Infura API密钥'); // 先连接合约获取必要信息 const contract = new ethers.Contract( '0x07865c6E87B9F70255377e024ace6630C1Eaa37F', abi, connection ); // 获取合约真实的name、version和地址nonce const contractName = await contract.name(); const contractVersion = await contract.version(); const nonce = await contract.nonces(sendingAddress); const types = { TransferWithAuthorization: [ { name: 'from', type: 'address' }, { name: 'to', type: 'address' }, { name: 'value', type: 'uint256' }, { name: 'validAfter', type: 'uint256' }, { name: 'validBefore', type: 'uint256' }, { name: 'nonce', type: 'bytes32' }, ], }; const domain = { name: contractName, version: contractVersion, chainId: 5, // 改为数字类型 verifyingContract: '0x07865c6E87B9F70255377e024ace6630C1Eaa37F', }; const message = { from: sendingAddress, to: '0x1bC152F3E47CC7baDF5629bc77CBEf9DaE813843', value: 100000, validAfter: 0, validBefore: Math.floor(Date.now() / 1000) + 3600, nonce: nonce, // 使用合约返回的有效nonce }; // 生成签名 const signature = await sendingWallet._signTypedData(domain, types, message); const { v, r, s } = ethers.utils.splitSignature(signature); /* 执行交易 */ const wallet = ethers.Wallet.fromMnemonic( 'youth tool amount venue exact birth opinion derive lend charge roof food', ); const signer = wallet.connect(connection); const writeContract = contract.connect(signer); const res = await writeContract.transferWithAuthorization( message.from, message.to, message.value, message.validAfter, message.validBefore, message.nonce, v, r, s, { gasLimit: 1000000 } ); console.log(res); const receipt = await res.wait(); console.log(receipt); } main();
内容的提问来源于stack exchange,提问作者ljenkins
相关产品推荐
相关产品推荐

