第四章 | Solidity 基础语法全面讲解
无论是写 ERC20、NFT,还是更复杂的 DeFi、DAO 合约,Solidity 的基础语法都是你绕不开的核心。
写合约跟普通的 JS、Python、Java 程序不同,它要跑在区块链上,任何一次失误,都会付出“真实代价”。
这一章,我们详细讲一讲 Solidity 的核心基础语法,手把手让你理解“为什么”和“怎么用”。
这篇文章读完,你就能自己写一个“可运行”的基础合约,跟链上世界打个招呼。
首先,Solidity 是一种静态类型语言。
建议直接用最新稳定版 0.8.x
系列,特别是 0.8.19
或更高。
为什么?
每个合约第一行代码写版本声明
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;
SPDX-License-Identifier
是开源协议声明,养成习惯一定要写。
值类型的数据,直接存储的是数值,而不是指向值的引用。
值类型复制时,值是复制的副本。
类型 | 说明 | 示例值 |
---|---|---|
bool |
布尔类型,值为 true 或 false |
true |
uint |
无符号整数,默认 uint256 |
100 |
int |
有符号整数 | -100 |
address |
区块链地址 | 0xabc... |
bytes |
定长字节数组(通常用于 hash) | bytes32 |
string |
字符串 | "Hello" |
enum |
枚举类型,预定义的状态机 | enum Status {ON, OFF} |
bool public isActive = true;
uint256 public totalSupply = 10000;
address public owner = msg.sender;
uint
默认等价于uint256
,链上永远不要偷懒写uint
。
引用类型的数据,存储的是地址引用,而不是实际的值。
修改引用类型数据时,可能直接影响链上数据(耗费 Gas)。
类型 | 说明 |
---|---|
array |
数组(静态/动态) |
mapping |
映射(key-value 对应关系) |
struct |
自定义结构体 |
关键字 | 场景 |
---|---|
storage |
永久存储在链上,读写都贵 |
memory |
临时数据,函数结束后消失 |
calldata |
外部调用参数,读更便宜 |
function setName(string memory _name) public {
name = _name; // name 为状态变量 storage
}
经常有新手
memory
和storage
混淆,记住:storage
是链上的“硬盘”,memory
是函数内存条。
public
公开读,自动生成 getter 方法uint public count = 0;
function foo() public pure returns (uint) {
uint temp = 123;
return temp;
}
类型 | 场景 |
---|---|
constant |
编译时确定,值永远不会变 |
immutable |
部署时确定,之后不能改 |
uint constant MAX_SUPPLY = 10000;
address immutable OWNER;
constructor() {
OWNER = msg.sender;
}
constant
常用于配置参数;immutable
用于合约初始化参数(例如所有者)。
function transfer(address _to, uint256 _amount) public returns (bool) {
// 函数逻辑
}
修饰符 | 说明 |
---|---|
public |
任何人/合约都能调 |
private |
仅合约内部 |
internal |
合约内部 + 继承合约 |
external |
只能外部账户/合约调用 |
external
调用需要this.方法()
,消耗 Gas 高,慎用!
修饰符 | 说明 |
---|---|
view |
只读状态,不消耗 Gas |
pure |
不读不写状态,纯计算 |
payable |
接收 ETH |
function getBalance() public view returns (uint) {
return address(this).balance;
}
事件是 DApp 前端监听合约状态变化的唯一途径。
举个例子,你发布 ERC20 代币,钱包页面余额变化监听的就是事件。
event Transfer(address indexed from, address indexed to, uint256 value);
function transfer(address _to, uint256 _amount) public {
emit Transfer(msg.sender, _to, _amount);
}
indexed
用来筛选日志,最多支持 3 个indexed
参数。
映射(mapping
)是 Solidity 最常用的数据结构!
用于地址余额、权限白名单、NFT 所有者等。
mapping(address => uint256) public balances;
function deposit() public payable {
balances[msg.sender] += msg.value;
}
.length
属性自定义数据结构,结构清晰、易管理。
struct User {
string name;
uint256 age;
}
mapping(address => User) public users;
function register(string memory _name, uint256 _age) public {
users[msg.sender] = User(_name, _age);
}
常用于权限控制、逻辑复用。
比如 onlyOwner
管理员权限。
address public owner;
modifier onlyOwner() {
require(msg.sender == owner, "Not owner");
_;
}
function withdraw() public onlyOwner {
payable(owner).transfer(address(this).balance);
}
Solidity 有三种常见的错误处理方式
函数 | 用途 |
---|---|
require |
参数/条件检查(推荐) |
revert |
主动触发回滚 |
assert |
内部状态检查(调试) |
function transfer(address _to, uint256 _amount) public {
require(balances[msg.sender] >= _amount, "Not enough balance");
balances[msg.sender] -= _amount;
balances[_to] += _amount;
}
这一章我们详细讲解了 Solidity 最基础的语法
✅ 数据类型和存储位置
✅ 状态变量和局部变量
✅ 函数结构和可见性
✅ 修饰符和事件
✅ 映射、结构体等关键数据结构
✅ 错误处理机制
Solidity 数据类型深度解析
memory
和 storage
的性能优化address payable
和 ETH 接收处理Error
类型节省 Gas