Specifically,new retail is consumer-centered,based on the digitalization of people,goods and services,supply chain and other links,connecting various consumption scenarios through data flow,including smart phones,mobile terminals,computers,physical stores,and new ways that can be realized in the future,and using digital technology to realize the comprehensive integration of physical and virtual retail supply chain,transaction delivery chain,and service chain,It provides consumers with a seamless consumption experience covering all channels,and is an efficient and inclusive pan-retail business characterized by logistics distribution replacing physical delivery.
//SWAP
//requires the initial amount to have already been sent to the first pair
//交易方法
//需要先将amounts[0]的金额已经转到第一个pair地址(即path[0]+path[1]组成的pair)!
function _swap(uint[]memory amounts,address[]memory path,address _to)internal virtual{
for(uint i;i<path.length-1;i++){//遍历整个path
//得到进/出token地址
(address input,address output)=(path,path[i+1]);
//排序得到token0
(address token0,)=UniswapV2Library.sortTokens(input,output);
//获取到output币种的输出量!
uint amountOut=amounts[i+1];
//根据token0,input得到amount0需要out,还是amount1是out,;注意其中之一一定是0,即入token的金额,不需要pair转出
(uint amount0Out,uint amount1Out)=input==token0?(uint(0),amountOut):(amountOut,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,另一个是要转出的金额,内部是转出输出量,并校验交易是否正确,更新储备量
IUniswapV2Pair(UniswapV2Library.pairFor(factory,input,output)).swap(
amount0Out,amount1Out,to,new bytes(0)
);
}
}
//输入精确的token,换取另一个token(输出量不确定)
function swapExactTokensForTokens(
uint amountIn,//输入金额
uint amountOutMin,//最小输出金额
address[]calldata path,//交易路径
address to,
uint deadline
)external virtual override ensure(deadline)returns(uint[]memory amounts){
//通过getAmountsOut获取整个path完整路径的输入/出量,下标0是用户实际输入额,最后一个位置是实际输出额
amounts=UniswapV2Library.getAmountsOut(factory,amountIn,path);
//需要满足计算得来最终输出量大于等于最小输出金额
require(amounts[amounts.length-1]>=amountOutMin,'UniswapV2Router:INSUFFICIENT_OUTPUT_AMOUNT');
//先将amounts[0]入金额转入第一个pair!!
TransferHelper.safeTransferFrom(
path[0],msg.sender,UniswapV2Library.pairFor(factory,path[0],path[1]),amounts[0]
);
//调用内部_swap方法
_swap(amounts,path,to);
}
//输入不确定数量A,换取精确输出的B(例:精确输出1个token,正常100u可以换1个token,由于发交易后其他人先交易过,导致价格变了,可能95或者105可以买1个token,95肯定交易通过,如果amountInMax是102,该交易就无法成交,回退)
function swapTokensForExactTokens(
uint amountOut,//精确的输出额
uint amountInMax,//最大允许的输入量
address[]calldata path,
address to,
uint deadline
)external virtual override ensure(deadline)returns(uint[]memory amounts){
//根据getAmountsIn计算出输入输出量
amounts=UniswapV2Library.getAmountsIn(factory,amountOut,path);
//需要第一个输入量小于等于计算来的实际输入量
require(amounts[0]<=amountInMax,'UniswapV2Router:EXCESSIVE_INPUT_AMOUNT');
//将计算得来的金额amounts[0]转入第一个pair
TransferHelper.safeTransferFrom(
path[0],msg.sender,UniswapV2Library.pairFor(factory,path[0],path[1]),amounts[0]
);
//调用内部_swap方法
_swap(amounts,path,to);
}
//输入精确的eth换取不定量的token,对应swapExactTokensForTokens,不过输入的是eth,换成weth就一样了
function swapExactETHForTokens(uint amountOutMin,address[]calldata path,address to,uint deadline)
external
virtual
override
payable
ensure(deadline)
returns(uint[]memory amounts)
{
//要求path[0]是weth地址
require(path[0]==WETH,'UniswapV2Router:INVALID_PATH');
//通过getAmountsOut,输入额是msg.value
amounts=UniswapV2Library.getAmountsOut(factory,msg.value,path);
//需要满足计算得来最终输出量大于等于最小输出金额
require(amounts[amounts.length-1]>=amountOutMin,'UniswapV2Router:INSUFFICIENT_OUTPUT_AMOUNT');
//pair中只会存weth,没有eth
IWETH(WETH).deposit{value:amounts[0]}();//兑换成weth
//将weth转入到第一个pair
assert(IWETH(WETH).transfer(UniswapV2Library.pairFor(factory,path[0],path[1]),amounts[0]));
//调用内部_swap方法
_swap(amounts,path,to);
}
//输入不定量的A,换取精确的输出ETH,对应swapTokensForExactTokens,只是内部将weth转成eth再给用户
function swapTokensForExactETH(uint amountOut,uint amountInMax,address[]calldata path,address to,uint deadline)
external
virtual
override
ensure(deadline)
returns(uint[]memory amounts)
{
//path最后一个输出地址是weth
require(path[path.length-1]==WETH,'UniswapV2Router:INVALID_PATH');
//
amounts=UniswapV2Library.getAmountsIn(factory,amountOut,path);
//需要第一个输入量小于等于计算来的实际输入量
require(amounts[0]<=amountInMax,'UniswapV2Router:EXCESSIVE_INPUT_AMOUNT');
//将计算得来的金额amounts[0]转入第一个pair
TransferHelper.safeTransferFrom(
path[0],msg.sender,UniswapV2Library.pairFor(factory,path[0],path[1]),amounts[0]
);
//调用内部_swap方法,注意第三个参数改成了当前路由地址!
_swap(amounts,path,address(this));
//交换成功后,将weth转换成eth,再转给to
IWETH(WETH).withdraw(amounts[amounts.length-1]);
TransferHelper.safeTransferETH(to,amounts[amounts.length-1]);
}
//输入精确的A换取不定量的eth swapExactTokensForTokens只是输出是eth
function swapExactTokensForETH(uint amountIn,uint amountOutMin,address[]calldata path,address to,uint deadline)
external
virtual
override
ensure(deadline)
returns(uint[]memory amounts)
{
//path最后一个输出地址是weth
require(path[path.length-1]==WETH,'UniswapV2Router:INVALID_PATH');
//
amounts=UniswapV2Library.getAmountsOut(factory,amountIn,path);
//注意输出要大于最小输出
require(amounts[amounts.length-1]>=amountOutMin,'UniswapV2Router:INSUFFICIENT_OUTPUT_AMOUNT');
//
TransferHelper.safeTransferFrom(
path[0],msg.sender,UniswapV2Library.pairFor(factory,path[0],path[1]),amounts[0]
);
//调用内部_swap方法,注意第三个参数改成了当前路由地址!
_swap(amounts,path,address(this));
//交换成功后,将weth转换成eth,再转给to
IWETH(WETH).withdraw(amounts[amounts.length-1]);
TransferHelper.safeTransferETH(to,amounts[amounts.length-1]);
}
//输入不定量的ETH换取精确的token输出,对应swapTokensForExactTokens,只是输入的是eth
function swapETHForExactTokens(uint amountOut,address[]calldata path,address to,uint deadline)
external
virtual
override
payable
ensure(deadline)
returns(uint[]memory amounts)
{
require(path[0]==WETH,'UniswapV2Router:INVALID_PATH');
amounts=UniswapV2Library.getAmountsIn(factory,amountOut,path);
//注意,实际输入需要小于msg.value,即eth输入量
require(amounts[0]<=msg.value,'UniswapV2Router:EXCESSIVE_INPUT_AMOUNT');
IWETH(WETH).deposit{value:amounts[0]}();
assert(IWETH(WETH).transfer(UniswapV2Library.pairFor(factory,path[0],path[1]),amounts[0]));
_swap(amounts,path,to);
//refund dust eth,if any
//如果实际不需要那么多eth,将剩余返还用户
if(msg.value>amounts[0])TransferHelper.safeTransferETH(msg.sender,msg.value-amounts[0]);
}