Uniswap-v2 Factory合约分析
原文发布在 https://github.com/33357/smartcontract-apps这是一个面向中文社区,分析市面上智能合约应用的架构与实现的仓库。欢迎关注开源知识项目!
Factory 合约是 Uniswap-v2 用来创建资金池的合约,通过分析它可以深入了解 Uniswap-v2 的运行逻辑。
演示代码仓库:https://github.com/33357/uniswap-v2-contract。
公共函数(合约内外部都可以调用)
代码速浏览
constructor(address _feeToSetter) public {
feeToSetter = _feeToSetter;
}
参数分析
函数 constructor
的入参有1个,出参有0个,对应的解释如下:
constructor(
address _feeToSetter // 手续费管理员地址
) public {
...
}
在合约初始化时,需要在函数 constructor
中传入 _feeToSetter
,该参数设置了手续费管理员地址。
实现分析
...
{
// 设置手续费管理员地址
feeToSetter = _feeToSetter;
}
总结
Factory 合约初始化时,需要传入手续费管理员地址。
外部函数(仅合约外部可以调用)
代码速浏览
function allPairsLength() external view returns (uint) {
return allPairs.length;
}
参数分析
函数 allPairsLength
的入参有0个,出参有1个,对应的解释如下:
function allPairsLength() external view returns (
uint // 资金池的数量
) {
...
}
函数 allPairsLength
返回了 allPairs
的长度,也就是当前资金池的数量。
实现分析
...
{
// 返回资金池数组的长度
return allPairs.length;
}
总结
获取 allPairs 需要输入 index,因此需要获取资金池数组。
代码速浏览
function createPair(address tokenA, address tokenB) external returns (address pair) {
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');
bytes memory bytecode = type(UniswapV2Pair).creationCode;
bytes32 salt = keccak256(abi.encodePacked(token0, token1));
assembly {
pair := create2(0, add(bytecode, 32), mload(bytecode), salt)
}
IUniswapV2Pair(pair).initialize(token0, token1);
getPair[token0][token1] = pair;
getPair[token1][token0] = pair;
allPairs.push(pair);
emit PairCreated(token0, token1, pair, allPairs.length);
}
参数分析
函数 createPair
的入参有2个,出参有1个,对应的解释如下:
function createPair(
address tokenA, // tokenA 的地址
address tokenB // tokenB 的地址
) external returns (
address pair // 资金池地址
) {
...
}
函数 createPair
可以创建新的资金池合约,需要传入 tokenA
和 tokenB
,返回资金池地址 pair
。
实现分析
...
{
// 需要 tokenA 不等于 tokenB
require(tokenA != tokenB, 'UniswapV2: IDENTICAL_ADDRESSES');
// 计算 tokenA 和 tokenB 中谁是 token0 和 token1
(address token0, address token1) = tokenA < tokenB ? (tokenA, tokenB) : (tokenB, tokenA);
// 需要 token0 不是全0地址
require(token0 != address(0), 'UniswapV2: ZERO_ADDRESS');
// 需要 token0 和 token1 没有创建过资金池
require(getPair[token0][token1] == address(0), 'UniswapV2: PAIR_EXISTS');
// 获取 UniswapV2Pair 合约的字节码
bytes memory bytecode = type(UniswapV2Pair).creationCode;
// 使用参数 token0, token1 计算 salt
bytes32 salt = keccak256(abi.encodePacked(token0, token1));
// 使用 create2 部署 Pair 合约
assembly {
pair := create2(0, add(bytecode, 32), mload(bytecode), salt)
}
// Pair 合约初始化
IUniswapV2Pair(pair).initialize(token0, token1);
// 记录 token0,token1 创建的资金池地址是 pair
getPair[token0][token1] = pair;
getPair[token1][token0] = pair;
// pair 加入资金池数组
allPairs.push(pair);
// 触发资金池创建事件
emit PairCreated(token0, token1, pair, allPairs.length);
}
总结
create2(0, add(bytecode, 32), mload(bytecode), salt)
中的四个参数意思分别是:创建合约发送的 ETH 数量、bytecode 起始位置、bytecode 长度、生成合约地址的随机盐值。
外部函数(仅合约外部可以调用)
代码速浏览
function setFeeTo(address _feeTo) external {
require(msg.sender == feeToSetter, 'UniswapV2: FORBIDDEN');
feeTo = _feeTo;
}
参数分析
函数 setFeeTo
的入参有1个,出参有0个,对应的解释如下:
function setFeeTo(
address _feeTo // 手续费接收地址
) external {
...
}
函数 setFeeTo
可以设置手续费接收地址,但需要手续费管理员调用才能成功。
实现分析
...
{
// 检查调用者是否是手续费管理员
require(msg.sender == feeToSetter, 'UniswapV2: FORBIDDEN');
// 设置手续费接收地址
feeTo = _feeTo;
}
总结
只有手续费管理员才能设置手续费接收地址。
代码速浏览
function setFeeToSetter(address _feeToSetter) external {
require(msg.sender == feeToSetter, 'UniswapV2: FORBIDDEN');
feeToSetter = _feeToSetter;
}
参数分析
函数 setFeeToSetter
的入参有1个,出参有0个,对应的解释如下:
function setFeeToSetter(
address _feeToSetter // 手续费管理员地址
) external {
...
}
函数 setFeeToSetter
可以设置手续费管理员地址,但需要手续费管理员调用才能成功。
实现分析
...
{
// 检查调用者是否是手续费管理员
require(msg.sender == feeToSetter, 'UniswapV2: FORBIDDEN');
// 设置手续费管理员地址
feeToSetter = _feeToSetter;
}
总结
只有手续费管理员才能设置手续费管理员地址。
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!