Solidity智能合约基础
基础学习使用
remix:ide
Remix - Ethereum IDE
evm:ethreum virtual machine evm字节码
强类型脚本语言 compile =>evm bytescode =>evm
hello的样例
声明的关键字:contract
// SPDX-License-Identifier: MIT
pragma solidity 0.8.26;
//pragma solidity ^0.8.0;//大于8.0版本
//pragma solidity >=0.8.0 <0.9.0;//大于等于8.0版本,小于9.0版本contract helloDto {string public hello="hello 3.0";
}
一、EVM 的数据结构
-
基于栈的架构:EVM 使用栈来存储数据,最大支持 1024 个栈元素,每个元素是 256 位的字(word),所有计算都在栈上完成。
-
三类存储结构:
- 程序代码存储区(ROM)(栈):1024个solt,每个solt是32个字节>=256bit,不可变,存储智能合约的字节码。
- 内存(Memory):可变,临时存储执行期间的数据,随调用结束而清除。
- 存储(Storage):持久化存储,每个智能合约都有一个唯一的存储区,存储合约状态。
二、 EVM 中的两种值类型
-
基本类型(Value Types)
- 定义:直接存储值的数据类型,变量之间赋值是复制值本身。
- 常见类型:
uint
,int
,bool
,address
,bytes1
到bytes32
- 存储特性:
- 分配在固定的
storage slot
- 赋值是值复制(copy by value)
- 分配在固定的
-
引用类型(Reference Types)
-
定义:存储的是对数据的“引用”或“指针”,赋值传的是地址。
-
常见类型:
array
(数组),mapping
(映射),struct
(结构体) -
存储特性:
-
变量本身保存的是对实际数据的引用
-
赋值是引用复制(copy by reference)
-
需要
keccak256(slot)
来定位实际数据地址(尤其是动态结构)
-
-
evm特性
交易流程图
1. 用户发起交易|↓
2. 创建新的 EVM 实例|↓
3. 加载合约字节码|↓
4. 分配 Stack 空间(1024 slots)|↓
5. 执行合约代码|↓
6. 更新区块链状态(如果需要)|↓
7. 销毁 EVM 实例
详细说明
-
用户发起交易
- 用户签名交易
- 设定 gas limit 和 gas price
- 指定目标合约地址和调用数据
-
创建新的 EVM 实例
- 为每笔交易创建独立的 EVM 环境
- 初始化执行上下文
- 准备内存和存储空间
-
加载合约字节码
- 从区块链状态中读取合约字节码
- 将字节码加载到 EVM 中
- 准备执行环境
-
分配 Stack 空间
- 分配 1024 个 slots
- 每个 slot 256 位
- 用于存储临时计算结果
-
执行合约代码
- 逐条执行操作码
- 进行状态检查和 gas 计算
- 处理函数调用和返回值
-
更新区块链状态
- 写入存储变更
- 更新账户余额
- 触发事件日志
-
销毁 EVM 实例
- 清理内存
- 释放资源
- 返回执行结果
注意事项
- 整个过程是原子性的:要么全部成功,要么全部失败
- Gas 限制贯穿整个执行过程
- 状态变更只在交易成功执行后才会提交
EVM 存储结构
1. Stack (栈)
+----------------+
| 基本类型的值 | <- 函数内的局部变量
| 引用的地址 | <- 指向其他存储位置
+----------------+2. Memory (内存)
+----------------+
| 临时数据 | <- 函数执行期间的临时数据
| 函数参数 | <- memory 类型的参数
| 返回数据 | <- 函数返回值
+----------------+3. Storage (存储)
+----------------+
| 状态变量 | <- 合约的永久存储数据
| 映射数据 | <- mapping 数据
| 数组数据 | <- storage 数组
+----------------+
EVM Stack (固定大小的栈空间)
+------------------+
| 空闲空间 | <- 1024 个槽位(slots)
| ⬇ | 每个槽位 32 字节(256 位)
+------------------+
| 当前使用空间 | <- 随函数执行压入/弹出
+------------------+
Memory: 存在于 EVM 执行环境中 临时性的,交易执行完就清除 线性寻址(0x00, 0x20, 0x40...)
Storage: 存在于区块链状态中 永久性的,写入区块 使用 slot 和 keccak256 哈希定位
基本类型
在 Solidity 中,直接存储在栈(Stack)中的基本类型包括:
1. 整型(Integer)
contract StackTypes {function integerTypes() public pure {// 所有整型都存储在栈中uint256 a = 1;uint8 b = 2;int256 c = -1;int8 d = -2;} }
2. 布尔型(Boolean)
function booleanTypes() public pure {bool isTrue = true;bool isFalse = false; }
3. 地址(Address)
function addressTypes() public pure {address addr = 0x123...;address payable payableAddr = payable(0x123...); }
- 固定大小字节数组(Fixed-size Bytes)
function bytesTypes() public pure {bytes1 b1 = 0x12;bytes32 b32 = 0x123...; }
- 枚举(Enum)
enum Status { Active, Inactive }function enumTypes() public pure {Status status = Status.Active; // 实际存储为 uint8 }
重要说明:
- 这些类型在函数(相关文档:数据位置修饰符)调用时:
- 作为参数传递时是值传递
- 不需要指定 memory 等位置修饰符
// ✅ 正确:不需要 memoryfunction example(uint256 num, bool flag, address addr)public pure {// ...}// ❌ 错误:不能为基本类型指定 memoryfunction wrong(uint256 memory num) public pure {// ...}
- 大小限制:
function stackLimits() public pure {// EVM 栈深度限制为 1024// 每个值占用一个栈槽(32字节) }
- 与引用类型对比:
contract TypeComparison {// 基本类型:直接存储在栈中uint256 public stackVar = 123;// 引用类型:存储引用在栈中,数据在其他位置string public stringVar = "hello"; // 数据在存储中uint256[] public arrayVar; // 数据在存储中 }
- 在函数(文档地址:数据位置修饰符)中的使用:
contract StackUsa