Uniswap V3:流动性提取和收集

225 阅读2分钟

2.png

作者:WongSSH

引言

本系列文章将带领读者从零实现Uniswap V3核心功能,深入解析其设计与实现。主要参考了 Constructor | Uniswap V3 Core Contract Explained 系列教程,并补充了 Uniswap V3 Development Book 和 Paco 博客 中的相关内容。所有示例代码可在 clamm 代码库中找到,以便实践和探索。

流动性提取和收集

进行流动性的提取实际上就是 mint 函数的反向操作,我们可以直接使用 _modifyPosition 函数。此处,我们会使用到Position.Info内的 tokensOwed0 和tokensOwed1变量,该变量用于记录 Position内部不作为流动性的代币。该函数实现如下:

function burn(int24 tickLower, int24 tickUpper, uint128 amount)  
    external  
    lock  
    returns (uint256 amount0, uint256 amount1)  
{  
    (Position.Info storage position, int256 amount0Int, int256 amount1Int) = _modifyPosition(  
        ModifyPositionParams({  
            owner: msg.sender,  
            tickLower: tickLower,  
            tickUpper: tickUpper,  
            liquidityDelta: -int256(uint256(amount)).toInt128()  
        })  
    );  
  
    amount0 = uint256(-amount0Int);  
    amount1 = uint256(-amount1Int);  
  
    if (amount0 > 0 || amount1 > 0) {  
        (position.tokensOwed0, position.tokensOwed1) =  
            (position.tokensOwed0 + uint128(amount0), position.tokensOwed1 + uint128(amount1));  
    }  
}

由于用户需要提取流动性,所以此处的 liquidityDelta 为 amount 的负数。在 burn 函数的最后,我们将输出的 amount0 和 amount1 添加到用户的 position内部。

在上述流程内,我们并没有完成代币的提取。我们可以使用 collect函数实现代币的提取,代码如下:

function collect(  
    address recipient,  
    int24 tickLower,  
    int24 tickUpper,  
    uint128 amount0Requested,  
    uint128 amount1Requested  
) external lock returns (uint128 amount0, uint128 amount1) {  
    Position.Info storage position = positions.get(msg.sender, tickLower, tickUpper);  
  
    amount0 = amount0Requested > position.tokensOwed0 ? position.tokensOwed0 : amount0Requested;  
    amount1 = amount1Requested > position.tokensOwed1 ? position.tokensOwed1 : amount1Requested;  
  
    if (amount0 > 0) {  
        position.tokensOwed0 -= amount0;  
        IERC20(token0).transfer(recipient, amount0);  
    }  
    if (amount1 > 0) {  
        position.tokensOwed1 -= amount1;  
        IERC20(token1).transfer(recipient, amount1);  
    }  
}

此处可以预告一下,在每次调用 _modifyPosition时都会完成代表价格区间的 position 更新。在这部分更新过程中,我们会重新计算区间获得手续费情况,这部分手续费就会被记录在 position.tokensOwed0 和 position.tokensOwed1内部。