语法教程

Solidity 语法速查手册

涵盖所有核心语法:数据类型、函数、修饰符、事件、继承、错误处理等

1. Solidity 简介

Solidity 是以太坊智能合约的主流编程语言,语法类似 JavaScript,面向对象,静态类型。

核心特性

  • 静态类型 - 编译时类型检查
  • 面向对象 - 支持继承、接口、抽象合约
  • EVM 字节码 - 编译为以太坊虚拟机字节码
  • 内置区块链特性 - msg.sender、block.timestamp 等全局变量

版本说明

  • 当前稳定版:^0.8.x(推荐)
  • 0.8.0+ 默认开启溢出检查
  • 使用 pragma 指定版本:pragma solidity ^0.8.20;

2. 合约结构

一个 Solidity 合约包含:状态变量、函数、修饰符、事件、错误、结构体、枚举等。

Contract Structure Example
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

import "@openzeppelin/contracts/access/Ownable.sol";

contract MyContract is Ownable {
    // State variables
    uint256 public count;
    mapping(address => uint256) public balances;

    // Events
    event CountIncreased(uint256 newCount);

    // Errors
    error InsufficientBalance(uint256 requested, uint256 available);

    // Modifiers
    modifier onlyPositive(uint256 value) {
        require(value > 0, "Value must be positive");
        _;
    }

    // Constructor
    constructor() Ownable(msg.sender) {
        count = 0;
    }

    // Functions
    function increment() public onlyPositive(1) {
        count++;
        emit CountIncreased(count);
    }
}

合约组成部分

  • License & Pragma - 许可证和版本声明
  • Import - 导入其他合约
  • State Variables - 状态变量(存储在链上)
  • Functions - 函数(external/public/internal/private)
  • Modifiers - 修饰符(函数执行前/后的检查)
  • Events - 事件(用于日志记录)
  • Errors - 自定义错误(0.8.4+)
  • Struct & Enum - 自定义数据结构

3. 数据类型

Solidity 支持值类型和引用类型。

Data Types Example
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

contract DataTypesDemo {
    // Value Types
    bool public isActive = true;
    uint256 public myUint = 123;
    int256 public myInt = -456;
    address public myAddress = 0x1234567890123456789012345678901234567890;
    address payable public myPayableAddress = payable(msg.sender);
    bytes32 public myBytes32 = "Hello";

    // Reference Types
    string public myString = "Hello World";
    bytes public myDynamicBytes = "Dynamic";
    uint256[] public myArray;
    mapping(address => uint256) public myMapping;

    struct Person {
        string name;
        uint256 age;
    }
    Person public myPerson = Person("Alice", 30);

    enum Status { Pending, Active, Closed }
    Status public currentStatus = Status.Pending;

    // Data locations
    function dataLocationExample(string memory _str) public pure returns (string memory) {
        // memory: temporary, cheap
        string memory tempStr = _str;
        return tempStr;
    }

    function dataLocationExample2(uint256[] calldata _arr) external pure returns (uint256) {
        // calldata: read-only, cheapest for external functions
        return _arr[0];
    }
}

值类型(Value Types)

  • bool - true / false
  • uint8 ~ uint256 - 无符号整数(uint 默认 uint256)
  • int8 ~ int256 - 有符号整数
  • address - 20字节以太坊地址
  • address payable - 可接收 ETH 的地址
  • bytes1 ~ bytes32 - 固定长度字节数组
  • enum - 枚举类型

引用类型(Reference Types)

  • string - 动态长度 UTF-8 字符串
  • bytes - 动态长度字节数组
  • array - 数组(T[] 或 T[5])
  • mapping - 映射(哈希表)
  • struct - 结构体

数据位置

  • storage - 永久存储(状态变量,gas 高)
  • memory - 临时存储(函数参数/返回值,gas 低)
  • calldata - 只读临时存储(external 函数参数,gas 最低)

4. 变量与常量

Solidity 有三种变量类型:状态变量、局部变量、全局变量。

Variables & Constants Example
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

