1.概述UniswapV2工厂合约是UniswapV2去中心化交易所的核心组件,负责创建、管理及查询ERC20代币交易对。本合约采用了CREATE2确定性部署机制,确保同一对代币在不同网络和部署中产生相同的合约地址,为前端路由和合约间互操作性提供了基础保证。1.1核心特性
<!--StartFragment-->
Uniswap V2 工厂合约是 Uniswap V2 去中心化交易所的核心组件,负责创建、管理及查询 ERC20 代币交易对。本合约采用了 CREATE2 确定性部署机制,确保同一对代币在不同网络和部署中产生相同的合约地址,为前端路由和合约间互操作性提供了基础保证。
IUniswapV2Factory (接口)
↓
UniswapV2Factory (实现)
| 变量名 | 类型 | 可见性 | 描述 |
|---|---|---|---|
feeTo |
address |
public | 协议费用接收地址,address(0) 表示关闭协议费 |
feeToSetter |
address |
public | 设置 feeTo 的权限地址 |
getPair |
mapping(address => mapping(address => address)) |
public | 双向代币对查询映射 |
allPairs |
address[] |
public | 所有已创建 Pair 的地址数组 |
为确保交易对地址与代币输入顺序无关,合约在创建 Pair 前执行地址排序:
// 确保 token0 始终是地址较小的代币
(address token0, address token1) = tokenA < tokenB ?
(tokenA, tokenB) : (tokenB, tokenA);
Pair 合约通过 CREATE2 操作码部署,其地址计算遵循以下公式:
pair_address = keccak256(0xFF, factory_address, salt, keccak256(creation_code))[12:]
其中:
keccak256(abi.encodePacked(token0, token1))UniswapV2Pair合约的完整创建字节码bytes memory bytecode = type(UniswapV2Pair).creationCode;
bytes32 salt = keccak256(abi.encodePacked(token0, token1));
assembly {
pair := create2(0, add(bytecode, 32), mload(bytecode), salt)
}
协议费用通过 feeTo地址控制:
feeTo = address(0)时:不收取协议费用feeTo ≠ address(0)时:收取 0.05% 的协议费用费用在 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;
}
setFeeTo(address _feeTo):仅 feeToSetter可调用setFeeToSetter(address _feeToSetter):转移设置权限createPair方法创建新的代币交易对,完整流程如下:
function createPair(address tokenA, address tokenB) external returns (address pair) {
// 1. 输入验证
require(tokenA != tokenB, 'UniswapV2: IDENTICAL_ADDRESSES');
(address token0, address token1) = tokenA < 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);
}
allPairsLengthfunction allPairsLength() external view returns (uint) {
return allPairs.length;
}
getPair映射提供两种查询方式:
getPair[token0][token1](token0 < token1)getPair[token1][token0](反向查询)PairCreated事件event PairCreated(
address indexed token0, // 较小的代币地址
address indexed token1, // 较大的代币地址
address pair, // 新创建的 Pair 合约地址
uint pairCount // 当前总 Pair 数量
);
事件用途:
| 验证条件 | 错误信息 | 目的 |
|---|---|---|
tokenA != tokenB |
IDENTICAL_ADDRESSES |
禁止相同代币创建交易对 |
token0 != address(0) |
ZERO_ADDRESS |
禁止零地址代币 |
getPair[token0][token1] == address(0) |
PAIR_EXISTS |
禁止重复创建 |
// 仅 feeToSetter 可设置 feeTo
require(msg.sender == feeToSetter, 'UniswapV2: FORBIDDEN');
前端通过 pairFor函数预先计算 Pair 地址:
// Uniswap V2 periphery 库中的实现
function pairFor(address factory, address tokenA, address tokenB)
internal pure returns (address pair) {
(address token0, address token1) =
tokenA < 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 哈希。
由于 CREATE2 的确定性,同一对代币在不同链上(如果工厂部署相同)会有:
// 假设已有工厂实例
IUniswapV2Factory factory = IUniswapV2Factory(0x5C69bEe...);
// 创建 WETH-DAI 交易对
address weth = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2;
address dai = 0x6B175474E89094C44Da98b954EedeAC495271d0F;
address pair = factory.createPair(weth, dai);
// 查询特定交易对
address pair = factory.getPair(weth, dai);
// 获取所有交易对数量
uint pairCount = factory.allPairsLength();
// 遍历所有交易对
for (uint i = 0; i < pairCount; i++) {
address pairAddress = factory.allPairs(i);
// 与 Pair 合约交互...
}
// 仅 feeToSetter 可执行
factory.setFeeTo(0x0000...); // 关闭协议费
factory.setFeeTo(0x1234...); // 设置协议费接收地址
// 转移 feeToSetter 权限
factory.setFeeToSetter(0x5678...);
<!--EndFragment-->
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!