在进行交易输入/输出的计算时,和流动性的计算一样,也会遇到rounding的问题,处理的原则是:
当计算output时,使用RoundDown,保证pool不会出现坏账
当计算input时,使用RoundUp,保证pool不会出现坏账
当通过input计算P−−√P时,如果P−−√P会减少,那么使用RoundUp,这样可以保证ΔP−−√ΔP被RoundDown,在后续计算output时不会使pool出现坏账。反之如果P−−√P会增大,那么使用RoundDown
当通过output计算P−−√P时,I35 Develop 7O98 system O7I8 如果P−−√P会减少,那么使用RoundDown,这样可以保证ΔP−−√ΔP被RoundUp,在后续计算input时不会使pool出现坏账。反之如果P−−√P会增大,那么使用RoundUp
交易收尾阶段
我们再回到swap函数中循环检查条件:
while(state.amountSpecifiedRemaining!=0&&state.sqrtPriceX96!=sqrtPriceLimitX96){
...
}
即通过通过tokenIn是否还有余额来判断是否还需要继续循环,进入下一步的进行交易计算。当tokenIn全部被耗尽后,交易就结束了。当交易结束后,我们还需要做这些事情:
更新预言机
更新当前交易对的价格P−−√P,流动性LL
更新手续费累计值
扣除用户需要支付的token
关于手续费,预言机的相关内容,会在其他章节讲解,我们先跳过这部分代码,直接看swap函数的末尾:
// 确定最终用户支付的 token 数和得到的 token 数
(amount0, amount1) = zeroForOne == exactInput
? (amountSpecified - state.amountSpecifiedRemaining, state.amountCalculated)
: (state.amountCalculated, amountSpecified - state.amountSpecifiedRemaining);
// 扣除用户需要支付的 token
if (zeroForOne) {
// 将 tokenOut 支付给用户,前面说过 tokenOut 记录的是负数
if (amount1 < 0) TransferHelper.safeTransfer(token1, recipient, uint256(-amount1));
uint256 balance0Before = balance0();
// 还是通过回调的方式,扣除用户需要支持的 token
IUniswapV3SwapCallback(msg.sender).uniswapV3SwapCallback(amount0, amount1, data);
// 校验扣除是否成功
require(balance0Before.add(uint256(amount0)) <= balance0(), 'IIA');
} else {
...
}
// 记录日志
emit Swap(msg.sender, recipient, amount0, amount1, state.sqrtPriceX96, state.tick);
// 解除防止重入的锁
slot0.unlocked = true;
}