The explanation of the new retail is that individuals and enterprises, relying on the Internet, upgrade and transform the production, circulation and sales process of goods by using advanced technical means such as big data, artificial intelligence and psychological knowledge, thus reshaping the business structure and ecosystem, and deeply integrating online services, offline experience and modern logistics.
//SWAP(supporting fee-on-transfer tokens)
//requires the initial amount to have already been sent to the first pair
//交易方法,支持转账扣手续费的代币
//需要先将amounts[0]的金额已经转到第一个pair地址(即path[0]+path[1]组成的pair)!
function _swapSupportingFeeOnTransferTokens(address[]memory path,address _to)internal virtual{
for(uint i;i<path.length-1;i++){
//得到进/出token地址
(address input,address output)=(path,path[i+1]);
//排序得到token0
(address token0,)=UniswapV2Library.sortTokens(input,output);
//获取pair
IUniswapV2Pair pair=IUniswapV2Pair(UniswapV2Library.pairFor(factory,input,output));
uint amountInput;//输入金额
uint amountOutput;//输入金额
{//scope to avoid stack too deep errors避免堆栈太深错误,用{}括部分临时变量
//或许两个币的储备量
(uint reserve0,uint reserve1,)=pair.getReserves();
//根据input,token0得出inToken的储备量,outToken的储备量
(uint reserveInput,uint reserveOutput)=input==token0?(reserve0,reserve1):(reserve1,reserve0);
//查询交易对的inToken余额,减掉最后记录的储备量,就是交易对实际获取到的inToken数量(TODO和_swap的区别就在这里,不是使用计算来的amounts[0]作为输入,而是通过查询pair余额再减去最后更新的储备量得到实际pair到账额!)
amountInput=IERC20(input).balanceOf(address(pair)).sub(reserveInput);
//通过实际得到的input量,计算实际会输出的output数量
amountOutput=UniswapV2Library.getAmountOut(amountInput,reserveInput,reserveOutput);
}
//根据token0,input得到amount0需要out,还是amount1是out,;注意其中之一一定是0,即入token的金额,不需要pair转出
(uint amount0Out,uint amount1Out)=input==token0?(uint(0),amountOutput):(amountOutput,uint(0));
//如果i小于path长度-2,就表示还需要继续交易,所以to是下一个交易对,如果一样就表示path结束了,to就是参数中的_to
address to=i<path.length-2?UniswapV2Library.pairFor(factory,output,path[i+2]):_to;
//调用pair的swap方法,其中一个out是0,另一个是要转出的金额,内部是转出输出量,并校验交易是否正确,更新储备量
pair.swap(amount0Out,amount1Out,to,new bytes(0));
}
}
/**
TODO带supportingFeeOnTransfer方法都是通过余额的方式计算输入/出
下面的三个方法,都是swapExactXXXForXX,而没有swapXXXForExactXX
如果是自己开发合约调用,可以随意选用哪个swap
在uniswap中,如果滑点改成49,会自动切换带supportingFeeOnTransfer的方法
还有些其他情况也会自动切,这个会前端的可以看看代码,什么情况下,前端会选择使用带supportingFeeOnTransfer的方法去交易
*/
//输入精确的token,换取另一个token,支持转账时扣手续费的token
function swapExactTokensForTokensSupportingFeeOnTransferTokens(
uint amountIn,//输入金额
uint amountOutMin,//最小输出金额,该金额只要够小,交易就一定可以成功
address[]calldata path,//交换路径
address to,
uint deadline
)external virtual override ensure(deadline){
//将输入金额转到第一个pair地址
TransferHelper.safeTransferFrom(
path[0],msg.sender,UniswapV2Library.pairFor(factory,path[0],path[1]),amountIn
);
//查询to用户当前最终输出token的余额
uint balanceBefore=IERC20(path[path.length-1]).balanceOf(to);
//调用内部交易方法
_swapSupportingFeeOnTransferTokens(path,to);
//通过查询余额的方式,校验交易前后的余额差,大于等于最小输出!
require(
IERC20(path[path.length-1]).balanceOf(to).sub(balanceBefore)>=amountOutMin,
'UniswapV2Router:INSUFFICIENT_OUTPUT_AMOUNT'
);
}
//输入精确eth换取另一个token
function swapExactETHForTokensSupportingFeeOnTransferTokens(
uint amountOutMin,
address[]calldata path,
address to,
uint deadline
)
external
virtual
override
payable
ensure(deadline)
{
require(path[0]==WETH,'UniswapV2Router:INVALID_PATH');
//将eth转成weth,并转给第一个pair地址
uint amountIn=msg.value;
IWETH(WETH).deposit{value:amountIn}();
assert(IWETH(WETH).transfer(UniswapV2Library.pairFor(factory,path[0],path[1]),amountIn));
//跟上面方法一样,通过查询余额的方式校验
uint balanceBefore=IERC20(path[path.length-1]).balanceOf(to);
_swapSupportingFeeOnTransferTokens(path,to);
require(
IERC20(path[path.length-1]).balanceOf(to).sub(balanceBefore)>=amountOutMin,
'UniswapV2Router:INSUFFICIENT_OUTPUT_AMOUNT'
);
}
//输入精确token换取输出eth,
function swapExactTokensForETHSupportingFeeOnTransferTokens(
uint amountIn,
uint amountOutMin,
address[]calldata path,
address to,
uint deadline
)
external
virtual
override
ensure(deadline)
{
require(path[path.length-1]==WETH,'UniswapV2Router:INVALID_PATH');
TransferHelper.safeTransferFrom(
path[0],msg.sender,UniswapV2Library.pairFor(factory,path[0],path[1]),amountIn
);
_swapSupportingFeeOnTransferTokens(path,address(this));
uint amountOut=IERC20(WETH).balanceOf(address(this));
require(amountOut>=amountOutMin,'UniswapV2Router:INSUFFICIENT_OUTPUT_AMOUNT');
IWETH(WETH).withdraw(amountOut);
TransferHelper.safeTransferETH(to,amountOut);
}