首页
学习
活动
专区
圈层
工具
发布
49 篇文章
1
《纸上谈兵·solidity》第 0 课:搭建 Solidity 开发环境(三种方式)
2
《纸上谈兵·solidity》第 1 课:部署你的第一个 Solidity 合约
3
《纸上谈兵·solidity》第 2 课:调用、修改、读取,Solidity 合约不是 REST API
4
《纸上谈兵·solidity》第 3 课:事件(Event)机制与链上日志——不是 print,是广播!
5
《纸上谈兵·solidity》第 4 课:Solidity 合约中的错误处理机制(`require`、`revert`、`assert`)和自定义错误
6
《纸上谈兵·solidity》第 5 课:依赖与外部调用 —— 合约交互的风险与防护
7
《纸上谈兵·solidity》第 6 课:Solidity 数据存储布局 —— memory、storage、calldata 傻傻分不清?
8
《纸上谈兵·solidity》第 7 课:Solidity 函数可见性和修饰器 —— public 和 private 不只是权限标签
9
《纸上谈兵·solidity》第 8 课:Solidity 中的继承与接口 —— 模块化不是“复制粘贴”的借口
10
《纸上谈兵·solidity》第 9 课:Solidity 事件与日志机制 —— 合约世界的“printf”工具
11
《纸上谈兵·solidity》第 10 课:Solidity `fallback` / `receive` 函数 —— 合约如何收 ETH 和响应未知调用?
12
《纸上谈兵·solidity》第 11 课:Solidity 错误处理与异常机制 —— 让合约优雅地失败
13
《纸上谈兵·solidity》第 12 课:Solidity 函数选择器与 ABI 编码原理
14
《纸上谈兵·solidity》第 13 课:Solidity 低级调用 call/delegatecall/staticcall —— 直接和 EVM“对话”
15
《纸上谈兵·solidity》第 14 课:Solidity 中的可升级合约模式 —— 从代理合约到透明代理、UUPS 与安全陷阱
16
《纸上谈兵·solidity》第 15 课:Solidity 库与可重用代码
17
《纸上谈兵·solidity》第 16 课:Pull over Push 支付模式与 Check-Effects-Interactions 原则
18
《纸上谈兵·solidity》第 17 课:合约设计模式实战(二)—— Access Control 与权限管理
19
《纸上谈兵·solidity》第 18 课:合约设计模式实战(三)—— 代理 + 插件化架构(Diamond Standard / EIP-2535)
20
《纸上谈兵·solidity》第 19 课:安全专题(一)—— 常见攻击手法与防御
21
《纸上谈兵·solidity》第 20 课:Solidity 安全专题(二)—— 编译器特性与低级漏洞
22
《纸上谈兵·solidity》第 21 课:Gas 优化与成本分析 —— 写出便宜的智能合约
23
《纸上谈兵·solidity》第 22 课:代币合约(ERC20)从零实现与扩展
24
《纸上谈兵·solidity》第 23 课:NFT 合约(ERC721 / ERC1155)实战
25
《纸上谈兵·solidity》第 24 课:去中心化众筹合约(Crowdfunding)实战
26
《纸上谈兵·solidity》第 25 课:简化版的去中心化交易所(DEX)简化版
27
《纸上谈兵·solidity》第 26 课:借贷合约简化实现
28
《纸上谈兵·solidity》第 27 课:DAO 治理合约(去中心化自治组织)
29
《纸上谈兵·solidity》第 28 课:智能合约安全审计案例复盘 -- The DAO Hack(2016)
30
《纸上谈兵·solidity》第 29 课:智能合约安全审计案例复盘 -- Parity Wallet Hack(2017)
31
《纸上谈兵·solidity》第 30 课:智能合约安全审计案例复盘 -- Nomad Bridge(2022)
32
《纸上谈兵·solidity》第 31 课:多签钱包在跨链桥中的应用 —— Nomad 事件复盘
33
《纸上谈兵·solidity》第 32 课:DeFi 基础合约
34
《纸上谈兵·solidity》第 33 课:多签钱包(Multisig Wallet)-- 合约设计与实现
35
《纸上谈兵·solidity》第 34 课:多签钱包(Multisig Wallet)-- 上线
36
《纸上谈兵·solidity》第 35 课:去中心化交易所(DEX)实战 — 合约设计
37
《纸上谈兵·solidity》第 36 课:去中心化交易所(DEX)实战 — 上线
38
《纸上谈兵·solidity》第 37 课:DeFi 实战 -- 资金池与利率模型
39
《纸上谈兵·solidity》第 38 课:DeFi 实战(2) -- 清算机制与价格预言机
40
《纸上谈兵·solidity》第 39 课:DeFi 实战(3) -- 利息累积与 aToken 设计
41
《纸上谈兵·solidity》第 40 课:DeFi 实战(4) -- 风险控制与防护
42
《纸上谈兵·solidity》第 41 课:DeFi 实战(5) -- 协议费与治理
43
《纸上谈兵·solidity》第 42 课:DeFi 实战(6) -- 跨资产借贷与多市场支持
44
《纸上谈兵·solidity》第 43 课:DeFi 实战(7) -- 清算机制进阶(多资产抵押清算路径、拍卖机制)
45
《纸上谈兵·solidity》第 44 课:DeFi 实战(8) -- 利率曲线与资金池优化(动态利用率模型)
46
《纸上谈兵·solidity》第 45 课:DeFi 实战(9) -- 利息累积与结算机制(可复利)
47
《纸上谈兵·solidity》第 46 课:DeFi 实战(10) -- 跨链借贷与流动性桥接
48
《纸上谈兵·solidity》第 47 课:DeFi 实战(11) -- 治理代币 & 激励机制(Tokenomics & Governance)
49
《纸上谈兵·solidity》第 48 课:DeFi 实战(12) -- 前端 DApp 集成与用户交互(React + ethers.js 实战)

