Uniswap V2 工厂合约(Factory)

  • 曲弯
  • 发布于 15小时前
  • 阅读 31

1.概述UniswapV2工厂合约是UniswapV2去中心化交易所的核心组件,负责创建、管理及查询ERC20代币交易对。本合约采用了CREATE2确定性部署机制,确保同一对代币在不同网络和部署中产生相同的合约地址,为前端路由和合约间互操作性提供了基础保证。1.1核心特性

<!--StartFragment-->

1. 概述

Uniswap V2 工厂合约是 Uniswap V2 去中心化交易所的核心组件,负责创建、管理及查询 ERC20 代币交易对。本合约采用了 CREATE2 确定性部署机制,确保同一对代币在不同网络和部署中产生相同的合约地址,为前端路由和合约间互操作性提供了基础保证。

1.1 核心特性

  • 确定性部署:通过 CREATE2 和固定字节码保证 Pair 地址确定性
  • 协议费用机制:可选的协议费用开关,支持协议可持续发展
  • 高效查询:双向映射支持 O(1) 时间复杂度的代币对查询
  • 权限控制:分层权限管理,分离 Pair 创建和协议管理权限

2. 合约架构

2.1 合约继承关系

IUniswapV2Factory (接口)
    ↓
UniswapV2Factory (实现)

2.2 状态变量说明

变量名 类型 可见性 描述
feeTo address public 协议费用接收地址,address(0) 表示关闭协议费
feeToSetter address public 设置 feeTo 的权限地址
getPair mapping(address => mapping(address => address)) public 双向代币对查询映射
allPairs address[] public 所有已创建 Pair 的地址数组

3. 核心机制详解

3.1 代币对地址确定性算法

3.1.1 地址排序

为确保交易对地址与代币输入顺序无关,合约在创建 Pair 前执行地址排序:

// 确保 token0 始终是地址较小的代币
(address token0, address token1) = tokenA &lt; tokenB ? 
    (tokenA, tokenB) : (tokenB, tokenA);

3.1.2 CREATE2 确定性部署

Pair 合约通过 CREATE2 操作码部署,其地址计算遵循以下公式:

pair_address = keccak256(0xFF, factory_address, salt, keccak256(creation_code))[12:]

其中:

  • factory_address: 当前工厂合约地址
  • salt: keccak256(abi.encodePacked(token0, token1))
  • creation_code: UniswapV2Pair合约的完整创建字节码

3.1.3 实现代码

bytes memory bytecode = type(UniswapV2Pair).creationCode;
bytes32 salt = keccak256(abi.encodePacked(token0, token1));

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

3.2 协议费用机制

3.2.1 费用开关

协议费用通过 feeTo地址控制:

  • feeTo = address(0)时:不收取协议费用
  • feeTo ≠ address(0)时:收取 0.05% 的协议费用

3.2.2 费用计算与分配

费用在 Pair 合约的 _mintFee函数中计算,具体逻辑如下:

// 伪代码:Pair 合约中的 _mintFee 函数
function _mintFee() private returns (bool) {
    if (feeTo == address(0)) return false;

    // 计算应铸造给 feeTo 的 LP 数量
    uint256 liquidity = (totalSupply * 1) / 6;  // 1/6 的增发量

    if (liquidity > 0) {
        _mint(feeTo, liquidity);
        return true;
    }
    return false;
}

3.2.3 权限管理

  • setFeeTo(address _feeTo):仅 feeToSetter可调用
  • setFeeToSetter(address _feeToSetter):转移设置权限

4. 关键方法实现

4.1 createPair方法

创建新的代币交易对,完整流程如下:

