Gas Mechanism and Transfer Design

Gas Mechanism and Transfer Design

Learning Objectives

  • Understand the economic model and incentive mechanism of blockchain
  • Master the concepts and relationships of Gas, Gas Price, and Gas Fee
  • Understand Ether units and conversions
  • Master contract transfer design (receive / fallback / payable)

Blockchain Economic System

Why is an Economic Model Needed?

  • Computing and storage resources are scarce: Every node must execute and store all transactions
  • Consensus and trustlessness require miners' work: Miners (validators) need incentives to continue participating
  • Transaction execution has a cost (gas): Gas fees become a direct incentive for miners
  • Ether is the native currency of the ecosystem: Used for paying gas fees, transfers, and contract interactions

Ether Units

1 ETH = 10^18 Wei (Wei is the smallest unit)

// 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);
}

Common unit conversions:

Unit Wei Value
1 Wei 1
1 Gwei 10^9
1 Ether 10^18

Gas, Gas Fee, Gas Price

Relationship Among the Three

Gas Fee = Gas × Gas Price
  • Gas: Determined entirely by the execution logic. For functions with fixed logic, gas consumption is constant (e.g., a single SSTORE operation consistently consumes 20000 gas)
  • Gas Price: Set by the transaction initiator in the transaction, with Gwei as the unit. Gas Price is determined by market supply and demand—prices rise during network congestion and fall when idle
  • Gas Fee: The final fee paid, equal to the actual gas consumed multiplied by the gas price

Gas Mechanism Diagram

Gas Limit

Gas Limit exists at three levels:

  1. Transaction Level: The transaction initiator sets the gas limit, indicating the maximum amount of gas they are willing to consume
  2. Contract Call Level: When calling between contracts, a gas limit can be specified to control the gas consumption of sub-calls
  3. Block Level: Each block has an upper gas limit, restricting the total computation for a single block

gasleft() Function

gasleft() returns the currently available gas, which can be used for:
- Monitoring gas consumption
- Exiting early before gas runs out
- Limiting gas consumption of sub-calls

Refund Rules

  • Unused gas remaining will be refunded to the transaction initiator
  • If a transaction fails (revert / out of gas), consumed gas is not refunded
// 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
    }
}

Contracts Holding Ether

  • Contracts can hold Ether! Contract addresses, like EOA addresses, have balances
  • Contracts can freely transfer between other contracts or EOAs
  • Typical applications: Multisig Wallets, DeFi protocols, ICO contracts

Transfer Design Philosophy

Core Understanding

Transfers do not have a separate function; they occur together with function calls.

When calling a contract function, Ether can be sent simultaneously by attaching the option {value: amount}:

// 通过 call 转账
_to.call{value: 1 ether}("");

// 通过具名函数转账
contract.deposit{value: 1 ether}();

Called Function Resolution Logic (Important!)

When a contract receives a call, the EVM decides which function to execute based on the following logic:

Send Ether (Function Call)
        │
   Is msg.data empty?
      /         \
    No           Yes
    /             \
Function name matches?    receive() exists?
  /    \          /       \
 Yes    No        Yes        No
 /      \        /          \
Matching function  fallback()  receive()  fallback()

payable Check

  • If msg.value > 0 and the target function does not have the payable modifier → Call fails (automatic revert)
  • The sole purpose of the payable modifier is to allow a function to receive Ether

receive() Function

receive() is a function specifically for handling pure Ether transfers (when calldata is empty), making the responsibilities of fallback clearer:

  • receive(): Handles pure Ether transfers
  • fallback(): Handles calls to non-existent functions
// 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;
    }
}

Comparison of Transfer Methods

Method Gas Limit Recommendation Level Description
transfer Fixed 2300 Not Recommended Old design, insufficient gas for complex logic
send Fixed 2300 Not Recommended Old design, returns bool but often ignored
call Customizable Recommended Flexible, secure (requires return value check)
// 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}();
    }
}

Comprehensive Example: ERC20 Token + ICO

Below is a simplified ERC20 token contract, combining ICO (Initial Coin Offering) functionality, demonstrating the practical application of transfer design:

// 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!");
    }
}

Contract Function Analysis:

  1. mint(): Users send ETH to call this function, and the contract records the corresponding token balance (payable allows receiving ETH)
  2. transfer(): Transfers between token holders (note that this is a token-level transfer, not an ETH transfer)
  3. withdraw(): The contract owner withdraws all ETH held by the contract to their address, using the call{value: ...} method for transfer
  4. isOwner modifier: Permission control, ensuring only the contract deployer can withdraw

主题测试文章,只做测试使用。发布者:Walker,转转请注明出处:https://walker-learn.xyz/archives/7496

(0)
Walker的头像Walker
上一篇 5 days ago
下一篇 3 days ago

Related Posts

EN
简体中文 繁體中文 English