Gas機制與轉賬設計

Gas機制與轉賬設計

學習目標

  • 理解區塊鏈的經濟模型與激勵機制
  • 掌握 Gas、Gas Price、Gas Fee 的概念與關係
  • 理解 Ether 單位與轉換
  • 掌握合約轉賬設計(receive / fallback / payable)

區塊鏈的經濟系統

爲什麼需要經濟模型?

  • 計算與存儲資源是稀缺的:每個節點都要執行和存儲所有交易
  • 共識和 trustless 需要礦工工作:礦工(驗證者)需要激勵纔會持續參與
  • Transaction 執行有成本(gas):gas 費成爲礦工的直接激勵
  • Ether 是生態系統的原生貨幣:用於支付 gas 費、轉賬、合約交互

Ether 單位

1 ETH = 10^18 Wei(Wei 是最小單位)

// SPDX-License-Identifier: MIT
pragma solidity >=0.8.2 <0.9.0;

contract EtherUnits {
    uint public oneWei = 1 wei;
    bool public isOneWei = (1 wei == 1);

    uint public oneEther = 1 ether;
    bool public isOneEther = (1 ether == 1e18);
}

常用單位換算:

單位 Wei 值
1 Wei 1
1 Gwei 10^9
1 Ether 10^18

Gas、Gas Fee、Gas Price

三者的關係

Gas Fee = Gas × Gas Price
  • Gas:完全由執行邏輯決定。固定邏輯的函數,gas 消耗是不變的(例如一次 SSTORE 操作固定消耗 20000 gas)
  • Gas Price:由交易發起者在 transaction 中設定,單位爲 Gwei。Gas Price 由市場供需決定——網絡擁堵時價格上漲,空閒時價格下降
  • Gas Fee:最終支付的費用,等於實際消耗的 gas 乘以 gas price

Gas機制示意圖

Gas Limit

Gas Limit 存在於三個層面:

  1. 交易層面:交易發起者設定 gaslimit,表示最多願意消耗多少 gas
  2. 合約調用層面:合約間調用時可以指定 gaslimit,控制子調用的 gas 消耗
  3. 區塊層面:每個區塊有 gaslimit 上限,限制單個區塊的總計算量

gasleft() 函數

gasleft() 返回當前剩餘可用的 gas,可用於:
- 監控 gas 消耗
- 在 gas 不足前提前退出
- 限制子調用的 gas 消耗

退款規則

  • 剩餘未用的 gas 會退款給交易發起者
  • 交易失敗(revert / out of gas)時,已消耗的 gas 不退
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.2 <0.9.0;

contract Gas {
    uint public i = 0;
    uint public remained;

    function forever() public {
        while (true) {
            if (i > 100) return;
            if (i == 10) {
                remained = gasleft(); // 記錄剩餘 gas
            }
            i += 1;
        }
    }
}

contract GasCaller {
    Gas private gas;

    constructor(Gas _gas) {
        gas = _gas;
    }

    function callForever() public {
        gas.forever{gas: 200000}(); // 限制子調用最多消耗 200000 gas
    }
}

合約持有 Ether

  • 合約可以持有 Ether! 合約地址和 EOA 地址一樣,都有餘額
  • 合約與其他合約或 EOA 之間可以自由轉賬
  • 典型應用:多籤錢包(Multisig Wallet)、DeFi 協議、ICO 合約

轉賬設計思想

核心理解

轉賬沒有單獨的函數,轉賬是與函數調用一起發生的。

調用合約函數時,通過附加選項 {value: amount} 來同時發送 Ether:

// 通過 call 轉賬
_to.call{value: 1 ether}("");

// 通過具名函數轉賬
contract.deposit{value: 1 ether}();

被調用函數解析邏輯(重要!)

當合約收到一筆調用時,EVM 按以下邏輯決定執行哪個函數:

發送 Ether(函數調用)
        │
   msg.data 是否爲空?
      /         \
    否           是
    /             \
函數名匹配?    receive() 存在?
  /    \          /       \
 是    否        是        否
 /      \        /          \
匹配函數  fallback()  receive()  fallback()

payable 檢查

  • 如果 msg.value > 0 且目標函數沒有 payable 修飾符 → 調用失敗(自動 revert)
  • payable 修飾符的唯一作用就是允許函數接收 Ether

receive() 函數

receive() 是專門處理純轉賬(calldata 爲空)的函數,使 fallback 的職責更加清晰:

  • receive():處理純 Ether 轉賬
  • fallback():處理調用不存在的函數
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.2 <0.9.0;

contract ReceiveEther {
    string public caller;

    receive() external payable {
        caller = "receive";
    }

    fallback() external payable {
        caller = "fallback";
    }

    function deposit() public payable {
        // 接收 Ether 的普通函數
    }

    function getBalance() public view returns (uint) {
        return address(this).balance;
    }
}

轉賬方式對比

