Uniswap – 智能合约V2代码导读

这篇文章主要介绍了Uniswap – 智能合约V2代码导读 ,文中通过代码以及文档配合进行讲解,很详细,它对在座的每个人的研究和工作具有很经典的参考价值。 如果需要,让我们与区块链资料网一起学习。

https://www.interchains.cc/17529.html

Uniswap – 智能合约V2代码导读是很好的区块链资料,他说明了区块链当中的经典原理,可以给我们提供资料,Uniswap – 智能合约V2代码导读学习起来其实是很简单的,

不多的几个较为抽象的概念也很容易理解,之所以很多人感觉Uniswap – 智能合约V2代码导读比较复杂,一方面是因为大多数的文档没有做到由浅入深地讲解,概念上没有注意先后顺序,给读者的理解带来困难

Uniswap – 智能合约V2代码导读

  • Uniswap
  • 智能合约

Uniswap V2提供了简洁的x-y-k自动做市商实现。代码主要由两部分组成:Core实现某个交易的Pair的管理逻辑,Periphery实现路由,即一个或者多个交易对的兑换逻辑。理解增加/抽取流动性以及swap操作,需要结合两部分一起看。核心是在Pair中管理了reserve和total supply。所有提供的流动性都以流动性Token来衡量,所有Token的总和就是total supply。

区块链blockchain技术是非常有趣的。更有趣的是,区块链blockchain技术让交易变得更丰富多彩。从中心化交易,到去中心化交易,再到去中心化AMM。每一种改变都尝试解决之前的问题,但本身也不是完美的。也值得一提的,每一点点进步都非常不容易。有种不积跬步,无以至千里的感觉。

很久之前,就看了Uniswap协议,当时理论分析,流动性提供者在价格波动的情况下,收入微薄。在这样的协议下,流动性是否充足,交易是否足够多,交易费是否有足够的吸引力等等,我觉得都是问题。

区块链blockchain – 深入理解Uniswap协议

没想到,Uniswap今年成了热点。在流动性的强需求下,之前的问题好像都不存在了。代币能很快流动交易起来,让很多项目方变得轻松。但是,也是隐隐觉得,之前的问题并没有解决,只是在流动性的需求下掩盖起来。无论如何,Uniswap V2的智能合约代码,还是要看看的。

Uniswap代码结构

Uniswap智能合约代码由两个github项目组成。一个是core,一个是periphery。

https://github.com/Uniswap/uniswap-v2-core.git

https://github.com/Uniswap/uniswap-v2-periphery.git

core偏核心逻辑,单个swap的逻辑。periphery偏外围服务,一个个swap的基础上构建服务。单个swap,两种代币形成的交易对,俗称“池子”。每个交易对有一些基本属性:reserve0/reserve1以及total supply。reserve0/reserve1是交易对的两种代币的储存量。total supply是当前流动性代币的总量。每个交易对都对应一个流动性代币(LPT – liquidity provider token)。简单的说,LPT记录了所有流动性提供者的贡献。所有流动性代币的总和就是total supply。Uniswap协议的思想是reserve0*reserve1的乘积不变。

Periphery逻辑

核心逻辑实现在UniswapV2Router02.sol中。称为Router,因为Periphery实现了“路由”,支持各个swap之间的连接。基本上实现了三个功能:1/ add liquidity(增加流动性)2/remove liqudity (抽取流动性) 3/ swap(交换)。

1. add liqudity

增加流动性,就是同时提供两种代币。因为代币有可能是ETH,针对不同情况有不同的接口。逻辑类似。

 function addLiquidity(  address tokenA,  address tokenB,  uint amountADesired,  uint amountBDesired,  uint amountAMin,  uint amountBMin,  address to,  uint deadline  ) external virtual override ensure(deadline) returns (uint amountA, uint amountB, uint liquidity)

