Solidity 速查表
合约结构
每个文件都以许可证和 pragma 开头;合约是部署的基本单位。
| 语法 | 含义 |
|---|---|
// SPDX-License-Identifier: MIT | 编译器要求写在第 1 行的许可证注释 |
pragma solidity ^0.8.0; | 该文件对应的编译器版本 |
contract Counter { ... } | 声明合约(状态 + 函数) |
constructor(uint start) { count = start; } | 部署时运行一次 |
import "./Token.sol"; | 导入另一个文件 |
import "@openzeppelin/contracts/token/ERC20/ERC20.sol"; | 导入库包 |
interface IToken { function balanceOf(address a) external view returns (uint); } | 声明接口(没有函数体) |
library Math { function min(uint a, uint b) internal pure returns (uint) { ... } } | 由可复用函数组成的库 |
值类型
整数的大小固定,没有小数;0.8 版本在溢出时会回滚。
| 类型 | 存储的内容 |
|---|---|
uint256 / uint | 无符号整数,0 到 2^256 - 1(uint 就是 uint256) |
uint8, uint16, ... uint128 | 更小的无符号整数,以 8 位为步长 |
int256 / int | 有符号整数(允许负数) |
bool | true 或 false |
address | 20 字节的账户或合约地址 |
address payable | 可以通过 transfer/send 接收 Ether 的地址 |
bytes32, bytes1... | 定长字节数组 |
enum Status { Open, Closed } | 一组具名常量,以 uint8 存储 |
1 ether, 1 gwei, 1 wei | Ether 单位:1 ether = 10^18 wei |
1 days, 2 hours, 30 minutes | 时间单位,以秒计 |
引用类型与数据位置
数组、字符串、bytes、结构体和 mapping 存放在 storage、memory 或 calldata 中。
| 语法 | 含义 |
|---|---|
string memory name | 动态 UTF-8 字符串(临时副本) |
bytes memory data | 动态字节数组 |
uint[] public scores; | storage 中的动态数组 |
uint[3] fixed; | 长度为 3 的定长数组 |
uint[] memory tmp = new uint[](5); | 分配一个 memory 数组 |
scores.push(42); | 向 storage 数组末尾添加元素 |
scores.pop(); | 移除最后一个元素 |
scores.length | 元素个数 |
storage | 持久保存在链上,写入成本高 |
memory | 临时存储,只在一次调用中存在 |
calldata | 只读的外部函数输入,成本最低 |
mapping 与结构体
mapping 是没有长度、不能遍历的哈希表;结构体把多个字段组合在一起。
| 语法 | 含义 |
|---|---|
mapping(address => uint) public balances; | 键值存储,每个键都存在(默认值为 0) |
balances[msg.sender] += 1; | 按键读写 |
mapping(address => mapping(address => uint)) allowance; | 嵌套 mapping |
struct User { string name; uint age; bool active; } | 定义结构体 |
User memory u = User("Ada", 36, true); | 在 memory 中创建结构体 |
User memory u = User({name: "Ada", age: 36, active: true}); | 按字段名创建 |
users[msg.sender] = u; | 把结构体存进 mapping |
users[msg.sender].age = 37; | 更新 storage 中的单个字段 |
delete users[msg.sender]; | 重置为默认值 |
函数与可见性
每个函数都要声明谁可以调用它,以及它是读取还是写入状态。
| 语法 | 含义 |
|---|---|
function add(uint a, uint b) public pure returns (uint) { return a + b; } | 带参数和返回值的函数 |
public | 任何地方都能调用(合约内部和外部) |
external | 只能从合约外部调用 |
internal | 本合约及继承它的合约 |
private | 仅限本合约 |
view | 读取状态,从不写入 |
pure | 完全不读取状态 |
payable | 调用时可以接收 Ether |
returns (uint sum, bool ok) | 多个具名返回值 |
(uint s, bool ok) = f(); | 解构多个返回值 |
uint public count; | public 状态变量会自动生成 getter count() |
修饰器、常量与继承
用修饰器复用检查逻辑;用 constant 和 immutable 固定值。
| 语法 | 含义 |
|---|---|
modifier onlyOwner() { require(msg.sender == owner, "Not owner"); _; } | 定义修饰器(_ 表示执行函数体) |
function withdraw() public onlyOwner { ... } | 应用修饰器 |
uint public constant MAX = 100; | 编译期常量 |
address public immutable owner; | 在构造函数中设置一次,之后不变 |
contract Token is ERC20, Ownable { ... } | 继承其他合约 |
function f() public virtual { ... } | 允许重写 |
function f() public override { ... } | 重写父合约的函数 |
super.f(); | 调用父合约的实现 |
abstract contract Base { function f() public virtual; } | 包含未实现函数的合约 |
流程控制
和 C 系语言一样的常见语句;没有 switch,而且循环每迭代一次都要消耗 gas。
| 语法 | 含义 |
|---|---|
if (x > 5) { ... } else if (x > 2) { ... } else { ... } | 条件分支 |
for (uint i = 0; i < n; i++) { ... } | 计数循环 |
while (x < 10) { x++; } | 条件成立时循环 |
do { ... } while (cond); | 至少执行一次 |
break; / continue; | 跳出循环 / 跳到下一次迭代 |
x > 0 ? a : b | 三元表达式 |
a / b | 整数除法(舍去余数) |
a % b | 取余 |
a ** 2 | 求幂 |
unchecked { x++; } | 跳过溢出检查(节省 gas,谨慎使用) |
错误处理:require、revert、assert
检查失败会撤销整笔交易,并退还未用完的 gas。
| 语法 | 含义 |
|---|---|
require(amount > 0, "Amount must be positive"); | 校验输入或状态;失败时带消息回滚 |
revert("Not allowed"); | 无条件中止 |
error Insufficient(uint available, uint requested); | 声明自定义错误(比字符串更省 gas) |
revert Insufficient(balance, amount); | 用自定义错误回滚 |
assert(total == a + b); | 检查一个永远不该失败的不变量 |
try token.transfer(to, amt) returns (bool ok) { ... } catch { ... } | 处理失败的外部调用 |
事件
事件写入的日志可以被链下应用订阅;合约本身无法读取它们。
| 语法 | 含义 |
|---|---|
event Transfer(address indexed from, address indexed to, uint value); | 声明事件 |
emit Transfer(msg.sender, to, amount); | 触发事件 |
indexed | 可用于过滤的参数(每个事件最多 3 个) |
event Log(string message); | 任何 ABI 类型都可以记录 |
Ether、地址与全局变量
交易上下文来自 msg、block 和 tx。
| 语法 | 含义 |
|---|---|
msg.sender | 调用当前函数的地址 |
msg.value | 随调用发送的 wei(需要 payable) |
block.timestamp | 当前区块时间(自 Unix 纪元起的秒数) |
block.number | 当前区块高度 |
tx.origin | 发起这笔交易的外部账户(不要用于权限校验) |
address(this).balance | 当前合约的 Ether 余额 |
payable(to).transfer(1 ether); | 发送 Ether,失败时回滚 |
(bool ok, ) = to.call{value: amt}(""); | 底层发送,返回是否成功 |
receive() external payable {} | 收到普通 Ether 转账时运行 |
fallback() external payable {} | 没有匹配的函数时运行 |
keccak256(abi.encodePacked(a, b)) | 计算哈希值 |
abi.encode(x), abi.decode(data, (uint)) | 编码 / 解码 ABI 数据 |
你会用到的 Solidity 语法,全都在这一页。这份 Solidity 速查表是以太坊及所有 EVM 链上智能合约语言的快速参考:声明合约、选择类型、用 mapping 和结构体存储数据、编写可见性和可变性都正确的函数,再用 require、修饰器和事件来保护它们。
这里的语法基于 Solidity 0.8,它默认检查算术溢出,适用于 Remix、Hardhat 和 Foundry。需要什么就复制什么,也可以在 Solidity 在线编辑器里直接试一试:编写合约、编译,然后在浏览器中的 EVM 上运行。
Solidity cheat sheet FAQ
Is this Solidity cheat sheet free?
Yes. This cheat sheet is free to use, with no sign-up required. Bookmark it and come back any time you need a quick reference.
Is this cheat sheet good for beginners?
Yes. It is organized by topic so you can find what you need at any level, and each entry links back to Coddy's free interactive Solidity course when you want to go deeper.