diff --git a/README.md b/README.md index 1d72187..5a6fc8e 100644 --- a/README.md +++ b/README.md @@ -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
工厂合约] --> Pair[UniswapV2Pair
交易对合约] + Pair --> ERC20[UniswapV2ERC20
流动性代币] + Pair --> SafeMath[SafeMath
安全数学运算] + Pair --> Math[Math
数学库] + Pair --> UQ112x112[UQ112x112
定点数库] + + 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 库 +- 以太坊社区的支持 + +--- + +**⚠️ 风险提示**: 本项目仅供学习和研究使用。在生产环境中使用前,请进行充分的安全审计。去中心化金融存在智能合约风险、无常损失等风险,请谨慎参与。 diff --git a/contracts/UniswapV2ERC20.sol b/contracts/UniswapV2ERC20.sol index 404a8be..ca4dc37 100644 --- a/contracts/UniswapV2ERC20.sol +++ b/contracts/UniswapV2ERC20.sol @@ -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); } } diff --git a/contracts/UniswapV2Factory.sol b/contracts/UniswapV2Factory.sol index a66ad82..c74f72a 100644 --- a/contracts/UniswapV2Factory.sol +++ b/contracts/UniswapV2Factory.sol @@ -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; diff --git a/contracts/UniswapV2Pair.sol b/contracts/UniswapV2Pair.sol index f87a1db..30ca4be 100644 --- a/contracts/UniswapV2Pair.sol +++ b/contracts/UniswapV2Pair.sol @@ -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; } diff --git a/contracts/interfaces/IUniswapV2Factory.sol b/contracts/interfaces/IUniswapV2Factory.sol index e73dc59..70cfcc1 100644 --- a/contracts/interfaces/IUniswapV2Factory.sol +++ b/contracts/interfaces/IUniswapV2Factory.sol @@ -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; } diff --git a/contracts/libraries/Math.sol b/contracts/libraries/Math.sol index 1cab10d..6d00708 100644 --- a/contracts/libraries/Math.sol +++ b/contracts/libraries/Math.sol @@ -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(默认值) } } diff --git a/contracts/libraries/SafeMath.sol b/contracts/libraries/SafeMath.sol index f2fbe16..ee83e2b 100644 --- a/contracts/libraries/SafeMath.sol +++ b/contracts/libraries/SafeMath.sol @@ -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'); } diff --git a/contracts/libraries/UQ112x112.sol b/contracts/libraries/UQ112x112.sol index a453f72..81854ef 100644 --- a/contracts/libraries/UQ112x112.sol +++ b/contracts/libraries/UQ112x112.sol @@ -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); }