function createPair(address tokenA, address tokenB) external returns (address pair) {
    // 1. 输入验证
    require(tokenA != tokenB, 'UniswapV2: IDENTICAL_ADDRESSES');
    (address token0, address token1) = tokenA &lt; tokenB ? 
        (tokenA, tokenB) : (tokenB, tokenA);
    require(token0 != address(0), 'UniswapV2: ZERO_ADDRESS');
    require(getPair[token0][token1] == address(0), 'UniswapV2: PAIR_EXISTS');

    // 2. 确定性部署 Pair 合约
    bytes memory bytecode = type(UniswapV2Pair).creationCode;
    bytes32 salt = keccak256(abi.encodePacked(token0, token1));
    assembly {
        pair := create2(0, add(bytecode, 32), mload(bytecode), salt)
    }

    // 3. 初始化 Pair
    IUniswapV2Pair(pair).initialize(token0, token1);

    // 4. 更新存储
    getPair[token0][token1] = pair;
    getPair[token1][token0] = pair;  // 双向映射
    allPairs.push(pair);

    // 5. 触发事件
    emit PairCreated(token0, token1, pair, allPairs.length);
}

4.2 查询方法

4.2.1 allPairsLength

function allPairsLength() external view returns (uint) {
    return allPairs.length;
}

4.2.2 getPair映射

提供两种查询方式:

  • getPair[token0][token1](token0 < token1)
  • getPair[token1][token0](反向查询)

5. 事件系统

5.1 PairCreated事件

event PairCreated(
    address indexed token0,  // 较小的代币地址
    address indexed token1,  // 较大的代币地址
    address pair,            // 新创建的 Pair 合约地址
    uint pairCount           // 当前总 Pair 数量
);

事件用途

  • 前端 DApp 监听新交易对创建
  • 索引服务更新代币对数据库
  • 链下分析工具记录创建历史

6. 安全设计与验证

6.1 输入验证

验证条件 错误信息 目的
tokenA != tokenB IDENTICAL_ADDRESSES 禁止相同代币创建交易对
token0 != address(0) ZERO_ADDRESS 禁止零地址代币
getPair[token0][token1] == address(0) PAIR_EXISTS 禁止重复创建

6.2 权限验证

// 仅 feeToSetter 可设置 feeTo
require(msg.sender == feeToSetter, 'UniswapV2: FORBIDDEN');

6.3 重入攻击防护

  • 无外部调用后状态修改(checks-effects-interactions 模式)
  • CREATE2 操作原子性保证

7. 与外围系统集成

7.1 前端路由计算

前端通过 pairFor函数预先计算 Pair 地址:

// Uniswap V2 periphery 库中的实现
function pairFor(address factory, address tokenA, address tokenB) 
    internal pure returns (address pair) {

    (address token0, address token1) = 
        tokenA &lt; tokenB ? (tokenA, tokenB) : (tokenB, tokenA);

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

hex'96e8ac...'是 UniswapV2Pair 创建字节码的 keccak256 哈希。

7.2 多链部署兼容性

由于 CREATE2 的确定性,同一对代币在不同链上(如果工厂部署相同)会有:

  • 相同的 Pair 合约地址
  • 相同的合约字节码
  • 不同的状态数据(流动性、价格等)

8. 使用示例

8.1 创建新交易对

// 假设已有工厂实例
IUniswapV2Factory factory = IUniswapV2Factory(0x5C69bEe...);

// 创建 WETH-DAI 交易对
address weth = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2;
address dai = 0x6B175474E89094C44Da98b954EedeAC495271d0F;

address pair = factory.createPair(weth, dai);

8.2 查询交易对信息

// 查询特定交易对
address pair = factory.getPair(weth, dai);

// 获取所有交易对数量
uint pairCount = factory.allPairsLength();

// 遍历所有交易对
for (uint i = 0; i &lt; pairCount; i++) {
    address pairAddress = factory.allPairs(i);
    // 与 Pair 合约交互...
}

8.3 管理协议费用

// 仅 feeToSetter 可执行
factory.setFeeTo(0x0000...);  // 关闭协议费
factory.setFeeTo(0x1234...);  // 设置协议费接收地址

// 转移 feeToSetter 权限
factory.setFeeToSetter(0x5678...);

<!--EndFragment-->

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

0 条评论

请先 登录 后评论
曲弯
曲弯
0xb51E...CADb
Don't give up if you love it. If you don't, then that's not good either, because one shouldn't do things they don't enjoy.