Hello World - Uniswap V2
type: channel
link: https://blog.jerichos.site/index.php/category/Uniswap-V2/
items: Uniswap v2-Flash Swap-Twap-Arbitrage
items: Uniswap v2-Liquidity
items: Uniswap v2-Swap
items: Uniswap v2

Uniswap v2-Flash Swap-Twap-Arbitrage
type: item
date: 2025-08-10T18:03:00+08:00
description: 一、Flash Swap在 Uniswap 的 Flash Swap（闪电兑换） 机制中，要求交易后储备金满足 reserve_after >= reserve_before 的核心目的是为了确保 流动性提供者（LP）的本金安全 和 系统的偿付能力。以下是具体分析：Flash Swap 的基本流程Flash Swap 允许用户 无需预先支付代币 即可临时借出资金，但需在同一笔交易内完成还款。其关键步骤如下：借出代币：用户从交易对中借出代币（如 amount0Out > 0 或 amount1Out > 0）。执行回调：用户合约实现 uniswapV2Call，在回调中完成任意操作（如套利、还款）。还款验证：合约检查还款后的储备金是否满足 reserve_after >= reserve_before。// Uniswap/v2-core/contracts/UniswapV2Pair.sol function swap(uint amount0Out, uint amount1Out, address to, bytes calldata data) external lock { require(amount0Out > 0 || amount1Out > 0, 'UniswapV2: INSUFFICIENT_OUTPUT_AMOUNT'); (uint112 _reserve0, uint112 _reserve1,) = getReserves(); // gas savings require(amount0Out < _reserve0 && amount1Out < _reserve1, 'UniswapV2: INSUFFICIENT_LIQUIDITY'); uint balance0; uint balance1; { // scope for _token{0,1}, avoids stack too deep errors address _token0 = token0; address _token1 = token1; require(to != _token0 && to != _token1, 'UniswapV2: INVALID_TO'); if (amount0Out > 0) _safeTransfer(_token0, to, amount0Out); // optimistically transfer tokens if (amount1Out > 0) _safeTransfer(_token1, to, amount1Out); // optimistically transfer tokens if (data.length > 0) IUniswapV2Callee(to).uniswapV2Call(msg.sender, amount0Out, amount1Out, data); balance0 = IERC20(_token0).balanceOf(address(this)); balance1 = IERC20(_token1).balanceOf(address(this)); } uint amount0In = balance0 > _reserve0 - amount0Out ? balance0 - (_reserve0 - amount0Out) : 0; uint amount1In = balance1 > _reserve1 - amount1Out ? balance1 - (_reserve1 - amount1Out) : 0; require(amount0In > 0 || amount1In > 0, 'UniswapV2: INSUFFICIENT_INPUT_AMOUNT'); { // scope for reserve{0,1}Adjusted, avoids stack too deep errors uint balance0Adjusted = balance0.mul(1000).sub(amount0In.mul(3)); uint balance1Adjusted = balance1.mul(1000).sub(amount1In.mul(3)); require(balance0Adjusted.mul(balance1Adjusted) >= uint(_reserve0).mul(_reserve1).mul(1000**2), 'UniswapV2: K'); } _update(balance0, balance1, _reserve0, _reserve1); emit Swap(msg.sen…

Uniswap v2-Liquidity
type: item
date: 2025-08-05T12:58:00+08:00
description: 一、Create pool1.createPair创建流动性时，需要确保交易对存在，如果不存在将会调用IUniswapV2Factory(factory).createPair创建交易对// Uniswap/v2-periphery/contracts/UniswapV2Router02.sol function _addLiquidity( address tokenA, address tokenB, uint amountADesired, uint amountBDesired, uint amountAMin, uint amountBMin ) internal virtual returns (uint amountA, uint amountB) { // create the pair if it doesn't exist yet if (IUniswapV2Factory(factory).getPair(tokenA, tokenB) == address(0)) { IUniswapV2Factory(factory).createPair(tokenA, tokenB); } .... }这个函数是 UniswapV2Factory 的核心方法，用于动态创建新的代币交易对合约。// Uniswap/v2-core/contracts/UniswapV2Factory.sol 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'); // single check is sufficient 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; // populate mapping in the reverse direction allPairs.push(pair); emit PairCreated(token0, token1, pair, allPairs.length); }参数校验require(tokenA != tokenB, 'UniswapV2: IDENTICAL_ADDRESSES'); (address token0, token1) = tokenA < tokenB ? (tokenA, tokenB) : (tokenB, tokenA); require(token0 != address(0), 'UniswapV2: ZERO_ADDRESS'); require(getPair[token0][token1] == address(0), 'UniswapV2: PAIR_EXISTS');禁止相同代币：防止创建无意义的交易对（如 ETH/ETH）。代币排序：按地址升序排列 token0 和 token1，确保唯一性（避免重复创建 ETH/USDT 和 USDT/ETH）。零地址检查：防止无效代币。重复创建检查：确保该交易对尚未存在。使用 CREATE2 部署合约bytes memory bytecode = type(UniswapV2Pair).creationCode; bytes32 salt = keccak256(abi.encodePacked(token0, token1)); assembly { pair := create2…

