EVM Opcode 執行成本與 Gas 消耗深度技術分析:以太坊黃皮書規範引用與實際執行案例

本文深入分析以太坊虛擬機器(EVM)各類 Opcode 的 Gas 消耗模型,基於以太坊黃皮書的正式規範,提供每個操作碼的數學計算公式、複雜度分析以及實際執行成本案例。研究涵蓋從最基礎的棧操作到複雜的密碼學計算,幫助開發者建立精確的 Gas 估算能力。

EVM Opcode Gas 執行成本深度技術分析:從基礎運算到 Gas 優化實戰

寫這篇文章的契機是有個朋友問我:「為什麼同樣是轉帳,我朋友的交易只花了 21,000 Gas,我的手續費卻是他的三倍?」

答案藏在 EVM 的Opcode(操作碼)裡。

每一筆以太坊交易的本質,就是一堆Opcode 按順序執行。搞懂Opcode 的Gas 消耗邏輯,不只能幫你省手續費,還能讓你寫出更高效的智能合約。身為工程師,我深深覺得這是每個區塊鏈開發者都該掌握的基本功。

本文數據截止 2026 年 3 月,所有 Gas 消耗依據 EIP-1559 後的修正版 EVM 規範。


EVM Opcode 到底是什麼?

EVM(Ethereum Virtual Machine)是以太坊的執行環境,專門負責運行智能合約。而Opcode 就是 EVM 能執行的底層機器指令。

你可以把Opcode 想成 CPU 的指令集。只不過以太坊的「CPU」是一個全球共享的、去中心化的虛擬機,而且每次執行指令都要付費。

EVM 目前支援大約 140 種Opcode,從最基本的加法乘法到複雜的密碼學運算都有覆蓋。每一種Opcode 都有固定的 Gas 消耗量,這個消耗量是由以太坊的核心開發者根據「執行成本」來計算的。

根據以太坊黃皮書(Yellow Paper)規範,每個Opcode 的 Gas 消耗公式化為:

C(s, v, I) = C_base + C_memory * words + C_compute

其中:


Gas 消耗的第一個原則:越複雜越貴

EVM Opcode 的 Gas 消耗可以分為幾個層次。讓我從最基礎的說起。

基本運算 Opcode:便宜的開始

加減乘除、邏輯運算、比較運算——這些最基礎的Opcode 反而最便宜。

Gas 消耗對照表(基本運算):

STOP         → 0 Gas    (停止執行,不消耗 Gas)
ADD          → 3 Gas    (加法)
MUL          → 5 Gas    (乘法)
SUB          → 3 Gas    (減法)
DIV          → 5 Gas    (整數除法)
SDIV         → 5 Gas    (有符號除法)
MOD          → 5 Gas    (取模運算)
SMOD         → 5 Gas    (有符號取模)
ADDMOD       → 8 Gas    (加法後取模)
MULMOD       → 8 Gas    (乘法後取模)
EXP          → 10 Gas   (指數運算,靜態部分)
SIGNEXTEND   → 5 Gas    (符號擴展)
LT           → 3 Gas    (小於比較)
GT           → 3 Gas    (大於比較)
SLT          → 3 Gas    (有符號小於)
SGT          → 3 Gas    (有符號大於)
EQ           → 3 Gas    (相等比較)
ISZERO       → 3 Gas    (零值檢測)
AND          → 3 Gas    (位元 AND)
OR           → 3 Gas    (位元 OR)
XOR          → 3 Gas    (位元 XOR)
NOT          → 3 Gas    (位元 NOT)
BYTE         → 3 Gas    (位元組讀取)
SHL           → 3 Gas   (左移,EIP-145 新增)
SHR           → 3 Gas   (右移,EIP-145 新增)
SAR           → 3 Gas   (算術右移,EIP-145 新增)

看見了嗎?加法只要 3 Gas,除法 5 Gas,邏輯比較也是 3 Gas。這些 Opcode 在 EVM 層面算是「免費午餐」等級的便宜。

但千萬別被數字騙了。基本運算便宜是便宜,但你在合約裡呼叫它們的次數乘上 Gas 單價,這筆帳可不小。

Solidity 程式碼示範基本運算的 Gas 消耗:

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.26;

