登链学习日记001-uniswapV2-01

  • Jack
  • 更新于 2023-04-21 19:26
  • 阅读 2007

UniswapV2学习记录

主要合约

UniswapV2Factory工厂合约

function createPair( address tokenA, address tokenB ) external returns (address pair) 用create2创建交易对,并初始化pair中的token

UniswapV2Pair交易对合约

ERC20合约,提供LP代币

function mint(address to) external lock returns (uint liquidity) 给流动性提供者铸造LP function burn( address to ) external lock returns (uint amount0, uint amount1) 销毁LP,拿回代币 function swap( uint amount0Out, uint amount1Out, address to, bytes calldata data ) external 兑换代币

UniswapV2Router01路由合约

前端交互合约

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

添加流动性,根据用户提供的代币和数量,计算出实际需要的代币数量和LP,然后调用Pair合约的mint铸造代币

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

移除流动性,调用Pair合约的burn销毁LP并返回代币

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

代币兑换,根据代币兑换路径和输入/输出代币,计算出输出/输入代币数量,调用Pair合约的swap进行交换

操作路径

添加流动性

  1. 调用Router合约的addLiquidity方法
  2. 计算出需要提供的代币数量,例如 池子里面 a:500 b:1000 amountDesired的比例最好是池子里的比例 现在想加流动性 a:100 b:300 =>最后是 a:100 b:200 1 现在想加流动性 a:100 b:180 =>最后是 a:90 b:180 2
  3. 把代币转给Pair合约,调用mint
  4. 由于Pair里面的reserve和balance没同步 amount = balance.sub(_reserve) 计算出刚转入的代币数量,再根据 xy = L^2(池子里还没有流动性) 或者 amounttotalSupply / reserve(按比例)计算出LP并mint给用户,最后更新Pair

交换代币

  1. 调用合约的swap方法
  2. 根据代币兑换路径和输入/输出代币数量计算出输出/输入代币数量。以x换y为例,根据(x1+x)(y-y1) = xy 推导出 x1y/(x+x1) = y1 手续费版 x1997y/(x1000+x1997) 计算出可以兑换出来的代币y的数量
  3. 由于可能是多路径兑换,比如 USDC=>WETH=>DAI ,在第一个池子中先兑换出WETH,再用WETH在第二个池子中兑换出DAI 返回的amounts 数组 中 amounts[0] 为输入的代币数量amounts[amounts.length-1]为想要兑换的代币数量
  4. 把代币转给第一个池子,再调用Pair合约的swap进行兑换
  5. 由于不确定我要兑换的代币是池子的token0还是token1,在调用swap(amount0Out, amount1Out, to, new bytes(0) 时传入两个数量,其中一个为0,不为0的是想要兑换的代币
  6. Pair合约把代币转给用户,此时是先把代币打进来的也不涉及回调,第一次用户先把代币1打给第一个池子,此时to为第二个池子的地址直接把代币2提前打进去,再次调用swap时to为用户地址把代币3转给用户,然后更新Pair
  7. function swap( uint amount0Out, uint amount1Out, address to, bytes calldata data ) 当to为合约地址时并且data不为空时可以实现闪电贷,合约先把代币转给to合约,to合约在uniswapV2Call函数中必须还回代币(代币0或者代币1都行),Pair合约会检查当前的代币乘积(去除手续费)不能小于原来的k值,否则回滚交易

移除流动性

  1. 调用合约的removeLiquidity方法
  2. 把LP转给Pair合约,调用burn
  3. 根据liquidity*balance / totalSupply 计算出可以拿到的代币数量转给用户并销毁LP,最后更新Pair

其他知识点

UniswapV2Library里的pairFor

不同的编译器结果不同,在UniswapV2Library中的pariFor拿池子的地址的hash由于是硬编码需要重新计算type(UniswapV2Pair).creationCode

Pair中的变量price0CumulativeLast

每次update都会跟新用于oracle计算价格。记录 Token1 对 Token0 的时间累积价格,用于下个时间段计算新的价格,新的价格 = (price0Cumulative - price0CumulativeLast) / (t - t0)

Pair中的_mintFee函数

UniswapV2Factory中的feeTo一旦设置,表示平台会收取一定手续费。平台收0.3%手续费的1/6 给到这个地址,每次 mint或者burn的时候收费,而不是用户swap的时候,收到的钱又添加到池子里继续拿lp。拿到增发的1/6 除以 (上次的+增发的5/6) = 等比例拿到池子里的lp => (k-klast)/6 / (klast+(k-klast)5/6) = lp/totalSupply => totalSupply(k-klast) / (k*5+klast) 用的这个公式

Pair中的MINIMUM_LIQUIDITY

liquidity = Math.sqrt(amount0.mul(amount1)).sub(MINIMUM_LIQUIDITY);代码中有最小流动性的限制。如果没有这个限制:

  1. 如果liquidity很小,池子里的代币很少,用户swap很容易导致价格大幅变化,而且流动性很差
  2. 恶意攻击,别人捐款给池子,然后调用sync这个函数让reserve 和balance同步(reserve很大,totalSupply很小),此时池子的利率不正常,,如果捐款了10个eth 那么添加流动性的人至少要超过10个eth才能拿到lp,amount0.mul(_totalSupply) / _reserve0 由于_reserve0很大,_totalSupply很小,那么amount0必须很大

闪电贷简单示例

  1. 合约中调用IUniswapV2Pair(pair).swap
  2. uniswapV2Call中先拿到Pair合约转过来的token
  3. 调用UniswapV2Library.getAmountsIn计算出要还的token数量(包含手续费)
  4. token授权给router,调用IUniswapV2Router01(router).swapExactTokensForTokens兑换token
  5. 如果换出来的token比原来借的多,就还给Pair,否则中断交易

声明

本人初学小白,记录智能合约学习知识,文章内容可信度低,仅供参考,谢谢!

点赞 2
收藏 1
分享
本文参与登链社区写作激励计划 ,好文好收益,欢迎正在阅读的你也加入。

0 条评论

请先 登录 后评论
Jack
Jack
0x7F5a...Cd97
江湖只有他的大名,没有他的介绍。