Uniswap v2-Swap
type: item
date: 2025-08-02T15:24:00+08:00
description: Uniswap v2一、Swap单个币种之间swap的流程多个币种之间swap的流程1.swapExactTokensForTokens 和 swapTokensForExactTokens这个函数是 Uniswap V2 路由器合约中的一个重要功能，用于将确切数量的某种代币交换为另一种代币（可能经过多个交易对）。// v2-periphery/contracts/UniswapV2Router02.sol // swapExactTokensForTokens (精确输入，不确定输出) // 功能：用确切数量的输入代币换取尽可能多的输出代币 function swapExactTokensForTokens( uint amountIn, uint amountOutMin, address[] calldata path, address to, uint deadline ) external virtual override ensure(deadline) returns (uint[] memory amounts) { amounts = UniswapV2Library.getAmountsOut(factory, amountIn, path); require(amounts[amounts.length - 1] >= amountOutMin, 'UniswapV2Router: INSUFFICIENT_OUTPUT_AMOUNT'); TransferHelper.safeTransferFrom( path[0], msg.sender, UniswapV2Library.pairFor(factory, path[0], path[1]), amounts[0] ); _swap(amounts, path, to); } // swapTokensForExactTokens 精确输出，不确定输入 // 用尽可能少的输入代币换取确切数量的输出代币 function swapTokensForExactTokens( uint amountOut, uint amountInMax, address[] calldata path, address to, uint deadline ) external virtual override ensure(deadline) returns (uint[] memory amounts) { amounts = UniswapV2Library.getAmountsIn(factory, amountOut, path); require(amounts[0] <= amountInMax, 'UniswapV2Router: EXCESSIVE_INPUT_AMOUNT'); TransferHelper.safeTransferFrom( path[0], msg.sender, UniswapV2Library.pairFor(factory, path[0], path[1]), amounts[0] ); _swap(amounts, path, to); }参数解释amountIn: 你想交换的输入代币数量amountOutMin: 你愿意接受的最小输出代币数量（防止高滑点）path: 交换路径（代币地址数组）to: 接收输出代币的地址deadline: 交易有效期限（Unix 时间戳）path 参数path 数组表示从代币 A 到代币 B 可能经过的所有中间代币。例如：直接交换：[代币A, 代币B]通过中间代币交换：[代币A, 代币WETH, 代币B]函数会按照路径顺序一一交换，直到最后一个目标币种。factory 参数factory 确实是指 UniswapV2Factory 合约（核心合约之一），它负责创建和管理交易对。amounts 数组amounts 是通过 UniswapV2Library.getAmountsOut() 计算得到的数组，它包含：沿交换路径每个阶段预期的代币数量数组的第一个元素是 amountIn（输入数量）最后一个元素是预期的输出代币数量中间元素是每个中间交换步骤的预期数量例如，对于路径 [A, B, C]，amounts 可能看起来像 [100 A, 50 B, 25 C]，表示：用 100 A 换 50 B再用 50 B 换 25 C函数执行流程计算沿路径的预期输出量 (getAmountsOut)检查最终输出是否满足用户的最小要求 (amountOutMin)将输入代币从用户转移到第一个交易对执行沿路径的交换 (_swap 内部函数)返回各阶段的实际交换数量这种设计允许复杂的多跳交换，同时确保用户获得…

