banner
zach

zach

github
twitter
medium

从零开始UniswapV2 | Part-2 预言机

什么是价格#

Uniswap 作为链上的去中心化交易所,承载着价值发现的功能,即用户或其他链上合约可以通过 Uniswap 来获取代币的价格,Uniswap 在这其中承担链上预言机的功能。

假设当前交易池中有 1 Ether 和 2000 USDC,那么以太币的价格就是 2000/1=2000 USDC,反之就是 USDC 的价格,因此价格就是一个比率,由于智能合约尚不支持小数,所以在 Uniswap 的代码中拓展了新的数据类型来存储价格,关于新的数据类型后面会专门做介绍。

TWAP 价格机制#

然而仅仅使用瞬时的代币数目之比作为价格是不安全的,存在人为操纵价格预言机的风险,由于 Uniswap 提供了闪电贷功能,因此在某个闪电贷交易的瞬间,交易对内的代币余额会产生剧烈波动。在 UniswapV2 中为了解决这个问题,采用了 TWAP(Time Weighted Average Price)即时间加权的价格预言机机制。

具体工作原理如下:

  • 假设过去一天,资产在前 20 个小时的价格为 20$,最近 4 小时的价格为 10$,那么 TWAP=($20*20+10$*4)/24 = 18.33$
  • 假设过去一天,资产在第一个小时的价格为 10$,最近 23 小时的价格为 15$,那么 TWAP=($10*1+15$*23)/24 = 14.79$

总结下来,TWAP 的公式如下,这里的 T 是时间段,P 是对应时间段的价格

image

在 UniswapV2 的合约中,只会记录分子部分,即记录每个时间段乘以单价的求和,而分母部分则需要使用方自行维护,交易对内有两个代币,所有有两个值来记录

通常我们只需关心某一时间区间内的代币价格,这是 TWAP 公式的历史价格公式:

image

假设我们的计价从 T4 开始,那么实际的计价公式应该如下:

image

前面已经提到,在合约内有一个变量会追踪分子的求和值,以 token0 的追踪计价变量 price0Cumulativelast 为例:

image

这个变量是记录了历史以来所有时间段的求和,那么我们只需要从 T4 开始的部分即可,计算方式也很简单,在 T3 时间点我们获取一个 price0Cumulativelast 变量的快照,在最新即 T6 时间点再获取一次,两次的差值即是 T4 - 最新时间段内 token0 的计价和

image

我们自己也维护了最近的窗口持续时间和,即:T4+T5+T6

那么这段时间内的 TWAP 价格即可计算得出:(price0Cumulativelast-UpToTime3)/(T4+T5+T6)

UniswapV2 的实现#

具体到 Uniswap 的实现中,对于每个交易对都维护了两个变量 price0Cumulativelast 和 price1Cumulativelast,在之前提到的_update 方法中进行求和,具体的代码如下:

  • 首先获取当前的区块时间戳blockTimestamp
  • 通过与 blockTimestampLast 相减,计算出距离上次更新过去了多少时间timeElapsed
  • 只有在timeElapsed >0 时,即进入下一个区块时,才会累加价格 * 时间段
  • 注意这里的价格计算用到了UQ112x112 的特殊格式,了解它是为了记录小数即可,后面会专门讲解这里的优化
  • 在处理完累加后,才会更新blockTimestampLast 到最新的区块时间戳
function _update(uint balance0, uint balance1, uint112 _reserve0, uint112 _reserve1) private {
    ...
    uint32 blockTimestamp = uint32(block.timestamp % 2**32);
    uint32 timeElapsed = blockTimestamp - blockTimestampLast;
    if (timeElapsed > 0 && _reserve0 != 0 && _reserve1 != 0) {
      // * never overflows, and + overflow is desired
      price0CumulativeLast += uint(UQ112x112.encode(_reserve1).uqdiv(_reserve0)) * timeElapsed;
      price1CumulativeLast += uint(UQ112x112.encode(_reserve0).uqdiv(_reserve1)) * timeElapsed;
    }
    blockTimestampLast = blockTimestamp;
    ...
  }

TWAP 的潜在问题#

  • 基于时间加权的价格计算,会提高攻击者的操纵成本,因为需要连续控制多个区块,这样的攻击成本是很高的。
    当价格产生剧烈波动时,由于有时间作为加权因素,预言机的价格无法在较短时间内反映出价格的波动,反而提供出过时的价格,尤其是在市场发生剧烈动荡时,这样的情况会导致 Uniswap 中的价格与外部市场产生较大的差异。

  • 使用 TWAP 预言机仍然依赖链下的定时触发,存在维护成本与中心化问题。

加载中...
此文章数据所有权由区块链加密技术和智能合约保障仅归创作者所有。