语法教程

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)
  • 滑点保护和截止时间检查