Uniswap V2 — 从代码解释 DeFi 协议

这篇具有很好参考价值的文章主要介绍了Uniswap V2 — 从代码解释 DeFi 协议。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

Uniswap V2 — 从代码解释 DeFi 协议

为了理解我们在分析代码时将要经历的不同组件,首先了解哪些是主要概念以及它们的作用是很重要的。所以,和我一起裸露吧,因为这是值得的。

我在 5 个段落中总结了您需要了解的主要重要概念,您将在本文结束时理解这些概念。

Uniswap 是一种去中心化交易协议。该协议是一套持久的、不可升级的智能合约,它们共同创建了一个自动化的做市商。

Uniswap 生态系统贡献流动性的流动性提供者、交换代币的交易员和与智能合约交互以开发代币新交互的开发人员组成。

每个 Uniswap智能合约或对管理一个由两个 ERC-20 代币储备组成的流动资金池。

每个流动性池重新平衡以保持 50/50 比例的加密货币资产,这反过来又决定了资产的价格。

流动性提供者可以是任何能够向 Uniswap 交易合约提供等值的 ETH 和 ERC-20 代币的人。作为回报,他们从交易合约中获得流动性提供者代币(LP 代币代表流动性提供者拥有的池的份额),可用于随时提取其在流动性池中的比例。

他们存储库中的主要智能合约是:

  • UniswapV2ERC20— 用于 LP 令牌的扩展 ERC20 实现。它还实施了 EIP-2612 以支持链下传输批准。
  • UniswapV2Factory— 与 V1 类似,这是一个工厂合约,它创建配对合约并充当它们的注册表。注册表使用 create2 来生成对地址——我们将详细了解它是如何工作的。
  • UniswapV2Pair— 负责核心逻辑的主合约。值得注意的是,工厂只允许创建独特的货币对,以免稀释流动性。
  • UniswapV2Router— Uniswap UI 和其他在 Uniswap 之上工作的网络和去中心化应用程序的主要入口点。
  • UniswapV2Library — 一组实现重要计算的辅助函数。

在这篇文章中,我们将提及所有这些,但我们将主要关注浏览UniswapV2RouterUniswapV2Factory编码,尽管UniswapV2Pair并且UniswapV2Library会涉及很多。

UniswapV2Router02.sol

该合约使创建货币对、添加和删除流动性、计算所有可能的掉期变化的价格以及执行实际掉期变得更加容易。路由器适用于通过工厂合约部署的所有对

您需要在合约中创建一个实例才能调用 addLiquidity、removeLiquidity 和 swapExactTokensForTokens 函数

address private constant ROUTER = 0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D;ROUTER = 0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D;

IUniswapV2Router02 public uniswapV2Router;
uniswapV2Router = IUniswapV2Router02(ROUTER);

现在让我们看看流动性管理:

函数 addLiquidity():

function addLiquidity(
    address tokenA,
    address tokenB,
    uint amountADesired,
    uint amountBDesired,
    uint amountAMin,
    uint amountBMin,
    address to,
    uint deadline
) external returns (uint amountA, uint amountB, uint liquidity);
  • tokenAtokenB:是我们需要获取或创建我们想要增加流动性的货币对的代币。
  • amountADesiredamountBDesired是我们要存入流动资金池的金额。
  • amountAMinamountBMin是我们要存入的最小金额。
  • to address 是接收 LP 代币的地址。
  • 截止日期,最常见的是block.timestamp

在内部 _addLiquidity() 中,它将检查这两个令牌中的一对是否已经存在,如果不存在,它将创建一个新令牌

if (IUniswapV2Factory(factory).getPair(tokenA, tokenB) == address(0)) {
    IUniswapV2Factory(factory).createPair(tokenA, tokenB);
}

然后它需要获取现有的代币数量或也称为reserveAand reserveB,我们可以通过 UniswapV2Pair 合约访问它

IUniswapV2Pair(pairFor(factory, tokenA, tokenB)).getReserves()

现在,外部函数 addLiquidity, 返回(uint amountA, uint amountB, uint liquidity),那么它是如何计算的呢?

通过UniswapV2Library拿到上面提到的reserves之后,还有一系列的检查

如果该对不存在,并且新创建 amountAamountB返回一个新的,则将amountADesired作为amountBDesired参数传递(见上文)。

否则,它会做这个操作