contract VariablesDemo {
    // State variables (stored on-chain)
    uint256 public stateVar = 100;

    // Constants (compile-time, save gas)
    uint256 public constant MY_CONSTANT = 123;
    address public constant OWNER = 0x1234567890123456789012345678901234567890;

    // Immutable (deploy-time, save gas)
    uint256 public immutable DEPLOY_TIMESTAMP;
    address public immutable DEPLOYER;

    constructor() {
        DEPLOY_TIMESTAMP = block.timestamp;
        DEPLOYER = msg.sender;
    }

    function globalVariablesExample() public payable returns (
        address sender,
        uint256 value,
        uint256 timestamp,
        uint256 blockNumber,
        uint256 contractBalance
    ) {
        // Global variables
        sender = msg.sender;          // caller address
        value = msg.value;            // ETH sent (in wei)
        timestamp = block.timestamp;  // current block time
        blockNumber = block.number;   // current block number
        contractBalance = address(this).balance;
    }
}

状态变量(State Variables)

  • 存储在链上,消耗 gas
  • 默认 internal 可见性
  • 可用 public/private/internal 修饰

常量(Constants & Immutable)

  • constant - 编译时常量,必须赋值为常量表达式
  • immutable - 部署时常量,可在 constructor 中赋值
  • 两者都节省 gas(不占用 storage slot) -

全局变量(Global Variables)

  • msg.sender - 调用者地址
  • msg.value - 发送的 ETH 数量(wei)
  • block.timestamp - 当前区块时间戳
  • block.number - 当前区块号
  • tx.gasprice - 交易 gas 价格
  • address(this).balance - 合约余额

5. 函数

函数是智能合约的核心逻辑。

Functions Example
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

contract FunctionsDemo {
    uint256 public count;

    // Visibility modifiers
    function publicFunc() public { count++; }          // callable by anyone
    function externalFunc() external { count++; }      // only external calls
    function internalFunc() internal { count++; }      // only this contract + derived
    function privateFunc() private { count++; }        // only this contract

    // State mutability
    function viewFunc() public view returns (uint256) {
        return count;  // read-only
    }

    function pureFunc(uint256 a, uint256 b) public pure returns (uint256) {
        return a + b;  // no state access
    }

    function payableFunc() public payable {
        // can receive ETH
    }

    // Return values
    function multipleReturns() public pure returns (uint256, bool, string memory) {
        return (123, true, "hello");
    }

    function namedReturns() public pure returns (uint256 x, bool y) {
        x = 456;
        y = false;
    }

    // Special functions
    constructor() {
        count = 0;  // runs once at deployment
    }

    receive() external payable {
        // called when ETH sent with no data
    }

    fallback() external payable {
        // called when function doesn't exist
    }
}

可见性(Visibility)

  • public - 内部和外部都可调用
  • external - 仅外部调用(gas 更省)
  • internal - 仅当前合约和子合约
  • private - 仅当前合约

状态可变性(State Mutability)

  • view - 只读函数,不修改状态
  • pure - 纯函数,不读不写状态
  • payable - 可接收 ETH
  • 默认 - 可读写状态

特殊函数

  • constructor - 部署时执行一次
  • receive - 接收 ETH(无 data)
  • fallback - 找不到函数时调用

6. 修饰符 (Modifier)

Modifier 用于在函数执行前/后进行检查,类似装饰器。

Modifiers Example
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

contract ModifiersDemo {
    address public owner;
    bool public paused;
    mapping(address => bool) public whitelist;

    constructor() {
        owner = msg.sender;
    }

    // Access control modifier
    modifier onlyOwner() {
        require(msg.sender == owner, "Not owner");
        _;  // function body executes here
    }

    // State check modifier
    modifier whenNotPaused() {
        require(!paused, "Contract is paused");
        _;
    }

    // Input validation modifier
    modifier validAddress(address _addr) {
        require(_addr != address(0), "Invalid address");
        require(_addr != address(this), "Cannot be contract address");
        _;
    }

    // Multiple modifiers
    function sensitiveAction(address _to)
        public
        onlyOwner
        whenNotPaused
        validAddress(_to)
    {
        // modifiers execute in order: onlyOwner -> whenNotPaused -> validAddress
        whitelist[_to] = true;
    }

    // Modifier with return value (runs after function)
    modifier logExecution() {
        _;
        emit ActionExecuted(msg.sender, block.timestamp);
    }

    event ActionExecuted(address indexed user, uint256 timestamp);
}

