作者: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
内部。