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