常见用途

  • 权限控制(onlyOwner)
  • 输入验证(检查参数合法性)
  • 重入保护(nonReentrant)
  • 状态检查(合约是否暂停)

_; 的作用

_; 表示被修饰函数的执行位置。在 _; 之前的代码在函数执行前运行,之后的代码在函数执行后运行。

7. 事件 (Event)

事件用于记录链上日志,前端可监听事件获取实时数据。

Events Example
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

contract EventsDemo {
    // Event declaration
    event Transfer(address indexed from, address indexed to, uint256 amount);
    event Deposit(address indexed user, uint256 amount, uint256 timestamp);
    event StatusChanged(string oldStatus, string newStatus);

    mapping(address => uint256) public balances;

    function transfer(address to, uint256 amount) public {
        require(balances[msg.sender] >= amount, "Insufficient balance");

        balances[msg.sender] -= amount;
        balances[to] += amount;

        // Emit event (frontend can listen to this)
        emit Transfer(msg.sender, to, amount);
    }

    function deposit() public payable {
        balances[msg.sender] += msg.value;

        // indexed parameters can be filtered in frontend
        emit Deposit(msg.sender, msg.value, block.timestamp);
    }

    // Why use events?
    // 1. Much cheaper than storage
    // 2. Frontend can listen in real-time
    // 3. Create permanent log records
    // 4. ERC20/721 standards require events
}

事件特性

  • emit 触发事件
  • indexed 参数可被过滤(最多3个)
  • 比存储在 storage 便宜得多
  • 前端通过 web3.js/ethers.js 监听

典型使用场景

  • ERC20 Transfer/Approval 事件
  • 所有权转移事件
  • 状态变更通知
  • 前端实时更新 UI

8. 错误处理

Solidity 提供三种错误处理方式。

Error Handling Example
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

contract ErrorsDemo {
    uint256 public balance;

    // Custom errors (0.8.4+, more gas efficient)
    error InsufficientBalance(uint256 requested, uint256 available);
    error Unauthorized(address caller);
    error InvalidAmount();

    function withdraw(uint256 amount) public {
        // Method 1: require (most common)
        require(amount > 0, "Amount must be positive");
        require(balance >= amount, "Insufficient balance");

        balance -= amount;
    }

    function withdrawWithCustomError(uint256 amount) public {
        // Method 2: custom error + revert (saves gas)
        if (amount == 0) revert InvalidAmount();
        if (balance < amount) {
            revert InsufficientBalance(amount, balance);
        }

        balance -= amount;
    }

    function directRevert() public pure {
        // Method 3: direct revert
        revert("Something went wrong");
    }

    function assertExample(uint256 x) public pure returns (uint256) {
        // Method 4: assert (internal errors only, consumes all gas)
        // NOT recommended for user input validation
        assert(x != 0);
        return 100 / x;
    }
}

错误处理方法

  • require(condition, "message") - 条件检查,失败回滚并退还剩余 gas
  • revert("message") - 直接回滚
  • assert(condition) - 内部错误检查,失败消耗所有 gas(不推荐)
  • 自定义 Error(0.8.4+)- 更省 gas -

自定义错误

使用 error 关键字定义,用 revert ErrorName() 触发,比字符串错误省 gas。

9. 继承与接口

Solidity 支持多重继承和接口。

Inheritance & Interfaces Example
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

// Parent contract
contract Animal {
    string public name;

    constructor(string memory _name) {
        name = _name;
    }

    function eat() public virtual returns (string memory) {
        return "eating";
    }
}

// Child contract
contract Dog is Animal {
    constructor(string memory _name) Animal(_name) {}

    // Override parent function
    function eat() public pure override returns (string memory) {
        return "dog is eating";
    }

    function bark() public pure returns (string memory) {
        return "woof!";
    }
}