add liqudity查看之前有没有创建相应的交易对。如果有相应的交易对,确定目前的兑换比例在希望的范围内(期望amountDesired和不低于amountMin)。如果兑换比例OK,将相应的代币转入对应的交易对池子,并调用其的mint函数。

2. remove liqudity

提供流动性的相反的操作就是抽取流动性。也就是说,流动性提供者不再提供相应的流动性:

 function removeLiquidity(  address tokenA,  address tokenB,  uint liquidity,  uint amountAMin,  uint amountBMin,  address to,  uint deadline  ) public virtual override ensure(deadline) returns (uint amountA, uint amountB) {

liquidity是抽取的流动性的量。amountMin是抽取代币的最小的个数。to是抽取代币的目标地址。deadline是个有意思的设计:抽取的操作有时效性。超过了一定的deadline(区块高度),这次抽取操作看成无效。

先收回需要抽取的Token,并且销毁:

IUniswapV2Pair(pair).transferFrom(msg.sender, pair, liquidity); // send liquidity to pair (uint amount0, uint amount1) = IUniswapV2Pair(pair).burn(to);
3. swap

swap是普通用户进行代币交易的操作。普通用户通过swap操作实现两种token之间的交易。

 function swapExactTokensForTokens(  uint amountIn,  uint amountOutMin,  address[] calldata path,  address to,  uint deadline  ) external virtual override ensure(deadline) returns (uint[] memory amounts) {

Uniswap支持多种代币的交换。具体的含义是,Uniswap提供了多级交易池的路由功能。举个例子,已有两个交易对TokenA-TokenB,以及TokenB-TokenC,通过swap接口,可以实现TokenA-TokenC的交换,其中经过的TokenA-TokenB,TokenB-TokenC,称为路径(path)。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的实现(在获取每个交易对的reserve信息后,调用getAmountOut函数):

 function getAmountOut(uint amountIn, uint reserveIn, uint reserveOut) internal pure returns (uint amountOut) {  require(amountIn > 0, 'UniswapV2Library: INSUFFICIENT_INPUT_AMOUNT');  require(reserveIn > 0 && reserveOut > 0, 'UniswapV2Library: INSUFFICIENT_LIQUIDITY');  uint amountInWithFee = amountIn.mul(997);  uint numerator = amountInWithFee.mul(reserveOut);  uint denominator = reserveIn.mul(1000).add(amountInWithFee);  amountOut = numerator / denominator;  }

注意,其中的997/1000的系数。在进入每个交易池之前,进入的金额先扣除了0.3%的本金。这个就是交易费。注意的是,路径上的交易池,每个池子都收。有点像高速收费站,一段段的收。

TransferHelper.safeTransferFrom(  path[0], msg.sender, UniswapV2Library.pairFor(factory, path[0], path[1]), amounts[0] );

将代币path[0],转入到交易对,数量为amounts[0]。转入代币后,进行真正的swap操作:

 function _swap(uint[] memory amounts, address[] memory path, address _to) internal virtual {  for (uint i; i   (address input, address output) = (path[i], path[i + 1]);  (address token0,) = UniswapV2Library.sortTokens(input, output);  uint amountOut = amounts[i + 1];  (uint amount0Out, uint amount1Out) = input == token0 ? (uint(0), amountOut) : (amountOut, uint(0));  address to = i   IUniswapV2Pair(UniswapV2Library.pairFor(factory, input, output)).swap(  amount0Out, amount1Out, to, new bytes(0)  );  }  }

原理比较简单,针对每一条路径,调用交易对的swap操作。

Core逻辑

Core逻辑实现了单个交易对的逻辑。通过UniswapV2Factory可以创建一个个Pair(交易池)。每个具体实现逻辑在UniswapV2Pair中。

1. mint

每个交易对创建流动性。

function mint(address to) external lock returns (uint liquidity) {

因为在调用mint函数之前,在addLiquidity函数已经完成了转账,所以,从这个函数的角度,两种代币数量的计算方式如下:

 uint balance0 = IERC20(token0).balanceOf(address(this));  uint balance1 = IERC20(token1).balanceOf(address(this));  uint amount0 = balance0.sub(_reserve0);  uint amount1 = balance1.sub(_reserve1);

当前的balance是当前的reserve加上注入的流动性的代币数量。

 uint _totalSupply = totalSupply; // gas savings, must be defined here since totalSupply can update in _mintFee  if (_totalSupply == 0) {  liquidity = Math.sqrt(amount0.mul(amount1)).sub(MINIMUM_LIQUIDITY);  _mint(address(0), MINIMUM_LIQUIDITY); // permanently lock the first MINIMUM_LIQUIDITY tokens  } else {  liquidity = Math.min(amount0.mul(_totalSupply) / _reserve0, amount1.mul(_totalSupply) / _reserve1);  }  _mint(to, liquidity);

流动性liquidity的计算方式在第一次提供流动性时和其他时候稍稍不同。第一次提供流动性的计算公式如下:

liquidity = sqrt(x0*y0) – min

其中min是10^3。也就是说,第一次提供流动性是有最小流动性要求的。其他提供流动性的计算公式如下:

liquidity = min((x0/reserve0totalsupply), (y0/reserve1totalsupply))

也就说,按照注入的流动性和当前的reserve的占比一致。

2. burn

burn函数用在抽取流动性。burn逻辑和mint逻辑类似。

function burn(address to) external lock returns (uint amount0, uint amount1) {
3. swap

swap函数实现两种代币的兑换。

function swap(uint amount0Out, uint amount1Out, address to, bytes calldata data) external lock {

一个交易池的swap操作支持两个方向的兑换,可以从TokenA换到TokenB,或者TokenB换到TokenA。

if (amount0Out > 0) _safeTransfer(_token0, to, amount0Out); // optimistically transfer tokens if (amount1Out > 0) _safeTransfer(_token1, to, amount1Out); // optimistically transfer tokens

因为在swapExactTokensForTokens的getAmountOut函数已经确定兑换处的金额。所以,先直接转账。

在不做swap之前,balance应该和reserve相等的。通过balance和reserve的差值,可以反推出输入的代币数量:

uint amount0In = balance0 > _reserve0 - amount0Out ? balance0 - (_reserve0 - amount0Out) : 0; uint amount1In = balance1 > _reserve1 - amount1Out ? balance1 - (_reserve1 - amount1Out) : 0;

确保反推的输入代币数量不小于零。

require(amount0In > 0 || amount1In > 0, 'UniswapV2: INSUFFICIENT_INPUT_AMOUNT');

Swap整体流程

Uniswap的代码和逻辑还是比较清晰的。画个图总结一下,swap的总体流程:

Uniswap - 智能合约V2代码导读

总结:

Uniswap V2提供了简洁的x-y-k自动做市商实现。代码主要由两部分组成:Core实现某个交易的Pair的管理逻辑,Periphery实现路由,即一个或者多个交易对的兑换逻辑。理解增加/抽取流动性以及swap操作,需要结合两部分一起看。核心是在Pair中管理了reserve和total supply。所有提供的流动性都以流动性Token来衡量,所有Token的总和就是total supply。

Uniswap - 智能合约V2代码导读

部分转自网络,侵权联系删除www.interchains.cchttps://www.interchains.cc/17529.html

区块链毕设网(www.interchains.cc)全网最靠谱的原创区块链毕设代做网站 部分资料来自网络,侵权联系删除! 最全最大的区块链源码站 !
区块链知识分享网, 以太坊dapp资源网, 区块链教程, fabric教程下载, 区块链书籍下载, 区块链资料下载, 区块链视频教程下载, 区块链基础教程, 区块链入门教程, 区块链资源 » Uniswap – 智能合约V2代码导读

提供最优质的资源集合

立即查看 了解详情