这一章我们分析pool中流动性的修改过程,
在阅读本章之前一定要先通读uniswap v3/v4 中pool的状态管理
在阅读本章之前一定要先通读uniswap v3/v4 中pool的状态管理
在阅读本章之前一定要先通读uniswap v3/v4 中pool的状态管理
代码位置如下:

这个方法提供增加或减少流动性的功能,并计算用户应得的手续费(feesAccrued)和流动性变化(callerDelta),同时支持钩子(Hooks)机制,允许在流动性修改前后执行自定义逻辑。
先看代码:
function modifyLiquidity(
PoolKey memory key,
IPoolManager.ModifyLiquidityParams memory params,
bytes calldata hookData
) external onlyWhenUnlocked noDelegateCall returns (BalanceDelta callerDelta, BalanceDelta feesAccrued) {
PoolId id = key.toId();
{
Pool.State storage pool = _getPool(id);
pool.checkPoolInitialized();
key.hooks.beforeModifyLiquidity(key, params, hookData);
BalanceDelta principalDelta;
(principalDelta, feesAccrued) = pool.modifyLiquidity(
Pool.ModifyLiquidityParams({
owner: msg.sender,
tickLower: params.tickLower,
tickUpper: params.tickUpper,
liquidityDelta: params.liquidityDelta.toInt128(),
tickSpacing: key.tickSpacing,
salt: params.salt
})
);
// fee delta and principal delta are both accrued to the caller
callerDelta = principalDelta + feesAccrued;
}
// event is emitted before the afterModifyLiquidity call to ensure events are always emitted in order
emit ModifyLiquidity(id, msg.sender, params.tickLower, params.tickUpper, params.liquidityDelta, params.salt);
BalanceDelta hookDelta;
(callerDelta, hookDelta) = key.hooks.afterModifyLiquidity(key, params, callerDelta, feesAccrued, hookData);
// if the hook doesn't have the flag to be able to return deltas, hookDelta will always be 0
if (hookDelta != BalanceDeltaLibrary.ZERO_DELTA) _accountPoolBalanceDelta(key, hookDelta, address(key.hooks));
_accountPoolBalanceDelta(key, callerDelta, msg.sender);
}
入参
-
PoolKey memory key:
表示池的唯一标识,这里面池的基本信息(代币对、手续费、tick 间距等)。通过 key.toId() 转换为池的唯一 ID。
-
具体参数如下:IPoolManager.ModifyLiquidityParams memory params:salt:用于唯一标识流动性位置的附加参数。liquidityDelta:流动性变化量(正值表示增加流动性,负值表示减少流动性)。tickUpper:流动性范围的上界。tickLower:流动性范围的下界。
-
钩子(Hooks)所需的附加数据,允许在流动性修改前后执行自定义逻辑。bytes calldata hookData:
这些参数都比较简单,唯一的需要解释的是salt参数,这里包含一个v4版本相对v3版本的升级,在v3版本中,增减流动性只需要提供tick的范围,合约根据发送者的地址和tick范围就可以定位到用户所拥有的流动性,并对其做增减。但是我们试想这样一个场景。用户可能希望在相同的价格范围内应用不同的策略,一部分资金用于长期流动性提供,另一部分用于短期套利。这样的话在同一个tick范围内统一做流动性管理就不太合适了。所以uniswap v4版本在此基础上对于同一用户,同一tick范围的流动性又做了一层分区,允许用户在不同的分区采取不同的交易策略,而这些分区就是用salt来区分位置的。需要注意的是salt 是一个 用户定义的参数,Uniswap 并不会自动生成 salt。用户需要自己管理它。
前面几行代码比较简单
- Pool.State storage pool = _getPool(id); //获取相应的pool
- pool.checkPoolInitialized();//校验当前pool是否初始化
- key.hooks.beforeModifyLiquidity(key, params, hookData);//执行更新流动性的相应钩子
更新pool
这个方法的核心步骤就是
(principalDelta, feesAccrued) = pool.modifyLiquidity( Pool.ModifyLiquidityParams({
owner: msg.sender,
tickLower: params.tickLower,
tickUpper: params.tickUpper,
liquidityDelta: params.liquidityDelta.toInt128(),
tickSpacing: key.tickSpacing,
salt: params.salt
}));
我们仔细分析一下:
function modifyLiquidity(State storage self, ModifyLiquidityParams memory params)
internal
returns (BalanceDelta delta, BalanceDelta feeDelta)
{
int128 liquidityDelta = params.liquidityDelta;
int24 tickLower = params.tickLower;
int24 tickUpper = params.tickUpper;
checkTicks(tickLower, tickUpper);
{
ModifyLiquidityState memory state;
// if we need to update the ticks, do it
if (liquidityDelta != 0) {
(state.flippedLower, state.liquidityGrossAfterLower) =
updateTick(self, tickLower, liquidityDelta, false);
(state.flippedUpper, state.liquidityGrossAfterUpper) = updateTick(self, tickUpper, liquidityDelta, true);
// `>` and `>=` are logically equivalent here but `>=` is cheaper
if (liquidityDelta >= 0) {
uint128 maxLiquidityPerTick = tickSpacingToMaxLiquidityPerTick(params.tickSpacing);
if (state.liquidityGrossAfterLower > maxLiquidityPerTick) {
TickLiquidityOverflow.selector.revertWith(tickLower);
}
if (state.liquidityGrossAfterUpper > maxLiquidityPerTick) {
TickLiquidityOverflow.selector.revertWith(tickUpper);
}
}
if (state.flippedLower) {
self.tickBitmap.flipTick(tickLower, params.tickSpacing);
}
if (state.flippedUpper) {
self.tickBitmap.flipTick(tickUpper, params.tickSpacing);
}
}
{
(uint256 feeGrowthInside0X128, uint256 feeGrowthInside1X128) =
getFeeGrowthInside(self, tickLower, tickUpper);
Position.State storage position = self.positions.get(params.owner, tickLower, tickUpper, params.salt);
(uint256 feesOwed0, uint256 feesOwed1) =
position.update(liquidityDelta, feeGrowthInside0X128, feeGrowthInside1X128);
// Fees earned from LPing are calculated, and returned
feeDelta = toBalanceDelta(feesOwed0.toInt128(), feesOwed1.toInt128());
}
// clear any tick data that is no longer needed
if (liquidityDelta < 0) {
if (state.flippedLower) {
clearTick(self, tickLower);
}
if (state.flippedUpper) {
clearTick(self, tickUpper);
}
}
}
if (liquidityDelta != 0) {
Slot0 _slot0 = self.slot0;
(int24 tick, uint160 sqrtPriceX96) = (_slot0.tick(), _slot0.sqrtPriceX96());
if (tick < tickLower) {
// current tick is below the passed range; liquidity can only become in range by crossing from left to
// right, when we'll need _more_ currency0 (it's becoming more valuable) so user must provide it
delta = toBalanceDelta(
SqrtPriceMath.getAmount0Delta(
TickMath.getSqrtPriceAtTick(tickLower), TickMath.getSqrtPriceAtTick(tickUpper), liquidityDelta
).toInt128(),
0
);
} else if (tick < tickUpper) {
delta = toBalanceDelta(
SqrtPriceMath.getAmount0Delta(sqrtPriceX96, TickMath.getSqrtPriceAtTick(tickUpper), liquidityDelta)
.toInt128(),
SqrtPriceMath.getAmou

最低0.47元/天 解锁文章
851

被折叠的 条评论
为什么被折叠?



