解析代理合约的存储状态
用真实 Proxy、Implementation、区块和 Slot 区分状态地址与代码布局来源。
案例目标
确认代理模式下状态存在哪里、应使用哪个合约的 Storage Layout,以及如何在指定区块读取和验证原始 Slot。
示例交易与背景
本例继续使用区块 17806056 中的交易:
0xa84aa065ce61dbb1eb50ab6ae67fc31a9da50dd2c74eefd561661bfce2f1620c分析对象是 Curve pETH-ETH-f 池。用户交互地址是 minimal proxy 0x9848482da3Ee3076165ce6497eDA906E66bB85C5,执行逻辑来自 implementation 0x6326DEbBAa15bCFE603d831e7D75f4fc10d9B43E。
预期回答的问题
- 状态应从 Proxy 还是 Implementation 读取?
- Storage Layout 应从哪个地址加载?
- 指定区块中的 slot
8如何与交易内 SSTORE 对应?
分析步骤
- 打开 Proxy 地址,在 Proxy Storage 中确认并加载 implementation。
- 保持状态地址为 Proxy,只把 implementation 作为源码和 Storage Layout 来源。
- 在 Storage Layout 中定位 slot
8;未能可靠解码变量名时,保留原始 Slot 作为查询键。 - 在区块
17806056查询该 Slot,并与交易的 State Changes 对照。 - 从 State Changes 跳转 Execution Trace,验证 implementation 代码中的 SSTORE 实际写入 Proxy 上下文。
关键证据
| 证据类型 | 值 |
|---|---|
| State address | Proxy 0x9848482da3Ee3076165ce6497eDA906E66bB85C5 |
| Layout / code source | Implementation 0x6326DEbBAa15bCFE603d831e7D75f4fc10d9B43E |
| Query block | 17806056 |
| Storage key | slot 8(0x...0008) |
| Before | 0x00000000000000000000000000000000000000000000014f4ef71451a6d37997 |
| After | 0x00000000000000000000000000000000000000000000003f3278e0363aaefcf |
| SSTORE traces | 69、87、105、144、194 |
这组证据同时保留了三个不同身份:
- 状态身份:Proxy 地址;
- 代码与布局身份:Implementation 地址;
- 历史上下文:交易和区块
17806056。
如果直接查询 implementation 的 slot 8,得到的是 implementation 自身的 Storage,不能代表池的业务状态。
案例结论
pETH-ETH-f 池的代码由 implementation 提供,但 slot
8的实际状态保存在 Proxy。交易中的五次 SSTORE 由 implementation 代码执行,State Changes 却正确归属于 Proxy 地址。读取代理状态时必须使用“implementation 的布局 + proxy 的 Storage + 目标区块”这一组合。
注意事项
- 历史区块必须匹配当时生效的 implementation;当前实现未必能解释旧状态。
- Implementation 自身也可能是 Proxy,需要继续加载嵌套实现。
@custom:storage-location命名空间应使用 EIP-7201 Decoder,不能按普通 slot0布局推断。- 未验证布局时,先报告地址、Slot 和原始值,不要把推测的变量名写成事实。
