Compare commits
10 Commits
6667730b74
...
3586e648b2
| Author | SHA1 | Date | |
|---|---|---|---|
| 3586e648b2 | |||
| ee547b1785 | |||
| b48663b1b5 | |||
| 8b82b04a0b | |||
| 4dd59067c7 | |||
| 816b9e3271 | |||
| 8d6bfb3473 | |||
| 5419f5409d | |||
| 816075049f | |||
| 5e59a166de |
25
.github/stale.yml
vendored
Normal file
25
.github/stale.yml
vendored
Normal file
@ -0,0 +1,25 @@
|
||||
# Configuration for probot-stale - https://github.com/probot/stale
|
||||
|
||||
issues:
|
||||
# Number of days of inactivity before an Issue or Pull Request becomes stale
|
||||
daysUntilStale: 7
|
||||
|
||||
# Number of days of inactivity before an Issue or Pull Request with the stale label is closed.
|
||||
# Set to false to disable. If disabled, issues still need to be closed manually, but will remain marked as stale.
|
||||
daysUntilClose: 7
|
||||
|
||||
# Only issues or pull requests with all of these labels are check if stale. Defaults to `[]` (disabled)
|
||||
onlyLabels:
|
||||
- question
|
||||
- autoclose
|
||||
|
||||
# Issues or Pull Requests with these labels will never be considered stale. Set to `[]` to disable
|
||||
exemptLabels:
|
||||
- p0
|
||||
- bug
|
||||
|
||||
# Comment to post when marking as stale. Set to `false` to disable
|
||||
markComment: >
|
||||
This issue has been automatically marked as stale because it has not had
|
||||
recent activity. It will be closed if no further activity occurs. Thank you
|
||||
for your contributions.
|
||||
@ -9,7 +9,8 @@
|
||||
"evm.deployedBytecode.object",
|
||||
"abi",
|
||||
"evm.bytecode.sourceMap",
|
||||
"evm.deployedBytecode.sourceMap"
|
||||
"evm.deployedBytecode.sourceMap",
|
||||
"metadata"
|
||||
],
|
||||
"": ["ast"]
|
||||
}
|
||||
|
||||
15
CITATION.cff
Normal file
15
CITATION.cff
Normal file
@ -0,0 +1,15 @@
|
||||
cff-version: 1.2.0
|
||||
message: "If you use this software, please cite it as below."
|
||||
authors:
|
||||
- family-names: "Zinsmeister"
|
||||
given-names: "Noah"
|
||||
- family-names: "Adams"
|
||||
given-names: "Hayden"
|
||||
- family-names: "Robinson"
|
||||
given-names: "Dan"
|
||||
- family-names: "Salem"
|
||||
given-names: "Moody"
|
||||
title: "v2-core"
|
||||
version: 1.0.1
|
||||
date-released: 2019-05-03
|
||||
url: "https://github.com/Uniswap/v2-core"
|
||||
268
README.md
268
README.md
@ -1,24 +1,270 @@
|
||||
# Uniswap V2
|
||||
# Uniswap V2 Core 核心合约
|
||||
|
||||
[](https://github.com/Uniswap/uniswap-v2-core/actions)
|
||||
[](https://www.npmjs.com/package/@uniswap/v2-core)
|
||||
|
||||
In-depth documentation on Uniswap V2 is available at [uniswap.org](https://uniswap.org/docs).
|
||||
## 📋 项目概述
|
||||
|
||||
The built contract artifacts can be browsed via [unpkg.com](https://unpkg.com/browse/@uniswap/v2-core@latest/).
|
||||
Uniswap V2 Core 是去中心化交易协议 Uniswap V2 的核心智能合约集合,实现了自动化做市商(AMM)机制。本项目包含了创建和管理流动性池、执行代币交换等核心功能的智能合约。
|
||||
|
||||
# Local Development
|
||||
### 🎯 核心特性
|
||||
|
||||
The following assumes the use of `node@>=10`.
|
||||
- **自动化做市商(AMM)**: 基于恒定乘积公式 `x * y = k` 的去中心化交易
|
||||
- **流动性挖矿**: 用户可以提供流动性获得手续费收入
|
||||
- **CREATE2 部署**: 使用确定性地址部署,便于前端集成
|
||||
- **EIP-712 支持**: 支持链下签名授权,提升用户体验
|
||||
- **时间加权平均价格(TWAP)**: 内置价格预言机功能
|
||||
|
||||
## Install Dependencies
|
||||
## 🏗️ 项目架构
|
||||
|
||||
`yarn`
|
||||
### 核心合约
|
||||
|
||||
## Compile Contracts
|
||||
```
|
||||
contracts/
|
||||
├── UniswapV2Factory.sol # 工厂合约,管理所有交易对的创建
|
||||
├── UniswapV2Pair.sol # 交易对合约,实现AMM核心逻辑
|
||||
├── UniswapV2ERC20.sol # 流动性代币,支持EIP-712
|
||||
├── interfaces/ # 接口定义
|
||||
│ ├── IUniswapV2Factory.sol
|
||||
│ ├── IUniswapV2Pair.sol
|
||||
│ ├── IUniswapV2ERC20.sol
|
||||
│ ├── IERC20.sol
|
||||
│ └── IUniswapV2Callee.sol
|
||||
├── libraries/ # 工具库
|
||||
│ ├── Math.sol # 数学运算库
|
||||
│ ├── SafeMath.sol # 安全数学运算
|
||||
│ └── UQ112x112.sol # 定点数运算
|
||||
└── test/ # 测试合约
|
||||
```
|
||||
|
||||
`yarn compile`
|
||||
### 合约关系图
|
||||
|
||||
## Run Tests
|
||||
```mermaid
|
||||
graph TB
|
||||
Factory[UniswapV2Factory<br/>工厂合约] --> Pair[UniswapV2Pair<br/>交易对合约]
|
||||
Pair --> ERC20[UniswapV2ERC20<br/>流动性代币]
|
||||
Pair --> SafeMath[SafeMath<br/>安全数学运算]
|
||||
Pair --> Math[Math<br/>数学库]
|
||||
Pair --> UQ112x112[UQ112x112<br/>定点数库]
|
||||
|
||||
User[用户] --> Factory
|
||||
User --> Pair
|
||||
LP[流动性提供者] --> Pair
|
||||
Trader[交易者] --> Pair
|
||||
```
|
||||
|
||||
`yarn test`
|
||||
## 🔧 核心概念
|
||||
|
||||
### 1. 自动化做市商(AMM)
|
||||
|
||||
Uniswap V2 使用恒定乘积公式实现自动化做市:
|
||||
|
||||
```
|
||||
x * y = k
|
||||
```
|
||||
|
||||
其中:
|
||||
- `x` 和 `y` 是池中两种代币的数量
|
||||
- `k` 是恒定值,只在添加/移除流动性时改变
|
||||
|
||||
### 2. 流动性提供
|
||||
|
||||
用户可以向交易对提供流动性,获得:
|
||||
- **流动性代币(LP Token)**: 代表在池中的份额
|
||||
- **交易手续费**: 每笔交易的0.3%手续费按比例分配
|
||||
|
||||
流动性计算公式:
|
||||
```solidity
|
||||
// 首次添加流动性
|
||||
liquidity = sqrt(amount0 * amount1) - MINIMUM_LIQUIDITY
|
||||
|
||||
// 后续添加流动性
|
||||
liquidity = min(amount0 * totalSupply / reserve0, amount1 * totalSupply / reserve1)
|
||||
```
|
||||
|
||||
### 3. 价格发现机制
|
||||
|
||||
交易价格由池中代币比例决定:
|
||||
```
|
||||
price = reserve1 / reserve0
|
||||
```
|
||||
|
||||
每次交易后,价格会自动调整以维持恒定乘积。
|
||||
|
||||
### 4. 时间加权平均价格(TWAP)
|
||||
|
||||
合约内置价格累积器,可以计算任意时间段的平均价格:
|
||||
```solidity
|
||||
price0CumulativeLast += (reserve1 / reserve0) * timeElapsed
|
||||
price1CumulativeLast += (reserve0 / reserve1) * timeElapsed
|
||||
```
|
||||
|
||||
## 💼 主要功能
|
||||
|
||||
### UniswapV2Factory (工厂合约)
|
||||
|
||||
**职责**: 创建和管理所有交易对
|
||||
|
||||
**核心函数**:
|
||||
- `createPair(address tokenA, address tokenB)`: 创建新的交易对
|
||||
- `getPair(address tokenA, address tokenB)`: 获取交易对地址
|
||||
- `allPairs(uint index)`: 按索引获取交易对
|
||||
- `setFeeTo(address feeTo)`: 设置协议费接收地址
|
||||
|
||||
**特点**:
|
||||
- 使用CREATE2确定性部署,地址可预测
|
||||
- 任何人都可以创建交易对
|
||||
- 自动排序代币地址确保唯一性
|
||||
|
||||
### UniswapV2Pair (交易对合约)
|
||||
|
||||
**职责**: 实现具体的AMM交易逻辑
|
||||
|
||||
**核心函数**:
|
||||
- `mint(address to)`: 铸造流动性代币
|
||||
- `burn(address to)`: 销毁流动性代币,取回资产
|
||||
- `swap(uint amount0Out, uint amount1Out, address to, bytes calldata data)`: 执行代币交换
|
||||
- `getReserves()`: 获取当前储备量和时间戳
|
||||
|
||||
**安全机制**:
|
||||
- 重入锁防止重入攻击
|
||||
- 最小流动性锁定防止攻击
|
||||
- 完整性检查确保K值不减少
|
||||
|
||||
### UniswapV2ERC20 (流动性代币)
|
||||
|
||||
**职责**: 标准ERC20流动性代币,支持高级功能
|
||||
|
||||
**核心功能**:
|
||||
- 标准ERC20功能 (转账、授权等)
|
||||
- EIP-712签名授权 (`permit`)
|
||||
- 域分隔符确保跨链安全
|
||||
|
||||
## 🚀 快速开始
|
||||
|
||||
### 环境要求
|
||||
|
||||
- Node.js >= 10
|
||||
- Yarn 包管理器
|
||||
|
||||
### 安装依赖
|
||||
|
||||
```bash
|
||||
yarn install
|
||||
```
|
||||
|
||||
### 编译合约
|
||||
|
||||
```bash
|
||||
yarn compile
|
||||
```
|
||||
|
||||
### 运行测试
|
||||
|
||||
```bash
|
||||
yarn test
|
||||
```
|
||||
|
||||
### 代码检查
|
||||
|
||||
```bash
|
||||
yarn lint
|
||||
```
|
||||
|
||||
## 📖 使用示例
|
||||
|
||||
### 1. 部署工厂合约
|
||||
|
||||
```solidity
|
||||
// 部署工厂合约
|
||||
UniswapV2Factory factory = new UniswapV2Factory(msg.sender);
|
||||
```
|
||||
|
||||
### 2. 创建交易对
|
||||
|
||||
```solidity
|
||||
// 创建 WETH/USDC 交易对
|
||||
address pair = factory.createPair(WETH, USDC);
|
||||
```
|
||||
|
||||
### 3. 添加流动性
|
||||
|
||||
```solidity
|
||||
// 向交易对转入代币
|
||||
IERC20(WETH).transfer(pair, wethAmount);
|
||||
IERC20(USDC).transfer(pair, usdcAmount);
|
||||
|
||||
// 铸造流动性代币
|
||||
uint liquidity = IUniswapV2Pair(pair).mint(msg.sender);
|
||||
```
|
||||
|
||||
### 4. 执行交换
|
||||
|
||||
```solidity
|
||||
// 计算输出数量 (需要使用路由合约)
|
||||
uint amountOut = getAmountOut(amountIn, reserveIn, reserveOut);
|
||||
|
||||
// 转入代币
|
||||
IERC20(tokenIn).transfer(pair, amountIn);
|
||||
|
||||
// 执行交换
|
||||
IUniswapV2Pair(pair).swap(amount0Out, amount1Out, msg.sender, "");
|
||||
```
|
||||
|
||||
## 🔐 安全考虑
|
||||
|
||||
### 重入攻击防护
|
||||
- 所有状态修改函数都使用重入锁
|
||||
- 采用检查-生效-交互模式
|
||||
|
||||
### 整数溢出防护
|
||||
- 使用SafeMath库防止算术溢出
|
||||
- 严格的边界检查
|
||||
|
||||
### 经济安全
|
||||
- 最小流动性锁定(1000 wei)
|
||||
- K值单调性检查
|
||||
- 合理的手续费机制
|
||||
|
||||
### 前端安全
|
||||
- CREATE2确定性地址
|
||||
- 完整的事件日志
|
||||
- 标准化接口
|
||||
|
||||
## 📚 深入学习
|
||||
|
||||
### 相关资源
|
||||
|
||||
- [Uniswap V2 白皮书](https://uniswap.org/whitepaper.pdf)
|
||||
- [官方文档](https://uniswap.org/docs)
|
||||
- [Solidity 最佳实践](https://consensys.github.io/smart-contract-best-practices/)
|
||||
|
||||
### 构建工件
|
||||
|
||||
编译后的合约可通过 [unpkg.com](https://unpkg.com/browse/@uniswap/v2-core@latest/) 浏览。
|
||||
|
||||
## 🤝 贡献指南
|
||||
|
||||
欢迎提交 Issue 和 Pull Request!
|
||||
|
||||
### 开发流程
|
||||
|
||||
1. Fork 本仓库
|
||||
2. 创建功能分支
|
||||
3. 提交更改
|
||||
4. 运行测试
|
||||
5. 提交 Pull Request
|
||||
|
||||
## 📄 许可证
|
||||
|
||||
本项目采用 [GPL-3.0-or-later](LICENSE) 许可证。
|
||||
|
||||
## 🙏 致谢
|
||||
|
||||
- Uniswap 团队的创新设计
|
||||
- DappHub 的 SafeMath 库
|
||||
- 以太坊社区的支持
|
||||
|
||||
---
|
||||
|
||||
**⚠️ 风险提示**: 本项目仅供学习和研究使用。在生产环境中使用前,请进行充分的安全审计。去中心化金融存在智能合约风险、无常损失等风险,请谨慎参与。
|
||||
|
||||
@ -3,29 +3,42 @@ pragma solidity =0.5.16;
|
||||
import './interfaces/IUniswapV2ERC20.sol';
|
||||
import './libraries/SafeMath.sol';
|
||||
|
||||
/**
|
||||
* @title UniswapV2ERC20 流动性代币合约
|
||||
* @notice 实现ERC20标准的流动性代币,支持EIP-712 permit功能
|
||||
* @dev 为Uniswap V2交易对提供流动性代币功能
|
||||
*/
|
||||
contract UniswapV2ERC20 is IUniswapV2ERC20 {
|
||||
using SafeMath for uint;
|
||||
|
||||
string public constant name = 'Uniswap V2';
|
||||
string public constant symbol = 'UNI-V2';
|
||||
uint8 public constant decimals = 18;
|
||||
uint public totalSupply;
|
||||
mapping(address => uint) public balanceOf;
|
||||
mapping(address => mapping(address => uint)) public allowance;
|
||||
// ERC20标准属性
|
||||
string public constant name = 'Uniswap V2'; // 代币名称
|
||||
string public constant symbol = 'UNI-V2'; // 代币符号
|
||||
uint8 public constant decimals = 18; // 小数位数
|
||||
uint public totalSupply; // 总供应量
|
||||
mapping(address => uint) public balanceOf; // 余额映射
|
||||
mapping(address => mapping(address => uint)) public allowance; // 授权映射
|
||||
|
||||
bytes32 public DOMAIN_SEPARATOR;
|
||||
// keccak256("Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)");
|
||||
// EIP-712相关
|
||||
bytes32 public DOMAIN_SEPARATOR; // 域分隔符
|
||||
// permit函数的类型哈希
|
||||
bytes32 public constant PERMIT_TYPEHASH = 0x6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c9;
|
||||
mapping(address => uint) public nonces;
|
||||
mapping(address => uint) public nonces; // 防重放nonce
|
||||
|
||||
// ERC20标准事件
|
||||
event Approval(address indexed owner, address indexed spender, uint value);
|
||||
event Transfer(address indexed from, address indexed to, uint value);
|
||||
|
||||
/**
|
||||
* @dev 构造函数,初始化EIP-712域分隔符
|
||||
*/
|
||||
constructor() public {
|
||||
uint chainId;
|
||||
// 内联汇编获取链ID
|
||||
assembly {
|
||||
chainId := chainid
|
||||
}
|
||||
// 计算域分隔符
|
||||
DOMAIN_SEPARATOR = keccak256(
|
||||
abi.encode(
|
||||
keccak256('EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)'),
|
||||
@ -37,40 +50,82 @@ contract UniswapV2ERC20 is IUniswapV2ERC20 {
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev 内部函数:铸造代币
|
||||
* @param to 接收地址
|
||||
* @param value 铸造数量
|
||||
*/
|
||||
function _mint(address to, uint value) internal {
|
||||
totalSupply = totalSupply.add(value);
|
||||
balanceOf[to] = balanceOf[to].add(value);
|
||||
emit Transfer(address(0), to, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev 内部函数:销毁代币
|
||||
* @param from 销毁地址
|
||||
* @param value 销毁数量
|
||||
*/
|
||||
function _burn(address from, uint value) internal {
|
||||
balanceOf[from] = balanceOf[from].sub(value);
|
||||
totalSupply = totalSupply.sub(value);
|
||||
emit Transfer(from, address(0), value);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev 内部函数:设置授权
|
||||
* @param owner 授权者
|
||||
* @param spender 被授权者
|
||||
* @param value 授权数量
|
||||
*/
|
||||
function _approve(address owner, address spender, uint value) private {
|
||||
allowance[owner][spender] = value;
|
||||
emit Approval(owner, spender, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev 内部函数:转账
|
||||
* @param from 发送者
|
||||
* @param to 接收者
|
||||
* @param value 转账数量
|
||||
*/
|
||||
function _transfer(address from, address to, uint value) private {
|
||||
balanceOf[from] = balanceOf[from].sub(value);
|
||||
balanceOf[to] = balanceOf[to].add(value);
|
||||
emit Transfer(from, to, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* @notice 授权spender使用value数量的代币
|
||||
* @param spender 被授权地址
|
||||
* @param value 授权数量
|
||||
* @return 是否成功
|
||||
*/
|
||||
function approve(address spender, uint value) external returns (bool) {
|
||||
_approve(msg.sender, spender, value);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @notice 转账代币给to地址
|
||||
* @param to 接收地址
|
||||
* @param value 转账数量
|
||||
* @return 是否成功
|
||||
*/
|
||||
function transfer(address to, uint value) external returns (bool) {
|
||||
_transfer(msg.sender, to, value);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @notice 从from地址转账代币给to地址(需要授权)
|
||||
* @param from 发送地址
|
||||
* @param to 接收地址
|
||||
* @param value 转账数量
|
||||
* @return 是否成功
|
||||
*/
|
||||
function transferFrom(address from, address to, uint value) external returns (bool) {
|
||||
// 如果授权量不是最大值,则减少授权量
|
||||
if (allowance[from][msg.sender] != uint(-1)) {
|
||||
allowance[from][msg.sender] = allowance[from][msg.sender].sub(value);
|
||||
}
|
||||
@ -78,17 +133,33 @@ contract UniswapV2ERC20 is IUniswapV2ERC20 {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @notice EIP-712签名授权,无需发送交易即可设置授权
|
||||
* @param owner 代币所有者
|
||||
* @param spender 被授权者
|
||||
* @param value 授权数量
|
||||
* @param deadline 授权截止时间
|
||||
* @param v 签名参数v
|
||||
* @param r 签名参数r
|
||||
* @param s 签名参数s
|
||||
*/
|
||||
function permit(address owner, address spender, uint value, uint deadline, uint8 v, bytes32 r, bytes32 s) external {
|
||||
require(deadline >= block.timestamp, 'UniswapV2: EXPIRED');
|
||||
|
||||
// 构造EIP-712消息哈希
|
||||
bytes32 digest = keccak256(
|
||||
abi.encodePacked(
|
||||
'\x19\x01',
|
||||
'\x19\x01', // EIP-191前缀
|
||||
DOMAIN_SEPARATOR,
|
||||
keccak256(abi.encode(PERMIT_TYPEHASH, owner, spender, value, nonces[owner]++, deadline))
|
||||
)
|
||||
);
|
||||
|
||||
// 恢复签名者地址
|
||||
address recoveredAddress = ecrecover(digest, v, r, s);
|
||||
require(recoveredAddress != address(0) && recoveredAddress == owner, 'UniswapV2: INVALID_SIGNATURE');
|
||||
|
||||
// 设置授权
|
||||
_approve(owner, spender, value);
|
||||
}
|
||||
}
|
||||
|
||||
@ -3,45 +3,94 @@ pragma solidity =0.5.16;
|
||||
import './interfaces/IUniswapV2Factory.sol';
|
||||
import './UniswapV2Pair.sol';
|
||||
|
||||
/**
|
||||
* @title UniswapV2Factory 工厂合约
|
||||
* @notice Uniswap V2协议的工厂合约,负责创建和管理所有交易对
|
||||
* @dev 使用CREATE2确定性部署,可预测交易对地址
|
||||
*/
|
||||
contract UniswapV2Factory is IUniswapV2Factory {
|
||||
// 协议手续费接收地址
|
||||
address public feeTo;
|
||||
// 有权设置feeTo的地址
|
||||
address public feeToSetter;
|
||||
|
||||
// 双重映射:token地址 => token地址 => 交易对地址
|
||||
mapping(address => mapping(address => address)) public getPair;
|
||||
// 所有交易对地址的数组
|
||||
address[] public allPairs;
|
||||
|
||||
// 创建交易对时触发的事件
|
||||
event PairCreated(address indexed token0, address indexed token1, address pair, uint);
|
||||
|
||||
/**
|
||||
* @dev 构造函数
|
||||
* @param _feeToSetter 初始的手续费设置者地址
|
||||
*/
|
||||
constructor(address _feeToSetter) public {
|
||||
feeToSetter = _feeToSetter;
|
||||
}
|
||||
|
||||
/**
|
||||
* @notice 获取所有交易对的数量
|
||||
* @return 交易对总数
|
||||
*/
|
||||
function allPairsLength() external view returns (uint) {
|
||||
return allPairs.length;
|
||||
}
|
||||
|
||||
/**
|
||||
* @notice 创建交易对
|
||||
* @dev 使用CREATE2确定性部署,任何人都可以调用
|
||||
* @param tokenA 第一个代币地址
|
||||
* @param tokenB 第二个代币地址
|
||||
* @return pair 新创建的交易对地址
|
||||
*/
|
||||
function createPair(address tokenA, address tokenB) external returns (address pair) {
|
||||
require(tokenA != tokenB, 'UniswapV2: IDENTICAL_ADDRESSES');
|
||||
// 按字典顺序排序代币地址,确保token0 < token1
|
||||
(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'); // single check is sufficient
|
||||
require(getPair[token0][token1] == address(0), 'UniswapV2: PAIR_EXISTS');
|
||||
|
||||
// 获取UniswapV2Pair合约的字节码
|
||||
bytes memory bytecode = type(UniswapV2Pair).creationCode;
|
||||
// 使用两个代币地址生成salt
|
||||
bytes32 salt = keccak256(abi.encodePacked(token0, token1));
|
||||
|
||||
// 使用CREATE2部署合约,地址可预测
|
||||
assembly {
|
||||
pair := create2(0, add(bytecode, 32), mload(bytecode), salt)
|
||||
}
|
||||
|
||||
// 初始化交易对
|
||||
IUniswapV2Pair(pair).initialize(token0, token1);
|
||||
|
||||
// 在映射中记录交易对(双向)
|
||||
getPair[token0][token1] = pair;
|
||||
getPair[token1][token0] = pair; // populate mapping in the reverse direction
|
||||
getPair[token1][token0] = pair;
|
||||
|
||||
// 添加到数组中
|
||||
allPairs.push(pair);
|
||||
|
||||
// 触发创建事件
|
||||
emit PairCreated(token0, token1, pair, allPairs.length);
|
||||
}
|
||||
|
||||
/**
|
||||
* @notice 设置协议手续费接收地址
|
||||
* @dev 只有feeToSetter可以调用
|
||||
* @param _feeTo 新的手续费接收地址
|
||||
*/
|
||||
function setFeeTo(address _feeTo) external {
|
||||
require(msg.sender == feeToSetter, 'UniswapV2: FORBIDDEN');
|
||||
feeTo = _feeTo;
|
||||
}
|
||||
|
||||
/**
|
||||
* @notice 设置有权修改feeTo的地址
|
||||
* @dev 只有当前feeToSetter可以调用
|
||||
* @param _feeToSetter 新的手续费设置者地址
|
||||
*/
|
||||
function setFeeToSetter(address _feeToSetter) external {
|
||||
require(msg.sender == feeToSetter, 'UniswapV2: FORBIDDEN');
|
||||
feeToSetter = _feeToSetter;
|
||||
|
||||
@ -8,26 +8,42 @@ import './interfaces/IERC20.sol';
|
||||
import './interfaces/IUniswapV2Factory.sol';
|
||||
import './interfaces/IUniswapV2Callee.sol';
|
||||
|
||||
/**
|
||||
* @title UniswapV2Pair 交易对合约
|
||||
* @notice 这是Uniswap V2协议的核心合约,实现了自动做市商(AMM)机制
|
||||
* @dev 继承自IUniswapV2Pair接口和UniswapV2ERC20合约,实现流动性代币功能
|
||||
*/
|
||||
contract UniswapV2Pair is IUniswapV2Pair, UniswapV2ERC20 {
|
||||
using SafeMath for uint;
|
||||
using UQ112x112 for uint224;
|
||||
|
||||
// 最小流动性常量,防止除零错误和攻击
|
||||
uint public constant MINIMUM_LIQUIDITY = 10**3;
|
||||
// ERC20 transfer函数的选择器,用于安全转账
|
||||
bytes4 private constant SELECTOR = bytes4(keccak256(bytes('transfer(address,uint256)')));
|
||||
|
||||
// 工厂合约地址
|
||||
address public factory;
|
||||
// 交易对中的两个代币地址(token0 < token1)
|
||||
address public token0;
|
||||
address public token1;
|
||||
|
||||
uint112 private reserve0; // uses single storage slot, accessible via getReserves
|
||||
uint112 private reserve1; // uses single storage slot, accessible via getReserves
|
||||
uint32 private blockTimestampLast; // uses single storage slot, accessible via getReserves
|
||||
// 储备量,使用uint112节省gas,三个变量共用一个存储槽
|
||||
uint112 private reserve0; // token0的储备量
|
||||
uint112 private reserve1; // token1的储备量
|
||||
uint32 private blockTimestampLast; // 上次更新的区块时间戳
|
||||
|
||||
uint public price0CumulativeLast;
|
||||
uint public price1CumulativeLast;
|
||||
uint public kLast; // reserve0 * reserve1, as of immediately after the most recent liquidity event
|
||||
// 价格累积器,用于计算时间加权平均价格(TWAP)
|
||||
uint public price0CumulativeLast; // token0相对于token1的累积价格
|
||||
uint public price1CumulativeLast; // token1相对于token0的累积价格
|
||||
uint public kLast; // 上次流动性事件后的k值 (reserve0 * reserve1)
|
||||
|
||||
// 重入锁状态
|
||||
uint private unlocked = 1;
|
||||
|
||||
/**
|
||||
* @dev 重入锁修饰符,防止重入攻击
|
||||
*/
|
||||
modifier lock() {
|
||||
require(unlocked == 1, 'UniswapV2: LOCKED');
|
||||
unlocked = 0;
|
||||
@ -35,17 +51,30 @@ contract UniswapV2Pair is IUniswapV2Pair, UniswapV2ERC20 {
|
||||
unlocked = 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* @notice 获取当前储备量和最后更新时间
|
||||
* @return _reserve0 token0的储备量
|
||||
* @return _reserve1 token1的储备量
|
||||
* @return _blockTimestampLast 最后更新的区块时间戳
|
||||
*/
|
||||
function getReserves() public view returns (uint112 _reserve0, uint112 _reserve1, uint32 _blockTimestampLast) {
|
||||
_reserve0 = reserve0;
|
||||
_reserve1 = reserve1;
|
||||
_blockTimestampLast = blockTimestampLast;
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev 安全转账函数,确保代币转账成功
|
||||
* @param token 代币合约地址
|
||||
* @param to 接收地址
|
||||
* @param value 转账数量
|
||||
*/
|
||||
function _safeTransfer(address token, address to, uint value) private {
|
||||
(bool success, bytes memory data) = token.call(abi.encodeWithSelector(SELECTOR, to, value));
|
||||
require(success && (data.length == 0 || abi.decode(data, (bool))), 'UniswapV2: TRANSFER_FAILED');
|
||||
}
|
||||
|
||||
// 事件定义
|
||||
event Mint(address indexed sender, uint amount0, uint amount1);
|
||||
event Burn(address indexed sender, uint amount0, uint amount1, address indexed to);
|
||||
event Swap(
|
||||
@ -58,13 +87,20 @@ contract UniswapV2Pair is IUniswapV2Pair, UniswapV2ERC20 {
|
||||
);
|
||||
event Sync(uint112 reserve0, uint112 reserve1);
|
||||
|
||||
/**
|
||||
* @dev 构造函数,设置工厂地址为部署者
|
||||
*/
|
||||
constructor() public {
|
||||
factory = msg.sender;
|
||||
}
|
||||
|
||||
// called once by the factory at time of deployment
|
||||
/**
|
||||
* @notice 初始化交易对,只能由工厂调用一次
|
||||
* @param _token0 第一个代币地址
|
||||
* @param _token1 第二个代币地址
|
||||
*/
|
||||
function initialize(address _token0, address _token1) external {
|
||||
require(msg.sender == factory, 'UniswapV2: FORBIDDEN'); // sufficient check
|
||||
require(msg.sender == factory, 'UniswapV2: FORBIDDEN');
|
||||
token0 = _token0;
|
||||
token1 = _token1;
|
||||
}
|
||||
|
||||
@ -1,17 +1,34 @@
|
||||
pragma solidity >=0.5.0;
|
||||
|
||||
/**
|
||||
* @title IUniswapV2Factory 工厂合约接口
|
||||
* @notice 定义Uniswap V2工厂合约的标准接口
|
||||
*/
|
||||
interface IUniswapV2Factory {
|
||||
/// @notice 创建新交易对时触发的事件
|
||||
event PairCreated(address indexed token0, address indexed token1, address pair, uint);
|
||||
|
||||
/// @notice 协议手续费接收地址
|
||||
function feeTo() external view returns (address);
|
||||
|
||||
/// @notice 有权设置feeTo的地址
|
||||
function feeToSetter() external view returns (address);
|
||||
|
||||
/// @notice 获取指定代币对的交易对地址
|
||||
function getPair(address tokenA, address tokenB) external view returns (address pair);
|
||||
|
||||
/// @notice 按索引获取交易对地址
|
||||
function allPairs(uint) external view returns (address pair);
|
||||
|
||||
/// @notice 获取交易对总数
|
||||
function allPairsLength() external view returns (uint);
|
||||
|
||||
/// @notice 创建新的交易对
|
||||
function createPair(address tokenA, address tokenB) external returns (address pair);
|
||||
|
||||
/// @notice 设置协议手续费接收地址
|
||||
function setFeeTo(address) external;
|
||||
|
||||
/// @notice 设置手续费设置者地址
|
||||
function setFeeToSetter(address) external;
|
||||
}
|
||||
|
||||
@ -1,17 +1,32 @@
|
||||
pragma solidity =0.5.16;
|
||||
|
||||
// a library for performing various math operations
|
||||
|
||||
/**
|
||||
* @title Math 数学计算库
|
||||
* @notice 提供各种数学运算函数
|
||||
* @dev 包含最小值计算和平方根计算等工具函数
|
||||
*/
|
||||
library Math {
|
||||
/**
|
||||
* @notice 返回两个数中的较小值
|
||||
* @param x 第一个数
|
||||
* @param y 第二个数
|
||||
* @return z 较小的数
|
||||
*/
|
||||
function min(uint x, uint y) internal pure returns (uint z) {
|
||||
z = x < y ? x : y;
|
||||
}
|
||||
|
||||
// babylonian method (https://en.wikipedia.org/wiki/Methods_of_computing_square_roots#Babylonian_method)
|
||||
/**
|
||||
* @notice 计算平方根(巴比伦算法)
|
||||
* @dev 使用巴比伦方法计算平方根,参考: https://en.wikipedia.org/wiki/Methods_of_computing_square_roots#Babylonian_method
|
||||
* @param y 需要计算平方根的数
|
||||
* @return z 平方根结果
|
||||
*/
|
||||
function sqrt(uint y) internal pure returns (uint z) {
|
||||
if (y > 3) {
|
||||
z = y;
|
||||
uint x = y / 2 + 1;
|
||||
// 迭代逼近,直到找到最优解
|
||||
while (x < z) {
|
||||
z = x;
|
||||
x = (y / x + x) / 2;
|
||||
@ -19,5 +34,6 @@ library Math {
|
||||
} else if (y != 0) {
|
||||
z = 1;
|
||||
}
|
||||
// 如果y为0,则z保持为0(默认值)
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,16 +1,41 @@
|
||||
pragma solidity =0.5.16;
|
||||
|
||||
// a library for performing overflow-safe math, courtesy of DappHub (https://github.com/dapphub/ds-math)
|
||||
|
||||
/**
|
||||
* @title SafeMath 安全数学运算库
|
||||
* @notice 提供防止溢出的数学运算函数
|
||||
* @dev 来自DappHub的ds-math库 (https://github.com/dapphub/ds-math)
|
||||
* @dev 在Solidity 0.8.0之前,需要手动检查算术溢出
|
||||
*/
|
||||
library SafeMath {
|
||||
/**
|
||||
* @notice 安全加法,防止溢出
|
||||
* @param x 第一个加数
|
||||
* @param y 第二个加数
|
||||
* @return z 两数之和
|
||||
* @dev 如果结果溢出则回滚交易
|
||||
*/
|
||||
function add(uint x, uint y) internal pure returns (uint z) {
|
||||
require((z = x + y) >= x, 'ds-math-add-overflow');
|
||||
}
|
||||
|
||||
/**
|
||||
* @notice 安全减法,防止下溢
|
||||
* @param x 被减数
|
||||
* @param y 减数
|
||||
* @return z 两数之差
|
||||
* @dev 如果结果下溢则回滚交易
|
||||
*/
|
||||
function sub(uint x, uint y) internal pure returns (uint z) {
|
||||
require((z = x - y) <= x, 'ds-math-sub-underflow');
|
||||
}
|
||||
|
||||
/**
|
||||
* @notice 安全乘法,防止溢出
|
||||
* @param x 第一个乘数
|
||||
* @param y 第二个乘数
|
||||
* @return z 两数之积
|
||||
* @dev 如果结果溢出则回滚交易,使用除法检查溢出
|
||||
*/
|
||||
function mul(uint x, uint y) internal pure returns (uint z) {
|
||||
require(y == 0 || (z = x * y) / y == x, 'ds-math-mul-overflow');
|
||||
}
|
||||
|
||||
@ -1,19 +1,32 @@
|
||||
pragma solidity =0.5.16;
|
||||
|
||||
// a library for handling binary fixed point numbers (https://en.wikipedia.org/wiki/Q_(number_format))
|
||||
|
||||
// range: [0, 2**112 - 1]
|
||||
// resolution: 1 / 2**112
|
||||
|
||||
/**
|
||||
* @title UQ112x112 定点数运算库
|
||||
* @notice 处理二进制定点数的库 (Q数字格式)
|
||||
* @dev 参考: https://en.wikipedia.org/wiki/Q_(number_format)
|
||||
* @dev 范围: [0, 2**112 - 1],精度: 1 / 2**112
|
||||
*/
|
||||
library UQ112x112 {
|
||||
// Q112常量,表示2^112,用于编码和解码
|
||||
uint224 constant Q112 = 2**112;
|
||||
|
||||
// encode a uint112 as a UQ112x112
|
||||
/**
|
||||
* @notice 将uint112编码为UQ112x112格式
|
||||
* @param y 要编码的uint112数值
|
||||
* @return z 编码后的UQ112x112数值
|
||||
* @dev 乘以Q112进行编码,永不溢出
|
||||
*/
|
||||
function encode(uint112 y) internal pure returns (uint224 z) {
|
||||
z = uint224(y) * Q112; // never overflows
|
||||
z = uint224(y) * Q112; // 永不溢出
|
||||
}
|
||||
|
||||
// divide a UQ112x112 by a uint112, returning a UQ112x112
|
||||
/**
|
||||
* @notice UQ112x112除以uint112,返回UQ112x112
|
||||
* @param x UQ112x112格式的被除数
|
||||
* @param y uint112格式的除数
|
||||
* @return z UQ112x112格式的商
|
||||
* @dev 用于价格计算,保持定点数精度
|
||||
*/
|
||||
function uqdiv(uint224 x, uint112 y) internal pure returns (uint224 z) {
|
||||
z = x / uint224(y);
|
||||
}
|
||||
|
||||
19
package.json
19
package.json
@ -1,14 +1,23 @@
|
||||
{
|
||||
"name": "@uniswap/v2-core",
|
||||
"version": "1.0.0-beta.3",
|
||||
"files": [
|
||||
"contracts",
|
||||
"build"
|
||||
],
|
||||
"description": "🎛 Core contracts for the UniswapV2 protocol",
|
||||
"version": "1.0.1",
|
||||
"homepage": "https://uniswap.org",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/Uniswap/uniswap-v2-core"
|
||||
},
|
||||
"keywords": [
|
||||
"uniswap",
|
||||
"ethereum",
|
||||
"v2",
|
||||
"core",
|
||||
"uniswap-v2"
|
||||
],
|
||||
"files": [
|
||||
"contracts",
|
||||
"build"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user