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

智能合约优化困惑:Hitchiker's Guide中迭代校验的Gas成本问题

Understanding Gas Cost Growth from Proof Iteration & How to Fix It

Great question—this is a super common pain point when building notarization-style smart contracts, so let’s break this down clearly.

Why Iterating All Proofs Causes Rising Gas Costs

First, let’s recall how Ethereum gas works: every operation your contract executes (reading/writing storage, looping, arithmetic) has a fixed gas cost attached. When you iterate through an array of proofs to check if a document is notarized, you’re reading a storage slot for every single proof in the array each time you run the check.

Note that every time we want to check if a document was notarized, we need to iterate through all existing proofs. This makes the contract spend more and more gas on each check as more documents are added.

As the quote says, if you start with 10 proofs, your check reads 10 storage slots. When you hit 1,000 proofs, it reads 1,000 slots—gas cost grows linearly with the number of documents. For users interacting with your contract, this means check operations get more expensive over time, which is terrible for UX and scalability.

The Fix: Use Mappings for O(1) Lookups

The simplest and most effective fix is to replace array-based proof storage with a mapping that directly indexes documents by their hash. Mappings in Solidity let you look up values in constant time (O(1)) regardless of how many entries exist—no looping required.

Here’s a simplified example of an optimized notary contract:

contract OptimizedNotary {
    // Map document hashes to a boolean indicating if they're notarized
    mapping(bytes32 => bool) public isNotarized;

    function notarize(bytes32 documentHash) external {
        require(!isNotarized[documentHash], "Document already notarized");
        isNotarized[documentHash] = true;
    }

    function checkNotarized(bytes32 documentHash) external view returns (bool) {
        // Direct lookup—no iteration, fixed gas cost every time
        return isNotarized[documentHash];
    }
}

In this setup, checkNotarized will cost the same amount of gas whether you’ve notarized 10 documents or 10,000. No more linear gas growth.

If You Need to Track Historical Proofs

If you still need a full record of all notarized documents (e.g., for frontend dashboards or audit trails), you can combine a mapping with an array:

contract OrderedNotary {
    struct Proof {
        bytes32 documentHash;
        uint256 timestamp;
        address notarizer;
    }

    mapping(bytes32 => bool) public isNotarized;
    Proof[] public allProofs; // Stores historical proofs for reference

    function notarize(bytes32 documentHash) external {
        require(!isNotarized[documentHash], "Document already notarized");
        isNotarized[documentHash] = true;
        allProofs.push(Proof(documentHash, block.timestamp, msg.sender));
    }

    function checkNotarized(bytes32 documentHash) external view returns (bool) {
        // Still O(1) lookup—no iteration here
        return isNotarized[documentHash];
    }

    // Optional: Fetch all proofs (only use this when necessary, as it costs gas proportional to array length)
    function getAllProofs() external view returns (Proof[] memory) {
        return allProofs;
    }
}

This way, your core check operation stays fast and cheap, while you still have access to the full history for use cases that need it.

Key Takeaway

The root issue is that linear iteration over storage scales poorly. By using mappings to index state directly, you eliminate the need for iteration in critical check operations, keeping gas costs predictable and scalable as your contract grows.

内容的提问来源于stack exchange,提问作者Mehmet Doğan

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.19 07:49:13