大多数 Stellar 上的 DEX 界面会为你的交换选择一条路由。它们检查订单簿,也许瞥一眼流动性池,然后给你一个数字。Shadow Pocket 做的事根本不同——它并行评估所有可能的路由,并将100%的交易发送到返回最多输出的那条路由。
单路由交换的问题
当你在 Stellar 上将资产 A 交换为资产 B 时,存在多个可能的执行场所:
- SDEX 订单簿 — 交易者和做市商放置的限价单。
- AMM 流动性池 — 恒定乘积池,价格由算法确定。
- 跨币对路径 — A 到 C 再到 B 的路由,使用中间资产作为桥梁。
单路由方式选择一个,然后寄希望于它是最好的。但流动性随着每个账本关闭(大约每5秒)而变化。订单簿可能在浅层深度有很好的价格,但对大额交易就崩溃了。池可能由于其连续的流动性曲线为大额交换提供更好的汇率。有时通过 XLM 或 USDC 的跨币对路径比两种直接选项都好。
选错了会让用户损失真金白银。
并行评估如何运作
Shadow Pocket 的后端针对每次报价请求,独立评估所有可用路由。流程如下:
- 发现 — 系统识别所有可行路由:直接订单簿、每个相关 AMM 池,以及通过主要桥接资产的跨币对路径。
- 并行模拟 — 每条路由使用确切的输入金额独立模拟。订单簿模拟逐层遍历挂单,在每个价格层级填充订单。池模拟使用恒定乘积公式。路径模拟将多个操作链式串联。
- 输出比较 — 每条路由返回一个模拟输出金额,并根据特定输入规模计入滑点。
- 选出最优 — 输出最高的路由被选中。没有拆分,没有部分成交——100%的交易通过最佳路径执行。
后端始终在响应中首先返回订单簿结果,然后是池路由,这样前端可以显示每条路由的流动性信息,尽管只有最佳路由会被执行。
为什么100%走一条路由
拆分路由在理论上听起来很聪明——将部分交易通过订单簿、部分通过池,以最小化价格冲击。但在实践中,在 Stellar 上,这引入了很少值回的复杂性:
- 事务手续费微不足道 — 没有因 gas 优化而需要批量处理的理由。
- 原子执行 — Stellar 事务要么完全成功,要么完全失败。拆分增加了操作数量,提高了部分失败的几率。
- 账本时序 — 价格每5秒变化一次。等到拆分订单执行时,最优拆分比例可能已经改变了。
对于 Stellar 上绝大多数交易规模,将100%发送到单一最佳路由的表现优于任何拆分策略。数学很简单:模拟所有选项,选出最优。
通过SSE的账本触发刷新
交换报价很快就会过时。Shadow Pocket 通过连接到 Stellar 账本流的 Server-Sent Events (SSE) 保持报价新鲜:
- 前端打开到
/ledgers?cursor=now端点的 SSE 连接。 - 每当新账本关闭(约5秒),事件触发。
- 前端进行 debounce 处理并触发新的报价请求。
- 后端使用当前订单簿和池状态重新评估所有路由。
- UI 更新显示的汇率、输出金额和每路由明细。
这意味着用户始终看到反映当前账本状态的报价——而非30秒前可能已经大幅变动的过时数据。
实现细节
几个确保可靠运行的工程决策:
Debounce 定时器管理 — 报价刷新的 debounce 定时器必须在 setTimeout 回调中正确置空。如果定时器引用已过期,UI 可能会无限期地卡在加载状态。
加载状态独立性 — 交换报价加载指示器在 abort signal 检查之外重置。这防止了被中止的请求导致 UI 永久处于加载状态的死锁。
Service Worker 绕过 — SSE 的 text/event-stream 内容类型被排除在 Service Worker 的 precache 逻辑之外。没有这个绕过,Service Worker 会拦截流式连接并破坏实时更新。
独立路由评估 — 后端不使用贪心方法(先检查订单簿,再用池填充剩余部分)。每条路由都针对全额独立评估,确保比较公平。
结果
用户看到的是一个实时更新的交换界面,精确显示其交易将走哪条路由,并保证在 Stellar 所有执行场所中获得最佳可用汇率。无需手动选择路由,无过时报价,无拆分猜测。
快速交换不在于原始速度——在于当你点击「交换」时,始终拥有最新鲜、最准确的信息。