方式 Gas Limit 推薦程度 說明
transfer 固定 2300 不推薦 舊設計,gas 不足以執行復雜邏輯
send 固定 2300 不推薦 舊設計,返回 bool 但常被忽略
call 可自定義 推薦 靈活,安全(需檢查返回值)
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.2 <0.9.0;

contract SendEther {
    // 推薦方式:通過 call 轉賬
    function sendViaCall(address payable _to) public payable {
        (bool sent, ) = _to.call{value: msg.value}("");
        require(sent, "Failed to send Ether");
    }

    // 通過具名函數轉賬
    function sendViaFoo(address payable _to) public payable {
        ReceiveEther re = ReceiveEther(_to);
        re.deposit{value: msg.value}();
    }
}

綜合實例:ERC20 Token + ICO

以下是一個簡化的 ERC20 代幣合約,結合了 ICO(首次代幣發行)功能,展示了轉賬設計的實際應用:

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

contract MyErc20Token {
    string public name = "MyDollar";
    string public symbol = "$";
    uint public decimals = 4;
    address public owner;
    mapping(address => uint) public balanceOf;

    constructor() {
        owner = msg.sender;
    }

    // 鑄幣:用戶發送 ETH 購買代幣
    function mint() external payable {
        balanceOf[msg.sender] += msg.value;
    }

    event TransferEvent(uint oldv, uint newv);

    modifier isOwner() {
        require(msg.sender == owner, "not owner!");
        _;
    }

    // 轉賬:從調用者賬戶轉給目標地址
    function transfer(address to, uint amount) public {
        address from = msg.sender;
        uint current = balanceOf[from];
        require(current >= amount, "not enough balance!");
        uint toc = balanceOf[to];

        current -= amount;
        toc += amount;
        balanceOf[from] = current;
        balanceOf[to] = toc;
    }

    // 提現:僅 owner 可以將合約中的 ETH 提取到自己的地址
    function withdraw() external isOwner {
        (bool suc, ) = owner.call{value: address(this).balance}("");
        require(suc, "failed!");
    }
}

合約功能解析

  1. mint():用戶發送 ETH 調用此函數,合約記錄對應的代幣餘額(payable 允許接收 ETH)
  2. transfer():代幣持有者之間的轉賬(注意這是代幣層面的轉賬,不是 ETH 轉賬)
  3. withdraw():合約 owner 將合約持有的所有 ETH 提取出來,使用 call{value: ...} 方式轉賬
  4. isOwner modifier:權限控制,確保只有合約部署者可以提現

主題測試文章,只做測試使用。發佈者:Walker,轉轉請注明出處:https://walker-learn.xyz/archives/7496

(0)
Walker的頭像Walker
上一篇 5天前
下一篇 3天前

相關推薦

  • Web3 概述與願景

    Web3 概述與願景 學習目標 理解 Web1、Web2、Web3 的演進歷程與核心區別 掌握 Web3 的核心理念:去中心化、數據確權、用戶主權 瞭解 Web3 帶來的創新機會與全新商業模式 熟悉 Web3 開發者的學習路線圖 前置知識 基本的互聯網使用經驗 對軟件開發有初步瞭解(非必須,但有助於理解技術部分) 一、Web1 → Web2 → Web3 的…

    Web3與WASM 19小時前
    600
  • 函數定義與訪問控制

    函數定義與訪問控制 學習目標 掌握 Solidity 函數定義、可見性修飾符、交易屬性、modifier 和構造函數。 函數定義 一般形式 function fname([參數]) [可見性][交易屬性][modifier...] returns(返回值) { ... } 函數簽名:fname([參數]) —— 唯一標識一個函數 返回值:returns(返回…

  • 引用類型詳解:數組、結構體、映射、字符串

    引用類型詳解:數組、結構體、映射、字符串 學習目標 掌握 Solidity 中四種引用類型(數組、結構體、映射、字符串/變長字節數組)的定義和使用方法。 前置知識 已學習值類型(整型、布爾、地址、定長字節數組等)。 數組(Array) storage 中的數組 Solidity 中數組分爲兩種: 靜態數組 T[K]:長度固定,編譯時確定 動態數組 T[]:長…

  • Solidity 入門與開發環境

    Solidity 入門與開發環境 學習目標 理解智能合約的本質與核心特性 掌握合約在以太坊上的運行原理(Transaction + EVM) 認識 Solidity 語言特點與開發工具鏈 編寫並部署第一個智能合約 前置知識 瞭解區塊鏈基本概念(區塊、交易、共識) 瞭解以太坊賬戶模型(EOA 與合約賬戶) 基本編程經驗(任意語言均可) 一、智能合約的根本性質 …

    1天前
    1200
  • Delegatecall 與代理模式

    Delegatecall 與代理模式 學習目標 理解 delegatecall 的工作原理 掌握 Storage Layout(存儲佈局)規則 掌握代理模式與合約升級 瞭解非結構化存儲(Unstructured Storage) delegatecall 原理 什麼是 delegatecall 語法:address.delegatecall(bytes ca…

    21小時前
    200
簡體中文 繁體中文 English