智能合约lending函数签名无效,求有效签名生成方法
解决Ethers.js调用合约lending函数时的签名无效问题
问题核心原因
签名无效的本质是前端生成签名的哈希与合约验证时使用的哈希完全不匹配,具体有两个关键差异:
- 哈希编码逻辑不一致:合约用
abi.encodePacked拼接参数后哈希,而前端用solidityKeccak256(等价于abi.encode后哈希),两者对参数的编码规则不同,导致原始哈希值不相等。 - 签名前缀缺失:合约验证时会对原始哈希添加以太坊标准签名前缀(
"\x19Ethereum Signed Message:\n32"),但前端直接签名了未加前缀的哈希,导致签名对应的消息与合约验证的消息不匹配。
修正后的完整代码
const contractAddress = "0x1C4E9F87c7F2bCd80c89A1999d776461d41545b9"; const provider = new ethers.providers.JsonRpcProvider( "https://rpc.ankr.com/bsc" ); const signer = new ethers.Wallet(privateKey, provider); const contract = new ethers.Contract(contractAddress, ABI, signer); const lending = async () => { const id = 992581; const tokenId = 16587857756452; const price = 500000000000; const expiredAt = Date.now() + 60; // 1. 完全匹配合约的哈希生成逻辑:用abi.encodePacked处理参数后哈希 const criteriaMessageHash = ethers.utils.keccak256( ethers.utils.concat([ ethers.utils.zeroPad(ethers.utils.hexlify(id), 32), ethers.utils.zeroPad(ethers.utils.hexlify(tokenId), 32), ethers.utils.zeroPad(ethers.utils.hexlify(price), 32), ethers.utils.zeroPad(ethers.utils.hexlify(expiredAt), 32) ]) ); // 2. 用signer的signMessage方法签名,自动添加以太坊签名前缀 const signatureString = await signer.signMessage(ethers.utils.arrayify(criteriaMessageHash)); // 3. 调用合约lending函数,适当提高gasLimit避免交易失败 const tx = await contract.lending( id, tokenId, price, expiredAt, signatureString, { gasLimit: 100000, } ); const receipt = await tx.wait(); console.log(receipt); };
关键修正说明
- 对齐哈希生成逻辑:
合约中abi.encodePacked会将每个uint256参数编码为32字节的大端字节串,前端用ethers.utils.concat拼接每个参数的32字节编码,确保生成的原始哈希与合约完全一致。 - 自动处理签名前缀:
合约用ECDSA.toEthSignedMessageHash添加标准前缀,而signer.signMessage方法会自动完成这一步骤,无需手动计算带前缀的哈希。 - 简化签名流程:
无需手动创建SigningKey,直接使用signer对象的签名方法,避免私钥处理错误,同时保证签名的正确性。
内容的提问来源于stack exchange,提问作者junis087678
相关产品推荐
相关产品推荐