amountBOptimal = amountADesired.mul(reserveB) / reserveA;

如果amountB小于或等于,amountBDesired那么它将返回:

(uint amountA, uint amountB) = (amountADesired, amountBOptimal)

否则,它将返回

(uint amountA, uint amountB) = (amountAOptimal, amountBDesired)

其中amountAOptimal的计算方式与amountBOptimal

然后,要计算liquidity返回值将经过以下过程:

首先,它将使用现有/新创建的对的地址部署 UniswapV2Pair 合约。

它是如何做到的?它计算一对的 CREATE2 地址而无需进行任何外部调用:(阅读有关 CREATE2 Opcode 的更多信息)

pair = address(uint(keccak256(abi.encodePacked(address(uint(keccak256(abi.encodePacked(
    hex'ff',
    factory,
    keccak256(abi.encodePacked(token0, token1)),
    hex'96e8ac4277198ff8b6f785478aa9a39f403cb768dd02cbee326c3e7da348845f' // init code hash
))));

然后,它获取新部署合约的地址,我们需要用它来从这对代币中铸造代币。

当您向货币对添加流动性时,合约会生成 LP 代币;当你移除流动性时,LP 代币就会被销毁。

pairFor因此,首先我们使用UniswapV2Library获取地址:

address pair = UniswapV2Library.pairFor(factory, tokenA, tokenB);UniswapV2Library.pairFor(factory, tokenA, tokenB);

因此,稍后可以铸造 ERC20 代币并计算返回的流动性:

liquidity = IUniswapV2Pair(pair).mint(to);

如果您想知道为什么它最终成为 ERC20,在 mint 函数中它是这样存储的https://github.com/Uniswap/v2-core/blob/ee547b17853e71ed4e0101ccfd52e70d5acded58/contracts/UniswapV2Pair.sol#L112)

uint balance0 = IERC20(token0).balanceOf(address(this));
uint balance1 = IERC20(token1).balanceOf(address(this));

****函数removeLiquidity():

function removeLiquidity(
    address tokenA,
    address tokenB,
    uint liquidity,
    uint amountAMin,
    uint amountBMin,
    address to,
    uint deadline
) external returns (uint amountA, uint amountB);

从池中移除流动性意味着燃烧 LP 代币以换取一定数量的基础代币。

IUniswapV2Pair(pair).transferFrom(msg.sender, pair, liquidity);

然后,外部函数返回两个值(uint amountA, uint amountB),这些值是使用传递给函数的参数计算的。

随提供的流动性返回的代币数量计算如下:

amount0 = liquidity.mul(balance0) / _totalSupply; 
amount1 = liquidity.mul(balance1) / _totalSupply;

然后它将这些数量的代币转移到指定的地址

_safeTransfer(_token0, to, amount0);
_safeTransfer(_token1, to, amount1);

您的 LP 代币份额越大,销毁后获得的储备份额就越大。

上面的这些计算发生在 burn 函数内部

IUniswapV2Pair(对).burn(对)

https://github.com/Uniswap/v2-periphery/blob/0335e8f7e1bd1e8d8329fd300aea2ef2f36dd19f/contracts/UniswapV2Router02.sol#L114)

IUniswapV2Pair(pair).burn(to)

****函数swapExactTokensForTokens()

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

Uniswap 的核心功能是交换代币,所以让我们弄清楚代码中发生了什么,以便更好地理解它

您很可能听说过流动资金池中使用的神奇公式

X * Y = K

所以,这将首先发生在 swap 函数内部getAmountOut()

里面用到的关键函数有:

TransferHelper.safeTransferFrom().safeTransferFrom()

代币金额发送到配对代币的地方

在 UniswapV2Pair 合约的较低级别交换功能中,它将是

_safeTransfer(_token, to, amountOut);

这将实际转移回预期地址。

我知道信息量很大,但您将有足够的时间阅读所有内容,直到完全理解为止。所以……

UniswapV2Factory.sol

工厂合约是所有已部署对合约的注册表。这个合约是必要的,因为我们不希望有成对的相同代币,这样流动性就不会分成多个相同的对。

该合约还简化了配对合约的部署:无需通过任何外部调用手动部署配对合约,只需调用工厂合约中的方法即可。

好吧,让我们倒回去,因为在上面的这些行中已经说了非常重要的事情。我们把它们拆分开来分别分析:

该合约是所有已部署对合约的注册表

只部署了一个工厂合约,该合约用作 Uniswap 交易对的官方注册处。

现在,我们在代码中的什么地方看到了它以及发生了什么:

address[] public allPairs;

它有 的数组allPairs,如上所述,存储在这个合约中。这些对被添加到一个方法中,该方法createPair()通过将新初始化的对推送到数组来调用。

allPairs.push(pair);push(pair);

这个合约是必要的,因为我们不想拥有成对的相同代币

mapping(address => mapping(address => address)) public getPair;

它具有该对的地址与构成该对的两个令牌的映射。这用于检查一对是否已经存在。

require(getPair[token0][token1] == address(0), 'UniswapV2: PAIR_EXISTS');

该合约还简化了配对合约的部署

这是一个更深层次的话题,但我将尝试总结一下这里发生的事情的重要性。

在以太坊中,合约可以部署合约。可以调用已部署合约的函数,该函数将部署另一个合约。

您不需要从您的计算机上编译和部署合约,您可以通过现有合约来执行此操作。

那么,Uniswap 是如何部署智能合约的呢?

通过使用操作码CREATE2

bytes memory bytecode = type(UniswapV2Pair).creationCode;type(UniswapV2Pair).creationCode;
bytes32 salt = keccak256(abi.encodePacked(token0, token1));
assembly {
    pair := create2(0, add(bytecode, 32), mload(bytecode), salt)
}

在第一行,我们得到创建字节码UniswapV2Pair

下一行创建了salt一个字节序列,用于确定性地生成新合约的地址。

最后一行是我们调用以使用+create2确定性地创建新地址的地方。部署。bytecode``salt``UniswapV2Pair

并得到对地址,我们可以看到这是createPair()函数的返回值

function createPair(
  address tokenA, address tokenA, 
  address tokenB
) external returns (address pair)

当提供的标记不是现有的对_addLiquidity()时,它在内部函数中使用。

所以,这就是关于 Uniswap 代码的全部内容。

现在,为了看到我们测试的所有内容,我可以推荐您查看 Smart Contract Programmer 在他的defi-by-example 内容中实现的代码,他已经在视频中进行了解释。

在这里你可以看到我们可以增加流动性的方式

function addLiquidity(
  address _tokenA,
  address _tokenB,
  uint _amountA,
  uint _amountB
) external {
  IERC20(_tokenA).transferFrom(msg.sender, address(this), _amountA);
  IERC20(_tokenB).transferFrom(msg.sender, address(this), _amountB);

  IERC20(_tokenA).approve(ROUTER, _amountA);
  IERC20(_tokenB).approve(ROUTER, _amountB);

  (uint amountA, uint amountB, uint liquidity) =
    IUniswapV2Router(ROUTER).addLiquidity(
      _tokenA,
      _tokenB,
      _amountA,
      _amountB,
      1,
      1,
      address(this),
      block.timestamp
    );

  emit Log("amountA", amountA);
  emit Log("amountB", amountB);
  emit Log("liquidity", liquidity);
}

以及我们必须如何考虑消除流动性

function removeLiquidity(address _tokenA, address _tokenB) external {
  address pair = IUniswapV2Factory(FACTORY).getPair(_tokenA, _tokenB);

  uint liquidity = IERC20(pair).balanceOf(address(this));
  IERC20(pair).approve(ROUTER, liquidity);

  (uint amountA, uint amountB) =
    IUniswapV2Router(ROUTER).removeLiquidity(
      _tokenA,
      _tokenB,
      liquidity,
      1,
      1,
      address(this),
      block.timestamp
    );

  emit Log("amountA", amountA);
  emit Log("amountB", amountB);
}

通过Github 获取更多区块链学习资料!

https://github.com/Manuel-yang/BlockChainSelfLearning文章来源地址https://www.toymoban.com/news/detail-421595.html

到了这里,关于Uniswap V2 — 从代码解释 DeFi 协议的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • “新DeFi”生态的构建,流支付协议Zebec或厚积薄发

    在今年3月,基于Solana区块链的流支付协议Zebec已通过公开和私人Token销售筹集了2800万美元,并推出其Token ZBC。 据悉该笔融资由由Solana Ventures 和 Distributed Global 领投,其余主要投资方包括OKX Blockdream Ventures 、Lightspeed Venture Partners、Circle、Coinbase、Alameda Research、DST Global Partners、

    2023年04月08日
    浏览(33)
  • 模块化与单片化优缺点解析:为什么单片链仍是 DeFi 协议的最好选择?

    目前模块化区块链热度不减,其诞生的原因源自于单片链的局限和缺陷。  什么是」模块化「? 在软件工程开发中,」模块化「是指将代码进行解耦, 使每个模块的功能独立, 模块之间的耦合程度低, 达到模块复用的目的。  模块化的本质是一种任务分工,不同的模块组合成的程

    2023年04月09日
    浏览(40)
  • 实验六 配置RIP – V2协议

    要求:     配置RIPV2协议,实现全网互通,并在合适的路由器上配置路由汇总,减小路由器的路由表。 基础配置     为路由器R1、R2和R3设置相应的IP地址。 RIPV2 的配置 1. 首先,在三台路由器上配置RIPV1: r1(config)#router rip r1(config-router)#net 172.16.0.0 r1(config-router)#net 12.0.0.0 r2和

    2024年02月09日
    浏览(38)
  • 网络协议之HTTP详细解释

    可能我接下的讲述可能有些赘述,但这些东西都是我们要了解的内容,循序渐进才行,不能一蹴而就,话不多说,我们开始吧!永远在路上. 在了解什么是协议之前,我们可以先来看看,web的发展阶段, 1.静态 Web 页面阶段:Web 诞生之初,主要是以静态页面为主,HTML 作为页面标记语言,网

    2024年02月06日
    浏览(51)
  • Linux服务器snmp协议v2/v3配置方法

    1、确保本机已经安装了snmp服务 [root@idc ~]# rpm -qa |grep snmp net-snmp-libs-5.1.2-11.EL4.7 net-snmp-5.1.2-11.EL4.7 如果没有,可以使用: “yum install net-snmp” 使其自动上网下载安装。如果机器无法上网,最好使用光盘rpm包安装,一般需要安装三个文件: vi /etc/snmp/snmpd.conf net-snmp lm_sensors_libs

    2024年02月05日
    浏览(43)
  • 华为路由器:RIP路由协议V2详细命令简单配置

    提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 软件:eNSP 环境:Win 10 路由信息协议(RIP) 是一种距离矢量协议,这表示它根据跳数来判断到达目标的最佳路由,但16跳不可达,有一定的局限性。所以仅适用于小型网络。 网络拓扑以及IP地址规划如下:

    2024年02月04日
    浏览(50)
  • Modbus通讯协议常用功能码解释

     Modbus是一种单主站的主/从通讯模式。Modbus网络上只有一个主站,主站在Modbus网络上没有地址,从站的地址范围为0-247,其中0为广播地址,从站的实际地址范围为1-247。 代码 名称 作用 01 读取线圈状态 取得一组逻辑线圈的当前状态(ON/OFF) 02 读取输入状态 取得一组开关输入

    2023年04月08日
    浏览(44)
  • 网络安全中常见的网络协议解释

    多播DNS(mDNS)协议用于在不包含本地名称服务器的 小型网络 中将主机名解析为IP地址 传统的 DNS用于将域名转换为 IP 地址,而 mDNS 则是在局域网环境中使用的一种零配置协议。它使用特定的多播地址(例如 IPv4 的 224.0.0.251 和 IPv6 的 ff02::fb),使设备能够向网络发送广播消息

    2024年02月02日
    浏览(58)
  • MQTT协议图解,一文看懂MQTT协议数据包(真实报文数据解析解释)

    本文主要介绍MQTT协议的结构和具体的2条报文数据解析,帮忙更简单、快速地理解mqtt协议,如果要深入了解实现完整的协议,可以查看文章最后的完整协议文档做更深入的研究。 MQTT协议在lot领域是使用的最广泛的通用协议,在一般企业级物联网产品开发中,通常会考虑的协

    2023年04月16日
    浏览(43)
  • 通过DNS数据包解释DNS协议各个字段含义

    通常来说,想要对DNS最为权威和全面的定义见RFC文档,这里。但是本文不是一篇面面俱到的DNS说明文档,是从使用示例出发,使用wireshark对于一个具体的DNS请求进行分析和呈现,并介绍DNS中最常见的一些字段的含义。 域名的出现是为了方便人们记住网络中的公开服务的地址,

    2024年02月09日
    浏览(41)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包