{"content":{"title":"Uniswap V3 释疑: 集中流动性, 无常损失和滑点","body":"> * 原文链接：https://kushgoyal.com/uniswap-v3-expalined-concentrated-liquidity/\r\n> * 译文出自：[登链翻译计划](https://github.com/lbc-team/Pioneer)\r\n> * 译者：[songmint](https://learnblockchain.cn/people/13263)  校对：[Tiny 熊](https://learnblockchain.cn/people/15)\r\n> * 本文永久链接：[learnblockchain.cn/article…](https://learnblockchain.cn/article/5202)\r\n\r\n\r\nUniswap 协议是一组原生的ETH的智能合约，它可以实现 ERC20代币与ERC20代币的交换, 以及ERC20代币与ETH之间的的交换。\r\n\r\n\r\nUniswap 使用自动做市商 (AMM) 算法来执行交易。用户以代币对的形式创建流动性池子,并在其中提供流动性。执行交易就是将所提供的代币存入池中,并从池中提取所请求的代币。 交易费则以被请求代币的形式, 分配给流动性提供者 (LP)。\r\n\r\n[Uniswap V3](https://uniswap.org/blog/uniswap-v3/)是该协议的最新版本，引入了集中流动性等诸多概念。在 V3 中，根据提供流动性的风险，存在几个可用的费用等级。费用在池中的2种代币上收取，而不会重新投资到池中。\r\n\r\nUNI 是 Uniswap 协议的治理代币。 将来,UNI 代币持有者可能有资格获得[协议费用](https://docs.uniswap.org/concepts/V3-overview/fees#protocol-fees)。当前的协议费率为 0%。 UNI 代币持有者可以更改协议费率。\r\n\r\n\r\n## 集中流动性\r\nUniswap V3 使用[集中流动性](https://docs.uniswap.org/concepts/V3-overview/concentrated-liquidity) 做市算法 (CLMM)，这是比标准的常数乘积做市 (CPMM) 算法更有效的算法.\r\n\r\n每个池中有两种代币，分别是token0 和 token1。token0 的价格 (P) 以 token1 表示。例如，UNI<>ETH 池中，每 1个ETH 可以兑换100个UNI。\r\n\r\n在 CLMM（ 集中流动性做市算法）中，LP必须选择合适的价格范围以提供流动性。如果价格P移到某个池的范围之外，该池的流动性将变为非活跃状态。交易将在下一个可用的池中进行。\r\n\r\n在 CLMM 中，池子跟踪[价格的平方根](https://uniswap.org/whitepaper-v3.pdf) (P) 和池中的流动性 (L)。 此时已不再需要池中的已有代币数量用来计算兑换结果。\r\n\r\n以下公式定义了代币数量、价格和流动性之间的关系。\r\n\r\n\r\n```js\r\n# x 表示token0的数量, y 表示token1的数量\r\n# 以token1为单位 计算出的token0的价格\r\nP = y / x\r\n\r\n# 流动性是代币数量的几何平方数\r\nL = sqrt(x*y)\r\n```\r\n在 V3 中，流动性被定义为:给定平方根P的变化值，token1 数量的变化值。 基于此概念，下面的公式可用于计算你请求的代币数量。\r\n\r\n\r\n```js\r\nΔy = Δ(√P) * L \r\nΔx = Δ(1/√P) * L\r\n```\r\n上述公式用于计算相邻tick的价格变动。 其中tick是一个整数，可用于计算价格。 tick计算价格的公式如下\r\n\r\n\r\n```js\r\nP = 1.0001^i\r\nsqrt(P) = 1.0001^(i/2)\r\ni = log(sqrt(P)) * 2 / log(1.0001)\r\n```\r\n\r\n每个tick与相邻tick的距离为0.1%。 如果一笔交易导致的价格变动超出了该tick对应的价格范围，则交易按照顺序跨越过一个个tick, 每达到一个tick就进行交换，直到交易请求中的所有代币都被交换完成。\r\n\r\n当价格处于两个tick之间的价格范围内时， CLMM遵循常数乘积公式。 因此CLMM可以被看做是常数乘积公式的变体。\r\n\r\n下面是我编写的python脚本，模拟了使用CLMM进行交易的过程。我忽略了交易手续费，只实现了从token1 到token0 的交换。\r\n\r\n\r\n```js\r\n\r\nimport math\r\n\r\n\r\ndef calc_tick(rp):\r\n    # P = 1.0001 ^ i\r\n    # sqrt(P) = 1.0001 ^ (i / 2)\r\n    # i = log(sqrt(P)) * 2 / log(1.0001)\r\n    return (math.log(rp) * 2) / math.log(1.0001)\r\n\r\n\r\n\r\ndef calc_sqrt_price(i):\r\n    # sqrt(P) = 1.0001 ^ (i / 2)\r\n    return math.pow(1.0001, i/2)\r\n\r\n\r\n\r\n\r\ndef swap(offered_y, x, y):\r\n    delta_y = offered_y\r\n    liquidity = math.sqrt(x * y)\r\n    delta_sqrt_price = delta_y / liquidity\r\n    sqrt_price = math.sqrt(y / x)\r\n    tick_start = math.floor(calc_tick(sqrt_price))\r\n    tick_finish = math.floor(calc_tick(sqrt_price + delta_sqrt_price))\r\n    diff = tick_finish - tick_start\r\n    delta_x = 0\r\n    for tick in range(0, diff):\r\n        # calculate the delta_sqrt_price\r\n        tick_sqrt_price = calc_sqrt_price(tick_start + tick + 1)\r\n        delta_sqrt_price = tick_sqrt_price - sqrt_price\r\n        inverse_delta_sqrt_price = (1 / sqrt_price) - (1 / tick_sqrt_price)\r\n        # check how much y is left to swap\r\n        if delta_y - (delta_sqrt_price * liquidity) > 0:\r\n            delta_y -= (delta_sqrt_price * liquidity)\r\n            delta_x += (liquidity * inverse_delta_sqrt_price)\r\n        else:\r\n            # delta_y is exhausted for the integer value of tick\r\n            break\r\n    # apply the same logic for an exchange within adjacent tick\r\n    if delta_y > 0:\r\n        delta_sqrt_price = delta_y / liquidity\r\n        fractional_tick = calc_tick(sqrt_price + delta_sqrt_price)\r\n        tick_sqrt_price = calc_sqrt_price(fractional_tick)\r\n        inverse_delta_sqrt_price = (1 / sqrt_price) - (1 / tick_sqrt_price)\r\n        delta_x += (liquidity * inverse_delta_sqrt_price)\r\n    return delta_x\r\n\r\n\r\n\r\n# Press the green button in the gutter to run the script.\r\nif __name__ == '__main__':\r\n    print(swap(1, 10000000, 100000))\r\n```\r\n在 V3 中，流动性池被表示为NFT， 这是因为每个池都彼此不同。由于交易对于价格的影响， 单个交易可能需要跨越多个流动性池。\r\n\r\n与标准常数乘积算法相比，集中流动性的效率更高。 CLMM在每个池子的价格范围内使用池子中的全部流动性。而CPMM将流动性分布在 0 到无穷大之间。 CLMM能做到这一点，是因为有不同的公式来计算池子的新状态（译者注：状态包含流动性，tick值，价格等）。\r\n\r\n## 价格影响\r\n当一笔交易再次与池子进行代币交换时，池中代币的比例会发生变化。池中代币的比例是代币 0 相对于代币 1 的价格（P）。\r\n\r\n在交换开始时，池子中的代币比例是 100UNI : 1ETH。但是直接用1ETH兑换是不会得到100UNI的，这是因为随着交换的进行，池子中的代币比例发生了变化。这称为交易的[价格影响](https://docs.uniswap.org/concepts/introduction/swaps#price-impact)。\r\n\r\n让我们以 UNI<>ETH 池子为例。当前比率为每1个ETH兑换100个UNI。我们将使用V2中的CPMM公式，因为计算起来相对容易，但是依然适用于V3。\r\n\r\n\r\n```js\r\n# x and y are number of tokens\r\n# x_uni = 10000, y_eth = 100\r\nx_uni * y_eth = k\r\n(x_uni - recieve) * (y_eth + deposit) = k\r\n(10000 - receive) * (100 + 1) = 10000 * 100\r\nreceive = 10000 - (10000 * 100 / 101)\r\nreceive = 99.0099\r\n```\r\n在上面的计算中，可以看到付出1 ETH， 可以获得99.0099 UNI 代币。虽然池中代币的比例发生了变化，但代币数量的乘积仍然相同。\r\n\r\n  ## 滑点\r\n一笔交易如果提供了更高的gas， 那么该笔交易先于其他较低gas的交易执行。 但是我们无法预测交易执行的具体时间点。在交易广播和交易执行之间的时间间隙中，可能池子已经发生了变化。池子状态的改变可能导致交易价格与预期的价格大相径庭。这种价格变化被认为是[滑点] (https://docs.uniswap.org/concepts/introduction/swaps#slippage).\r\n\r\n## 无常损失\r\n流动性提供者通过提供流动性来承担风险。池中的代币比例将根据当前市场价格不断变化。套利者与流动性池进行交易，使得代币比率（就是价格）与其他更大市场中的代币比率（价格）相匹配。这种代币的再平衡对LP 来说是有风险的。 因为当他们决定从池中撤回资金时，池中会有更多已经相对贬值的代币。\r\n\r\n举个例子，下面的示例使用 V2的CPMM，因为它有一个简单的公式，但 V3 的概念也相同。\r\n\r\nAlice 和 Bob 决定了 BTC<>ETH 池的资金。我们将看到不同时间点，流动池的状态。 为了计算池的状态，我们需要使用两个方程。\r\n\r\n\r\n```js\r\n# token_x and token_y 分别是代币的数量\r\n# k 是常数乘积，r是代币的比率\r\ntoken_x * token_y = k\r\ntoken_x / token_y = r\r\n# substituting the value of token_y\r\n# 替换方程中的token_y，计算得到\r\ntoken_x^2 / r = k\r\ntoken_x = √(k*r)\r\ntoken_y = √(k/r)\r\n```\r\ntoken_x = BTC, token_y = ETH\r\n\r\nAt T0 r = 1/10 初始池中状态 = 900 BTC + 9000 ETH Alice 存入 100 BTC + 1000 ETH 最终池中状态 = 1000 BTC + 10000 ETH Alice 拥有10% 的池子份额\r\n\r\nAt T1 r = 1/8 初始池中状态 = 1118 BTC + 8944 ETH Bob 存入 80 BTC + 640 ETH 最终池中状态 = 1198 BTC + 9584 ETH Bob 拥有 6.67% 的池子份额 Alice 如今拥有9.33%的池子份额\r\n\r\nAt T2 r = 1/5 初始池中状态 = 1515.36 BTC + 7576.8 ETH\r\n\r\nAlice 决定提取资金 Alice 将获得整个池代币的9.33%么，计算得到为141.38 BTC + 706.91 ETH. 按当前价格计算，折合为208.76 BTC. 如果Alice选择直接持有代币而非提供流动性，那么她将拥有100 BTC + 1000 ETH, 按照当前价格计算，折合为300 BTC. 所以Alice因为做市，实际损失了17.24个BTC\r\n\r\n最终池中状态 = 1373.98 BTC + 6869.89 ETH\r\n\r\nBob 拥有了7.356%的池中份额，并且决定继续保留资金在池子中.\r\n\r\nAt T3 r = 1:8 初始池中状态 = 1086.22 BTC + 8689.76 ETH Bob 决定提取资金 Bob 将获得整个池代币的7.356% ，计算得到为 79.9 BTC + 639.218 ETH. 按照当前价格， 折合为159.8 BTC. (由于进位错误，我们直接看做160 而不是159.8） 如果Bob没有注入流动性，那么 他将拥有80 BTC + 640 ETH。 按照当前价格计算，折合为160 BTC. 我们看到Bob并没有损失， 这是因为此时池中的代币比率相对他的存入时刻的比率， 并没有发生变化。\r\n\r\n这就是它被称为无常损失的原因。如果池中的代币比率与你存入代币时的比率相同，将不会有任何损失。\r\n\r\nLP从每笔交易中收取交易费。如果 LP收取的交易费用大于无常损失，则 LP可以从池中提取资金，获得正收益。"},"author":{"user":"https://learnblockchain.cn/people/13263","address":null},"history":"QmXscm8uPsEuPTGGZnLUvFAj3hwFmZNkDAJWKqSngNxkR9","timestamp":1671438868,"version":1}