Solidity 智能开发知识点记录
-
- [1. 特性](#1. 特性)
- [2. ==存储区域==](#2. ==存储区域==)
- [3. 事件(Event)](#3. 事件(Event))
- ABI
1. 特性
Solidity 的核心特性包括:
- 面向合约 :以
contract为基本单位,合约类似面向对象语言中的类,包含状态变量、函数、事件等。 - 静态类型:变量类型在编译时确定,且不可动态改变。
- 继承与多态 :支持多重继承,使用
is关键字,并允许子合约重写父合约函数。 - EVM 原生兼容:编译后生成以太坊虚拟机字节码,可直接部署在以太坊及兼容链上。
- 错误处理 :提供
require、assert、revert等断言式错误抛出机制。 - 内置区块链信息 :可访问
block.number、msg.sender、tx.gasprice等环境变量。 - Gas 感知 :通过
view、pure等修饰符标明函数消耗 Gas 的情况。
2. 存储区域
Solidity 中的数据有明确的存储位置,主要分为四类:
| 区域 | 关键字 | 生命周期 | 适用场景 | 特点 |
|---|---|---|---|---|
| Storage | storage |
永久(合约状态) | 状态变量(默认) | 写入需消耗 Gas,数据存储在链上 |
| Memory | memory |
临时(函数调用) | 函数内临时变量、函数参数(某些类型)、动态数组拷贝 | 读写成本低于 Storage,调用结束释放 |
| Calldata | calldata |
临时(仅外部调用) | 外部函数的参数(不可修改) | 只读,不占用 Memory 拷贝,节省 Gas |
| Stack | 无关键字 | 短暂(表达式内) | 局部基本类型(如 uint、bool),存放指令操作数 |
深度限制 1024 个元素 |
重要规则:
- 状态变量(合约内声明的变量)默认 在
storage。 - 函数局部变量的存储位置需要显式指定(结构体、数组、映射等复杂类型)。
- 赋值时注意引用传递:
storage赋值给storage会传递引用;memory赋值给storage会拷贝。
solidity
contract Example {
uint[] public arr; // storage
function test(uint[] calldata source) external {
uint[] memory tmp = new uint[](10); // memory
arr = tmp; // 从 memory 拷贝到 storage
arr = source; // 从 calldata 拷贝到 storage
}
}
常见数据类型:uint、int、bool、address、bytes、string、mapping、数组、结构体、enum。
3. 事件(Event)
事件是 Solidity 中日志 的抽象,用于和链外应用通信。

核心作用:
- 智能合约返回数据给前端/后端监听者。
- 比普通返回值更 Gas 高效(日志存储在交易收据中,合约无法访问)。
- 可以带索引参数(
indexed),方便链外按条件快速检索。
语法:
solidity
event EventName(
type indexed param1,
type param2
);
- 事件日志由两部分组成:主题(topics)和数据(data)
- 第一个参数:事件签名的哈希值。

- 第二个参数:自定义 indexed 参数
- 第三个参数:自定义 indexed 参数
- 第四个参数:自定义 indexed 参数
- 第一个参数:事件签名的哈希值。
indexed参数最多 3 个,会被日志的 topic 区域存储,可用于过滤。- 非索引参数(data 区域)原样存储,无法直接检索
- 合约执行每触发一次事件,在交易回执里的 logs 数据项数组里就会多一条日志条目
java
{
"jsonrpc": "2.0",
"id": 1,
"result": {
"blockHash": "0x6a0e591eb3c266bd1d499f17caea929dc3a1b396b6e08d3d12b1df7abb4b1daf",
"blockNumber": "0x12d11ab",
"contractAddress": null,
"cumulativeGasUsed": "0x679b6b",
"effectiveGasPrice": "0x1ff2895dc",
"from": "0xe4de99fbf078eff6ba02b076e503b304c2a7b1c5",
"gasUsed": "0xe074",
"logs": [
{
"address": "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48",
"topics": [
"0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef",
"0x000000000000000000000000e4de99fbf078eff6ba02b076e503b304c2a7b1c5",
"0x0000000000000000000000006177d42837d88b1f6dd0ad14e2f80c17df3d0617"
],
"data": "0x000000000000000000000000000000000000000000000000000000000ee6b280",
"blockNumber": "0x12d11ab",
"transactionHash": "0xe26a8add9f159c22d8d10f4b4069ed20ec9245ba07bf742dc2054d6b2561654a",
"transactionIndex": "0x4b",
"blockHash": "0x6a0e591eb3c266bd1d499f17caea929dc3a1b396b6e08d3d12b1df7abb4b1daf",
"logIndex": "0xb2",
"removed": false
}
],
"logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000800000000000000000008000008000400000000010000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000100000000000000000000000010000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000002000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"status": "0x1",
"to": "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48",
"transactionHash": "0xe26a8add9f159c22d8d10f4b4069ed20ec9245ba07bf742dc2054d6b2561654a",
"transactionIndex": "0x4b",
"type": "0x2"
}
}
触发事件:
solidity
emit EventName(param1, param2);
链外监听示例(JavaScript + ethers.js):
javascript
contract.on("EventName", (param1, param2, event) => {
console.log(param1, param2);
});
典型应用场景:代币转账 Transfer 事件、去中心化交易所订单状态变更、治理投票记录等。
ABI
ABI(Application Binary Interface,应用二进制接口)是智能合约与外部世界(前端、其他合约、钱包等)交互的标准化规范。
智能合约在以太坊上以字节码形式存在,人类无法直接理解。ABI 就像是"说明书",告诉外部程序:
- 合约有哪些函数
- 函数参数和返回值是什么类型
- 如何编码调用数据
- 如何解码返回结果
solidity
// ContractABI.json
[
{
"inputs": [{ "internalType": "uint256", "name": "x", "type": "uint256" }],
"name": "set",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [],
"name": "get",
"outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }],
"stateMutability": "view",
"type": "function"
},
{
"anonymous": false,
"inputs": [
{ "indexed": true, "internalType": "address", "name": "caller", "type": "address" },
{ "indexed": false, "internalType": "uint256", "name": "value", "type": "uint256" }
],
"name": "ValueSet",
"type": "event"
}
]
如何调用?
java
package com.example;
import org.web3j.abi.FunctionEncoder;
import org.web3j.abi.FunctionReturnDecoder;
import org.web3j.abi.TypeReference;
import org.web3j.abi.datatypes.Address;
import org.web3j.abi.datatypes.Function;
import org.web3j.abi.datatypes.Type;
import org.web3j.abi.datatypes.generated.Uint256;
import org.web3j.crypto.Credentials;
import org.web3j.protocol.Web3j;
import org.web3j.protocol.core.DefaultBlockParameterName;
import org.web3j.protocol.core.methods.request.Transaction;
import org.web3j.protocol.core.methods.response.EthCall;
import org.web3j.protocol.core.methods.response.EthGetLogs;
import org.web3j.protocol.core.methods.response.EthSendTransaction;
import org.web3j.protocol.core.methods.response.Log;
import org.web3j.protocol.core.methods.response.TransactionReceipt;
import org.web3j.protocol.http.HttpService;
import org.web3j.tx.RawTransactionManager;
import org.web3j.tx.gas.DefaultGasProvider;
import org.web3j.tx.gas.StaticGasProvider;
import java.math.BigInteger;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
public class CallContractWithABI {
private static final String RPC_URL = "https://sepolia.infura.io/v3/YOUR_KEY";
private static final String CONTRACT_ADDRESS = "0x1234567890abcdef...";
private static final String PRIVATE_KEY = "YOUR_PRIVATE_KEY";
public static void main(String[] args) throws Exception {
Web3j web3j = Web3j.build(new HttpService(RPC_URL));
Credentials credentials = Credentials.create(PRIVATE_KEY);
// 调用只读方法
callReadMethod(web3j);
// 调用写入方法
callWriteMethod(web3j, credentials);
// 查询历史事件
queryEvents(web3j);
// 实时监听事件
listenEvents(web3j);
web3j.shutdown();
}
/**
* 调用只读方法(view/pure)- 不消耗 Gas
*/
private static void callReadMethod(Web3j web3j) throws Exception {
System.out.println("=== 调用 get() 方法 ===");
// 1. 创建 Function 对象
Function function = new Function(
"get", // 函数名
Collections.emptyList(), // 参数列表(无参数)
Arrays.asList(new TypeReference<Uint256>() {}) // 返回值类型
);
// 2. 编码调用数据
String encodedFunction = FunctionEncoder.encode(function);
// 3. 创建交易请求
Transaction transaction = Transaction.createEthCallTransaction(
null, // from(只读可以为 null)
CONTRACT_ADDRESS, // to
encodedFunction // data
);
// 4. 执行调用
EthCall response = web3j.ethCall(transaction, DefaultBlockParameterName.LATEST).send();
// 5. 解码返回值
if (response.isReverted()) {
System.out.println("调用失败: " + response.getRevertReason());
return;
}
String rawOutput = response.getResult();
List<Type> decoded = FunctionReturnDecoder.decode(rawOutput, function.getOutputParameters());
if (!decoded.isEmpty()) {
BigInteger value = (BigInteger) decoded.get(0).getValue();
System.out.println("当前存储值: " + value);
}
}