// Multiple inheritance
contract Owner {
    address public owner;
    constructor() { owner = msg.sender; }
}

contract Pet is Animal, Owner {
    constructor(string memory _name) Animal(_name) Owner() {}

    // Call parent function
    function parentEat() public view returns (string memory) {
        return super.eat();
    }
}

// Interface
interface IERC20 {
    function transfer(address to, uint256 amount) external returns (bool);
    function balanceOf(address account) external view returns (uint256);
}

// Abstract contract
abstract contract ERC20Base {
    mapping(address => uint256) public balances;

    // Unimplemented function
    function mint(address to, uint256 amount) public virtual;

    // Implemented function
    function balanceOf(address account) public view returns (uint256) {
        return balances[account];
    }
}

继承

  • 使用 is 关键字继承
  • 支持多重继承(逗号分隔)
  • virtual - 父合约函数可被重写
  • override - 子合约重写父函数
  • super - 调用父合约函数

接口(Interface)

  • 只声明函数签名,不实现
  • 所有函数必须是 external
  • 不能有状态变量和 constructor
  • 用于定义标准(如 IERC20)

抽象合约(Abstract)

  • 包含至少一个未实现的函数
  • 不能被部署,只能被继承
  • 用 abstract 关键字标记

10. 高级特性

一些进阶语法和最佳实践。

Advanced Features Example
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

// Library
library SafeMath {
    function add(uint256 a, uint256 b) internal pure returns (uint256) {
        uint256 c = a + b;
        require(c >= a, "SafeMath: addition overflow");
        return c;
    }
}

contract AdvancedDemo {
    using SafeMath for uint256;  // Attach library to type

    uint256 public value;

    // Use library function
    function addValue(uint256 x) public {
        value = value.add(x);  // Instead of value + x
    }

    // Inline assembly (advanced, use with caution)
    function getCodeSize(address _addr) public view returns (uint256 size) {
        assembly {
            size := extcodesize(_addr)  // Get contract code size
        }
    }

    // Reentrancy guard pattern
    bool private locked;

    modifier nonReentrant() {
        require(!locked, "Reentrancy detected");
        locked = true;
        _;
        locked = false;
    }

    function withdrawAll() public nonReentrant {
        uint256 amount = address(this).balance;
        (bool success, ) = msg.sender.call{value: amount}("");
        require(success, "Transfer failed");
    }
}

库(Library)

  • 类似合约,但不能有状态变量
  • 使用 using for 语法扩展类型
  • 常见:SafeMath、Address、Strings

内联汇编(Assembly)

  • 使用 assembly { ... } 编写 Yul 代码
  • 直接操作 EVM,极致优化 gas
  • 高风险,需谨慎使用

安全最佳实践

  • 检查 - 效果 - 交互模式(避免重入)
  • 使用 ReentrancyGuard
  • 避免使用 tx.origin(用 msg.sender)
  • 整数溢出检查(0.8.0+ 自动)
  • 合理使用 require 而非 assert

11. 学习资源

精选的 Solidity 学习资料和工具。

官方文档

Solidity 官方文档 ↗

最权威的参考资料,涵盖所有语法细节

Solidity by Example ↗

通过代码示例学习,简洁明了(强烈推荐)

交互式教程

CryptoZombies ↗

游戏化学习 Solidity,适合零基础入门

Ethernaut ↗

通过破解智能合约学习安全知识

Remix IDE ↗

在线 Solidity IDE,无需本地环境

进阶资源

OpenZeppelin Contracts ↗

经过审计的智能合约库(ERC20/721/1155 等)

Ethereum.org Developers ↗

以太坊开发者文档,涵盖生态全貌

WTF Academy ↗

中文 Solidity 教程,内容详实

开发工具

Foundry ↗

快速的 Solidity 开发框架(推荐)

Hardhat ↗

流行的 JavaScript 开发环境

Tenderly ↗

智能合约监控和调试平台

12. 实战项目路线图