Uniswap v2
type: item
date: 2025-05-16T23:48:00+08:00
description: {collapse}{collapse-item label="一、背景" open}传统的中心化交易所买家和卖家进行订单匹配，去中心的交易所引入开创了一种全新的交易方式，通过一种预设的数学公式（比如，常数乘积公式）创建一个流动性池，使得用户可以随时进行交易。自动做市商（Automated Market Maker，简称 AMM）是一种算法，或者说是一种在区块链上运行的智能合约，它允许数字资产之间的去中心化交易。{/collapse-item}{collapse-item label="二、常见的AMM算法" open} ## 1. 恒定乘积做市商（Constant Product Market Maker, CPMM）公式x * y = kx：代币A的数量y：代币B的数量k：恒定乘积（流动性池的总价值）特点代表协议：Uniswap V1/V2滑点机制：交易量越大，价格偏离越多（非线性）。流动性分布：全价格区间均匀分布，资本效率低。2. 恒定和做市商（Constant Sum Market Maker, CSMM）公式x + y = k特点零滑点：价格固定为 1:1（如稳定币交易）。缺点：流动性易耗尽（套利者会抽干某一代币）。代表协议：早期稳定币兑换池（已淘汰）。示例池中有 100 DAI + 100 USDC，用户用 10 DAI 换 10 USDC，池变为 110 DAI + 90 USDC。3. 恒定均值做市商（Constant Mean Market Maker, CMMM）公式x^a y^b z^c = ka,b,c：代币权重（可自定义）。特点支持多代币池（如Balancer的3种代币池）。灵活权重：例如 80% ETH + 20% BTC 的指数基金池。代表协议：Balancer。4. 集中流动性AMM（Concentrated Liquidity）代表协议：Uniswap V3原理LP可自定义价格区间（如 ETH/USDC 在 $1800-$2200 提供流动性）。公式分段处理：在区间内：使用 CPMM（x * y = k）。在区间外：流动性失效（变为单一资产）。优势资本效率提升：最高达4000倍（相比V2）。支持限价单：通过流动性区间模拟。5. 动态费用AMM（Dynamic Fees）代表协议：Trader Joe（Liquidity Book）特点根据市场波动动态调整手续费（如高波动时提高费率）。分档流动性：将价格划分为多个“档位”，每档独立计算流动性。AMM算法的核心问题与解决方案问题解决方案协议示例资本效率低集中流动性（Uniswap V3）Uniswap V3无常损失动态费率或对冲策略Bancor V3多代币池支持恒定均值算法（Balancer）Balancer总结CPMM（Uniswap）：简单通用，适合大部分代币对。CSMM：仅适合稳定币，已淘汰。CMMM（Balancer）：灵活支持多代币和自定义权重。集中流动性（Uniswap V3）：提升资本效率，适合专业做市。{/collapse-item}{collapse-item label="三、实现一个简单的swap" open} SimpleSwap 继承了 ERC20 代币标准，方便记录流动性提供者提供的流动性。在构造器中，我们指定一对代币地址 token0 和 token1，交易所仅支持这对代币。reserve0 和 reserve1 记录了合约中代币的储备量。contract SimpleSwap is ERC20 { // 代币合约 IERC20 public token0; IERC20 public token1; // 代币储备量 uint public reserve0; uint public reserve1; // 构造器，初始化代币地址 constructor(IERC20 _token0, IERC20 _token1) ERC20("SimpleSwap", "SS") { token0 = _token0; token1 = _token1; } }交易所主要有两类参与者：流动性提供者（Liquidity Provider，LP）和交易者（Trader）。下面我们分别实现这两部分的功能。流动性提供流动性提供者给市场提供流动性，让交易者获得更好的报价和流动性，并收取一定费用。首先，我们需要实现添加流动性的功能。当用户向代币池添加流动性时，合约要记录添加的LP份额。这个LP份额主要是用户提供流动性的凭证，交易费的一定比例按 LP 份额分配给流动性提供者，同时也可以赎回代币，销毁 LP 代币，按LP比例取回 tokenA 和 tokenB,具体的tokenA和tokenB数量取决于他们现有数量然后乘LP比例。根据 Uni…