Solidity Uniswap V2 factory contract

 factory contract

        工厂合约是所有已部署pair合约的注册表。我们不希望出现相同的pair,这样流动性就不会被分割成多个。factory合约还简化了pair合约的部署:无需手动部署pair合约,只需调用factory合约中的方法即可。

        https://github.com/XuHugo/solidityproject/tree/master/uniswapv2xq

        Uniswap 只部署了一个factory合约,该合约是 Uniswap 的官方注册机构。这对发现pair也很有用:人们可以通过token地址查询合约,找到pair。此外,还可以通过扫描合约的历史事件来查找所有已部署的pair。当然,我们也可以手动部署pair,而不在factory合约中注册。

contract ZuniswapV2Factory {

    error IdenticalAddresses();

    error PairExists();

    error ZeroAddress();



    event PairCreated(

        address indexed token0,

        address indexed token1,

        address pair,

        uint256

    );



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

    address[] public allPairs;

...

        工厂合约非常简洁明了:它只在创建pair时发出 PairCreated 事件,并存储所有已创建pair的列表和映射。

不过,创建pair很麻烦:

function createPair(address tokenA, address tokenB)

  public

  returns (address pair)

{

  if (tokenA == tokenB) revert IdenticalAddresses();



  (address token0, address token1) = tokenA < tokenB

    ? (tokenA, tokenB)

    : (tokenB, tokenA);



  if (token0 == address(0)) revert ZeroAddress();



  if (pairs[token0][token1] != address(0)) revert PairExists();



  bytes memory bytecode = type(ZuniswapV2Pair).creationCode;

  bytes32 salt = keccak256(abi.encodePacked(token0, token1));

  assembly {

    pair := create2(0, add(bytecode, 32), mload(bytecode), salt)

  }



  IZuniswapV2Pair(pair).initialize(token0, token1);



  pairs[token0][token1] = pair;

  pairs[token1][token0] = pair;

  allPairs.push(pair);



  emit PairCreated(token0, token1, pair, allPairs.length);

}

        首先,我们不允许pair使用相同的Token。请注意,我们并不检查Token合约是否真的存在,我们并不关心,因为这取决于用户是否提供有效的 ERC20Token地址。

        接下来,我们对Token地址进行排序,这对于避免重复很重要(pair合约允许双向交换)。此外,token地址还用于生成pair地址,我们将在下一节中讨论。

使用create2 创建合约

        在以太坊中,合约可以部署合约。人们可以调用已部署合约的函数,该函数将部署另一个合约,这使得部署合约变得更加容易。你不需要从电脑上编译和部署一个合同,你可以通过现有的合同来完成。

在 EVM 中,有两个操作码可以部署合约:

        1、CREATE,从一开始就存在于 EVM 中。此操作码创建一个新账户,并在此地址部署合约代码。新地址是根据部署者合约的 nonce 计算出来的,这与手动部署合约时确定合约地址的方式相同。Nonce 是地址成功交易的计数器:发送交易时,nonce 会增加。生成新账户地址时对 nonce 的依赖使得 CREATE 具有非确定性:地址取决于部署者合约的 nonce,而这是无法控制的。你可以知道它,但当你部署你的合约时,nonce 可能已经不同了。

        2、CREATE2 在 EIP-1014 中添加。该操作码的作用与 CREATE 完全相同,但它允许确定性地生成新合约地址。CREATE2 不使用外部状态(如其他合约的 nonce)来生成合约地址,并允许我们完全控制地址的生成方式。你不需要知道 nonce,只需要知道已部署的合约字节码(它是静态的)和盐(它是由你选择的字节序列)。

...

bytes memory bytecode = type(ZuniswapV2Pair).creationCode;

bytes32 salt = keccak256(abi.encodePacked(token0, token1));

assembly {

    pair := create2(0, add(bytecode, 32), mload(bytecode), salt)

}

...

        第一行是 ZuniswapV2Pair 合约的创建字节码。创建字节码是实际的智能合约字节码。它包括

        1、构造逻辑。这部分负责智能合约的初始化和部署。它不存储在区块链上。

        2、运行时字节码,即合约的实际业务逻辑。正是这些字节码存储在以太坊区块链上。

        我们希望在此使用完整的字节码。

        下一行创建salt,这是一个字节序列,用于确定性地生成新合约的地址。我们通过散列一对token的地址来创建salt,这意味着每一对独一无二的token都会产生一个独一无二的salt,每一对令牌都会有独一无二的salt和地址。

        最后一行是我们调用 create2 的地方:

        1、使用字节码 + 盐,确定性地创建一个新地址。

        2、部署一个新的 ZuniswapV2Pair 合约。

        3、获取该pair的地址。

// ZuniswapV2Pair.sol

function initialize(address token0_, address token1_) public {

  if (token0 != address(0) || token1 != address(0))

    revert AlreadyInitialized();



  token0 = token0_;

  token1 = token1_;

}

        createPair 的其余部分应该很清楚:

        1、部署配对后,我们需要对其进行初始化,简单地说就是设置其标记:

        2、然后,新的pair将存储在pairs和 allPairs 数组中。

        3、最后,我们就可以发出 PairCreated 事件了。

  • 7
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

0xweb3q

有钱的捧个钱场,没钱的捧个人场

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值