本文档介绍以太坊智能合约开发基础,涵盖 Solidity 语言、EVM 操作码、ERC-20 标准、Hardhat 开发框架、类型系统、Gas 机制、签名验证等核心知识。
| 语言 | 特点 |
|---|---|
| Solidity | 最流行,面向对象 |
| Vyper | 类 Python 语法,优化安全性和可读性 |
| Fe | 类 Rust 语法,模块化代码共享 |
| Huff / Etk | 底层汇编级别 |
常用资源:
graph LR
A["Solidity 源码"] --> B["编译器"]
B --> C["字节码 Bytecode"]
B --> D["ABI"]
C --> E["部署到 EVM"]
E --> F["链上执行"]
操作码(Opcodes):EVM 的低级指令集,参考 evm.codes
预编译合约:与固定地址的 EVM 捆绑,提供高级功能(如椭圆曲线运算、哈希等),以确定的 Gas 成本调用。
EVM 是基于栈的:
// 返回 42 的字节码
604260005260206000F3
60 42 // PUSH 42
60 00 // PUSH 00
52 // MSTORE
60 20 // PUSH 32 (length)
60 00 // PUSH 00 (offset)
F3 // RETURNnpm install --save-dev hardhat
npx hardhat initpragma solidity ^0.8.0;
contract Greeter {
string private greeting;
constructor(string memory _greeting) {
greeting = _greeting;
}
function greet() public view returns (string memory) {
return greeting;
}
function setGreeting(string memory _greeting) public {
greeting = _greeting;
}
}pragma:指定编译器版本constructor:构造函数,部署时执行一次view 函数:只读,不修改状态npx hardhat compile # 编译
npx hardhat test # 执行测试
npx hardhat run scripts/deploy.js --network sepolia # 部署
npx hardhat verify --network sepolia <地址> "参数" # 验证EIP(以太坊改进提案)描述了以太坊平台标准。其中 ERC-20 制定了代币标准,ERC-721 制定了 NFT 标准。
interface IERC20 {
function totalSupply() external view returns (uint256);
function balanceOf(address account) external view returns (uint256);
function transfer(address to, uint256 amount) external returns (bool);
function allowance(address owner, address spender) external view returns (uint256);
function approve(address spender, uint256 amount) external returns (bool);
function transferFrom(address from, address to, uint256 amount) external returns (bool);
}mapping(address => uint256) private _balances; // 余额
mapping(address => mapping(address => uint256)) private _allowed; // 授权额度| 类型 | 特点 |
|---|---|
| Contract | 类似类,包含状态变量、函数、事件等 |
| Interface | 不能实现函数、无状态变量、所有函数为 external |
| Library | 代码复用,通过 DELEGATECALL,在调用合约上下文执行 |
值类型 vs 引用类型:
| 值类型 | 引用类型 |
|---|---|
| bool, int/uint | struct |
| address | array |
| bytes1-bytes32 | mapping |
| 枚举, 函数 | string |
数据位置:
| 位置 | 说明 |
|---|---|
| memory | 函数调用期间有效 |
| storage | 合约存在期间永久存储 |
| calldata | 只读,保存函数参数 |
**映射(Mapping)**特性:
keccak256(key) 的哈希值可见性:
| 修饰符 | 说明 |
|---|---|
external | 仅外部调用(this.f() 可以) |
public | 内部和外部都可调用 |
internal | 当前合约和派生合约 |
private | 仅当前合约 |
状态修饰:
| 修饰符 | 说明 |
|---|---|
view | 不修改状态 |
pure | 不读取也不修改状态 |
| (无) | 可读写状态 |
用于在函数执行前自动检查条件:
modifier nonReentrant() {
require(locked == 1, "REENTRANCY");
locked = 2;
_; // 执行函数体
locked = 1;
}常见使用场景:
事件(Events):用于记录日志,通过 emit 触发,indexed 参数可被过滤。
错误处理:
require(condition, "message"):条件检查,失败时回退revert("message") 或 revert CustomError():主动回退assert(condition):仅用于测试内部错误| 成员 | 说明 |
|---|---|
balance | 查询余额 |
transfer | 发送 ETH(失败回退) |
send | 发送 ETH(返回 bool) |
call | 低级调用 |
delegatecall | 委托调用(使用对方代码,本方状态) |
staticcall | 静态调用(不允许修改状态) |
EIP-1559 后的 Gas 模型:
Gas Price = Base Fee + Max Priority Fee
Transaction Fee = Gas Price × Gas Used| 组成 | 说明 |
|---|---|
| Base Fee | 网络自动调节,基于区块使用率,被销毁 |
| Max Priority Fee | 矿工小费,用于优先打包 |
| Max Fee | 用户愿意付出的最高单位费用 |
Base Fee 调节规则:
合约最多有一个 fallback 函数,处理没有匹配函数选择器的调用。可用于代理模式(Proxy Pattern)。
create:地址 = keccak256(rlp([sender, nonce]))
create2(加盐):地址 = keccak256(0xff + sender + salt + keccak256(bytecode))
create2 可在合约创建前推导出地址,提供更大的灵活性。
EIP-712 提供结构化数据签名标准,包含三部分:
| 部分 | 说明 |
|---|---|
| Domain | 合约相关信息(名称、版本、chainId、合约地址) |
| TypeHash | 数据结构的哈希 |
| Message | 实际签名数据 |
签名验证流程:
bytes32 digest = keccak256(abi.encodePacked(
"\x19\x01",
DOMAIN_SEPARATOR,
hash(message)
));
address signer = ecrecover(digest, v, r, s);相比 eth_sign 和 personal_sign,EIP-712 提供更好的安全性(防重放)和用户体验(格式化展示签名内容)。
graph TB
subgraph "开发流程"
A["Solidity 编写"] --> B["Hardhat 编译/测试"]
B --> C["部署到测试网"]
C --> D["验证合约"]
end
subgraph "核心知识"
E["ERC-20 标准"]
F["Gas 机制"]
G["签名验证"]
H["代理模式"]
end
智能合约开发需要理解 EVM 的执行模型和 Solidity 的特殊性。关键差异在于:所有操作都是交易、状态存储在链上、Gas 机制限制计算资源、address 类型是独有概念。