contract GasBasicOps {
    // 基本加法:~45,000 Gas(含部署)
    function add(uint256 a, uint256 b) public pure returns (uint256) {
        return a + b;
    }
    
    // 基本乘法:稍微貴一點
    function multiply(uint256 a, uint256 b) public pure returns (uint256) {
        return a * b;
    }
    
    // 組合運算:成本會累加
    function complexCalc(uint256 x) public pure returns (uint256) {
        uint256 a = x + 10;
        uint256 b = x * 2;
        uint256 c = a % b;
        return (a + b) * c;
    }
}

JavaScript(使用 ethers.js)測量這些函數的 Gas 消耗:

const { ethers } = require("ethers");

async function measureGas() {
    const provider = new ethers.JsonRpcProvider("https://mainnet.infura.io/v3/YOUR_PROJECT_ID");
    const signer = await provider.getSigner();
    
    const contractAddress = "YOUR_CONTRACT_ADDRESS";
    const abi = [
        "function add(uint256 a, uint256 b) pure returns (uint256)",
        "function multiply(uint256 a, uint256 b) pure returns (uint256)",
        "function complexCalc(uint256 x) pure returns (uint256)"
    ];
    
    const contract = new ethers.Contract(contractAddress, abi, signer);
    
    // 估算 Gas(不需要實際發送交易)
    const gasAdd = await contract.add.estimateGas(100, 200);
    const gasMultiply = await contract.multiply.estimateGas(100, 200);
    const gasComplex = await contract.complexCalc.999);
    
    console.log(`add() Gas: ${gasAdd.toString()}`);
    console.log(`multiply() Gas: ${gasMultiply.toString()}`);
    console.log(`complexCalc() Gas: ${gasComplex.toString()}`);
}

measureGas().catch(console.error);

Python 版本(使用 web3.py):

from web3 import Web3

def measure_gas():
    w3 = Web3(Web3.HTTPProvider("https://mainnet.infura.io/v3/YOUR_PROJECT_ID"))
    
    contract_address = "YOUR_CONTRACT_ADDRESS"
    abi = [
        {
            "inputs": [{"name": "a", "type": "uint256"}, {"name": "b", "type": "uint256"}],
            "name": "add",
            "outputs": [{"type": "uint256"}],
            "stateMutability": "pure",
            "type": "function"
        }
    ]
    
    contract = w3.eth.contract(address=contract_address, abi=abi)
    
    # 估算 Gas(不發送交易)
    gas_estimate = contract.functions.add(100, 200).estimate_gas()
    print(f"add() Gas 估算: {gas_estimate}")
    
    # 估算 Gas 費用(以 Gwei 為單位)
    current_gas_price = w3.eth.gas_price
    gas_fee_wei = gas_estimate * current_gas_price
    gas_fee_eth = w3.from_wei(gas_fee_wei, 'ether')
    print(f"預估費用: {gas_fee_eth} ETH")

measure_gas()

區塊鏈讀寫操作:真正燒錢的地方

基本運算便宜歸便宜,但真正讓你的錢包失血的是區塊鏈讀寫操作。讓我直接說重點:

SLOAD:讀取狀態變數,貴!

SLOAD → 2100 Gas(冷讀取)/ 100 Gas(熱讀取)

冷讀取是指第一次讀取某個儲存槽位,熱讀取是指短時間內再次讀取同一槽位。熱讀取便宜將近 20 倍!

這個設計很合理:讀取一個已存在的狀態,比讀取一個從未讀取過的狀態要省事得多。

實務建議:如果你需要在函數內多次讀取同一個狀態變數,先把它存到記憶體(memory)變數裡。

Solidity 示範:

// 不好的寫法:每次都 SLOAD
function badExample(address user) public view returns (uint256) {
    return balance[user] + balance[user] * rate / 100; // SLOAD x2
}

// 好的寫法:只 SLOAD 一次
function goodExample(address user) public view returns (uint256) {
    uint256 userBalance = balance[user]; // SLOAD x1
    return userBalance + userBalance * rate / 100;
}

Gas 差異:假設是熱讀取,這個簡單的最佳化就能省下 100 Gas。

SSTORE:寫入狀態,天價!

SSTORE → 20000 Gas(將 0 寫成非 0)
SSTORE → 2900 Gas(將非 0 保持非 0)
SSTORE → 100 Gas(將非 0 寫成 0,退還)
SSTORE → 200 Gas(將 0 寫成 0,實際無效)

SSTORE 是所有 Opcode 裡最貴的之一。寫入一個新的狀態變數要 20,000 Gas,而讀取只要 2,100 Gas。差了快 10 倍。

而且這裡有個有趣的 refund 機制:如果你把一個值從非零改成零,可以獲得 10,000 Gas 的退款(上限是總消耗的一半)。這個機制本意是獎勵「清理狀態」的行為。

