Web3系统学习系列06-Web3j基础

Web3系统学习系列06-Web3j基础

Posted by 十渊 on 2026-05-18

很好。

你现在已经完成了:

功能 状态
连接节点
调用 RPC
查询 ETH 余额
调用 ERC20 balanceOf
ABI 编码/解码

接下来进入:

Web3 真正核心能力

监听链上事件(Event)

这是:

id
1
绝大多数 Web3 后端系统的核心

例如:

场景 本质
充值到账 监听 Transfer
DEX Swap 监听 Swap Event
NFT Mint 监听 Transfer
清算 监听 Liquidation
游戏资产变化 监听 Event

很多 Web3 公司:

id
1
80% 的后端逻辑

本质都是:

id
1
Event Listener

一、先彻底理解 Ethereum Event

这个非常重要。


Solidity Event 本质

你以前写过:

id
1
2
3
4
5
event Transfer(
address indexed from,
address indexed to,
uint256 value
);

很多人以为:

id
1
Event = 打日志

其实不是。

Ethereum Event 更像:

链上消息队列


Event 的底层本质

当智能合约执行:

id
1
emit Transfer(...)

EVM 会生成:

id
1
Log

并写入:

id
1
Transaction Receipt

结构类似:

id
1
2
3
4
{
"topics": [...],
"data": "0x..."
}

为什么 Event 非常重要?

因为:

Ethereum 节点允许:

id
1
按 Topic 检索日志

这意味着:

id
1
链本身就像一个可搜索消息系统

二、Transfer Event 长什么样?

ERC20 标准:

id
1
2
3
4
5
event Transfer(
address indexed from,
address indexed to,
uint256 value
);

注意:

id
1
indexed

非常重要。


indexed 是什么?

表示:

id
1
进入 Topics

Ethereum Log:

部分 内容
topic0 Event 签名
topic1 from
topic2 to
data value

为什么这么设计?

因为:

节点可以:

id
1
快速过滤

例如:

id
1
只看某个地址收到的 Transfer

而不用扫描整个链。


三、现在开始 Java 监听


Step 1:添加 Import

先加:

id
1
2
3
4
5
6
import io.reactivex.disposables.Disposable;

import org.web3j.abi.EventEncoder;
import org.web3j.abi.datatypes.Event;

import org.web3j.protocol.core.methods.request.EthFilter;

为什么会出现 RxJava?

因为:

Ethereum 是:

id
1
持续产生新区块

天然是:

id
1
数据流(Stream)

Web3j 使用:

RxJava

处理:

id
1
异步事件流

四、定义 Transfer Event

新增:

id
1
2
3
4
5
6
7
8
Event transferEvent = new Event(
"Transfer",
Arrays.asList(
new TypeReference<Address>(true) {},
new TypeReference<Address>(true) {},
new TypeReference<Uint256>() {}
)
);

这里到底在干什么?

你是在告诉 Web3j:

id
1
“我要监听 ERC20 Transfer Event”

true 的含义(非常关键)

id
1
new TypeReference<Address>(true)

这个:

id
1
true = indexed

对应 Solidity:

id
1
address indexed from

如果写错会怎样?

你后面解析 Topics 时:

id
1
全部错位

这是 Web3j 新手高频错误。


五、生成 Event Topic

继续:

id
1
2
3
String topic = EventEncoder.encode(transferEvent);

System.out.println(topic);

这是什么?

输出类似:

id
1
0xddf252ad...

image-20260518212603419

这是:

Event Signature Hash


Ethereum 如何识别 Event?

不是靠名字:

id
1
Transfer

而是:

id
1
Keccak256 Hash

即:

id
1
2
3
keccak256(
"Transfer(address,address,uint256)"
)

topic0 永远是 Event Hash

Ethereum Log:

Topic 内容
topic0 Event Hash
topic1 from
topic2 to

六、创建 Filter(关键)

新增:

id
1
2
3
4
5
6
7
EthFilter filter = new EthFilter(
DefaultBlockParameterName.LATEST,
DefaultBlockParameterName.LATEST,
contractAddress
);

filter.addSingleTopic(topic);

这里发生了什么?

你在告诉节点:

id
1
2
3
4
5
只监听:

某个合约
+
Transfer Event

参数详细解释


1)LATEST

id
1
DefaultBlockParameterName.LATEST

表示:

id
1
从最新区块开始

只监听未来事件。


2)contractAddress

表示:

id
1
只监听这个 ERC20

否则全链 Transfer 太多。


3)addSingleTopic()

表示:

id
1
只接收 Transfer Event

七、真正开始订阅


Step 2:订阅 Event Stream

新增:

id
1
2
3
4
5
6
7
Disposable subscription = web3j.ethLogFlowable(filter)
.subscribe(log -> {

System.out.println("Transfer Event:");
System.out.println(log);

});

ethLogFlowable 是什么?

本质:

id
1
持续接收节点推送的日志流

类似:

Web2 Web3
Kafka Consumer Event Listener
MQ Consumer Flowable
WebSocket Stream Log Stream

八、为什么现在可能“没反应”?

因为:

id
1
没有新的 Transfer

你监听的是:

id
1
未来新区块

九、如何测试?

最简单:

去 MetaMask 转一次 Token

或者:

自己给自己转。


十、保持程序不退出

否则:

id
1
2
3
main 方法结束
JVM 退出
监听终止

所以加:

id
1
Thread.sleep(Long.MAX_VALUE);

十一、现在你会看到什么?

当发生 Transfer:

id
1
2
3
4
5
6
Transfer Event:
Log{
removed=false,
logIndex=...
...
}

说明:

id
1
你已经成功监听链上事件

这是:

真正 Web3 后端核心能力


十二、下一步(真正高级)

现在你只是:

id
1
打印原始 Log

下一步你会:

解析 Event

得到:

id
1
2
3
from
to
amount

也就是:

id
1
真正业务数据

这是下一阶段最关键部分。