router合约实现了不同的Token交换方式。其中最常见的是,当我们拥有一定数量的Token,并希望通过计算得到一定数量的其他Token。
GitHub - XuHugo/solidityproject: DApp go go go !!!
function swapExactTokensForTokens(
uint256 amountIn,
uint256 amountOutMin,
address[] calldata path,
address to
) public returns (uint256[] memory amounts) {
...
这是一个将精确的输入金额(amountIn)与不小于 amountOutMin 的输出金额进行交换的函数。它沿着指定路径(简单来说就是一组Token地址)进行链式交换。最终数量将发送到地址 to。
路径参数看似复杂,其实只是一个Token地址数组。如果我们想直接用Token A 交换Token B,路径将只包含Token A 和Token B 的地址。如果我们想通过Token B 将Token A 换成Token C,路径将包含Token A 地址、Token B 地址、Token C 地址;合约将用Token A 交换Token B,然后用Token B 交换Token C。我们将在测试中看看效果如何。
在函数中,我们首先要预先计算路径上的所有输出量:
amounts = ZuniswapV2Library.getAmountsOut(
address(factory),
amountIn,
path
);
getAmountsOut(注意复数 "amounts")是我们尚未实现的新函数。为简洁起见,我就不解释它的实现了,您可以查看代码来了解。该函数简单地从路径中提取标记对(例如[[tokenA, tokenB],[tokenB, tokenC]]),然后迭代调用每个标记对的 getAmountOut,以建立一个输出金额数组。
获得输出金额后,我们就可以立即验证最终金额:
if (amounts[amounts.length - 1] < amountOutMin)
revert InsufficientOutputAmount();
如果最终的金额是适合的,合约就会通过向第一对发送输入代币来初始化交换:
_safeTransferFrom(
path[0],
msg.sender,
ZuniswapV2Library.pairFor(address(factory), path[0], path[1]),
amounts[0]
);
然后执行链式交换:
_swap(amounts, path, to);
让我们仔细看看这个函数实现:该函数接收一个输出量数组和一条路径,并对路径进行遍历。
function _swap(
uint256[] memory amounts,
address[] memory path,
address to_
) internal {
for (uint256 i; i < path.length - 1; i++) {
...
它从路径中获取当前和下一个Token地址,并对它们进行排序。之所以需要排序,是因为在pair合约中,Token地址是按升序存储的,但在路径中,它们是按逻辑排序的:输入Token在前,然后是 0 个或多个中间输出Token,最后是最终输出Token。
(address input, address output) = (path[i], path[i + 1]);
(address token0, ) = ZuniswapV2Library.sortTokens(input, output);
接下来,我们要对金额进行排序,使其与成对标记的顺序相匹配。在交换时,我们要正确选择输出Token。
uint256 amountOut = amounts[i + 1];
(uint256 amount0Out, uint256 amount1Out) = input == token0
? (uint256(0), amountOut)
: (amountOut, uint256(0));
算出金额后,我们需要找到最终Token的地址。这里我们有两个选择:
1、如果当前pair不是路径中的最终配对,我们希望直接将代币发送到下一pair。这样可以节省gas。
2、如果当前pair是最终pair,我们要将token发送到 to_ 地址,也就是发起交换的地址。
address to = i < path.length - 2
? ZuniswapV2Library.pairFor(
address(factory),
output,
path[i + 2]
)
: to_;
获得所有交换参数后,我们就可以进行实际交换了:
IZuniswapV2Pair(
ZuniswapV2Library.pairFor(address(factory), input, output)
).swap(amount0Out, amount1Out, to, "");