很好。
你现在已经完成了:
| 功能 | 状态 |
|---|---|
| 连接节点 | ✅ |
| 调用 RPC | ✅ |
| 查询 ETH 余额 | ✅ |
| 调用 ERC20 balanceOf | ✅ |
| ABI 编码/解码 | ✅ |
接下来进入:
Web3 真正核心能力
监听链上事件(Event)
这是:
1 | 绝大多数 Web3 后端系统的核心 |
例如:
| 场景 | 本质 |
|---|---|
| 充值到账 | 监听 Transfer |
| DEX Swap | 监听 Swap Event |
| NFT Mint | 监听 Transfer |
| 清算 | 监听 Liquidation |
| 游戏资产变化 | 监听 Event |
很多 Web3 公司:
1 | 80% 的后端逻辑 |
本质都是:
1 | Event Listener |
一、先彻底理解 Ethereum Event
这个非常重要。
Solidity Event 本质
你以前写过:
1 | event Transfer( |
很多人以为:
1 | Event = 打日志 |
其实不是。
Ethereum Event 更像:
链上消息队列
Event 的底层本质
当智能合约执行:
1 | emit Transfer(...) |
EVM 会生成:
1 | Log |
并写入:
1 | Transaction Receipt |
结构类似:
1 | { |
为什么 Event 非常重要?
因为:
Ethereum 节点允许:
1 | 按 Topic 检索日志 |
这意味着:
1 | 链本身就像一个可搜索消息系统 |
二、Transfer Event 长什么样?
ERC20 标准:
1 | event Transfer( |
注意:
1 | indexed |
非常重要。
indexed 是什么?
表示:
1 | 进入 Topics |
Ethereum Log:
| 部分 | 内容 |
|---|---|
| topic0 | Event 签名 |
| topic1 | from |
| topic2 | to |
| data | value |
为什么这么设计?
因为:
节点可以:
1 | 快速过滤 |
例如:
1 | 只看某个地址收到的 Transfer |
而不用扫描整个链。
三、现在开始 Java 监听
Step 1:添加 Import
先加:
1 | import io.reactivex.disposables.Disposable; |
为什么会出现 RxJava?
因为:
Ethereum 是:
1 | 持续产生新区块 |
天然是:
1 | 数据流(Stream) |
Web3j 使用:
RxJava
处理:
1 | 异步事件流 |
四、定义 Transfer Event
新增:
1 | Event transferEvent = new Event( |
这里到底在干什么?
你是在告诉 Web3j:
1 | “我要监听 ERC20 Transfer Event” |
true 的含义(非常关键)
1 | new TypeReference<Address>(true) |
这个:
1 | true = indexed |
对应 Solidity:
1 | address indexed from |
如果写错会怎样?
你后面解析 Topics 时:
1 | 全部错位 |
这是 Web3j 新手高频错误。
五、生成 Event Topic
继续:
1 | String topic = EventEncoder.encode(transferEvent); |
这是什么?
输出类似:
1 | 0xddf252ad... |

这是:
Event Signature Hash
Ethereum 如何识别 Event?
不是靠名字:
1 | Transfer |
而是:
1 | Keccak256 Hash |
即:
1 | keccak256( |
topic0 永远是 Event Hash
Ethereum Log:
| Topic | 内容 |
|---|---|
| topic0 | Event Hash |
| topic1 | from |
| topic2 | to |
六、创建 Filter(关键)
新增:
1 | EthFilter filter = new EthFilter( |
这里发生了什么?
你在告诉节点:
1 | 只监听: |
参数详细解释
1)LATEST
1 | DefaultBlockParameterName.LATEST |
表示:
1 | 从最新区块开始 |
只监听未来事件。
2)contractAddress
表示:
1 | 只监听这个 ERC20 |
否则全链 Transfer 太多。
3)addSingleTopic()
表示:
1 | 只接收 Transfer Event |
七、真正开始订阅
Step 2:订阅 Event Stream
新增:
1 | Disposable subscription = web3j.ethLogFlowable(filter) |
ethLogFlowable 是什么?
本质:
1 | 持续接收节点推送的日志流 |
类似:
| Web2 | Web3 |
|---|---|
| Kafka Consumer | Event Listener |
| MQ Consumer | Flowable |
| WebSocket Stream | Log Stream |
八、为什么现在可能“没反应”?
因为:
1 | 没有新的 Transfer |
你监听的是:
1 | 未来新区块 |
九、如何测试?
最简单:
去 MetaMask 转一次 Token
或者:
自己给自己转。
十、保持程序不退出
否则:
1 | main 方法结束 |
所以加:
1 | Thread.sleep(Long.MAX_VALUE); |
十一、现在你会看到什么?
当发生 Transfer:
1 | Transfer Event: |
说明:
1 | 你已经成功监听链上事件 |
这是:
真正 Web3 后端核心能力
十二、下一步(真正高级)
现在你只是:
1 | 打印原始 Log |
下一步你会:
解析 Event
得到:
1 | from |
也就是:
1 | 真正业务数据 |
这是下一阶段最关键部分。