init
Some checks failed
CI / test (10.x, ubuntu-latest) (push) Has been cancelled
CI / test (12.x, ubuntu-latest) (push) Has been cancelled

This commit is contained in:
2025-07-08 01:27:47 +08:00
parent ee547b1785
commit 3586e648b2
8 changed files with 517 additions and 44 deletions

268
README.md
View File

@ -1,24 +1,270 @@
# Uniswap V2
# Uniswap V2 Core 核心合约
[![Actions Status](https://github.com/Uniswap/uniswap-v2-core/workflows/CI/badge.svg)](https://github.com/Uniswap/uniswap-v2-core/actions)
[![Version](https://img.shields.io/npm/v/@uniswap/v2-core)](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/>定点数库]
`yarn test`
User[用户] --> Factory
User --> Pair
LP[流动性提供者] --> Pair
Trader[交易者] --> Pair
```
## 🔧 核心概念
### 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 库
- 以太坊社区的支持
---
**⚠️ 风险提示**: 本项目仅供学习和研究使用。在生产环境中使用前,请进行充分的安全审计。去中心化金融存在智能合约风险、无常损失等风险,请谨慎参与。

View File

@ -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);
}
}

View File

@ -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;

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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默认值
}
}

View File

@ -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');
}

View File

@ -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);
}