CrossTickSwap CrossTickSwap 指集中流动性自动做市商中的跨刻度交换。交易推动当前价格越过一个或多个已初始化刻度时,协议必须分段计算成交量,并在每个边界更新有效流动性。理解该过程的关键是区分当前有效流动性、刻度上的净流动性变化和价格方向。
核心对象
sqrtPriceX96:当前价格平方根乘以 2^96 后的定点数。
tick:离散价格索引,价格近似满足 price = 1.0001^tick。
liquidity:当前价格区间内可参与交易的有效流动性。
liquidityNet:穿越某个已初始化刻度时应加入或移除的净流动性。
feePips:以百万分之一为精度表示的手续费率。
跨刻度执行流程 1 2 3 4 5 6 7 8 9 10 11 12 13 flowchart TD A[接收输入数量与价格限制] --> B[定位当前刻度和有效流动性] B --> C[查找交易方向上的下一个初始化刻度] C --> D[计算到目标价格所需的输入和输出] D --> E{本轮输入是否足够到达目标价格} E -- 否 --> F[在当前区间内计算最终价格] E -- 是 --> G[价格到达刻度边界] G --> H[更新手续费增长量] H --> I[按方向应用 liquidityNet] I --> J{剩余输入是否为零} J -- 否 --> C J -- 是 --> K[返回成交结果] F --> K
零换一方向表示价格下降。穿越刻度时,需要对该刻度记录的 liquidityNet 取反后再作用于当前流动性。一换零方向表示价格上升,可直接应用 liquidityNet。方向处理错误会造成流动性突然增大、下溢或报价失真。
单步计算框架 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 while (amountRemaining > 0 && sqrtPriceX96 != sqrtPriceLimitX96) { int24 nextTick = nextInitializedTick(currentTick, zeroForOne); uint160 nextPrice = getSqrtRatioAtTick(nextTick); uint160 targetPrice = selectTarget(nextPrice, sqrtPriceLimitX96, zeroForOne); SwapStep memory step = computeSwapStep( sqrtPriceX96, targetPrice, liquidity, amountRemaining, feePips ); amountRemaining -= step.amountIn + step.feeAmount; amountOut += step.amountOut; sqrtPriceX96 = step.sqrtPriceNextX96; if (sqrtPriceX96 == nextPrice) { int128 delta = ticks[nextTick].liquidityNet; liquidity = zeroForOne ? addDelta(liquidity, -delta) : addDelta(liquidity, delta); currentTick = zeroForOne ? nextTick - 1 : nextTick; } else { currentTick = getTickAtSqrtRatio(sqrtPriceX96); } }
验证重点
检查交易方向与刻度搜索方向是否一致。
检查到达边界和未到达边界时的舍入方向。
检查跨刻度前后有效流动性是否符合仓位范围。
检查价格限制能否阻止循环越界。
对连续空刻度、极端价格和流动性为零的区间执行测试。