文章首发于公众号:Keegan小钢
Swap 可分为两种场景:单池交易和跨池交易。在 PoolManager 合约里,要完成交易流程,会涉及到 lock()、swap()、settle()、take() 四个函数。单池交易时只需要调一次 swap() 函数,而跨池交易时则需要多次调用 swap() 函数来完成。
我们先来聊聊单池交易如何实现,以下是流程图:

第一步,和其他操作一样,先执行 lock(),锁定住接下来的系列操作。
第二步,就是在 lockAcquired() 回调函数里执行 swap() 函数。这一步执行完之后,记账系统中会记录用户欠池子的资产数量,即用户需要支付的代币;以及池子欠用户的资产数量,即用户此次交易可得的代币。
第三步,执行 settle() 函数,完成代币的支付。
第四步,执行 take() 函数,取回所得的代币。
最后,lock() 函数完成,返回结果。
而如果是跨池交易的话,则需要在 Router 层面确定好交易路径,然后根据路径执行多次 swap。举个例子,现在要用 A 兑换成 C,但是 A 和 C 之间没有直接配对的池子,但是有中间代币 B,存在 A 和 B 配对的池子,也存在 B 和 C 配对的池子。那交易路径就可以先用 A 换成 B,再将 B 换成 C,最终实现了 A 换成 C。而不管中间经过了多少次 swap,最后,只需要完成一次 settle 操作,即支付 A,也只需要执行一次 take 操作,即取回最后所得的 C。整个流程大致如下图所示:

下面,我们主要剖析讲解 swap() 函数的内部实现。
首先,看看其函数声明,如下:
function swap(PoolKey memory key, IPoolManager.SwapParams memory params, bytes calldata hookData)
external
override
noDelegateCall
onlyByLocker
returns (BalanceDelta delta)
key 指定了要进行交易的池子,params 是具体的交易参数,hookData 即需要回传给 hooks 合约的数据。
来看看 params 具体有哪些参数:
struct SwapParams {
bool zeroForOne;
int256 amountSpecified;
uint160 sqrtPriceLimitX96;
}
zeroForOne 指名了要用 currency0 兑换 currency1,为 false 的话则反过来用 currency1 兑换 currency0。amountSpecified 是指定的确定数额,正数表示输入,负数表示输出。sqrtPriceLimitX96 是滑点保护的限定价格。如果之前已经了解过 UniswapV3,那对这几个字段应该不陌生。
两个函数修饰器 noDelegateCall 和 onlyByLocker,和之前文章介绍的一样,就不赘述了。
返回值 delta,其组成里的两个数,正常情况下就是一个正数,一个负数。
接下来,看看函数体了。先看前面一段代码:
PoolId id = key.toId();
_checkPoolInitialized(id);
if (key.hooks.shouldCallBeforeSwap()) {
bytes4 selector = key.hooks.beforeSwap(msg.sender, key, params, hookData);
// Sentinel return value used to signify that a NoOp occurred.
if (key.hooks.isValidNoOpCall(selector)) retur

最低0.47元/天 解锁文章
714

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



