
去中心化交易协议uniswap 和 aaveuniswapuniswap是去中心化交易所的标杆项目v1、v2、v3概念说明AMM自动做市商做市商是指对于一种资产双向报价概念gas费由于链的出块需要支付矿工打包的费用所以用户的交易需要支付gas费矿工打包区块需要花费计算资源维护区块链的一致性。恒定乘积公式x*yk公式流动性池两种代币做交换的庄家流动性反映流动性池的深度庄家的资产多少滑点原本可以买到100个娃娃实际到手91个9个就是此次交易的滑点在出现uniswap之前区块链使用的是传统的订单薄模式只是在去中心化约束下实现的。交易过程中需要撮合开市商者和交易者交易属于链下磋商链上交易。由于底层公链性能限制交易后订单薄存储在链下存在中心化问题。为什么不支持订单薄上链因为以太网链低吞吐量每秒只能处理15-30笔交易出块时间最少12秒因为订单薄是一个高频操作高延时不说 gas费更是天价。为了解决这种不完全去中心化的需求uniswap孕育而生。v1使用合约把交易的撮合过程上链开创了AMM模式引入恒定式乘积公式也就是x*yk公式。在这个模型中流动性池包括两种代币比如ETH和USDT。流动性提供者将两种代币按照当前的加个比例存入池子获取LP代币作为凭证。交易者可以用一种代币兑换另一种代币每次交易都会改变池子中两种代币的数量但是保持k不变。举例初始池10 ETH 40,000 USDC。k 10 * 40,000 400,000。你想用1个ETH买USDC。你先存入1个ETH池子变成x 11 ETH。为了保持k 400,000USDC的数量必须变为y k / x 400,000 / 11 ≈ 36,363.64。这意味着你可以拿走的USDC是40,000 - 36,363.64 3,636.36USDC。你得到的汇率是1 ETH 3,636.36 USDC低于初始的4000 USDC这是因为你的交易对池子产生了影响滑点。但是只支持ETH、ERC20之间的交易ERC20之间的交易需要以ETH作为媒介进行两次交易。这导致交易高滑点高gas。v2v2解决了强制通过ETH交易的限制。v2的特点是流动性在整个价格区间均匀分布无论价格在哪里所有的流动性都在工作这种设计简单直观但是资金利用率不高比如ETH价格在2000U附近但池子中大部分资金分布在1000U或3000U的价格区间实际上这些资金很少被使用。v2的手续费固定为0.3%对所有交易对相同没有差异。用一个ETH买USDC那就是1 * 0.997 x * y1 k和上面一样将原本池子中usdc的数量减去算出的y1就是你买到的u的数量。v3v3的突破是引入了虚拟流动性底层还是恒定乘积公式只是做了离散化的处理。设想如果我认为ETH在1900 - 2100 之间波动我可以只在这个区间提供流动性这样我的资金在这个区间利用率得到提升可以获取更多的手续费把连续的价格区间离散化为一系列的Tick点每个Tick对应一个特定的价格价格只能在Tick之间跳跃价格公式P(i) 2000 × (1.0001)^i其中i为 Tick 索引当前价格对应i 0即P2000 USDC/ETH。Tickstruct Tick { uint128 liquidityGross; // 经过该tick的总流动性 int128 liquidityNet; // 穿过该tick时活跃流动性的净变化 // 其他字段手续费增长值等 }假设有两个流动性仓位仓位A区间[1000, 2000]流动性L 500仓位B区间[2000, 3000]流动性L 300Tick 2000 的状态为liquidityGross 500 300 800 liquidityNet -500 300 -200负值表示当价格从左向右穿过 Tick 2000 时活跃流动性减少 200L sqrtx * yxy是虚拟代币的数量每次交易xy的数量都会变化但是L的大小不变。流动性池在价格区间[Plower, Pupper]中xy实际的数量可以推出P是当前价格Pupper是流动性池中的最高价格、Plower是最低价格有k为何还需要L因为L可以线性反映池子的滑点。举例说明我们考虑一个ETH/USDC交易对并做以下简化Tick 间距每个 Tick 对应价格变化10%实际中约为 0.01%这里为简化计算。价格公式P(i) 2000 × (1.1)^i其中i为 Tick 索引当前价格对应i 0即P2000 USDC/ETH。三个流动性提供者LP及其仓位LP1价格区间[-2, 2]约1653 ~ 2420 USDC提供流动性L₁ 1000LP2价格区间[0, 4]约2000 ~ 2928 USDC提供流动性L₂ 2000LP3价格区间[2, 6]约2420 ~ 3546 USDC提供流动性L₃ 1500初始状态当前价格P 2000对应Tick 0。活跃流动性覆盖当前 Tick 的仓位有 LP1 和 LP2因此LactiveL1L2100020003000交易过程假设一名交易者想用USDC 购买 ETH且交易量足够大将推动价格从 Tick 0 上升至 Tick 3 以上。交易将按以下步骤分段进行第一步价格从 Tick 0 (P02000) 移动到 Tick 1 (P12200)流动性L1000 2000 3000计算所需USDC 和 ETH在此tick的区间的流动性池中可以交换一下数量的代币Tick 1 处流动性变化检查 Tick 1 是否为任何仓位的边界。此处 LP1 区间为[-2,2]Tick 1 在其内部LP2 区间为[0,4]Tick 1 也在内部LP3 区间为[2,6]Tick 1 低于其下限。因此没有仓位在 Tick 1 边界生效活跃流动性保持不变仍为3000。第二步价格从 Tick 1 (P2200) 移动到 Tick 2 (P2420)当前活跃流动性L 3000计算累计Δx 3.12 2.976 6.096 ETHΔy 6548.4 6868.02 13416.42 USDCTick 2 处流动性变化Tick 2 是LP1 的上限区间[-2,2]当价格从左侧穿过 Tick 2价格上涨LP1 的流动性不再活跃应移除1000。Tick 2 是LP3 的下限区间[2,6]当价格从左侧穿过 Tick 2价格上涨LP3 的流动性开始活跃应添加1500。LP2 的区间[0,4]包含 Tick 2继续有效。因此穿过 Tick 2 后新的活跃流动性为 LactiveL2L3200015003500第三步价格从 Tick 2 (P2420) 移动到 Tick 3 (P2662)当前活跃流动性L 3500计算累计Δx 6.096 3.3145 9.4105 ETHΔy 13416.42 8403.745 21820.165 USDCTick 3 处流动性变化Tick 3 不是任何仓位的边界LP2 上限为 Tick 4LP3 上限为 Tick 6因此活跃流动性保持不变仍为3500。交易结果交易者总共支付约 21820.165 USDC获得约 9.4105 ETH平均价格约为2318.5 USDC/ETH。价格从2000 USDC/ETH上升至2662 USDC/ETH穿过了 3 个 Tick。流动性随着价格跨越 Tick 边界而动态调整在 Tick 2 处LP1 退出LP3 加入活跃流动性从3000变为3500手续费v3的手续费和v2类似但是有多档对于稳定币这种交易量多一点写的手续费会低一些0.05%对于高风险币手续费会高一些1%。并且只有在你的价格区间内你才能赚取手续费。超过你设定的区间流动性就停止工作不再赚取手续费而且当价格重新回到区间时你的资产比例已经改变可能造成更大的无常损失因此v3需要流动性提供者更加主动的管理仓位定期调整自己的价格区间。v3实际上是v2的推广我们把v3的价格区间设置为0-无穷大v3就退化为v2了我们可以把v2看作v3的特例v2流动性在整个价格区间均匀分布v3允许流动性在不同区间有不同的密度这是最关键的区别。他们底层都是恒定乘积公式只是v3通过离散分段的算法实现了虚拟流动性也叫集中流动性通过坐标变换实现了滑点和流动性的线性变化。凭证v3使用的凭证是非同质化代币v2中使用的同质化代币因为v2流动性提供者提供的仓位都是相同的v3中每个提供者提供的价格区间不同仓位是独特的所以转为用同质化代币NFT作为凭证。熟悉这些算法对我们有什么好处呢1、价格预测在交易之前我们需要预估能够获取的输出数量。对于v2直接套公式即可对于v3需要模拟跨越多个Tick计算每个tick贡献的输出数量。2、滑点控制大额交易容易导致较大的滑点v3的流动性集中产生的滑点通常比v2小。3、路径优化有时通过多跳路径可以获得更好的价格我们可以根据最短路径算法维护我们常用代币的交易图选择最优路径。4、Gas优化v3算法复杂gas消耗高。对于小额交易最好选择v2开发的过程中需要在价格优势和gas费中做权衡。