《纸上谈兵·solidity》第 21 课:Gas 优化与成本分析 —— 写出便宜的智能合约

课程目标

  • 理解智能合约的主要 Gas 消耗来源
  • 掌握常见的 优化技巧与模式
  • 通过 Foundry 对比 优化前后 Gas 成本
  • 建立“写出高效合约”的思维框架

1、Gas 的构成

在 EVM 中,Gas 主要分为三类:

  1. 计算消耗:算术运算、函数调用等
  2. 存储消耗:对 storage 的读写最昂贵
  3. 交易消耗:部署合约、转账、事件日志

其中最关键的是 storage 写入,单次写入约 20,000 gas(如果从 0 改为非 0)。


2、优化点概览

优化点

建议做法

原因

存储变量访问

先读入 memory 变量,再多次使用

减少重复 SLOAD 成本

storage vs memory vs calldata

尽量使用 calldata 作为函数参数

最便宜,避免复制

变量打包

将多个 uint128 合并到一个 uint256 slot

节省存储槽

固定长度数组

bytes32 替代 string/bytes

减少动态存储开销

循环

避免无限增长数组迭代

每次循环线性增加 Gas

事件

只记录必要字段

日志存储在链上也要付费

External call

批处理、懒执行

减少重复调用


3、常见优化技巧

1. 避免重复 SLOAD

代码语言:solidity
复制
// 差的写法
function bad(uint256 x) external {
    for (uint i = 0; i < 10; i++) {
        storageVar += x;
    }
}

// 优化写法
function good(uint256 x) external {
    uint256 tmp = storageVar;
    for (uint i = 0; i < 10; i++) {
        tmp += x;
    }
    storageVar = tmp;
}

在循环中访问 storage 十次,成本远高于一次写回。


2. 使用 calldata 代替 memory

代码语言:solidity
复制
// 差的写法
function sum(uint256[] memory arr) external pure returns (uint256 s) {
    for (uint i = 0; i < arr.length; i++) {
        s += arr[i];
    }
}

// 优化写法
function sum(uint256[] calldata arr) external pure returns (uint256 s) {
    for (uint i = 0; i < arr.length; i++) {
        s += arr[i];
    }
}

memory 会复制数组,而 calldata 直接引用,节省大量 gas。


3. 变量打包

代码语言:solidity
复制
// 差的写法
struct Bad {
    uint128 a;
    uint128 b;
    uint128 c;
}

// 优化写法(两个 slot -> 一个 slot)
struct Good {
    uint128 a;
    uint128 b;
    uint256 c;
}

Solidity 会将多个小于 256bit 的变量打包进一个存储槽。


