Uniswap V4 探析(一) —— 新特性解读

Uniswap 官宣下一代 AMM Uniswap V4 的开发进展,并公开了白皮书和代码仓库。这次 V4 的白皮书仅仅只有 3 页,原因是 Uniswap 并没有对 AMM 的核心算法逻辑做太多修改,而是在 V3 的基础上,增加了一些新的特性,以满足更多的场景需求。本文将基于目前已开源的代码,来看看 V4 带来了哪些新的特性。

AMM 算法

在 AMM 算法层面,Uniswap V4 并没有对 V3 做任何修改,依然使用 V3 基于 x*y=k 的集中流动性算法。

在 Uniswap V3,每一个交易对可以有 4 个池子(原本是 3 个,后来增加了一个新的 1bp pool),分别代表 0.01%, 0.05%, 0.3%, 1% 费率的池子,这些池子对应的 tick space 也各不相同,在创建池子的时候,只能选择这 4 种中的任一个。

在 Uniswap V4,每一个交易对理论上可以有任意数量的 pool,并且每一个 pool 的 fee rate 也可以是任意值,这些 pool 的 tick space 也可以是任意值。

这同时也带来了一个问题:Uniswap V4 中交易对的流动性将被碎片化,因此需要一个更有效的 router/aggregator 来帮助用户找到最优的交易路径。

Hooks

Hooks 是一组由第三方或者 Uniswap 官方开发的合约,在创建 pool 的时候,pool 可以选择绑定一个 hook. 之后在交易的特定阶段,pool 都会自动调用与之绑定的 hook 合约。Uniswap V4 一共定义了这些可以执行 hook 合约代码的阶段:

  • beforeInitialize
  • afterInitialize
  • beforeModifyPosition
  • afterModifyPosition
  • beforeSwap
  • afterSwap
  • beforeDonate
  • afterDonate

分别表示在初始化 pool,添加/移除流动性,交易,捐赠等操作的前后,都可以调用 hook 合约。

Hook 合约需要显示指定在上述的哪些阶段进行执行,而 pool 则需要知道对应的 Hook 在某个阶段是否需要执行,为了节省 gas,这些 flag 都没有在合约中进行存储,而是需要 Hook 使用特定的地址来标明。具体判断的代码如下:

可以看出,Hook 地址的前 8bit 都被用来标记在特定阶段此 Hook 是否需要执行的 flag.

因此,Hook 的开发者需要在部署合约的时候,产生出满足 Pool 要求的地址,这通常需要使用 Create2 + 随机 Salt 来实现,这里有一个通过 Huff 合约来计算 Hook 地址的范例:HookMineAndSinker

利用 Hooks 可以实现一些高级功能,例如 TWAMM,Limit Order,TWAP Oracle 等。

下图是一个在用户 swap 前后执行的 hook 的例子:

可以看到在执行 swap 的前后,pool 会先检查 pool 对应的 hook 是否开启了相应的 flag,如果开启了,就会自动调用 hook 合约的相应函数。

Uniswap 这次也开源了一些作为范例的 Hook 合约代码,例如 Limit Order, TWAMM 等,我会在后续文章对 TWAMM 进行一些解读。

除了可以在特定阶段执行代码之外,Hook 还可以决定某一个 pool 的 swap fee 费率,和 withdraw 费率。withdraw 费率指的是用户在移除流动性时需要向 hook 支付的费率。除此之外,Hook 还可以指定在 swap fee 中抽成一部分给自己。

在创建 pool 时,需要使用 fee 参数(uint24)前 4个 bit 来标记此 pool 是否使用动态 fee,以及是否启动 hook swap fee 和 withdraw fee:

如果启动了动态 fee,那么 pool 会在每次 swap 之前,调用 Hook 合约来获取当前的 swap fee ratio. Hook 合约需要实现 getFee() 函数,返回当前的 swap fee ratio.

Hooks 让 Uniswap V4 成为了一个开发者平台,给了 AMM 更多的可能性。一些可以用 Hooks 来实现的功能:

  • Limit Order
  • TWAMM
  • 自动管理用户的 LP,将 LP 的 fee 自动复投到 LP 中
  • 通过 frontrun/backrun 套利获取 MEV 利润
  • 实现 Oracle
  • 根据波动率调整 swap fee

Singleton 合约

Uniswap V3 中每次创建新的 pool 都需要部署一个新的合约,这会消耗大量 gas,但是其实这些 pool 使用的代码是相同的,只是初始化参数不相同而已。Uniswap V4 引入了 Singleton 合约,用来管理所有的 pool,这样创建新的 pool 不再需要部署新的合约了,节省了部署合约的 gas.

另外,使用 Singleton 合约的好处是,可以减少交易过程中 token 的转账,因为所有的 pool 都在同一个合约中,所以可以直接在合约内部完成跨 pool 的 swap,而在 V3 中,跨 pool 的 swap 会需要将 token 在不同 pool 中转来转去,这会增加 gas.

同时,在 V4 中,所有 pool 使用同一个合约,并且合约内部的 token 记账也被简化为每种 token 按 token 来记账,而不是按 pool 来记账,这样一来如果想使用闪电贷借大量 token 也会更方便。

为了方便 Hook 和其他合约的 integration,V4 合约增加了 extload 函数,这样合约所有内部 states 都变成外部可读了,所有 pool 的状态将对外部完全透明。

Flash Accounting

为了减少跨 pool swap 的 token 转账,V4 同时使用被称为 Flash Accounting 的方法,将 swap, add/remove liquidity/flash loan 的过程都标准化成一种类似闪电贷的过程:

  • 用户获取一个 lock
  • 用户进行任何操作,例如在多个 pool 中 swap,add/remove liquidity,或者通过闪电贷向 pool 借 token
  • 用户所有操作所产生的 token 转账都会被记录在 lock 中
  • 所有操作结束后,用户可以取走他获得的 token,同时需要支付 lock 中记录他需要支付的 token

这些过程需要发生在一个交易中。

这样一来,如果一个交易中需要跨多个 pool 进行 swap,在结算时只需要两笔转账就够了。例如,在一次 ETH->USDC-BTC 这样的 swap 中,USDC 作为中间 token 完全不需要任何转账。

ERC1155 mint/burn

Flash Accounting 可以减少同一个笔交易中 swap 的 token 转账,通过使用 ERC1155 token ,可以进一步减少多个交易的 token 转账。

V4 允许通过 ERC1155 mint,将属于你的 token 保存在 V4 合约中,这样你就可以在多个交易中使用这些 token,而不需要每次都将 token 转账到 V4 合约中。

使用 ERC1155 burn 可以将保存在 V4 合约中的 token 取出。

ERC1155 适合频繁 swap 或 add/remove liquidity 的用户,这些用户可以将常用的 token 直接保存在 V4 合约中,这样可以减少 token 转账的 gas 开销。

总结

总的来说这次 Uniswap V4 更新主要是基于 V3 架构的优化,核心逻辑没有太多改变,新增的 Hook 机制可以让开发者实现更多高级的功能,但这方面潜力还有待挖掘,希望能看到更多有趣的 Hook 合约,后文也会解析一些目前 V4 开源的 Hook 范例合约。