意图识别器
一个库. 把 feed 里的每一笔交易翻成「它想对 Pons 做什么」: 发币, 买, 卖, 或只是碰到. 办法只有两个: 解析 calldata, 用只读的 state 做计算. 认出什么给什么.
方案 2026-11-09, 最终版, 待动工. 全史数字来自 R2 段 0–108, facts .v1, 到块 108,999,999. 本页按方案正文复述.
它站在哪
节点机把链折成 state, 推一份只读副本到 bot 那台机器. 识别器同时拿三样东西: 这一帧, 钉在块 A 的 state, 一份带版本的知识包. 交出去的是意图卡. chain 不改. facts 只多一张代理实现变更表.
runs/ 在宿主那边记两样: 录下的帧, 和每块的视野 (A, state 版本, 知识包版本, 洞, 叠加层起点). 意图本身不落盘, 有这两样就能逐位重算.
四样输入
| 输入 | 是什么 | 算不算链上数据 |
|---|---|---|
| feed 帧 | 排序器广播的一个 L2 块, 原样字节加到达时刻. 批作业也可以直接给拆好的块 | 对象本身 |
| state 视图 @A | 钉在 A 块的只读视图. 热路径唯一的链上数据 | 是 |
| 知识包 | 怎么读懂 calldata: 入口规则, 路由布局, 调用包表 | 不是数据, 是读法 |
| 叠加层 | 识别器自己刚认出的东西, 只在内存里 | 用完就扔 |
对外一个函数: recognize(帧或块, StateView) → 意图块. 收 feed, 重连, 选路, 归宿主.
要不同的答案, 去不同的地方
| 要什么 | 找谁 | 多快 |
|---|---|---|
| 想做什么 | 意图识别器, 读 feed | 微秒 |
| 做成了什么 | 节点 WS 或 RPC | 等节点执行完 |
| 事实加折叠后的状态 | facts, state | 再晚零点几秒到一两秒, 待量 |
不做
不执行合约, 不预测成败和成交量, 不算储备, 税, 毕业, 不管毕业以后的 V4 池. 不改 state 或 facts, 不存意图, 不联网, 不读磁盘. 不判断人, 平台和方向. 不管 feed 的连接.
用户定的 15 条
| # | 约束 | 落在哪 |
|---|---|---|
| 1 | 只识别意图, 不模拟成败 | 卡上没有成交量, 没有成功与否 |
| 2 | 纯解析, 或用状态计算 | 第 4 步解码, 第 5 步计算 |
| 3 | 不在本地跑 EVM | crate 不依赖执行器 |
| 4 | feed 解析封装在里面 | 拆帧是第 1 步 |
| 5 | 只看发币和曲线买卖 | 毕业后的池子不认 |
| 6 | 只读, 不改, 不做 RPC | StateView 全是点查 |
| 7 | state 比 feed 晚, 是设计 | 叠加层填 A 和 n 之间的空档 |
| 8 | 结果给 bot, 可以不是链上事实 | 依据写成解码 / 计算 / 扫到 / 暂定 |
| 9 | 知道什么返回什么, 不追 100% | 不知道必须带原因 |
| 10 | 只计算, 不做决定 | 99% 的规则标暂定, 用不用由 bot 定 |
| 11 | 名字 intent_recognizer, 意图不存 | runs/ 只记帧和视野 |
| 12 | 自己算出的可以临时缓存 | 叠加层, 不写 state |
| 13 | 只写需要的最终版 | 方案不分版本 |
| 14 | facts 加代理表, 槽给 state, 布局离线学 | 知识包和 state 那两节 |
| 15 | 100% 标计算, 至少 99% 标暂定, 扫到的也出卡 | 入口规则和意图卡 |
原话, 11-08 和 11-09
只识别意图, 不模拟这笔能不能成交, 成交多少, 发射成功没有. 靠纯解析或状态计算. 不为了 EVM 在本地再做一套 EVM. feed 的解析封装在这一层, 外部只看见 feed, 然后识别, 然后结果. 交易只关系曲线, 毕业后不关心.
永远不改数据和状态, 不做 RPC, 要绝对的快. state 比 feed 晚一点点是预期内的. 结果给 bot 和策略, 不一定是链上事实. 能识别多少识别多少. 识别器没有任何主观, 只计算, 交给 bot.
入口规则在 state 外面, 带截止块和生效块, 意图不存. 临时缓存不改 state. 不写第一版第二版. facts 连 beacon 换实现一起记. state 留入口写过的槽的最新值, 调用包合约的槽不留. 全史 100% 的规则标计算, 成立至少 99% 的标暂定.
一笔怎么走
一条 feed 消息是一个 L2 块. 拆帧按块顺序做. 身份, 初筛, 解码按笔并行. 状态计算必须按块内顺序走, 因为前面认出的新币要给后面的买单用. 点一步看它的规则. 虚线那步是: 算出新币之后, 同块后面再筛一遍.
1 拆帧
| 规则 | 做法 |
|---|---|
| 一条消息 | 一个 L2 块. sequenceNumber 就是块号. 时间和 L1 块号在帧头 |
| 帧头 sender | 排序器, 等于 coinbase, 不是签名人 |
| 块时间 | max(帧头时间, 上一帧的时间). 延迟收件箱要这样钳位 |
| 上一帧是洞 | 退到 state 块 A 的时间, 这块的时间标暂定 |
| 延迟收件箱 | 存款和 retryable 不认. 存款没有调用. retryable 的 to 和 data 是明文, 但在范围外 |
| 同块号, 同 hash | 重发, 丢掉 |
| 同块号, 换 hash | 重组. 叠加层截掉大于等于该块的条目, 重认. 意图块带「作废 ≥ 该块」交给宿主 |
| 块号跳了 | 记成洞. 依赖叠加层的字段标暂定 |
帧头对得上, 来自 2 万帧逐块核对, 其中 34 块需要钳位. 实证 10-12, 已超过 14 天, 当线索, 动工时对现帧复验. 现生产的帧序跟踪已经区分重发和重组.
2 交易身份
| 字段 | 怎么来 |
|---|---|
| tx hash | keccak(原始交易). 和 facts / chain 对账只用它 |
| feed_index | 这笔在 feed 块里的序号 |
| transaction_index | feed_index + 1. Nitro 每块第 0 笔是内部交易 |
| 签名人 | secp256k1 恢复, 只给过了初筛的笔算, 大约 28 微秒一笔 |
| 7702 授权列表 | 放在卡上. 本笔里「某地址真正跑的码」先用本笔的授权. 只在识别器内部, 不改任何数据 |
3 初筛: 过任意一条就往下
| 条件 | 为什么有它 |
|---|---|
| to 是认得的地址 | 协议合约, 还在曲线上的币和曲线, 知识包里的入口 / 路由 / 调用包, 发过币的入口 (发币次数 > 0), 叠加层里的新币 |
| calldata 任意偏移含发币选择器 | launchToken, 带免税的 launchToken, launchAndBuy, executeAtomicLaunchV2 |
| 任意偏移含还在曲线上的币或曲线 | 先比 4 字节前缀, 再比 20 字节 |
| 任意偏移含协议合约地址 | 协议 owner 是 Safe 多签, 改参数的交易 to 是 Safe, 只能靠这条看见 |
| 长得像直调曲线 | buy / sell 选择器, 输入 100 字节, 即使 to 还没登记 |
任意字节偏移, 不要求 32 字节对齐. facts 量过, 任意偏移比对齐多认 53% 的「提到」(实证 11-04, 段 104, 失败交易口径). 误放的代价是一次签名人恢复加一次解码.
| 抽 4 段看到的 | 数 | 分母 |
|---|---|---|
| 过筛 | 14.2% | 段 40 / 70 / 95 / 108, 共 3,431 万笔, 占 Pons 时代 82 段的 4.9% |
| 过筛的笔里和 Pons 相关 | 78.0% | 过筛的笔 |
| 误放里 to 本来就认得 | 88.5 万 / 106.8 万 | 误放的笔 |
| 有边界帧却没过筛 | 1,693 / 834,165 = 0.20% | to 集中在 0xad1a0f, 0x5b8e11, 0x1f9798 |
本课题 19, 11-09. 工厂和 hook 的 owner 都是 0x263ed295, Safe 门槛 2 / 3 (本课题 10).
4 解码: 只读字节, 展开成声明的调用
一条声明是 (调用者, 目标, value, 输入). 四条路, 先认边界和调用包, 再认路由, 最后才扫描. 硬解析就在这一步, 是识别器自己的代码. 知识包只告诉它「这个地址用哪一种读法」.
| 遇到 | 怎么处理 | 依据 |
|---|---|---|
| Pons 边界 | 工厂发币, forwarder launchAndBuy, 曲线 buy / sell, 币的 approve / transfer / transferFrom. 直接出意图 | 解码 |
| 调用包 | 按调用包表展开子调用, 递归往下. 子调用的调用者按表里写死的那一列 | 解码 |
| 路由 | (to, 选择器) 在布局表里, 而且 to 真正跑的实现等于这一行绑的实现. 读出每一腿 | 解码 |
| 实现对不上 | 仍按这一行读. 多数升级不改布局, 一升级就全标不知道会让召回塌 | 暂定 |
| 都不认得 | 兜底扫描 | 扫到, 或碰到 |
兜底扫描的三刀
| 刀 | 条件 | 出什么 |
|---|---|---|
| 发币选择器 | 任意偏移, 从选择器往后按 ABI 解, 解得开并且过合约硬限 | 发币意图 |
| 曲线 buy / sell | 任意偏移, 后面 96 字节解成 (数量, 最少拿回, 收款人), 而且这笔 calldata 里只提到一条已登记曲线 | 买或卖, 依据 = 扫到 |
| 币或曲线地址 | 上面两刀都没接住 | 碰到 |
兜底第二刀在同样 4 段里扫到 54,868 次. 方向 + 收款人对上 80.2%, 有真值帧但对不上 3.0%, 同笔没有真值帧 16.8%. 失败帧里数量和最少拿回也对上 4,969 次. 照出卡. 本课题 19.
同一段字节只出一个意图
扫到蓝色区间里: 丢掉. 扫到右边: 另出一张, 例如「碰到」. 同一笔里对同一个币的两个真动作 (先卖后买, 捆绑) 各出各的.
5 状态计算, 按块内顺序
定目标时先查叠加层, 再查 state. 然后求入口规则, 用 CREATE2 算地址, 核经济指纹. 算出的新币写进叠加层. 来自 state 的依据是计算. 来自叠加层的依据是暂定, 带第几块第几笔.
| 算什么 | 依赖谁 |
|---|---|
| 这条曲线是哪个币 | 叠加层里的新币, 然后 state 的币记录. 都没有就标目标没登记, 地址照给, 卡照出 |
| dev | 直调按函数表. 第三方入口按知识包. 没有规则就标不知道, 地址也不算 |
| 曲线和币地址 | dev 加上 calldata 里的 TokenParams, 加上 state 里的配置和费策略 |
| 经济指纹 | state@A 的参数重算 calldata 里的 expectedEconomics |
| 实际免税名单 | 声明的名单, 工厂自动加的 dev 和收税人, forwarder 自动加的首买收款人 |
| 入口计数 | state@A 的真值 + 叠加层里这次空档的次数, 乘步长 |
交出去的意图块
一个 feed 块一份. 里面只放碰到 Pons 的交易, 按块内序. 发币意图在块内一认出就可以交给宿主, 不必等整块认完.
| 附在块上的 | 用途 |
|---|---|
| 块号, 块 hash, 时间, 帧头种类, 到达时刻 | 这笔意图属于哪一块 |
| 作废 ≥ b | 重组时告诉宿主, 旧块的卡不要了 |
| 视野 | A, state 版本, 空档块数, 洞, 叠加层起点 |
| 知识包版本, 识别器代码版本 | 重放时用同一套读法 |
| 计数 | 交易数, 拆不开的, 两遍初筛各过了几笔, 出了几张卡 |
五步各自交出什么
| 步 | 顺序 | 写出什么 |
|---|---|---|
| 1 拆帧 | 一块接一块 | 块号, 时间, 交易列表, 重发 / 重组 / 洞 |
| 2 身份 | 哈希每笔都算; 签名人只给过筛的 | tx hash, feed_index, 签名人, 7702 授权列表 |
| 3 初筛 | 第一遍每笔并行 | 过了才往下 |
| 4 解码 | 过筛的笔 | 声明的调用, 以及扫到的命中 |
| 5 计算 | 块内顺序 | 目标, 地址, dev, 指纹, 免税, 新的叠加层条目 |
发币
解码先把发币调用解出来. 计算再定 dev, 算出曲线和币的地址, 核对经济指纹, 补上实际免税名单. 一笔里有几个发币, 就出几个意图, 按 calldata 顺序. to 是发过币的入口, 却解不出发币调用, 就出「碰到 (发币入口)」, 参数留空.
认哪些函数
| 调用 | 选择器 | dev |
|---|---|---|
| 工厂 launchToken | f35abbcf | 调用者 |
| launchToken + 免税 | a72101af | 调用者 |
| launchTokenFor | d6a0eef5 | 参数 originalDeployer, 只许 forwarder 调 |
| forwarder launchAndBuy | f85f8e41 | forwarder 的调用者 |
| 第三方入口 | executeAtomicLaunchV2 等 | 按入口规则. 没有规则则不知道 |
解出来的字段: 名字, 符号, logo, 描述, 社交 5 项, 收税人, 创作者税, 回购开关, 经济指纹, salt, 配置号, 配对币, 首买 (投入, 最少拿回, 收款人), 声明的免税名单.
全史 727,029 笔成功发币
块 27,027,321–108,998,003. 本课题 17, 11-09. 环心是笔数.
四条路径
| 路径 | 笔 | 占 | dev | 参数能扫到 | 毕业率 |
|---|---|---|---|---|---|
| 直调工厂或 forwarder | 622,660 | 85.6% | 签名人, 100% | 100% | 1.2% |
| 第三方入口 | 68,561 | 9.4% | 入口规则 | 83.2% | 2.9% |
| 7702 自调 | 30,261 | 4.2% | 签名人 99.1% | 95.3% | 3.3% |
| 调用包 | 5,547 | 0.8% | 调用包表 | 90–100% | 0–2.0% |
毕业率只用选择段, 2026-10-21 00:00 UTC (块 93,848,723) 以前. 非直调的毕业率是直调的 2–3 倍. 调用包里 Multicall3 有 5,096 笔, 4337 EntryPoint 有 451 笔. 「参数能扫到」= calldata 里找得到发币选择器, 而且 TokenParams.salt 原样出现.
入口规则的召回
纵轴是门槛, 条长是 10-21 以后的召回. 两档都是 0 错. 本课题 20.
按天前推, Δ = 1 天, V = 7 天, 只在 9-21 到 10-20 选规则, 10-21 以后报一次. 卡召回的是「必须全史 100%」, 样本门槛几乎不起作用. 所以 100% 标计算, ≥ 99% 也收, 标暂定.
调用包里, 子调用的调用者
| 调用包 | 子调用的调用者 |
|---|---|
| 顶层直调 | 签名人 |
| 7702 自调, 账户直接调工厂或 forwarder | 签名人. 这种发币里 dev = 签名人占 99.1% |
| 7702 自调, 又经别的合约发币 | 那个合约, 再按入口规则. 剩下 0.9% |
| Multicall3 | Multicall3 自己. 现生产就是 tx_to, 全史 99.9% |
| ERC-4337 handleOps | userOp.sender. 展开两层: EntryPoint, 账户 execute, 工厂 |
| ERC-7821 / execute / executeBatch | 账户或合约自己 |
| 0x AllowanceHolder.exec | AllowanceHolder 自己 |
dev 规则是封闭的几种形
识别器只会算这几种. 不按字节偏移去抠变长字符串后面的地址.
| 形 | 识别器怎么算 |
|---|---|
| 签名人, tx.to | 交易本身 |
| 解出的发币调用里的字段 | 收税人, 免税名单第 k 个. 按字段名取 |
| calldata 固定偏移 | 只给定长的 calldata |
| 常量地址 | 这一组的 dev 恒为同一个合约 |
| CREATE(入口, nonce + j) | state 的入口 nonce, 加上叠加层里的次数 |
非直调有 11,499 个入口组 (tx.to, 选择器), 其中 9,442 个一辈子只发过 1 次币. 它发币的当时, 知识包里不可能有它. dev = 不知道 (入口没规则), 发币卡照出, 其余字段照给. 当天要补, 就手工往知识包加一行, 只加不改. 离线学到的规则进下一版.
地址公式
| 步 | 公式 |
|---|---|
| 收税人 | TokenParams.creatorFeeRecipient. 零地址就用 dev |
| salt' | keccak(abi.encode(dev, TokenParams.salt)) |
| 曲线 | CREATE2(LaunchDeployer, salt', keccak(曲线创建代码 ‖ 16 个参数)) |
| 币 | CREATE2(LaunchDeployer, salt', keccak(币创建代码 ‖ abi.encode(名字, 符号, logo, 描述, 社交, dev, 曲线, 工厂, 发行量))) |
曲线创建代码 11,480 字节, 币创建代码 7,178 字节. keccak 必须等于 facts_decode 常量里的哈希. 工厂, LaunchDeployer, hook, 托管, 回购库是 immutable 或只能设一次.
| 输入 | 来自 |
|---|---|
| TokenParams, 配置号, 配对币 | calldata, 解码 |
| dev | 上面的规则. 用到叠加层就标暂定 |
| 发行量, 曲线费, ETH 盘的虚拟储备和毕业线 | state: LaunchConfig[配置号] |
| ERC-20 盘的虚拟储备和毕业线 | state: 配对币参数[配对币] |
| 费策略 5 项 | 协议收费地址, 分成, 回购销毁, hook 费, 最大冲击 |
| forwarder | state. 全史只设过 1 次 |
| 工厂, LaunchDeployer, hook, 托管, 回购库, 两份创建代码 | facts_decode 的常量 |
经济指纹
expectedEconomics 非零时, 工厂会用当时的参数算一个摘要, 不等就 revert. 摘要盖 10 项: 虚拟储备, 毕业线, 发行量, 曲线费, 池费, tick 间距, 费策略的 4 个 bps. 协议收费地址不在摘要里. tick 间距按 int256 编码, 负数要补码.
| 比对 | 卡上怎么写 |
|---|---|
| 对上 | 这 10 项和 state@A 一致 |
| 对不上 | 地址标暂定. state 晚了, 或这笔会失败 |
| 没写 | 指纹字段标没写 |
最近 80 个发币都带指纹, 重算 80 / 80 对上. ETH 盘 65, ERC-20 盘 15 (本课题 08). 其中 79 个 dev = 签名人就算对 (forwarder 71, 工厂直调 8), 剩下 1 个经 0x6319, dev 等于 creatorFeeRecipient. 三种 ERC-20 配对币都不是 USDG.
实际免税名单
狙击要知道谁不交狙击税. 依据 = 计算.
| 谁加的 | 加什么 |
|---|---|
| calldata | 声明的名单 |
| 工厂 | 自动加 dev 和收税人 |
| forwarder | 自动加首买收款人 |
协议参数改得有多勤
| 参数 | 全史 |
|---|---|
| LaunchConfig | 只加过 1 次, 没改过 |
| 配对币参数 | 改过 98 次, 37 个块, 最近块 100,393,945 |
| 协议收费地址 | 改过 2 次 |
| hook 的 4 个 bps | 没改过 |
| forwarder | 只设过 1 次 |
本课题 09. 空档里真改参数是稀有事件, 所以指纹对不上时把地址标暂定, 而不是停掉整条流水线. 最近 80 笔的块范围是 109,612,091–109,712,091.
曲线上的买卖
全史成功的曲线边界帧 33,950,072 个, ETH 盘 735,350 ETH. 另有失败帧 2,751,983 个, 只计笔. 前五档是固定读法, 合计 81.9% 的帧, 82.3% 的 ETH. 接不住的出扫到, 定长布局, 或「碰到」. 方向猜不出来就不猜.
横条是百分比. 各档 ETH 相加是 735,349, 与合计 735,350 差 1, 是四舍五入. 来源: 本课题 18, 11-09. 先中先算.
| 档 | 帧 | 占帧 | ETH | 占 ETH |
|---|---|---|---|---|
| 直调曲线 | 12,423,840 | 36.6% | 357,099 | 48.6% |
| forwarder 发币首买 | 394,790 | 1.2% | 24,981 | 3.4% |
| 现生产已有解码的路由 (10 个) | 13,547,209 | 39.9% | 194,983 | 26.5% |
| 调用包 | 639,553 | 1.9% | 8,103 | 1.1% |
| 7702 自调 | 784,370 | 2.3% | 19,842 | 2.7% |
| 兜底扫到 | 740,789 | 2.2% | 13,618 | 1.9% |
| 定长候选 | 2,494,152 | 7.3% | 66,316 | 9.0% |
| 碰到 | 2,769,246 | 8.2% | 47,322 | 6.4% |
| 什么都没提到 | 156,123 | 0.5% | 3,085 | 0.4% |
每种形态交出什么
| 形态 | 认法 | 参数 |
|---|---|---|
| 直调曲线 | buy 59a87bc1 / sell d04c6983 | 方向, 投入, 最少拿回, 收款人. 输入 100 字节 |
| 发币带首买 | launchAndBuy 里的首买 | 同上. 目标 = 本卡的发币 |
| 经路由 | 知识包里的布局 | 方向, 外层投入, 最少拿回, 收款人. 跟单另给「跟的是谁」 |
| 捆绑买 | 布局 bundle-buy | 每个收款人一个买, 同一条曲线 |
| 调用包里 | 展开后再认 | 每个打到 Pons 的调用一个意图 |
| 兜底扫到 | 解码的第二刀 | 买或卖, 依据 = 扫到 |
| 认不出 | 提到币或曲线, 或 approve / transfer / transferFrom | 碰到. 方向 = 不知道 |
已知路由的份额在掉
占成功帧的百分比, 方案给的约数, 不是逐段曲线. 本课题 18.
目标, 金额, 人, 各给各的
| 字段 | 规则 |
|---|---|
| 目标 | 曲线地址先查叠加层 (空档里的, 本块前面的, 本卡前面的), 再查 state 币记录. 都没有: 目标 = 没登记, 地址照给, 卡照出 |
| 外层投入 | 签名人在外层写的数. 路由会先抽成. 布局声明了换算, 才另给「进曲线投入」 |
| 三个人都给 | 收款人, 签名人, 边界调用者. 路由会替人收款, 也会替人卖. 4337 / 7702 真正的人是边界调用者 |
| 方向 | 只从布局或选择器读. 不从 value 猜 |
直调曲线的形状, 当时 to 还没登记的有 10,351 笔 (抽 4 段). 其中 79% 的 to 后来登记成了 Pons 曲线, 是发币前就往算好的地址打的抢跑单. 21% 从来不是 Pons. 所以没登记的也出卡. 调用包和 7702 自调里, 97–99% 的帧 calldata 提到了曲线或币.
0xd6d35b56 的定长布局
最近窗口 (块 109,637,351–109,717,351, 2,578 笔) 里, 「布局不认识」的 895 笔集中在 86 个 (to, 选择器) 上, 头号 1 个占这一档 ETH 的 68.4%. 这个合约的 calldata 定长 484 字节.
374 / 374 笔都成立 (本课题 12). 它的 169 笔买单 value 全是 0, 按 value 猜方向会全错 (本课题 06). 按 (to, 选择器, 方向) 分组, 每组至少 5 笔, 位置都固定的, 占这一档 56.1% 的笔, 71.9% 的 ETH. 每组至少 3 笔则是 59.9% 和 77.2%. 金额和收款人的位置还没对 chain.calls, 留在待量.
三份记忆
查一个地址只有一个顺序: 先叠加层, 再 state. 知识包不参加这个查找, 它只管「这段字节怎么读」.
叠加层按 (块, 笔, 序) 排. 空档从哪来: chain 每 250 毫秒问一次链头, 存档每秒落盘, facts 才往下走, 再加上 trace, 解码, 折叠, 跨机推送. 完整分布是待量.
叠加层里记什么
| 条目 | 拿来干什么 |
|---|---|
| 认出的新币 | 曲线, 币, 配对币, 地址依据. 后面的买卖认得出它. 初筛的地址表里也有它 |
| 每个入口发了几次币 | 计数 = state@A 的真值 + 叠加层里的次数 × 步长 |
| 每个入口被碰过 | to 是它, 或 calldata 提到它. 用到这个入口的字段标暂定, 因为没认出的调用一样会动 nonce |
| 改参数的调用 | 初筛提到协议地址, 又扫到改参数选择器. 之后的发币地址标暂定 |
| 洞 | feed 缺的块号区间. 依赖叠加层的字段标暂定 |
什么时候截, 什么时候停用
| 情形 | 怎么办 |
|---|---|
| state 折过块 b | 拿 b 的发币预测和 state 里块 b 新登记的币对一遍, 然后截掉 ≤ b 的条目 |
| 重组 | 截掉 ≥ 该块的条目, 重认 |
| state 超前, A ≥ 正在认的块 | 状态计算的字段一律「不知道 (state 已过这块)」, 不进叠加层 |
| A 倒退, 或程序重启 | 清空, 只重放 A 之后的录帧. 缺的那段记成洞. 重启点记进 runs/ |
| 空档超过 K 块 | 状态计算的字段一律「不知道 (空档太大)」. 解码出的字段照给. K 还没量 |
知识包的一行
知识包相当于现在 rl 里那两张对照表放大以后的样子: 4 行入口白名单, 加 8 行路由「这个地址用哪种读法」. 拆 calldata 的函数还是代码, 不在包里.
| 内容 | 键 | 值 |
|---|---|---|
| 入口规则 | 入口, 进入口那一层的选择器, 内嵌的发币函数, 绑的实现 | dev 用哪一种封闭规则, 加上成绩 (成立比例, 样本数) |
| 路由布局 | 地址或码哈希, 选择器, 绑的实现 | 人写的读法名, 或学出的定长布局 |
| 调用包表 | 选择器 + 形状 | 怎么展开, 子调用的调用者是谁 |
| 手工行 | 同上 | 可以加, 不能改. 带来源「手工」和日期 |
定长布局是一组偏移: 目标在第几字节, 方向是常量还是「目标在哪个偏移」(最多 2 种位置), 投入在第几个字或就是 value, 最少拿回在第几个字, 收款人在第几个字.
| 学什么 | 门槛 | 依据 |
|---|---|---|
| 入口规则 | 选择段 ≥ 1 笔, 验证段 ≥ 2 笔, 全部样本 100% | 计算 |
| 入口规则 | 同样的样本门槛, 成立 ≥ 99% 但不到 100% | 暂定. 卡上带成绩 |
| 定长布局的目标和方向 | 选择段 ≥ 1 笔, 100%. 只用一笔一个曲线边界帧的交易 | 解码 |
| 定长布局的收款人 | 选择段 ≥ 20 笔, 100% | 解码 |
学入口规则时, 对每一组拿发币调用的调用者, TokenParams, 免税名单, 曲线真值 (facts launch_calls), 加上那一层调用的入参 (chain.calls), nonce (contract_births), 槽 (state 的入口槽), 去试那五种形. 定长布局 m = 1 时目标和方向召回 52.5%, 24 万笔错 1. 收款人偏移要 m = 20 才 0 错, 召回大约 15% (本课题 20). 金额和最少拿回还没对 chain.calls.
| 版本字段 | 含义 |
|---|---|
| sha256 | 这一版的内容 |
| 截止 T | 学它时用到的最后一块 |
| 生效 E | T + Δ. Δ 写在包里, 构建时就定 |
| 实盘 | 用 E ≤ 当前块的最新版 |
| 重放第 b 块 | 只用 E ≤ b 的版本. runs/ 记下这一块实际用的版本 |
历史版本用 facts / chain 全史按天前推, 和实盘同一个构建程序. 人写的读法 (okx-dag, axiom 命令串, 调用包展开, 捆绑, 跟单平台) 是识别器里的代码. 新读法改代码, 离线核过才登记. 哪个地址用哪种读法是数据行. 不放进知识包的常量: 创建代码, 选择器, 工厂的 immutable 布线, 都在 facts_decode.
为什么每一行都要绑实现
点名的 13 个路由, 入口, 头号未知合约里, 7 个是 EIP-1967 可升级代理. 代理升级只换实现, 代理自己的码不变. 不绑实现, 布局一变就会照老布局静默读错, 还标成解码. 每一行绑核过的那份「真正跑的实现」: 代理绑实现地址和码哈希, 7702 绑委托目标, 1167 克隆绑实现. 实现对不上时照旧读, 标暂定. 离线复核过了再加新行.
横条是全史升级次数. 最近一次: GMGN 块 107,557,685, APM 块 108,412,591. 本课题 10, 11-09.
facts 只加一张表: 代理实现变更
| 类 | 记什么 | 从哪来 |
|---|---|---|
| 代理换实现 / 换 beacon | 全链每次写 EIP-1967 实现槽或 beacon 槽: 位置, 代理地址, 哪个槽, 旧值, 新值 | facts 判成员时已经在扫这两个槽. 新表把扫到的全部留下 |
| beacon 换实现 | 位置, beacon 地址, 旧实现, 新实现 | 实现存在 beacon 自己的存储里. 用 Upgraded 事件, 或代理调用时问 beacon 的回包. 动工时再定哪一种 |
当过 Pons 相关交易 to 的 beacon 型代理有 36,944 个, 11.2 万笔, 其中 28 个是发币入口. 背后 91 个 beacon 有 83 个换过实现. 0xa125 一个 beacon 管 36,102 个代理, 换过 3 次. beacon 一换, 这些代理跑的码一起变, 代理自己的槽一个字节都不动. 全链 beacon 型代理 44.6 万个. 按事件数, 不发事件的会漏. chain 本身不改: 全块 calldata, 每一帧的调用者和入参, 全链存储改动都已经有了.
state 要留的记录
都从 facts 折. 不为识别器单开一层. 识别器只读其中几个字段.
| 记录 | 字段 | 从 facts 哪来 |
|---|---|---|
| 协议参数 · 配置号 | 发行量, 曲线费, 虚拟储备, 毕业线, 池费, tick 间距 | 工厂存储槽. 池费和 tick 间距在一个打包槽里, 要拆位. setting_changes 只带配置号, 不带值 |
| 协议参数 · 配对币 | 虚拟储备, 毕业线, 小数位 | PairTokenEconomicsUpdated |
| 协议参数 · 当前 | 费策略 5 项, forwarder | hook 的 5 种事件 + 初值槽; LaunchForwarderSet |
| 入口 | nonce; 每个槽的最新值 (知道 / 全史为零 / 不知道); 发过几次币 | transactions, contract_births, eip7702; member_storage_changes, launch_entry_reads; launch_calls |
| 币 | 币 ↔ 曲线, 配对币, 发币块, 阶段 (曲线上 / 已毕业) | launch_calls, pool_graduations. 键是币, 另有曲线索引 |
| 真正跑的码 | 码哈希, 实现地址. beacon 型代理 = 它的 beacon 当前的实现 | 新的代理表 + contract_births + eip7702_authorizations |
| 水位 | A, state 版本 | 折叠进度 |
入口槽全部 816 万行, 大约 750 MB, 其中 EntryPoint 三个版本 568 万行, 占 70%. 调用包合约的槽不留, 因为展开它们用调用包表, 不读它们的槽. 去掉以后大约 248 万行 (本课题 16). 入成员以前的槽值从发币笔里补, 没见过 = 不知道. nonce 和 facts 的核对用同一套规则.
不留: 地址 nonce (交易里自带), 余额, 持仓, 曲线和币的原始槽, 合约代码本身. 块头只在上一帧是洞时当时间兜底.
StateView, 全是点查
block() 返回 A. version() 返回折叠代码版本, 或 A 处的 state 哈希.
launch_config(配置号), pair_economics(配对币), fee_policy(), forwarder().
entry(入口) 返回 nonce, 每个槽的知道 / 全史为零 / 不知道, 发币次数.
running_code(地址) 返回码哈希和实现地址. coin_by_address(曲线或币).
block_timestamp() 只当上一帧是洞时的时间兜底.
批作业里, state 的折叠代码当库用, 按块推进到任意 A, 给出同一个接口. 三种重放都要它. 0.4 微秒来自 10-20 的节点机原型, 已超过 14 天, 动工前复测.
意图卡的数据结构
每个字段是二选一. 知道, 就给值和依据: 解码, 计算, 扫到, 或者暂定加原因. 不知道, 就给原因. 每个字段只有一个产出者, 所以没有「两边不一致」要裁决.
| 字段 | 含义 |
|---|---|
| 块号, 块 hash, 时间, 帧头种类, 到达时刻 | 这一帧是谁 |
| 作废 ≥ b | 重组时才有. 告诉宿主从哪一块开始旧卡无效 |
| 视野 | A, state 版本, 空档块数, 洞, 叠加层起点 |
| 知识包版本, 识别器代码版本 | 重放要钉死的两样 |
| 意图卡[] | 只含碰到 Pons 的交易, 按块内序 |
| 计数 | 交易数, 拆不开的, 第一遍过筛, 第二遍过筛, 出卡数 |
| 字段 | 含义 |
|---|---|
| tx hash, feed_index, transaction_index | 身份. 对账用 hash |
| 签名人, to, 选择器, value, nonce, 交易类型 | 交易信封 |
| gas, gasPrice, maxFee, priority | 认对手 bot 要用的 gas 四项 |
| 7702 授权列表 | 本笔生效的委托 |
| 意图[] | 按 calldata 顺序. 键 = (tx hash, 序号 k) |
| 字段 | 取值 |
|---|---|
| k | 这笔里的第几个意图 |
| 平台 | pons_v2. 以后别的发币平台也带自己的平台名 |
| 行为 | 发币, 买, 卖, 碰到 |
| 目标 | 曲线, 币, 来源. 来源是 state 币记录, 或叠加层 @块·笔·序, 或没登记 |
| 边界调用者 | 真正打到 Pons 边界的那一层是谁 |
| 怎么认出的 | 边界调用, 调用包展开路径, 路由布局, 扫到 @偏移, 提到 @偏移, 调币合约 |
| 参数 | 每个字段单独是「知道」或「不知道」 |
| 行为 | 参数 |
|---|---|
| 发币 | TokenParams 全部, 配置号, 配对币, dev, 收税人, 声明的免税, 实际免税, 经济指纹 (对上 / 对不上 / 没写), 曲线, 币 |
| 买 | 投入 (外层, 配对币最小单位), 最少拿回, 收款人, 截止时间. 布局声明了才有进曲线投入. 跟单平台才有「跟的是谁」 |
| 卖 | 卖出币量, 最少拿回, 收款人, 截止时间 |
| 碰到 | 怎么碰的: 提到 @偏移, approve 给谁, transfer, transferFrom, 发币入口但没解出调用. 外加 value |
| 不知道 | 什么时候 |
|---|---|
| 入口没规则 | 发币经过的入口不在知识包里, dev 说不出, 地址也不算 |
| 参数缺 | state 里没有这个配置号或配对币 |
| 布局不认识 | 碰到了, 但读不出行为 |
| 不在 calldata | 这一项不在 calldata. 例如用自己的余额当投入 |
| 目标没登记 | 地址不在 state, 也不在叠加层 |
| 空档太大 | 空档超过 K 块 |
| state 已过这块 | A ≥ 正在认的块 |
| 扫描截断 | calldata 超过扫描上限 |
| 暂定 | 什么时候 |
|---|---|
| 用了叠加层 | 新币或入口计数来自叠加层, 带块和笔 |
| 空档里有人碰过这个入口 | 没认出的调用可能动过 nonce 或计数 |
| 指纹对不上 | 经济指纹和 state 参数算的不同 |
| 空档里改过参数 | 叠加层记到改参数的调用 |
| feed 缺块 | 空档里有洞 |
| 读法所绑的实现换了 | 真正跑的实现 ≠ 这一行绑的 |
| 规则不是全史 100% | 成立 ≥ 99% 但不到 100%. 卡上带比例和样本数 |
两张例子
签名人 0xAAAA 调 forwarder 的 launchAndBuy. 一张卡, 两个意图.
| 字段 | 值 | 依据 | 产出者 |
|---|---|---|---|
| 0 行为 | 发币 | 解码 | 解码 |
| 0 dev | 签名人 | 解码. forwarder 的调用者 | 解码 |
| 0 名字, 符号, 配对币, salt, 首买 | calldata 里的 TokenParams | 解码 | 解码 |
| 0 曲线, 币 | CREATE2 算出来 | 计算, 指纹对上 | 计算 |
| 0 实际免税 | 声明的 + dev + 收税人 + 首买收款人 | 计算 | 计算 |
| 1 行为 | 买 | 解码 | 解码 |
| 1 目标 | 本卡意图 0 的新币 | 叠加层 @本卡 0, 暂定 | 计算 |
| 1 投入, 最少拿回, 收款人 | 首买参数 | 解码 | 解码 |
买的目标来自本卡前面刚算出的发币, 所以标暂定, 直到 state 折到这块并登记了这个币.
to 是 0xd6d3, 知识包里有学出的定长布局. value 是 0, 方向不看 value.
| 字段 | 值 | 依据 |
|---|---|---|
| 行为 | 卖 | 解码. 币出现在第 240 字节 |
| 目标 | 币 0x52f7, 曲线从 state 币记录来 | 计算 |
| 卖出币量 | 第 2 个字 | 解码 |
| 怎么认出的 | 路由布局, 定长 | 解码 |
如果这笔执行时, 合约真正跑的实现已经不是这一行绑的那份, 同样的偏移照读, 字段改标「读法所绑的实现换了」.
怎么知道它对
真值按字段写出处. 投入不去比 facts 里的 curve_buys: 那一列是实际花掉的钱, 曲线快毕业时会截量, 少于请求的投入. 配对键是 (tx hash, 曲线, 方向, 第几次).
| 意图字段 | 真值 | 怎么比 |
|---|---|---|
| 发币的 TokenParams, 配置号, 配对币, dev, 曲线, 币 | facts launch_calls. 成败都收. 成功的才有 deployer, 曲线, 币 | 逐字段相等 |
| forwarder 首买的投入和收款人 | launch_calls 里 forwarder 的那几列 | 相等 |
| 直调, 调用包里, 兜底扫到的曲线调用 | chain.calls 里 to = 曲线, 选择器 buy / sell 那一帧的入参和 from. 成败都有 | 投入, 最少拿回, 收款人, 调用者逐字段相等 |
| 路由腿的目标, 方向, 收款人, 进曲线投入 | 同一笔里对应的曲线边界帧 | 按 (曲线, 方向, 第几次) 配对. 进曲线投入要布局声明了才比 |
| 路由腿的外层投入, 最少拿回 | 没有独立真值, 它就是 calldata 本身 | 只核位置真的存在, 而且解得开 |
| 碰到 | failed_transaction_mentions, token_approvals, token_transfers | 只量漏了多少 |
| 实际花掉的, 换出, 退款 | 不是意图 | 不比 |
三种重放
| 重放 | 知识包 | A 取什么 | 要求 |
|---|---|---|---|
| 审计 | runs/ 记的每块实际版本 | runs/ 记的 A, 连同 state 版本, 叠加层起点, 重启点 | 和实盘逐位一致 |
| 研究 | 每块用 E ≤ b 的历史版本 | 有录帧用录下的视野. 没录帧的历史按空档分布取 A. 分布还没量 | 不偷看. 字段口径和实盘一样 |
| 验收 | 任一版. 查代码, 不查外推 | 全视野, A = 正在认的块减 1 | 依据为解码或计算的字段, 和真值的差必须是 0 |
差不是 0, 就是解码, 规则或折叠的 bug. 「扫到」和「暂定」不要求差为 0, 只报出错率. 一条规则能不能外推, 只看截止块 T 之后的块. 叠加层是逐块累积的, 重放第 b 块必须从录帧起点或重启点顺序跑到 b, 不能单块跑. runs/ 每块记: 录帧, A, state 版本, 知识包版本, 洞, 叠加层起点, 重启点. 意图本身不存.
bot 那台机器没有 facts 时
state 折过块 b, 就拿 b 的发币意图和 state 里块 b 新登记的币对一遍. 三种结果: 对上; 意图有, state 没有 (发币失败或算错); state 有, 意图没有 (漏认).
宿主状态页上的计数
| 计数 | 看什么 |
|---|---|
| 空档块数分布, 洞 | state 晚多少 |
| 拆不开的 | 帧坏了多少 |
| 两遍初筛各过几笔 | 第二遍是不是在干活 |
| 碰到的占比 | 知识包该补谁 |
| 实现换了的笔数 | 代理升级后旧行还在被用 |
| 每块耗时 p50 / p99 / 最大 | 要不要开并行 |
验收指标
| 指标 | 口径 |
|---|---|
| 召回 | 按行为, 同时按笔和按金额 |
| 精确 | 声称的意图在真值里有对应边界帧的比例 |
| 发币地址准确率 | 验收时, 依据为解码或计算的必须 100% |
| 碰到的金额 | 按 (to, 选择器) 排, 就是知识包下一步该补的清单 |
| 暂定字段的出错率 | 按暂定原因分. 「规则不是全史 100%」单独报 |
| 没登记目标的噪声率 | 现在 21% 从来不是 Pons |
| 兜底扫到的精确率 | 现在方向 + 收款人对上 80.2%, 对不上 3.0% |
| 每块耗时 | 典型块和忙块 |
性能, 代码, 还没量的
单价, 微秒
哈希取 0.6–0.7 的中点, 输入大约 500 字节. 发币地址是两次 keccak, 创建代码前缀预先算好; 不预先算是 26 微秒. 签名人只给过初筛的笔. 本机 M1 Max, 合成输入, 11-09 跑了两次 (本课题 07). state 点查是 10-20 原型, 超 14 天, 动工前复测.
一块大概多久
| 块 | 估计 |
|---|---|
| 典型 | 大约 0.1 毫秒. 段 104 是 6,416,697 笔 / 100 万块, 约 6–8 笔. 全链 14.2% 过初筛, 大约 1 笔往下走 |
| 忙 | 亚毫秒. 窗口里成功的曲线买卖最多 61 条 / 块 |
这两格是推断, 等 crate 出来换掉. 并行只在过筛笔数多的块开. 超大 calldata 设扫描上限, 超了这笔标「扫描截断」, 不拖后面的笔. 热路径不联网, 不读盘, 不分配大块内存. 知识包换版是原子替换.
crate 里每个文件
platforms/robinhood-chain/crates/intent_recognizer/. 依赖 facts_decode 的协议常量, sha3, secp256k1, serde. 不依赖网络库, 不执行合约.
| 文件 | 职责 |
|---|---|
| lib.rs | Recognizer::new(知识包). recognize(帧或块, StateView) 返回意图块 |
| chain/ | 链常量与块环境: chain id, 序号到块号的偏移, 帧头字段, 时间钳位 |
| feed/ | 拆帧, 重发 / 重组 / 洞, 交易 RLP, 签名人恢复 |
| screen.rs | 初筛两遍: 地址前缀表, 发币选择器, 协议地址, 直调曲线形状 |
| decode/ | 边界 ABI, 调用包读法, 人写的路由读法, 定长布局求值, 任意偏移扫描和字节区间去重 |
| compute/ | 定目标, CREATE2, 入口规则求值, 经济指纹, 实际免税 |
| overlay.rs | 叠加层: 空档加本块, 截断, 重启 |
| knowledge/ | 知识包格式, 校验, 按生效块选版本, 实现绑定核对 |
| intent.rs | 意图块, 意图卡, 意图, 知道 / 不知道 / 暂定 |
| view.rs | StateView |
谁跑它
| 谁 | 做什么 |
|---|---|
| 宿主 = bot 进程 | 收 feed, 给钉在 A 的 state 视图, 装知识包, 每块调 recognize, 把录帧和视野记进 runs/ |
| 每个 bot 进程 | 自己跑一份. 它本来就持有 state 副本. 识别和打分读同一个 A |
| 批作业 | 三种重放, 学规则和定长布局, 按天前推历史版本. 训练和回测用同一份代码重算 |
和现在的 rl 怎么接
复制四块已经在生产跑过的逻辑当起点, 不 import: 帧, RLP, 签名人, 帧序; 发币扫描和 CREATE2; 路由, 捆绑, 跟单解码. 要改三处: 费策略从 state 读 (现生产写死的历史表, 块 47,259,464 以前取的收费地址是错的); 输出改成意图卡; 类型换成这个工作区自己的.
验收时拿同一份录帧, 只比块 47,259,464 以后. 新识别器的发币 (曲线, 币, dev) 要和现生产逐笔相同.
五步建造
| 步 | 做什么 | 依赖 |
|---|---|---|
| 1 | facts 加代理实现变更表, 1967 两个槽加 beacon 换实现, 补全史 | 只要现有 chain / facts |
| 2 | crate: 拆帧到意图卡, 外加知识包格式. 同一份录帧上, 发币和现生产逐笔相同 | 不等 state 引擎 |
| 3 | state 先折识别器要的几类: 协议参数, 入口, 币, 真正跑的码 | state 本来就要这些 |
| 4 | 学规则和定长布局, 按天前推历史版本, 三种重放跑全史, 再补人写的读法 | 步 1 的表补完 |
| 5 | 进 bot. 每块调用, 录帧和视野进 runs/, 在线健康. 和现生产并行对拍 | 步 2 和步 3 |
还没量, 不影响这个形状
| 编号 | 量什么 | 定什么 | 什么时候 |
|---|---|---|---|
| 5 | 实盘空档分布, 暂定字段的出错率 | K, 研究重放用的空档分布, Δ | state 折叠和推送建好 |
| 6 | bot 机器上每块耗时, 两遍各过几笔, 大 calldata 的扫描耗时 | 并行门槛, 扫描上限 | 步 2 的 crate |
| 3′ | 不发事件就写 beacon 槽的代理, beacon 换实现走哪条路取 | 新表 beacon 那一类怎么取 | 动工步 1 |
| 2′ | 布局的金额和最少拿回位置, 按绑的实现分组以后的召回 | 定长布局里这两项收不收 | 步 1 的表补完全史 |
| 8′ | 误放的「to 认得」是哪些 to, 漏放的 3 个 to 是什么合约 | 初筛要不要收紧, 知识包补谁 | 现在就能做 |
以后加东西, 动哪里
| 扩展 | 改什么 | 不动什么 |
|---|---|---|
| RH 上别的发币平台 | 加一个平台模块: 边界 ABI, 地址算法, 改参数选择器. 再加 facts 新库和 state 新记录. 意图, 目标, 知识包行都带平台 | 拆帧, 身份, 初筛, 调用包和路由展开, 叠加层, 知识包机制, 意图卡的形状 |
| 跟单 | 不改识别器. 跟单读卡上的签名人, 边界调用者, 收款人, 以及「跟的是谁」 | 全部 |
| 别的 Nitro 链 | 换 chain/ 的常量, 含序号偏移. 知识包按链各一份 | 其余全部 |