# Uniswap V2 设计迷思

• ripwu
• 更新于 2021-09-15 10:54
• 阅读 6716

Uniswap V2 设计上的一些考虑

## 首次铸币的漏洞

$$s{m} = \frac{\sqrt{k{2}} - \sqrt{k{1}}}{5 \cdot \sqrt{k{2}} + \sqrt{k{1}}} \cdot s{1}$$

$$s{m} \approx \left\lfloor \frac{\sqrt{k{2}}}{5 \cdot \sqrt{k{2}}} \cdot s{1} \right\rfloor = \left\lfloor \frac{1}{5} \cdot 1 \right\rfloor = 0$$

contract UniswapV2Pair is IUniswapV2Pair, UniswapV2ERC20 {
using SafeMath  for uint;
uint public constant MINIMUM_LIQUIDITY = 10**3;

function mint(address to) external lock returns (uint liquidity) {
if (_totalSupply == 0) {
liquidity = Math.sqrt(amount0.mul(amount1)).sub(MINIMUM_LIQUIDITY);
_mint(address(0), MINIMUM_LIQUIDITY); // permanently lock the first MINIMUM_LIQUIDITY tokens
}
}
require(liquidity > 0, 'UniswapV2: INSUFFICIENT_LIQUIDITY_MINTED');
}

// 如果还是觉得门槛很高，记住：穷是我等散户的问题

## 谁的利益

// given an input amount of an asset and pair reserves, returns the maximum output amount of the other asset
function getAmountOut(uint amountIn, uint reserveIn, uint reserveOut) internal pure returns (uint amountOut) {
require(amountIn > 0, 'UniswapV2Library: INSUFFICIENT_INPUT_AMOUNT');
require(reserveIn > 0 && reserveOut > 0, 'UniswapV2Library: INSUFFICIENT_LIQUIDITY');
uint amountInWithFee = amountIn.mul(997);
uint numerator = amountInWithFee.mul(reserveOut);
amountOut = numerator / denominator;
}

// given an output amount of an asset and pair reserves, returns a required input amount of the other asset
function getAmountIn(uint amountOut, uint reserveIn, uint reserveOut) internal pure returns (uint amountIn) {
require(amountOut > 0, 'UniswapV2Library: INSUFFICIENT_OUTPUT_AMOUNT');
require(reserveIn > 0 && reserveOut > 0, 'UniswapV2Library: INSUFFICIENT_LIQUIDITY');
uint numerator = reserveIn.mul(amountOut).mul(1000);
uint denominator = reserveOut.sub(amountOut).mul(997);
}

_update()，误差会累加，无所谓偏向谁的利益，只是提供一个机制，预言机自行决定实现；如果交易对精度很高 (decimals 很小)，误差会很大

mint() 注入流动性时，偏向已有的流动性提供者

burn() 销毁流动性时，偏向仍旧留下的流动性提供

_mintFee() 平台收取手续费时，偏向平台

## 矿工的攻击

### 两种攻击收益

1. 滑点收益

Uniswap 部分了解决这个问题：periphery 允许用户在交易时设置最小输出参数 amountOutMin

1. 手续收益

Uniswap V2 未解决这个问题

### 两个攻击例子

#### 例子2

// 受害人声泪俱下，围观地址 (空降指挥部 30:15)

samczsun 大佬协助联系白帽，取回了部分资产，参考：

### 解决?

Flashbots 似乎很常见了，比如我最近的两笔 SushiSwap 交易，都由 Flashbots 节点挖出：

### 反思

Flash Boys 并非万无一失，也要承担风险：如果它挖出抢跑区块时正好发生分叉，所挖区块被大多数节点当成叔块..

## 非标准的 ERC-20

ERC-20 定义的 transferFrom 接口如下

interface IERC20 {
}

recipient 实际收到的数额，会比 amount 小，因此我们不能简单以其返回值 true 作为判断依据

interface IUniswapV2Router02 is IUniswapV2Router01 {
function swapExactTokensForTokensSupportingFeeOnTransferTokens(
uint amountIn,
uint amountOutMin,
) external;
}

## 预言机

Uniswap V2 使用基于时间权重的算数平均数，所以无法像 V1 一样，根据一个代币的 spot price，求其倒数作为另一个代币的价格

priceA

$$\small\text{WAM}_{priceA} = \frac{10200}{1000} \times \frac{7}{20} + \frac{10300}{1000} \times \frac{8}{20} + \frac{10500}{1000} \times \frac{5}{20} = \frac{10200 \times 7 + 10300 \times 8 + 10500 \times 5}{1000 * 20}$$

PriceB

$$\small\text{WHM}_{PriceB} = \frac{1}{\frac{\frac{7}{20}}{\frac{1000}{10200}} + \frac{\frac{8}{20}}{\frac{1000}{10300}} + \frac{\frac{5}{20}}{\frac{1000}{10500}}} = \frac{20}{\frac{7}{\frac{1000}{10200}} + \frac{8}{\frac{1000}{10300}} + \frac{5}{\frac{1000}{10500}}} = \frac{1000 * 20}{10200 \times 7 + 10300 \times 8 + 10500 \times 5}$$

$$\small\text{WAM}{priceA} = \frac{1}{\small\text{WHM}{priceB}}$$

$$\small\text{WGM}_{priceA} = \sqrt[7+8+5]{{\frac{10200}{1000}}^7 \times {\frac{10300}{1000}}^8 \times {\frac{10500}{1000}}^5}$$

$$\small\text{WGM}_{priceB} = \sqrt[7+8+5]{{\frac{1000}{10200}}^7 \times {\frac{1000}{10300}}^8 \times {\frac{1000}{10500}}^5}$$

$$\small\text{WGM}{priceA} = \frac{1}{\small\text{WGM}{priceB}}$$

ripwu