You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.23 05:54:53