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

这个 swap 方法是 Uniswap v4 中池子(Pool)的核心交换函数,负责处理一次 swap(兑换)操作的完整流程。下面逐步解析其主要逻辑,先上代码:
function swap(PoolKey memory key, SwapParams memory params, bytes calldata hookData)
external
onlyWhenUnlocked
noDelegateCall
returns (BalanceDelta swapDelta)
{
if (params.amountSpecified == 0) SwapAmountCannotBeZero.selector.revertWith();
PoolId id = key.toId();
Pool.State storage pool = _getPool(id);
pool.checkPoolInitialized();
BeforeSwapDelta beforeSwapDelta;
{
int256 amountToSwap;
uint24 lpFeeOverride;
(amountToSwap, beforeSwapDelta, lpFeeOverride) = key.hooks.beforeSwap(key, params, hookData);
// execute swap, account protocol fees, and emit swap event
// _swap is needed to avoid stack too deep error
swapDelta = _swap(
pool,
id,
Pool.SwapParams({
tickSpacing: key.tickSpacing,
zeroForOne: params.zeroForOne,
amountSpecified: amountToSwap,
sqrtPriceLimitX96: params.sqrtPriceLimitX96,
lpFeeOverride: lpFeeOverride
}),
params.zeroForOne ? key.currency0 : key.currency1 // input token
);
}
BalanceDelta hookDelta;
(swapDelta, hookDelta) = key.hooks.afterSwap(key, params, swapDelta, hookData, beforeSwapDelta);
// 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, swapDelta, msg.sender);
}
先看入参:
/// @notice Parameter struct for `Swap` pool operations
struct SwapParams {
/// Whether to swap token0 for token1 or vice versa
bool zeroForOne;
/// The desired input amount if negative (exactIn), or the desired output amount if positive (exactOut)
int256 amountSpecified;
/// The sqrt price at which, if reached, the swap will stop executing
uint160 sqrtPriceLimitX96;
}
-
bool zeroForOne:表示兑换方向。
true:用 token0 换 token1(token0 → token1);false:用 token1 换 token0(token1 → token0)。 -
int256 amountSpecified:表示本次 swap 的数量和类型。如果为负数(
< 0):表示“exactIn”,即指定输入数量,用户希望用多少 token 进行兑换。如果为正数(> 0):表示“exactOut”,即指定输出数量,用户希望获得多少 token。 -
uint160 sqrtPriceLimitX96:表示本次 swap 允许到达的价格极限(以 sqrt(price) 的 Q96 定点数表示)。当池子的价格变动到该极限时,swap 会停止执行,防止滑点过大或价格异常。
其处理交换主要分为以下几个步骤:
- 参数与前置检查
- beforeSwap 钩子
- 执行 swap
- afterSwap 钩子
- 资产变化结算
我们逐一分析:
参数与前置检查
if (params.amountSpecified == 0) SwapAmountCannotBeZero.selector.revertWith();
PoolId id = key.toId();
Pool.State storage pool = _getPool(id);
pool.checkPoolInitialized();
- 检查兑换数量不能为 0。
- 通过
key获取池子的唯一 ID 和状态。 - 检查池子是否已初始化。
swap处理
在正式开始swap之前先进行beforeSwap钩子函数的调用,具体可以看这篇文章uniswap v4 hooks详解中beforeSwap相关的部分,调用这个钩子后会返回3个参数:
amountToSwap:实际要 swap 的数量,可能被钩子调整。beforeSwapDelta:钩子返回的 delta 信息(如特殊调整)。lpFeeOverride:如果池子支持动态费率,钩子可以覆盖 LP 费率。
用返回的参数结合SwapParams传参调用相应pool的swap函数:
/// @notice Executes a swap against the state, and returns the amount deltas of the pool
/// @dev PoolManager checks that the pool is initialized before calling
function swap(State storage self, SwapParams memory params)
internal
returns (BalanceDelta swapDelta, uint256 amountToProtocol, uint24 swapFee, SwapResult memory result)
{
Slot0 slot0Start = self.slot0;
bool zeroForOne = params.zeroForOne;
uint256 protocolFee =
zeroForOne ? slot0Start.protocolFee().getZeroForOneFee() : slot0Start.protocolFee().getOneForZeroFee();
// the amount remaining to be swapped in/out of the input/output asset. initially set to the amountSpecified
int256 amountSpecifiedRemaining = params.amountSpecified;
// the amount swapped out/in of the output/input asset. initially set to 0
int256 amountCalculated = 0;
// initialize to the current sqrt(price)
result.sqrtPriceX96 = slot0Start.sqrtPriceX96();
// initialize to the current tick
result.tick = slot0Start.tick();
// initialize to the current liquidity
result.liquidity = self.liquidity;
// if the beforeSwap hook returned a valid fee override, use that as the LP fee, otherwise load from storage
// lpFee, swapFee, and protocolFee are all in pips
{
uint24 lpFee = params.lpFeeOverride.isOverride()
? params.lpFeeOverride.removeOverrideFlagAndValidate()
: slot0Start.lpFee();
swapFee = protocolFee == 0 ? lpFee : uint16(protocolFee).calculateSwapFee(lpFee);
}
// a swap fee totaling MAX_SWAP_FEE (100%) makes exact output swaps impossible since the input is entirely consumed by the fee
if (swapFee >= SwapMath.MAX_SWAP_FEE) {
// if exactOutput
if (params.amountSpecified > 0) {
InvalidFeeForExactOut.selector.revertWith();
}
}
// swapFee is the pool's fee in pips (LP fee + protocol fee)
// when the amount swapped is 0, there is no protocolFee applied and the fee amount paid to the protocol is set to 0
if (params.amountSpecified == 0) return (BalanceDeltaLibrary.ZERO_DELTA, 0, swapFee, result);
if (zeroForOne) {
if (params.sqrtPriceLimitX96 >= slot0Start.sqrtPriceX96()) {
PriceLimitAlreadyExceeded.selector.revertWith(slot0Start.sqrtPriceX96(), params.sqrtPriceLimitX96);
}
// Swaps can never occur at MIN_TICK, only at MIN_TICK + 1, except at initialization of a pool
// Under certain circumstances outlined below, the tick will preemptively reach MIN_TICK without swapping there
if (params.sqrtPriceLimitX96 <= TickMath.MIN_SQRT_PRICE) {
PriceLimitOutOfBounds.selector.revertWith(params.sqrtPriceLimitX96);
}
} else {
if (params.sqrtPriceLimitX96 <= slot0Start.sqrtPriceX96()) {
PriceLimitAlreadyExceeded.selector.revertWith(slot0Start.sqrtPriceX96(), params.sqrtPriceLimitX96);
}
if (params.sqrtPriceLimitX96 >= TickMath.MAX_SQRT_PRICE) {
PriceLimitOutOfBounds.selector.revertWith(params.sqrtPriceLimitX96);
}
}
StepComputations memory step;
step.feeGrowthGlobalX128 = zeroForOne ? self.feeGrowthGlobal0X128 : self.feeGrowthGlobal1X128;
// continue swapping as long as we haven't used the entire input/output and haven't reached the price limit
while (!(amountSpecifiedRemaining

最低0.47元/天 解锁文章
1153

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



