Ethers.js utils.id方法调用报错,智能合约事件异常排查求助
问题:ethers.utils.id调用报错与合约事件触发异常
问题背景
开发了CertificateManagement智能合约,部署成功但部分功能(如issueCertificate)存在事件触发器问题。编写调试脚本debug.js排查时发现ethers.utils.id方法报错,随后编写testUtilsId.js专门测试该方法,均出现相同错误。
报错信息
执行npx hardhat run scripts/debug.js时的错误
TypeError: Cannot read properties of undefined (reading 'id') at main (/Users/MASTERS/ACV/scripts/debug.js:22:38)
执行node scripts/testUtilsId.js时的错误
(base) ACV % node scripts/testUtilsId.js TypeError: Cannot read properties of undefined (reading 'id') at testUtilsId (/Users/MASTERS/ACV/scripts/testUtilsId.js:6:43) at Object.<anonymous> (/Users/MASTERS/ACV/scripts/testUtilsId.js:15:1) at Module._compile (node:internal/modules/cjs/loader:1364:14) at Module._extensions..js (node:internal/modules/cjs/loader:1422:10) at Module.load (node:internal/modules/cjs/loader:1203:32) at Module._load (node:internal/modules/cjs/loader:1019:12) at Function.executeUserEntryPoint [as runMain] (node:internal/modules/run_main:128:12) at node:internal/main/run_main_module:28:49
相关代码
CertificateManagement.sol
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; contract CertificateManagement { // Struct to store certificate information struct Certificate { uint256 id; string studentName; string course; uint256 issueDate; bool isValid; string issuer; } // Mapping from certificate ID to certificate details mapping(uint256 => Certificate) public certificates; // Mapping from an address to a boolean indicating if it is authorized to issue and revoke certificates mapping(address => bool) public authorizedIssuers; // Event emitted when a certificate is issued event CertificateIssued(uint256 indexed certificateID, string studentName, string issuer); // Event emitted when a certificate is revoked event CertificateRevoked(uint256 indexed certificateID); // Modifier to check if the caller is an authorized issuer modifier onlyAuthorized() { require(authorizedIssuers[msg.sender], "Caller is not authorized"); _; } constructor() { // The deployer of the contract is the initial authorized issuer authorizedIssuers[msg.sender] = true; } // Function to authorize a new issuer function addIssuer(address issuer) external onlyAuthorized { authorizedIssuers[issuer] = true; } // Function to revoke issuer rights function removeIssuer(address issuer) external onlyAuthorized { authorizedIssuers[issuer] = false; } // Function to issue a new certificate function issueCertificate(string memory studentName, string memory course, string memory issuer) public onlyAuthorized returns (uint256) { uint256 newCertificateID = uint256(keccak256(abi.encodePacked(block.timestamp, msg.sender, studentName, course))); certificates[newCertificateID] = Certificate(newCertificateID, studentName, course, block.timestamp, true, issuer); emit CertificateIssued(newCertificateID, studentName, issuer); // Ensure this line is correct return newCertificateID; } // Function to revoke a certificate function revokeCertificate(uint256 certificateID) public onlyAuthorized { require(certificates[certificateID].isValid, "Certificate already revoked"); certificates[certificateID].isValid = false; emit CertificateRevoked(certificateID); } // Function to verify the validity of a certificate function verifyCertificate(uint256 certificateID) public view returns (bool) { return certificates[certificateID].isValid; } }
debug.js
require("@nomicfoundation/hardhat-toolbox"); const { ethers } = require("hardhat"); const utils = ethers.utils; async function main() { const [deployer] = await ethers.getSigners(); const CertificateManagement = await ethers.getContractFactory("CertificateManagement"); const certManagement = await CertificateManagement.deploy(); await certManagement.waitForDeployment(); console.log("CertificateManagement deployed to:", await certManagement.getAddress()); await certManagement.addIssuer(deployer.address); const tx = await certManagement.issueCertificate("John Doe", "Blockchain 101", "University"); const receipt = await tx.wait(); console.log("Transaction Receipt:", receipt); const logs = receipt.logs; console.log("Transaction Logs:", logs); if (logs.length > 0) { // Calculate the event signature const eventSignature = utils.id("CertificateIssued(uint256,string,string)"); console.log("Event Signature:", eventSignature); // Log each topic and data for inspection logs.forEach((log, index) => { console.log(`Log ${index} Address:`, log.address); console.log(`Log ${index} Topics:`, log.topics); console.log(`Log ${index} Data:`, log.data); }); // Find the log corresponding to the event const event = logs.find(log => log.topics[0] === eventSignature); if (event) { console.log("Event found:", event); // The first topic is the event signature, and the second topic is the indexed parameter (certificateID) const certificateID = event.topics[1]; // Decode the data part (non-indexed parameters: studentName and issuer) const decodedData = utils.defaultAbiCoder.decode( ["string", "string"], event.data ); const studentName = decodedData[0]; const issuer = decodedData[1]; console.log("Issued Certificate ID:", certificateID); console.log("Student Name:", studentName); console.log("Issuer:", issuer); } else { console.log("CertificateIssued event not found in the transaction receipt."); } } else { console.log("No logs found in the transaction receipt."); } } main().catch((error) => { console.error(error); process.exitCode = 1; });
testUtilsId.js
const { ethers } = require("hardhat"); async function testUtilsId() { // Random string const randomString = "Hello World"; const randomStringHash = ethers.utils.id(randomString); console.log(`Hash of "${randomString}": ${randomStringHash}`); // Event signature const eventSignatureString = "CertificateIssued(uint256,string,string)"; const eventSignatureHash = ethers.utils.id(eventSignatureString); console.log(`Hash of "${eventSignatureString}": ${eventSignatureHash}`); } testUtilsId().catch((error) => { console.error(error); process.exitCode = 1; });
解决方案
1. 修复ethers.utils.id调用错误
该报错源于Hardhat环境中,从hardhat导入的ethers对象未直接暴露utils.id方法,或存在版本兼容性问题,可通过以下两种方式解决:
方式一:直接导入标准ethers.js库
替换脚本中的导入方式,直接使用独立的ethers.js库:
// 替换原导入语句 const ethers = require("ethers");
此方式可确保使用标准的ethers.js工具方法,避免Hardhat封装带来的差异。
方式二:用基础工具方法替代id功能
ethers.utils.id()本质是keccak256(toUtf8Bytes(str)),可直接用这两个方法组合实现相同功能:
// 替换utils.id("xxx")为以下代码 const eventSignature = ethers.utils.keccak256(ethers.utils.toUtf8Bytes("CertificateIssued(uint256,string,string)"));
2. 合约事件触发问题排查
修复utils.id错误后,重新运行调试脚本即可正常解析事件日志。另外注意:
- 合约构造函数已将部署者设为授权 issuer,
await certManagement.addIssuer(deployer.address);这行代码可删除,避免冗余操作; - 若仍无法找到事件,需检查:
- 交易是否成功执行(查看
receipt.status是否为1); - 调用
issueCertificate的地址是否为授权 issuer(可通过await certManagement.authorizedIssuers(deployer.address)验证)。
- 交易是否成功执行(查看
内容的提问来源于stack exchange,提问作者Melody
相关产品推荐
相关产品推荐