JavaScript 示範監控 SSTORE 成本:

const { ethers } = require("ethers");

async function analyzeSstore() {
    const provider = new ethers.JsonRpcProvider("https://mainnet.infura.io/v3/YOUR_PROJECT_ID");
    
    // 查詢歷史交易的 Gas 使用
    const txHash = "YOUR_TRANSACTION_HASH";
    const tx = await provider.getTransactionReceipt(txHash);
    
    console.log(`交易 hash: ${tx.hash}`);
    console.log(`Gas 使用量: ${tx.gasUsed.toString()}`);
    console.log(`有效 Gas 價格: ${tx.effectiveGasPrice.toString()} Gwei`);
    
    // 計算總費用
    const totalFee = tx.gasUsed * tx.effectiveGasPrice;
    console.log(`總費用: ${ethers.formatEther(totalFee)} ETH`);
}

analyzeSstore().catch(console.error);

Python 版本:

from web3 import Web3

def analyze_sstore():
    w3 = Web3(Web3.HTTPProvider("https://mainnet.infura.io/v3/YOUR_PROJECT_ID"))
    
    tx_hash = "YOUR_TRANSACTION_HASH"
    receipt = w3.eth.get_transaction_receipt(tx_hash)
    
    print(f"交易 hash: {receipt['transactionHash'].hex()}")
    print(f"Gas 使用量: {receipt['gasUsed']}")
    print(f"有效 Gas 價格: {receipt['effectiveGasPrice']} Wei")
    
    total_fee_wei = receipt['gasUsed'] * receipt['effectiveGasPrice']
    total_fee_eth = w3.from_wei(total_fee_wei, 'ether')
    print(f"總費用: {total_fee_eth} ETH")

analyze_sstore()

實務教學:如何計算合約部署的 Gas

合約部署是 Gas 消耗最大的操作之一。讓我實際算給你看。

Solidity 合約部署 Gas 計算:

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.26;

// 部署這個合約大約需要 500,000 - 600,000 Gas
contract SampleContract {
    uint256 public value;
    address public owner;
    mapping(address => uint256) public balances;
    
    event ValueChanged(uint256 newValue);
    
    constructor(uint256 _initialValue) {
        value = _initialValue;
        owner = msg.sender;
    }
    
    function setValue(uint256 _newValue) external {
        require(msg.sender == owner, "Not owner");
        value = _newValue;
        emit ValueChanged(_newValue);
    }
    
    function deposit() external payable {
        balances[msg.sender] += msg.value;
    }
}

部署成本的決定因素:

部署 Gas 成本分解:

1. 基礎建立成本(Creation Gas):
   - 合約位元組碼大小 x 200 Gas
   - 通常為 500 - 2000 Gas

2. 構造函數執行:
   - 根據構造函數邏輯計算
   - 記憶體操作、參數編碼等

3. 合約位元組碼部署:
   - 每個位元組 200 Gas(非零位元組)
   - 每個位元組 4 Gas(零位元組)
   - 通常 1-5 KB = 200,000 - 1,000,000 Gas

4. 構造函數中的 SSTORE:
   - 每個狀態變數初始化

預估總成本:500,000 - 800,000 Gas

CALL 相關 Opcode:跨合約交互的代價

現代以太坊應用幾乎不可能不跨合約交互。CALL、SDELEGATECALL、CALLCODE、STATICCALL——這些Opcode 控制了合約之間的溝通方式。

CALL:最常用的跨合約呼叫

CALL → 2600 Gas(冷)/ 100 Gas(熱)
+ 額外記憶體成本
+ 被呼叫合約的執行成本

每次呼叫另一個合約,光是「建立連接」就要消耗約 2,600 Gas。這還沒算被呼叫合約內部的 Gas 消耗。

實務建議:減少跨合約呼叫次數

// 不好的設計:迴圈中多次 CALL
function badMultiCall(address[] calldata targets, bytes[] calldata data)
    external
    returns (bytes[] memory results)
{
    results = new bytes[](targets.length);
    for (uint256 i = 0; i < targets.length; i++) {
        (bool success, bytes memory result) = targets[i].call(data[i]);
        require(success, "Call failed");
        results[i] = result;
    }
}

// 改進設計:批次處理
function batchCall(BatchRequest[] calldata requests)
    external
    returns (BatchResponse[] memory responses)
{
    responses = new BatchResponse[](requests.length);
    
    // 如果可能的話,在同一個交易中處理
    // 減少多次 CALL 的開銷
    for (uint256 i = 0; i < requests.length; i++) {
        responses[i] = executeSingle(requests[i]);
    }
}

