Uniswap代码结构 Uniswap智能合约代码由两个github项目组成。一个是核心,一个是外围。 https://github.com/Uniswap/un... https://github.com/Uniswap/un... core部分核心逻辑,(LP流动性挖矿系统开发,详解对接v+hkkf5566)单次swap的逻辑。Periphery是一种外围服务,服务建立在一个一个交换的基础上。单次掉期,由两种代币组成的交易对,俗称“池子”。每个交易对都有一些基本属性:reserve0/reserve1和总供应量。reserve0/reserve1是交易对的两个代币的存储量。总供应量是当前流动性代币的总量。每个交易对对应一个流动性代币(LPT-liquidityprovidertoken)。简单来说,LPT记录了所有流动性提供者的贡献。所有流动性代币的总和就是总供应量。Uniswap协议的思想是reserve0*reserve1的乘积不变。 外围逻辑 核心逻辑在UniswapV2Router02.sol中实现。之所以称为Router,是因为Periphery实现了“路由”,支持swap之间的连接。基本上实现了三个功能:1/addliquidity(增加流动性)2/removeliquidity(抽取流动性)3/swap(兑换)。 1.增加流动性 增加流动性就是同时提供两种代币。因为token可能是ETH,所以不同的情况会有不同的接口。逻辑是相似的。 函数addLiquidity( 地址代币A, 地址代币B, 单位数量ADesired, 单位数量BDesired, 单位数量AMin, 单位数量BMin, 地址到, uint截止日期 )externalvirtualoverrideensure(deadline)returns(uintamountA,uintamountB,uintliquidity) addliquidity检查之前是否创建过对应的交易对。如果存在对应的交易对,请确保当前兑换比例在期望范围内(期望amountDesired且不小于amountMin)。如果兑换比例OK,将对应的代币转入对应的交易对池,并调用其铸币函数。 2。removeliquidity 提供流动性的相反操作是抽取流动性。也就是说,流动性提供者不再提供相应的流动性: functionremoveLiquidity( addresstokenA, addresstokenB, uintliquidity, uintamountAMin, uintamountBMin, addressto, uintdeadline )publicvirtualoverrideensure(deadline)returns(uintamountA,uintamountB){ liquidity是提取流动性的数量。amountMin是要提取的最小代币数量。to是提取代币的目的地址。deadline是一个有趣的设计:提取操作是时间敏感的。如果超过某个期限(区块高度),提取操作将被视为无效。 先取出需要取出的Token,并销毁: IUniswapV2Pair(pair).transferFrom(msg.sender,pair,liquidity);//发送流动性给pair (uintamount0,uintamount1)=IUniswapV2Pair(pair).burn(to); 3.swap swap是普通用户进行代币交易的操作。普通用户通过互换操作实现两种代币之间的交易。 函数swapExactTokensForTokens( uintamountIn, uintamountOutMin, address[]calldatapath, addressto, uintdeadline )externalvirtualoverrideensure(deadline)returns(uint[]memoryamounts){ Uniswap支持多代币兑换。具体意思是Uniswap为多级交易池提供路由功能。例如,有两个交易对TokenA-TokenB和TokenB-TokenC。通过交换接口,可以实现TokenA-TokenC的交换。传递的TokenA-TokenB和TokenB-TokenC称为路径。amountIn是路径中第一个令牌的数量,amountOutMin是交换后所需的最小数量。 amounts=UniswapV2Library.getAmountsOut(factory,amountIn,path); require(amounts[amounts.length-1]>=amountOutMin,'UniswapV2Router:INSUFFICIENT_OUTPUT_AMOUNT'); amounts是每条路径后的数量交换。amounts[amounts.length-1]是最后一条路径的输出量。注意,UniswapV2Library.getAmountsOut的实现(获取各交易对的储备信息后,调用getAmountOut函数): functiongetAmountOut(uintamountIn,uintreserveIn,uintreserveOut)internalpurereturns(uintamountOut){ require(amountIn>0,'UniswapV2Library:INSUFFICIENT_INPUT_AMOUNT'); require(reserveIn>0&&reserveOut>0,'UniswapV2Library:INSUFFICIENT_LIQUIDITY'); uintamountInWithFee=amountIn.mul(997); uintnumerator=amountInWithFee.mul(reserveOut); uintdenominator=reserveIn.mul(1000).add(amountInWithFee); amountOut=分子/分母; } 注意系数997/1000。在进入每个交易池之前,进入的金额先扣除本金的0.3%。这是交易费。需要注意的是,路径上的交易池是由每个池收集的。有点像高速收费站,一段一段收费。 TransferHelper.safeTransferFrom( path[0],msg.sender,UniswapV2Library.pairFor(factory,path[0],path[1]),amounts[0] ); 将替换Coinpath[0],转入交易对,金额为amounts[0]。token转出后,进行真正的swap操作:1;i++){ (地址输入,地址输出)=(路径,路径[i+1]); (地址token0,)=UniswapV2Library.sortTokens(输入,输出); uintamountOut=amounts[i+1]; (uintamount0Out,uintamount1Out)=input==token0?(uint(0),amountOut):(amountOut,uint(0)); addressto=i
