什么是dApp和Web3应用?
如果不提及dApp,关于Web3的讨论就不会完整。简而言之,dApp或去中心化应用程序是Web3革命的支柱。术语“Web3应用程序”、“Web3应用程序”、“去中心化应用程序”、“dApps”或“Web3 dApps”在很大程度上都是同义词。它们都指作为Web3一部分的分散的、通常基于区块链的应用程序。
那么,什么是Web3应用程序?许多dApp或Web3应用程序的一个组成部分是所谓的“智能合约”。有区块链技术经验的人可能熟悉智能合约的概念。智能合约本质上是自动执行的软件协议,是在以太坊区块链等区块链上运行的代码片段。这些会自动“运行”,或在满足一组相关条款时执行。因此,这些“合约”可以自动验证和执行不同方之间的交易。
内部函数(仅供合约内部调用)
_swapSupportingFeeOnTransferTokens
代码速浏览
function _swapSupportingFeeOnTransferTokens(address[]memory path,address _to)internal virtual{
for(uint i;i<path.length-1;i++){
(address input,address output)=(path<i>,path[i+1]);
(address token0,)=UniswapV2Library.sortTokens(input,output);
IUniswapV2Pair pair=IUniswapV2Pair(UniswapV2Library.pairFor(factory,input,output));
uint amountInput;
uint amountOutput;
{
(uint reserve0,uint reserve1,)=pair.getReserves();
(uint reserveInput,uint reserveOutput)=input==token0?(reserve0,reserve1):(reserve1,reserve0);
amountInput=IERC20(input).balanceOf(address(pair)).sub(reserveInput);
amountOutput=UniswapV2Library.getAmountOut(amountInput,reserveInput,reserveOutput);
}
(uint amount0Out,uint amount1Out)=input==token0?(uint(0),amountOutput):(amountOutput,uint(0));
address to=i<path.length-2?UniswapV2Library.pairFor(factory,output,path[i+2]):_to;
pair.swap(amount0Out,amount1Out,to,new bytes(0));
参数分析
函数swapETHForExactTokens的入参有2个,出参有0个,对应的解释如下:
function _swapSupportingFeeOnTransferTokens(
address[]memory path,//交易路径列表
address _to//交易获得的token发送到的地址
)internal virtual{
函数_swapSupportingFeeOnTransferTokens相比函数_swap为了支持path中有交易后可变数量的代币,不需要输入amounts,但需要额外做一些操作。
实现分析
...
{
//循环交易路径列表
for(uint i;i<path.length-1;i++){
//从path中取出input和output
(address input,address output)=(path<i>,path[i+1]);
//从input和output中算出谁是token0
(address token0,)=UniswapV2Library.sortTokens(input,output);
//获得input,output的流动池
IUniswapV2Pair pair=IUniswapV2Pair(UniswapV2Library.pairFor(factory,input,output));
uint amountInput;
uint amountOutput;
{
//获取流动池库存reserve0,reserve1
(uint reserve0,uint reserve1,)=pair.getReserves();
//如果input==token0,那么(reserveInput,reserveOutput)就是(reserve0,reserve1);反之则相反
(uint reserveInput,uint reserveOutput)=input==token0?(reserve0,reserve1):(reserve1,reserve0);
//amountInput等于流动池余额减去reserveInput
amountInput=IERC20(input).balanceOf(address(pair)).sub(reserveInput);
//获取amountOutput
amountOutput=UniswapV2Library.getAmountOut(amountInput,reserveInput,reserveOutput);
}
//如果input==token0,那么amount0Out就是0,amount1Out就是amountOut;反之则相反
(uint amount0Out,uint amount1Out)=input==token0?(uint(0),amountOutput):(amountOutput,uint(0));
//如果这是最后的一笔交易,那么to地址就是_to,否则to地址是下一笔交易的流动池地址
address to=i<path.length-2?UniswapV2Library.pairFor(factory,output,path[i+2]):_to;
//执行input和output的交易
pair.swap(amount0Out,amount1Out,to,new bytes(0));
总结
可以看到,因为没有amounts,需要使用流动池余额减去库存来计算amountInput。
外部函数(仅供合约外部调用)
swapExactTokensForTokensSupportingFeeOnTransferTokens
代码速浏览
function swapExactTokensForTokensSupportingFeeOnTransferTokens(
uint amountIn,
uint amountOutMin,
address[]calldata path,
address to,
uint deadline
)external virtual override ensure(deadline){
TransferHelper.safeTransferFrom(
path[0],msg.sender,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'
参数分析
函数swapExactTokensForTokensSupportingFeeOnTransferTokens的入参有5个,出参有0个,对应的解释如下:
function swapExactTokensForTokensSupportingFeeOnTransferTokens(
uint amountIn,//交易支付代币数量
uint amountOutMin,//交易获得代币最小值
address[]calldata path,//交易路径列表
address to,//交易获得的token发送到的地址
uint deadline//过期时间
)external virtual override ensure(deadline){
函数swapExactTokensForTokensSupportingFeeOnTransferTokens相比函数swapExactTokensForTokens,少了amounts,因为交易后可变数量的代币不能做amounts的预测。
实现分析
...
//检查交易是否过期
ensure(deadline)
{
//将amountIn数量的path[0]代币从用户账户中转移到path[0],path[1]的流动池
TransferHelper.safeTransferFrom(
path[0],msg.sender,UniswapV2Library.pairFor(factory,path[0],path[1]),amountIn
);
//记录to地址path[path.length-1]代币的余额
uint balanceBefore=IERC20(path[path.length-1]).balanceOf(to);
//按path列表执行交易集合
_swapSupportingFeeOnTransferTokens(path,to);
//如果to地址获得的代币数量小于amountOutMin,交易失败
require(
IERC20(path[path.length-1]).balanceOf(to).sub(balanceBefore)>=amountOutMin,
'UniswapV2Router:INSUFFICIENT_OUTPUT_AMOUNT'
);
}
该函数适用于支付确定数量的代币,获得不定数量的代币,且在path路径列表中有交易后数量可变的代币。