剖析DeFi交易产品之UniswapV3:Pool合约

本文首发于公众号:Keegan小钢


UniswapV3Pool 合约则复杂很多了,其引用的库合约就达到了 13 个,通过 using 方式使用的也达到了 9 个,如下所示:

using LowGasSafeMath for uint256;
using LowGasSafeMath for int256;
using SafeCast for uint256;
using SafeCast for int256;
using Tick for mapping(int24 => Tick.Info);
using TickBitmap for mapping(int16 => uint256);
using Position for mapping(bytes32 => Position.Info);
using Position for Position.Info;
using Oracle for Oracle.Observation[65535];

LowGasSafeMath 是用于加减乘除算法计算的,SafeCast 用于类型转换,TickTickBitmap 用于管理 tick 处理相关的操作和计算,Position 则主要用于更新流动性的头寸,Oracle 则是用于预言机计算的。

接着,来看看定义了哪些状态变量:

address public immutable override factory;
address public immutable override token0;
address public immutable override token1;
uint24 public immutable override fee;
int24 public immutable override tickSpacing;
uint128 public immutable override maxLiquidityPerTick;

struct Slot0 {
    // the current price
    uint160 sqrtPriceX96;
    // the current tick
    int24 tick;
    // the most-recently updated index of the observations array
    uint16 observationIndex;
    // the current maximum number of observations that are being stored
    uint16 observationCardinality;
    // the next maximum number of observations to store, triggered in observations.write
    uint16 observationCardinalityNext;
    // the current protocol fee as a percentage of the swap fee taken on withdrawal
    // represented as an integer denominator (1/x)%
    uint8 feeProtocol;
    // whether the pool is locked
    bool unlocked;
}
Slot0 public override slot0;

uint256 public override feeGrowthGlobal0X128;
uint256 public override feeGrowthGlobal1X128;

// accumulated protocol fees in token0/token1 units
struct ProtocolFees {
    uint128 token0;
    uint128 token1;
}
ProtocolFees public override protocolFees;

uint128 public override liquidity;

mapping(int24 => Tick.Info) public override ticks;
mapping(int16 => uint256) public override tickBitmap;
mapping(bytes32 => Position.Info) public override positions;
Oracle.Observation[65535] public override observations;

前 5 个变量我们都已经了解过了,第 6 个变量 maxLiquidityPerTick 表示每个 tick 能接受的最大流动性,是在构造函数中根据 tickSpacing 计算出来的。

slot0 记录了当前的一些状态值,都封装在了结构体 Slot0 中,其共有 7 个字段。sqrtPriceX96 是当前价格,记录的是根号价格,且做了扩展,准确来说:sqrtPriceX96 = (token1数量 / token0数量) ^ 0.5 * 2^96。换句话说,这个值代表的是 token0 和 token1 数量比例的平方根,经过放大以获得更高的精度。这样设计的目的是为了方便和优化合约中的一些计算。如果想从 sqrtPriceX96 得出具体的价格,还需要做一些额外的计算。tick 记录了当前价格对应的价格点。observationIndexobservationCardinalityobservationCardinalityNext 是跟 observations 数组有关的,也是计算预言机价格时需要的,这在之前的文章《价格预言机的使用总结(三):UniswapV3篇》讲解 UniswapV3 预言机时已经介绍过,这里不再赘述。feeProtocol 则用来存储协议费率,初始化时为 0,可通过 setFeeProtocol 函数来重置该值。unlocked 记录池子的锁定状态,初始化时为 true,主要作为一个防止重入锁来使用。

feeGrowthGlobal0X128feeGrowthGlobal1X128 记录两个 token 的每单位流动性所获取的手续费。

protocolFees 则记录了两个 token 的累计未被领取的协议手续费。

liquidity 记录了池子当前可用的流动性。注意,这里不是指注入池子里的所有流动性总量,而是包含了当前价格的那些有效头寸的流动性总量。

ticks 记录池子里每个 tick 的详细信息,key 为 tick 的序号,value 就是详细信息。tickBitmap 记录已初始化的 tick 的位图。如果一个 tick 没有被用作流动性区间的边界点,即该 tick 没有被初始化,那在交易过程中可以跳过这个 tick。而为了更高效地寻找下一个已初始化的 tick,就使用了 tickBitmap 来记录已初始化的 tick。如果 tick 已被初始化,位图中对应于该 tick 序号的位置设置为 1,否则为 0。

positions 记录每个流动性头寸的详细信息,具体信息如下:

library Position {
    // 用于存储每个用户的头寸信息
    struct Info {
        // 当前头寸的总流动性
        uint128 liquidity;
        // 截止最后一次更新流动性或所欠费用时,每单位流动性的费用增长
        uint256 feeGrowthInside0LastX128;
        uint256 feeGrowthInside1LastX128;
        // 欠头寸所有者的费用
        uint128 tokensOwed0;
        uint128 tokensOwed1;
    }
    ...
}

observations 则是存储了计算预言机价格相关的累加值,包括 tick 累加值和流动性累加值。具体用法在《价格预言机的使用总结(三):UniswapV3篇》一文中已经介绍过,这里也不再赘述。

接下来就到合约函数了,UniswapV3Pool 核心的函数在 IUniswapV3PoolActions 接口里有定义,该接口共定义了 7 个函数:

  • initialize:初始化 slot0 状态
  • mint:添加
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值