按难度从低到高的实战项目建议,每个项目都能巩固特定的 Solidity 技能。

初级(1~2天能写完)

ERC-20 Token

⭐ 1天

标准代币合约

Skills: 基础语法, 状态变量, 事件
Extensions
  • 添加 mint/burn 功能
  • 添加 pause 暂停机制
  • 实现代币时间锁

ERC-721 NFT

⭐⭐ 1-2天

非同质化代币(NFT)

Skills: tokenId 管理, tokenURI 元数据, 枚举
Extensions
  • 白名单 mint
  • 荷兰拍卖
  • 版税机制

多签钱包 (MultiSig Wallet)

⭐⭐ 2天

N 个人签名才能转账

Skills: mapping 嵌套, 权限控制, 事件驱动
Extensions
  • 每日限额
  • 交易队列
  • 紧急暂停

中级(3~7天)

AMM DEX(类 Uniswap V2)

⭐⭐⭐ 5-7天

恒定乘积做市商

Skills: x*y=k 公式, 流动性池, LP token, swap 交易
Features
  • 添加/移除流动性
  • 手续费分配
  • 价格计算
  • 滑点保护

💡 综合性强,推荐!涵盖 ERC-20 交互、数学计算、手续费机制

借贷协议(类 Aave 简化版)

⭐⭐⭐ 5-7天

存款生息 + 抵押借贷

Skills: 利率模型, 清算机制, 预言机集成
Features
  • 存款/取款
  • 借款/还款
  • 清算
  • Chainlink 喂价

💡 涉及复杂的利率计算和风险管理

Staking 合约

⭐⭐ 3-4天

质押代币赚收益

Skills: 时间加权收益, 奖励分配算法, 锁仓期管理
Features
  • 质押/解押
  • 领取奖励
  • 复利
  • 提前解锁惩罚

💡 常见 DeFi 组件,适合练习数学计算

Vesting 合约

⭐⭐ 3天

代币线性解锁

Skills: 时间锁, 归属计划, cliff 期
Features
  • 线性释放
  • 阶梯释放
  • 可撤销/不可撤销

💡 适合模拟员工期权、项目方锁仓

高级(2周+)

Uniswap V3 集中流动性

⭐⭐⭐⭐⭐ 2-3周

价格区间流动性

Skills: tick 系统, sqrt price, 位图索引, 复杂数学
Features
  • 集中流动性
  • 多价格区间
  • 手续费等级
  • NFT LP

⚠️ 数学复杂度极高,需要深入理解定点数和价格计算

跨链桥

⭐⭐⭐⭐ 2周

资产跨链转移

Skills: lock-mint/burn-mint 模型, relayer 监听, 重放攻击防御
Features
  • 双链部署
  • 事件监听
  • 默克尔证明
  • 挑战期

⚠️ 需要理解跨链通信原理和安全风险

Perpetual(永续合约)

⭐⭐⭐⭐⭐ 3-4周

去中心化衍生品交易

Skills: 资金费率, 保证金系统, 清算引擎, 预言机
Features
  • 开仓/平仓
  • 杠杆
  • 资金费率
  • 自动清算

⚠️ 难度极高,类似 Hyperliquid 的核心协议

建议学习路径

1

ERC-20已完成 ✓

掌握基础语法和代币标准

2

MultiSig Wallet推荐

练习权限控制和 mapping 嵌套

3

AMM DEX V2核心

理解 DeFi 核心逻辑(推荐从 SimpleSwap 扩展)

4

借贷协议进阶

学习预言机集成和清算机制

5

跨链桥 or V3高级

挑战复杂的跨链或数学问题

💡 下一步建议

你现在 Sepolia 上的 SimpleSwap 就是 AMM 的雏形,直接在那个基础上扩展成完整的 Uniswap V2 是最顺的路线。可以逐步添加:

  • 流动性池管理(addLiquidity / removeLiquidity)
  • LP Token 铸造和销毁
  • 手续费累积和分配(0.3% 给 LP)
  • 价格预言机(TWAP)
  • 滑点保护和截止时间检查