JavaScript 批次呼叫示範:

const { ethers } = require("ethers");

async function batchCalls() {
    const provider = new ethers.JsonRpcProvider("https://mainnet.infura.io/v3/YOUR_PROJECT_ID");
    const signer = await provider.getSigner();
    
    // 使用 Multicall3 合約(0xcA11bde05977b3631167028862bE2a173976CA11)
    const multicallAddress = "0xcA11bde05977b3631167028862bE2a173976CA11";
    const abi = [
        "function aggregate3(tuple(address target, bool allowFailure, bytes callData)[] calls) payable returns (tuple(bool success, bytes returnData)[])"
    ];
    
    const multicall = new ethers.Contract(multicallAddress, abi, provider);
    
    // ERC20 代幣餘額查詢批次
    const tokens = [
        { address: "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2", symbol: "WETH" }, // WETH
        { address: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48", symbol: "USDC" }, // USDC
        { address: "0xdAC17F958D2ee523a2206206994597C13D831ec7", symbol: "USDT" }  // USDT
    ];
    
    const walletAddress = "0x742d35Cc6634C0532925a3b844Bc9e7595f5b5b0";
    
    // 建構批次呼叫
    const calls = tokens.map(token => ({
        target: token.address,
        allowFailure: false,
        callData: new ethers.Interface([
            "function balanceOf(address) view returns (uint256)"
        ]).encodeFunctionData("balanceOf", [walletAddress])
    }));
    
    // 單一 RPC 呼叫獲取多個餘額
    const results = await multicall.aggregate3.staticCall(calls);
    
    results.forEach((result, i) => {
        const balance = ethers.formatUnits(result.returnData, 18);
        console.log(`${tokens[i].symbol}: ${balance}`);
    });
}

batchCalls().catch(console.error);

Python 版本:

from web3 import Web3
from eth_abi import encode

def batch_calls_multicall():
    w3 = Web3(Web3.HTTPProvider("https://mainnet.infura.io/v3/YOUR_PROJECT_ID"))
    
    multicall_address = "0xcA11bde05977b3631167028862bE2a173976CA11"
    
    # Multicall3 ABI
    multicall_abi = [{
        "inputs": [{
            "components": [
                {"name": "target", "type": "address"},
                {"name": "allowFailure", "type": "bool"},
                {"name": "callData", "type": "bytes"}
            ],
            "name": "calls",
            "type": "tuple[]"
        }],
        "name": "aggregate3",
        "outputs": [{
            "components": [
                {"name": "success", "type": "bool"},
                {"name": "returnData", "type": "bytes"}
            ],
            "name": "",
            "type": "tuple[]"
        }],
        "stateMutability": "payable",
        "type": "function"
    }]
    
    multicall = w3.eth.contract(address=multicall_address, abi=multicall_abi)
    
    tokens = [
        ("0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2", "WETH"),
        ("0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48", "USDC"),
    ]
    
    wallet_address = "0x742d35Cc6634C0532925a3b844Bc9e7595f5b5b0"
    
    # 建構批次呼叫
    calls = []
    for token_address, symbol in tokens:
        token_abi = [{
            "inputs": [{"name": "owner", "type": "address"}],
            "name": "balanceOf",
            "outputs": [{"type": "uint256"}],
            "stateMutability": "view",
            "type": "function"
        }]
        token = w3.eth.contract(address=token_address, abi=token_abi)
        
        call_data = token.functions.balanceOf(wallet_address).build_transaction()['data']
        
        calls.append({
            'target': token_address,
            'allowFailure': False,
            'callData': call_data
        })
    
    # 單一呼叫獲取多個餘額
    result = multicall.functions.aggregate3(calls).call()
    
    for i, (token_address, symbol) in enumerate(tokens):
        if result[i]['success']:
            balance = int.from_bytes(result[i]['returnData'], 'big')
            print(f"{symbol}: {balance / 10**18:.4f}")

batch_calls_multicall()

密碼學 Opcode:區塊鏈安全的基石

以太坊內建了幾個重要的密碼學 Opcode,這些是智慧合約能夠驗證簽名、計算哈希的基礎。

SHA3(Keccak-256):哈希函數

SHA3 → 30 Gas + 6 Gas per word

Keccak-256 是以太坊使用的標準哈希函數。Solidity 的 keccak256() 底層就是呼叫這個 Opcode。

CALLDATALOAD:讀取輸入資料

CALLDATALOAD → 3 Gas(熱)/ 2 Gas(?)
CALLDATACOPY → 3 Gas per word + memory cost

每次從外部呼叫讀取資料,都要消耗 Gas。這就是為什麼 calldatamemory 便宜的原因之一——calldata 的資料不需要複製。

Solidity 實驗室:

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.26;

contract CalldataExperiment {
    // 使用 calldata:更便宜
    function processCalldata(uint256 a, uint256 b, uint256 c) 
        public 
        pure 
        returns (uint256) 
    {
        return (a + b) * c;
    }
    
    // 使用 memory:較貴(需要複製)
    function processMemory(uint256 a, uint256 b, uint256 c) 
        public 
        pure 
        returns (uint256) 
    {
        uint256[] memory arr = new uint256[](3);
        arr[0] = a;
        arr[1] = b;
        arr[2] = c;
        return (arr[0] + arr[1]) * arr[2];
    }
    
    // 不必要的複製:浪費 Gas
    function wastefulCopy(uint256[] calldata data) 
        public 
        pure 
        returns (uint256) 
    {
        uint256[] memory temp = new uint256[](data.length);
        for (uint256 i = 0; i < data.length; i++) {
            temp[i] = data[i]; // 不必要的複製
        }
        return temp[0] + temp[1] + temp[2];
    }
    
    // 直接使用 calldata:節省 Gas
    function efficientCopy(uint256[] calldata data) 
        public 
        pure 
        returns (uint256) 
    {
        return data[0] + data[1] + data[2]; // 直接訪問
    }
}

LOG 相關 Opcode:事件監聽

LOG0 → 375 Gas + memory cost
LOG1 → 375 + 8*topic Gas + memory cost
LOG2 → 375 + 8*topic Gas + memory cost
LOG3 → 375 + 8*topic Gas + memory cost
LOG4 → 375 + 8*topic Gas + memory cost

每個 LOG Opcode 都有基礎成本,加上「主題」(topics)的額外成本。每個主題額外消耗 8 Gas。

Solidity 的 emit Transfer() 實際上就是呼叫 LOG 系列的 Opcode。

實務建議:如果你的事件不需要被程式過濾,盡量減少主題數量。

// 不好的設計:過多主題
event TransferWithTooManyTopics(
    address indexed from,      // 主題 0
    address indexed to,        // 主題 1
    address indexed token,     // 主題 2(通常不需要索引)
    uint256 value              // 主題 3(通常不需要索引)
);

// 好的設計:只索引必要欄位
event Transfer(
    address indexed from,
    address indexed to,
    uint256 value
);

// 更好的設計:放到 data 區段
event TransferData(
    bytes32 indexed fromTo,    // 雜湊 from + to
    uint256 value,
    bytes extraData
);

記憶體操作Opcode:動態成本模型

EIP-2028 之後,記憶體操作的 Gas 成本採用了「平方根模型」:

C_memory(a) = 3 * a + floor(a^2 / 512)

其中 a 是記憶體使用的位元組數(以 32 位元組為單位計算)。

這個模型的特點是:記憶體使用量小的時候,成本幾乎線性增長;但當記憶體使用量變大,成本增長速度會越來越快。

// 記憶體成本實驗
contract MemoryCost {
    function useLittleMemory() public pure {
        uint256 a;
        uint256 b;
        uint256 c;
        // 這些變數存在棧上,不消耗額外記憶體 Gas
        a = 1;
        b = 2;
        c = a + b;
    }
    
    function useModerateMemory() public pure {
        // 申請 64 位元組記憶體
        uint256[2] memory arr;
        arr[0] = 1;
        arr[1] = 2;
    }
    
    function useLargeMemory() public pure {
        // 申請 1KB 記憶體
        uint256[32] memory arr;
        for (uint256 i = 0; i < 32; i++) {
            arr[i] = i;
        }
    }
}

Gas 優化實戰:工程師的省錢秘笈

經過前面的 Opcode 分析,讓我總結幾個實用的 Gas 優化策略。

策略一:善用熱讀取優惠

// 糟糕的代碼
function badPattern(address user) public view {
    if (balance[user] > 0 && balance[user] > minAmount) {
        // ...
    }
}

// 每次都 SLOAD
// 改進後的代碼
function goodPattern(address user) public view {
    uint256 userBalance = balance[user]; // 一次 SLOAD
    if (userBalance > 0 && userBalance > minAmount) {
        // ...
    }
}

策略二:批量操作減少 CALL 成本

// 單筆鑄造:每次 65000 Gas
function mintManySingle(address[] calldata recipients) external {
    for (uint256 i = 0; i < recipients.length; i++) {
        _mint(recipients[i], tokenId++);
    }
}

// 批量鑄造:攤平固定成本
function mintManyBatch(address[] calldata recipients) external {
    uint256 batchSize = 10;
    uint256 remaining = recipients.length;
    
    while (remaining > 0) {
        uint256 toMint = remaining >= batchSize ? batchSize : remaining;
        _mintBatch(recipients[recipients.length - remaining],
                   tokenId, toMint);
        remaining -= toMint;
        tokenId += toMint;
    }
}

策略三:使用事件而不是回傳值

// 不好的設計:狀態變數讀取
function getValue() public view returns (uint256) {
    return storedValue; // SLOAD
}

// 好的設計:監聽事件
event ValueSet(uint256 newValue);

function setValue(uint256 _newValue) external {
    storedValue = _newValue;
    emit ValueSet(_newValue); // LOG0 便宜很多
}

// 查詢時用事件歷史

數據來源與延伸閱讀

一級來源(官方規格)

1. 以太坊黃皮書(Ethereum Yellow Paper)
   https://ethereum.github.io/yellowpaper/paper.pdf
   這是以太坊的正式技術規格,定義了所有 Opcode 的 Gas 消耗

2. EIP-150(Gas 成本重設)
   https://eips.ethereum.org/EIPS/eip-150
   引入了冷/熱讀取的 Gas 成本差異

3. EIP-1559(Fee 市場改革)
   https://eips.ethereum.org/EIPS/eip-1559
   改變了交易費用的計算方式

4. EIP-2028(Calldata Gas 成本降低)
   https://eips.ethereum.org/EIPS/eip-2028
   將 Calldata 每位元組成本從 68 降至 16 Gas

5. go-ethereum(geth)原始碼
   https://github.com/ethereum/go-ethereum/blob/master/core/vm/gas.go
   實作層面的 Gas 計算邏輯

二級來源(學術分析)

1. Ethereum Gas 追蹤器
   https://etherscan.io/gastracker
   即時 Gas 價格和區塊使用情況

2. Solidity 文件 - Gas 優化章節
   https://docs.soliditylang.org/en/latest/style-guide.html#avoiding-pitfalls
   官方推荐的 Gas 優化實踐

3. Vitalik 的 Gas 相關文章
   https://vitalik.ca/general/2019/12/09/gas_and_gold.html
   解释 Gas 成本設計背後的經濟學原理

工具推薦

Gas 估算工具:
- https://gasnow.xyz/ - 即時 Gas 預言機
- https://ethgasstation.info/ - Gas 費用預測

智能合約 Gas 分析:
- https://github.com/crytic/slither - Slither 安全分析
- https://github.com/crytic/echidna - 模糊測試工具

Gas 追蹤:
- Tenderly.co - 交易模擬和 Gas 分析
- https://dashboard.tenderly.co/

結語:搞懂 Opcode 才能真正最佳化

寫到這裡,我想強調一個核心觀念:Gas 不是魔術數字,是有邏輯的

每一個 Gas 單位的消耗,背後都有明確的計算依據——或許是 CPU 運算成本,或許是記憶體讀寫成本,或許是狀態存取成本。搞懂這些邏輯,你就不只是「憑感覺寫合約」,而是能真正從底層思考效率問題。

記住幾個關鍵點:

  1. 狀態讀寫是瓶頸:SLOAD 和 SSTORE 佔 Gas 消耗的大頭
  2. 熱讀取比冷讀取便宜 20 倍:善用這個特性
  3. 跨合約呼叫昂貴:盡量批量處理
  4. 記憶體成本呈平方增長:不需要的資料不要存

下次當你的合約 Gas 超標的時候,試著用這些Opcode 的視角去分析。你可能會發現一些意想不到的最佳化空間。


資料截止日期:2026 年 3 月

提醒:Opcode 的 Gas 消耗可能隨著 EIP 升級而改變。建議在部署前使用測試網實際驗證 Gas 消耗。

一級來源引用

延伸閱讀與來源

這篇文章對您有幫助嗎?

評論

發表評論

注意:由於這是靜態網站,您的評論將儲存在本地瀏覽器中,不會公開顯示。

目前尚無評論,成為第一個發表評論的人吧!