这是我的 AI + Web3 实战营的第三篇研发日志,前两篇如下:
另外,关于 AI + Web3 实战营的相关介绍则有如下几篇文章:
背景
上篇日志里我们完成了 BlockETFCore 底层合约,它能处理多资产按比例的申购赎回。问题是,这样的操作对普通用户来说太复杂了:要一次性准备好多种资产,并且还要按比例。
所以我们需要在 Core 合约之上,再搭建一个更友好的入口 —— Router 合约。它的目标很简单:让用户只用 单一资产(比如 USDT) 就能直接申购和赎回 ETF。
为什么选择 Router 模式?
一开始我们评估了几种方案:
- 直接改 Core:会破坏架构的纯净性
- Wrapper 包装:逻辑变复杂,不利于长期维护
- Router 独立层:职责清晰,逻辑独立,可单独升级
最终,我们选择了 Router 模式,它能保证 Core 保持干净,Router 负责对接用户体验,两者分工明确。
MVP 功能
第一版我们只聚焦几个关键点:
- 单一入口:只支持 USDT
- 目标资产:BTCB、ETH、XRP、SOL、WBNB
- 核心函数:
mintWithUSDT()和burnToUSDT() - 滑点保护:避免用户损失
- DEX 集成:先用 PancakeSwap 做流动性入口
研发过程中的几个挑战
1. PancakeSwap 版本选择
一开始我以为直接上 V3 就行,但发现不同资产的流动性分布差异很大:
- BTCB/ETH/XRP/SOL:V3 更好
- WBNB:只在 V2 有流动性
所以最后采用了 V2+V3 混合架构。
2. 路由策略
最初的设计是所有兑换都走 USDT→WBNB→目标资产。但会发现,这样多走了一跳,增加了滑点和 Gas 成本。更重要的是,USDT/WBNB 在 V3 中并没有流动性。
于是改成了:
- 有配置的池就走配置池
- 没有配置则走默认直换
这个调整大大减少了额外损耗。
3. 费率配置
从一开始的“所有对统一 0.25%”,到尝试“动态费率算法”,最后回归到最简单的方式:直接配置池地址。实践证明,简单才是最可靠的。
4. 混合 DEX 集成
根据资产特性决定走 V2 还是 V3,比如:
- WBNB:走 V2
- 其他:走 V3
Router 内部做了自动判断,用户无感知。
实现要点
Router 主要暴露了两个函数:
function mintWithUSDT(...) external returns (uint256 shares);
function burnToUSDT(...) external returns (uint256 usdtAmount);
流程很直观:
- 申购:用户给 USDT → Router 拆分兑换成目标资产 → 调用 Core 铸造份额 → 返回多余 USDT
- 赎回:用户交回份额 → Core 退回底层资产 → Router 换回 USDT → 转给用户
这中间的优化点在于:
- 用
mintExactShares来提高精度 - 支持管理员灵活配置池和滑点参数
- 保留紧急暂停功能
成果展示
最终架构大致是这样的:
USDT <——> Router <——> Core
│
├─ PancakeSwap V2 (WBNB)
└─ PancakeSwap V3 (BTCB/ETH/XRP/SOL)
它的特性包括:
- 智能路由,自动走最优路径
- V2+V3 混合,充分利用流动性
- 精确份额控制,减少滑点
- 灵活配置,可快速调整
我的收获
- 实地调研很重要:不要依赖假设,链上真实流动性才是决策依据
- 从复杂到简单:很多时候演进过程很曲折,但最终最简洁的方案往往最好
- 架构分离的价值:Core 专注 ETF,Router 专注交互,这样清晰又可维护
- 用户体验优先:功能选择始终围绕“用户用起来是否方便”
下一步
完成 ETFRouterV1 之后,下一步我们就要实现 RebalanceManager 了,这也是最复杂的一个模块。
📌 小结一下:Router 合约让用户终于可以用一枚 USDT 就轻松申购/赎回 ETF 了。对用户来说体验更直观,对架构来说依旧保持了清晰分离。这是我们实战营迭代中迈出的关键一步。