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
其中:
C_base:基本固定成本C_memory:記憶體操作成本(按記憶體使用量計算)C_compute:實際計算成本(因Opcode 而異)
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。這就是為什麼 calldata 比 memory 便宜的原因之一——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 運算成本,或許是記憶體讀寫成本,或許是狀態存取成本。搞懂這些邏輯,你就不只是「憑感覺寫合約」,而是能真正從底層思考效率問題。
記住幾個關鍵點:
- 狀態讀寫是瓶頸:SLOAD 和 SSTORE 佔 Gas 消耗的大頭
- 熱讀取比冷讀取便宜 20 倍:善用這個特性
- 跨合約呼叫昂貴:盡量批量處理
- 記憶體成本呈平方增長:不需要的資料不要存
下次當你的合約 Gas 超標的時候,試著用這些Opcode 的視角去分析。你可能會發現一些意想不到的最佳化空間。
資料截止日期:2026 年 3 月
提醒:Opcode 的 Gas 消耗可能隨著 EIP 升級而改變。建議在部署前使用測試網實際驗證 Gas 消耗。
一級來源引用:
- Ethereum Yellow Paper: https://ethereum.github.io/yellowpaper/paper.pdf
- EVM Opcode 規範: https://www.evm.codes/
- go-ethereum Gas 實作: https://github.com/ethereum/go-ethereum/tree/master/core/vm
相關文章
- 以太坊 EVM Opcodes 完整參考手冊:Gas 消耗數學推導與實戰最佳化指南 — 本文深入剖析 EVM 完整 opcode 指令集,從基礎的算術運算到複雜的存儲操作,提供完整的 Gas 消耗數學推導與實戰最佳化指南。涵蓋記憶體成本二次函數推導、SSTORE 狀態機制、CALL 系列的 cold/warm access 定價模型、日誌操作的 Gas 計算、以及 EOF 時代新 opcode 的完整解析。提供大量 Solidity 和 Assembly 程式碼範例,幫助開發者編寫更省 Gas 的智能合約。
- 以太坊虛擬機Opcode執行成本數學推導與量化分析完整指南 — 本文從量化工程師的視角,深入推導 EVM Opcode 執行成本的數學基礎。我們從計算理論角度分析不同 Opcode 的資源消耗,建立完整的 Gas 成本模型,包括記憶體擴展成本的二次函數特性、儲存操作的層級定價、密碼學操作的複雜度分析、以及密碼學預編譯合約的成本函數。同時提供 Solidity、Python 和 TypeScript 的實作程式碼範例。
- EVM Opcode 層級 Gas 優化完全指南:從底層原理到實戰技巧 — 深入理解 EVM Opcode 層面的 Gas 消耗機制,並據此進行優化,不僅可以顯著降低用戶的交易成本,還能提升合約的整體效率。本文從 EVM Opcode 的基礎出發,系統性地分析各類 Opcode 的 Gas 消耗特性,並提供大量可直接應用於實際項目的優化技巧。
- Solidity 位元運算優化完整指南:Gas 節省與智能合約效能極致優化 — 本指南從 EVM 機器碼層級出發,系統性地分析各類位元運算 opcode 的 Gas 消耗模型,提供可直接應用於生產環境的優化策略與程式碼範例。涵蓋定點數學與定標因子運算、位元遮罩與旗標操作、雜湊與簽章驗證優化、壓縮資料結構與位元封裝等進階主題。
- 以太坊 EVM 深度技術分析:從 Solidity 編譯到 EVM Opcode 追蹤 — 本文深入剖析以太坊虛擬機(EVM)的底層運作原理。從 Solidity 編譯過程開始,追蹤合約如何轉換為 bytecode、函數選擇器的運作機制,以及 EVM 三層儲存模型(Stack、Memory、Storage)的設計原理與 Gas 消耗分析。提供完整的 opcode 追蹤範例,包含 Solidity 0.8.x 的編譯最佳化、Storage 讀寫的冷熱存取差異、以及實用的 Gas 節省技巧。
延伸閱讀與來源
- Ethereum.org Developers 官方開發者入口與技術文件
- EIPs 以太坊改進提案完整列表
- Solidity 文檔 智慧合約程式語言官方規格
- EVM 代碼庫 EVM 實作的核心參考
- Alethio EVM 分析 EVM 行為的正規驗證
這篇文章對您有幫助嗎?
請告訴我們如何改進:
評論
發表評論
注意:由於這是靜態網站,您的評論將儲存在本地瀏覽器中,不會公開顯示。
目前尚無評論,成為第一個發表評論的人吧!