4. 事件优化

代码语言:solidity
复制
// 差的写法
event Deposit(address indexed user, uint256 amount, string memo);

// 优化写法
event Deposit(address indexed user, uint256 amount);

string 会额外消耗存储空间,除非业务必须,尽量避免。


4、Foundry 实战:Gas 对比测试

合约:未优化 vs 优化

代码语言:solidity
复制
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

contract BadContract {
    uint256 public value;

    function addMany(uint256 x) external {
        for (uint i = 0; i < 10; i++) {
            value += x; // 每次都访问 storage
        }
    }
}

contract GoodContract {
    uint256 public value;

    function addMany(uint256 x) external {
        uint256 tmp = value;
        for (uint i = 0; i < 10; i++) {
            tmp += x;
        }
        value = tmp; // 只写一次 storage
    }
}

测试:比较 Gas 消耗

代码语言:solidity
复制
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

import "forge-std/Test.sol";
import "../src/Gas.sol";

contract GasTest is Test {
    BadContract bad;
    GoodContract good;

    function setUp() public {
        bad = new BadContract();
        good = new GoodContract();
    }

    function testGasBad() public {
        bad.addMany(1);
    }

    function testGasGood() public {
        good.addMany(1);
    }
}

执行测试:

代码语言:bash
复制
➜  tutorial git:(main) ✗ forge test --match-path test/Gas.t.sol --gas-report -vvv 

[⠊] Compiling...
[⠒] Compiling 2 files with Solc 0.8.30
[⠑] Solc 0.8.30 finished in 506.84ms
Compiler run successful!

Ran 2 tests for test/Gas.t.sol:GasTest
[PASS] testGasBad() (gas: 53693)
[PASS] testGasGood() (gas: 51716)
Suite result: ok. 2 passed; 0 failed; 0 skipped; finished in 3.57ms (894.88µs CPU time)

╭----------------------------------+-----------------+-------+--------+-------+---------╮
| src/Gas.sol:BadContract Contract |                 |       |        |       |         |
+=======================================================================================+
| Deployment Cost                  | Deployment Size |       |        |       |         |
|----------------------------------+-----------------+-------+--------+-------+---------|
| 152487                           | 489             |       |        |       |         |
|----------------------------------+-----------------+-------+--------+-------+---------|
|                                  |                 |       |        |       |         |
|----------------------------------+-----------------+-------+--------+-------+---------|
| Function Name                    | Min             | Avg   | Median | Max   | # Calls |
|----------------------------------+-----------------+-------+--------+-------+---------|
| addMany                          | 48203           | 48203 | 48203  | 48203 | 1       |
╰----------------------------------+-----------------+-------+--------+-------+---------╯

╭-----------------------------------+-----------------+-------+--------+-------+---------╮
| src/Gas.sol:GoodContract Contract |                 |       |        |       |         |
+========================================================================================+
| Deployment Cost                   | Deployment Size |       |        |       |         |
|-----------------------------------+-----------------+-------+--------+-------+---------|
| 153147                            | 492             |       |        |       |         |
|-----------------------------------+-----------------+-------+--------+-------+---------|
|                                   |                 |       |        |       |         |
|-----------------------------------+-----------------+-------+--------+-------+---------|
| Function Name                     | Min             | Avg   | Median | Max   | # Calls |
|-----------------------------------+-----------------+-------+--------+-------+---------|
| addMany                           | 46257           | 46257 | 46257  | 46257 | 1       |
╰-----------------------------------+-----------------+-------+--------+-------+---------╯


Ran 1 test suite in 6.50ms (3.57ms CPU time): 2 tests passed, 0 failed, 0 skipped (2 total tests)

5、进阶优化策略

  1. 批处理:合并多次转账为一次
  2. 懒执行:只在必要时更新状态变量(例如延迟结算)
  3. 预计算:在链下完成复杂运算,把结果上链
  4. 库函数优化:使用 unchecked {} 避免多余溢出检查(适合已知安全场景)

6、总结

  • Gas 优化的关键:减少 storage 写入,避免不必要的复制,合理打包变量
  • 使用 Foundry 的 --gas-report 工具可以直观对比优化效果
  • 优化要 兼顾安全与可读性,不要为省 gas 牺牲合约清晰度
下一篇
举报
领券