1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240
| function swap( address recipient, //收款地址 bool zeroForOne, //代币交换方向,token0/token为true,token1/token0为false int256 amountSpecified, //交换的数量,数值可正可负 uint160 sqrtPriceLimitX96, //价格平方根的Q64.96,如果是token0/token1方向的兑换,价格不能低于sqrtPriceLimitX96,如果是token1/token0方向,则不能大于 bytes calldata data //回调函数的参数 ) external override noDelegateCall returns (int256 amount0, int256 amount1) { //检查交易数量是否为0 require(amountSpecified != 0, 'AS');
//将交易前的数据保存在内存中,后续的访问通过 `MLOAD` 完成,节省 gas Slot0 memory slot0Start = slot0;
//检查是否有正在进行的token交易操作 require(slot0Start.unlocked, 'LOK'); //根据交易方向检查价格是否满足条件 require( zeroForOne ? sqrtPriceLimitX96 < slot0Start.sqrtPriceX96 && sqrtPriceLimitX96 > TickMath.MIN_SQRT_RATIO : sqrtPriceLimitX96 > slot0Start.sqrtPriceX96 && sqrtPriceLimitX96 < TickMath.MAX_SQRT_RATIO, 'SPL' );
//更新slot0.unlocked的状态,防止交易过程中回调到合约中其他的函数中修改状态变量 slot0.unlocked = false;
//缓存交易数据 SwapCache memory cache = SwapCache({ liquidityStart: liquidity, blockTimestamp: _blockTimestamp(), feeProtocol: zeroForOne ? (slot0Start.feeProtocol % 16) : (slot0Start.feeProtocol >> 4), secondsPerLiquidityCumulativeX128: 0, tickCumulative: 0, computedLatestObservation: false });
//检查交换的数量是否大于0 bool exactInput = amountSpecified > 0;
//存储交易状态信息 SwapState memory state = SwapState({ amountSpecifiedRemaining: amountSpecified, amountCalculated: 0, sqrtPriceX96: slot0Start.sqrtPriceX96, tick: slot0Start.tick, feeGrowthGlobalX128: zeroForOne ? feeGrowthGlobal0X128 : feeGrowthGlobal1X128, protocolFee: 0, liquidity: cache.liquidityStart });
// continue swapping as long as we haven't used the entire input/output and haven't reached the price limit //通过 tokenIn 是否还有余额来判断是否还需要继续循环,进入下一步的进行交易计算。当 tokenIn 全部被耗尽后,交易就结束了。 while (state.amountSpecifiedRemaining != 0 && state.sqrtPriceX96 != sqrtPriceLimitX96) { // 交易过程每一次循环的状态变量 StepComputations memory step;
// 交易的起始价格 step.sqrtPriceStartX96 = state.sqrtPriceX96;
// 通过位图找到下一个可以选的交易价格,这里可能是下一个流动性的边界,也可能还是在本流动性中 (step.tickNext, step.initialized) = tickBitmap.nextInitializedTickWithinOneWord( state.tick, tickSpacing, zeroForOne );
// ensure that we do not overshoot the min/max tick, as the tick bitmap is not aware of these bounds if (step.tickNext < TickMath.MIN_TICK) { step.tickNext = TickMath.MIN_TICK; } else if (step.tickNext > TickMath.MAX_TICK) { step.tickNext = TickMath.MAX_TICK; }
// get the price for the next tick //从 tick index 计算 sqrt(price) step.sqrtPriceNextX96 = TickMath.getSqrtRatioAtTick(step.tickNext);
// compute values to swap to the target tick, price limit, or point where input/output amount is exhausted // 计算当价格到达下一个交易价格时,tokenIn 是否被耗尽,如果被耗尽,则交易结束,还需要重新计算出 tokenIn 耗尽时的价格 // 如果没被耗尽,那么还需要继续进入下一个循环 // 会返回需要的手续费 // 下面有对此函数的分析 (state.sqrtPriceX96, step.amountIn, step.amountOut, step.feeAmount) = SwapMath.computeSwapStep( state.sqrtPriceX96, (zeroForOne ? step.sqrtPriceNextX96 < sqrtPriceLimitX96 : step.sqrtPriceNextX96 > sqrtPriceLimitX96) ? sqrtPriceLimitX96 : step.sqrtPriceNextX96, state.liquidity, state.amountSpecifiedRemaining, fee );
// 更新 tokenIn 的余额,以及 tokenOut 数量,注意当指定 tokenIn 的数量进行交易时,这里的 tokenOut 是负数. //如果exactInput为true则表示input不为负数 if (exactInput) { //amountSpecifiedRemaining:剩余期望兑换的数量 //amountCalculated:已经兑换的数量 state.amountSpecifiedRemaining -= (step.amountIn + step.feeAmount).toInt256(); state.amountCalculated = state.amountCalculated.sub(step.amountOut.toInt256()); } else { state.amountSpecifiedRemaining += step.amountOut.toInt256(); state.amountCalculated = state.amountCalculated.add((step.amountIn + step.feeAmount).toInt256()); }
// if the protocol fee is on, calculate how much is owed, decrement feeAmount, and increment protocolFee //检查协议费用是否开启 if (cache.feeProtocol > 0) { //计算欠费多少 uint256 delta = step.feeAmount / cache.feeProtocol; step.feeAmount -= delta; state.protocolFee += uint128(delta); }
// update global fee tracker // 更新交易的 f_g,这里需要除以流动性 L // 更新时使用此步骤的手续费总额除以此步骤的流动性 L ,以得出每一份流动性所对应的手续费数值。 if (state.liquidity > 0) state.feeGrowthGlobalX128 += FullMath.mulDiv(step.feeAmount, FixedPoint128.Q128, state.liquidity);
// shift tick if we reached the next price // 按需决定是否需要更新流动性 L 的值 // 当价格到达当前步骤价格区间的边界时,可能需要穿过下一个 tick if (state.sqrtPriceX96 == step.sqrtPriceNextX96) { // if the tick is initialized, run the tick transition //检查 tick index 是否为另一个流动性的边界 if (step.initialized) { // check for the placeholder value, which we replace with the actual value the first time the swap // crosses an initialized tick if (!cache.computedLatestObservation) { (cache.tickCumulative, cache.secondsPerLiquidityCumulativeX128) = observations.observeSingle( cache.blockTimestamp, 0, slot0Start.tick, slot0Start.observationIndex, cache.liquidityStart, slot0Start.observationCardinality ); cache.computedLatestObservation = true; } int128 liquidityNet = // 在这里需要更新 tick 的 f_o ticks.cross( step.tickNext, (zeroForOne ? state.feeGrowthGlobalX128 : feeGrowthGlobal0X128), (zeroForOne ? feeGrowthGlobal1X128 : state.feeGrowthGlobalX128), cache.secondsPerLiquidityCumulativeX128, cache.tickCumulative, cache.blockTimestamp ); // if we're moving leftward, we interpret liquidityNet as the opposite sign // safe because liquidityNet cannot be type(int128).min // 根据价格增加/减少,即向左或向右移动,增加/减少相应的流动性 if (zeroForOne) liquidityNet = -liquidityNet;
//更新流动性 state.liquidity = LiquidityMath.addDelta(state.liquidity, liquidityNet); }
//在这里更 tick 的值,使得下一次循环时让 tickBitmap 进入下一个 word 中查询 state.tick = zeroForOne ? step.tickNext - 1 : step.tickNext; } else if (state.sqrtPriceX96 != step.sqrtPriceStartX96) { // recompute unless we're on a lower tick boundary (i.e. already transitioned ticks), and haven't moved //如果 tokenIn 被耗尽,那么计算当前价格对应的 tick state.tick = TickMath.getTickAtSqrtRatio(state.sqrtPriceX96); } }
// update tick and write an oracle entry if the tick change //当trick改变时写入一个oracle条目 if (state.tick != slot0Start.tick) { (uint16 observationIndex, uint16 observationCardinality) = observations.write( slot0Start.observationIndex, cache.blockTimestamp, slot0Start.tick, cache.liquidityStart, slot0Start.observationCardinality, slot0Start.observationCardinalityNext ); (slot0.sqrtPriceX96, slot0.tick, slot0.observationIndex, slot0.observationCardinality) = ( state.sqrtPriceX96, state.tick, observationIndex, observationCardinality ); } else { // otherwise just update the price //否则只更新价格 slot0.sqrtPriceX96 = state.sqrtPriceX96; }
// update liquidity if it changed //如果流动性发生变化则更新流动性 if (cache.liquidityStart != state.liquidity) liquidity = state.liquidity;
// update fee growth global and, if necessary, protocol fees // overflow is acceptable, protocol has to withdraw before it hits type(uint128).max fees // 更新费用 // 在交易步骤完成后,更新合约的 f_g if (zeroForOne) { feeGrowthGlobal0X128 = state.feeGrowthGlobalX128; if (state.protocolFee > 0) protocolFees.token0 += state.protocolFee; } else { feeGrowthGlobal1X128 = state.feeGrowthGlobalX128; if (state.protocolFee > 0) protocolFees.token1 += state.protocolFee; }
//确定最终用户支付的 token 数和得到的 token 数 (amount0, amount1) = zeroForOne == exactInput ? (amountSpecified - state.amountSpecifiedRemaining, state.amountCalculated) : (state.amountCalculated, amountSpecified - state.amountSpecifiedRemaining);
// do the transfers and collect payment //转账和费用收取 if (zeroForOne) { // 将 tokenOut 支付给用户,前面说过 tokenOut 记录的是负数 if (amount1 < 0) TransferHelper.safeTransfer(token1, recipient, uint256(-amount1));
uint256 balance0Before = balance0(); // 还是通过回调的方式,扣除用户需要支持的 token //这里还是通过回调完成用户支付 token 的费用。因为发送用户 token 是在回调函数之前完成的,因此这个 swap 函数是可以被当作 flash swap 来使用的。 IUniswapV3SwapCallback(msg.sender).uniswapV3SwapCallback(amount0, amount1, data); // 校验扣除是否成功 require(balance0Before.add(uint256(amount0)) <= balance0(), 'IIA'); } else { if (amount0 < 0) TransferHelper.safeTransfer(token0, recipient, uint256(-amount0));
uint256 balance1Before = balance1(); IUniswapV3SwapCallback(msg.sender).uniswapV3SwapCallback(amount0, amount1, data); require(balance1Before.add(uint256(amount1)) <= balance1(), 'IIA'); }
//记录日志 emit Swap(msg.sender, recipient, amount0, amount1, state.sqrtPriceX96, state.liquidity, state.tick); //解除防止重入的锁 slot0.unlocked = true; }
|