本文深入解析了Uniswap v4的四种互换类型:zeroToOne、oneToZero、exactInputForOutput和exactOutputForInput。
你是否曾好奇 Uniswap 如何能如此精准高效地处理各种代币兑换?秘密在于它的四种不同兑换类型,每种类型都在 DeFi 生态系统中扮演着独特的角色。无论你是一位寻求优化策略的交易者,还是一个在 Uniswap 基础上构建的开发者,理解这些兑换机制都至关重要。
在这篇深度解析中,我们将揭示 Uniswap 兑换功能的复杂性。你将学习到:
在本指南结束时,你将对 Uniswap v4 的兑换机制有全面的理解,从而能够构建更强大的 DeFi 应用程序并做出更明智的交易决策。无论你是想优化你的交易、构建更高效的 DeFi 应用程序,还是仅仅满足你对 Uniswap 内部运作的好奇心,本指南都能满足你的需求。
准备好成为 Uniswap 兑换大师了吗?
在我们深入细节之前,让我们先想象一下这四种兑换类型。下图展示了兑换方向(zeroToOne 和 oneToZero)和精确性(exactInputForOutput 和 exactOutputForInput)的不同组合:
1 Token0 Token1
2 | |
3 | |
4 v v
5 +----------------+---------------------+
6 | | |
7 | zeroToOne | exactInputForOutput
8 | | ------------------>
9 | | |
10 | zeroToOne | exactOutputForInput
11 | | ------------------>
12 | | |
13 | oneToZero | exactInputForOutput
14 | | <------------------
15 | | |
16 | oneToZero | exactOutputForInput
17 | | <------------------
18 | | |
19 +----------------+---------------------+
这个简单的可视化图有助于我们理解 Token0 和 Token1 之间的关系,以及不同兑换类型如何在它们之间操作。箭头表示兑换的方向,标签描述了每个操作的精确性类型。
首先,我们有 zeroToOne 和 oneToZero。听起来像是二进制代码,对吗?实际上比那简单。还记得 Uniswap 如何对代币对进行排序吗?Token0 总是“较小”的地址,而 Token1 则是“较大”的地址。所以,zeroToOne 意味着你正在从 Token0 兑换到 Token1,而 oneToZero 则是……你猜对了,反过来!
让我们看看 Uniswap v4 中的实际函数签名:
1function swap(
2 PoolKey memory key,
3 SwapParams memory params,
4 bytes calldata hookData
5) external returns (BalanceDelta);
6
7struct SwapParams {
8 bool zeroForOne;
9 int256 amountSpecified;
10 uint160 sqrtPriceLimitX96;
11}
这里,zeroForOne 是一个布尔值,用于确定兑换方向。true 表示 zeroToOne,而 false 表示 oneToZero。
等等,还有更多!我们还有 exactInputForOutput 和 exactOutputForInput。现在,这才是事情变得有趣的地方。
使用 exactInputForOutput 时,你是在说:“这是我确切想兑换进去的数量,给我另一个代币中等值的任何数量。”这就像带着 100 美元去货币兑换处说:“我能换多少欧元?”
另一方面,exactOutputForInput 是指你说的:“我需要确切的这么多输出代币,拿走我所需的任何输入代币。”这就像告诉货币兑换处:“我需要 100 欧元,这会花我多少美元?”
在代码中,这由 amountSpecified 参数表示:
1int256 amountSpecified;
如果 amountSpecified 是正数,则它是 exactInputForOutput 兑换。如果它是负数,则它是 exactOutputForInput 兑换。
现在我们已经掌握了基础知识,你可能会想:“好吧,但我如何在我的代码中实际使用这些兑换类型呢?”
好问题!让我们卷起袖子,深入了解一些真实的 Uniswap v4 代码。
让我们用宇宙自动售货机类比。在 Uniswap v4 中,你按下进行兑换的“按钮”是 swap 函数。让我们再看一下函数的参数:
1function swap(
2 PoolKey memory key,
3 SwapParams memory params,
3 bytes calldata hookData
5) external returns (BalanceDelta);
PoolKey memory key: 这就像我们自动售货机的地址。它告诉 Uniswap 我们想与哪个特定的池子进行交互。把它想象成说:“我想要在这个代币和那个代币之间进行兑换。”
SwapParams memory params: 这就是真正神奇发生的地方。它是一个结构体,包含了我们兑换的所有详细信息。我们稍后会更深入地探讨这一点,但请记住,这就是我们指定兑换方向和数量等信息的地方。
bytes calldata hookData: 这是为池子附加的 hooks 可能需要的任何额外数据。Hooks 就像可以修改池子行为的小插件。对于大多数基本兑换,你可以将其留空,但知道它的存在是好的。
那我们能得到什么回报呢?
一个 BalanceDelta。它精确地告诉我们兑换后余额是如何变化的。它就像我们宇宙自动售货机的收据,显示我们放入了什么,取出了什么。
现在,让我们放大那个 SwapParams 结构体。这就是我们兑换之旅中的关键所在...
1struct SwapParams {
2 bool zeroForOne;
3 int256 amountSpecified;
4 uint160 sqrtPriceLimitX96;
5}
让我们解码这个宇宙谜题:
zeroForOne: 这个小小的布尔值告诉 Uniswap 我们兑换的方向。它就像一个开关,决定我们是从 Token0 到 Token1 (true),还是反过来 (false)。
amountSpecified: 这是我们指定兑换数量的地方。但这里有一个转折——这个数字的符号(正或负)决定了我们是进行 exactInputForOutput 还是 exactOutputForInput 兑换。是不是很烧脑?
sqrtPriceLimitX96: 不要被这个听起来可怕的名字吓到。它只是我们的安全网,确保如果市场在我们的兑换过程中波动太大,我们不会受到不公平的待遇。(你不知道这是什么?请查看我们关于 sqrtPriceX96 的词汇表)
现在,你可能想知道,“我如何实际使用它来进行我们之前讨论的四种兑换类型呢?”
好吧,很高兴你问了!让我们看一些例子:
精确地用 1 个 Token0 兑换尽可能多的 Token1:
1SwapParams memory params = SwapParams({
2 zeroForOne: true,
3 amountSpecified: 1 ether,
4 // 不要在生产环境中使用!
5 sqrtPriceLimitX96: TickMath.MIN_SQRT_RATIO + 1
6});
兑换精确的 1 个 Token1,使用所需数量的 Token0:
1SwapParams memory params = SwapParams({
2 zeroForOne: true,
3 amountSpecified: -1 ether,
4 // 不要在生产环境中使用!
5 sqrtPriceLimitX96: TickMath.MIN_SQRT_RATIO + 1
6});
看到我们做了什么吗?我们将 amountSpecified 的符号翻转为负,砰——我们就从 exactInputForOutput 切换到了 exactOutputForInput!
另外两种类型(oneToZero-exactInputForOutput 和 oneToZero-exactOutputForInput)遵循相同的模式,只是 zeroForOne 设置为 false。
现在,当我们按下那个比喻性的按钮并调用
swap之后,我们得到了什么回报呢?
一个 BalanceDelta:
1struct BalanceDelta {
2 int256 amount0;
3 int256 amount1;
4}
这是 Uniswap 告诉我们兑换中实际发生了什么的方式。如果 amount0 是负数,这意味着 Token0 离开了池子(我们卖出了它)。如果它是正数,Token0 进入了池子(我们买入了它)。amount1 和 Token1 也是如此。
还记得我们之前提到的 sqrtPriceLimitX96 参数吗?它不仅仅是一个花哨的名字——它是你抵御意外价格波动的第一道防线。在我们之前的示例中,我们将其设置为最小(或最大)可能值,这就像告诉自动售货机“我接受你给我的任何东西!”这对于示例来说没问题,但在现实世界中呢?就没那么好了。
1// 不要在生产环境中使用!
2sqrtPriceLimitX96: TickMath.MIN_SQRT_RATIO + 1
相反,根据当前价格和你可接受的 滑点 计算一个合理的价格限制。这就像为你的兑换设置一个“我不会支付超过 X 的价格”的限制。
并非所有代币都是生而平等的——至少在小数点位数方面是这样。有些代币可能有 18 位小数,有些可能有 6 位,甚至 0 位!
当你兑换具有不同小数位数的代币时,请确保你在计算中考虑了这一点。否则,你最终可能会兑换比你预期多(或少)1000 倍的数量!
想一次性进行大规模兑换?你可能需要重新考虑。大规模兑换可能会对市场产生显著影响,可能导致更高的滑点和更糟糕的交易。相反,考虑将你的大额兑换分成小块。这就像吃鲸鱼——你必须一口一口地吃!
请记住,exactOutputForInput 兑换往往比 exactInputForOutput 兑换更消耗 gas。如果 gas 效率是首要考虑(何时不是呢?),你可能需要尽可能倾向于 exactInputForOutput 兑换。
swap 函数并非总是能成功。可能没有足够的流动性,或者你可能达到了你的价格限制。始终准备好在你的代码中优雅地处理这些情况。请记住,在智能合约的世界里,一笔失败的交易仍然会让你付出 gas 费!
如果你正在处理附带 hooks 的池子(这是另一个我们现在不打开的潘多拉魔盒),请注意这些 hooks 可能会以意想不到的方式修改你的兑换行为。始终了解你正在交互的池子附加了哪些 hooks。
最后但同样重要的是,彻底测试你的兑换实现。使用各种输入数量、不同的代币对,并模拟不同的市场条件。在 DeFi 的世界里,你永远不会嫌太小心!
请记住,强大的兑换能力伴随着巨大的责任。通过牢记这些注意事项,你将很好地成为 Uniswap v4 兑换大师。祝你兑换愉快,愿流动性永远对你有利!
理解这些兑换类型不仅仅是知道调用哪个函数。它是关于打造高效、安全和用户友好的 DeFi 应用程序。无论你是要构建下一个大型 DEX 聚合器,还是只是想在你的交易中获得最佳交易,掌握这些概念都将大有裨益。
请记住,在 Uniswap 的世界里,知识就是力量(并可能带来利润)。所以下次你设置兑换时,花点时间考虑哪个方向和精确性最适合你的需求。祝你兑换愉快!
在 Zealynx,我们深刻理解复杂的 AMM 设计,从集中流动性到新兴的 hook 架构,以及 Uniswap 等协议的安全挑战。无论你是构建新的 DeFi 协议、审计现有协议,还是需要关于 AMM 项目安全性的专家指导,我们的团队随时准备提供帮助——请联系我们。
想了解更多像这样深入的分析吗?订阅我们的新闻简报,确保你不会错过未来的洞察。
在 Uniswap 中,代币对按地址排序,Token0 是“较小”的地址,Token1 是“较大”的地址。zeroToOne 意味着从 Token0 兑换到 Token1(设置 zeroForOne: true),而 oneToZero 则是相反方向(设置 zeroForOne: false)。这个约定确保了所有池子无论如何创建都保持一致的顺序。
当你确切知道要花费多少,并且对收到什么持弹性态度时,使用 exactInputForOutput(正数 amountSpecified)——比如用 100 美元兑换任意数量的欧元。当你需要特定的输出数量而不计成本时,使用 exactOutputForInput(负数 amountSpecified)——比如需要精确的 100 欧元,并支付所需的任何美元金额。
exactOutputForInput 兑换需要额外的计算来确定达到所需输出所需的精确输入量。这涉及到迭代价格计算直到达到精确的输出,而 exactInputForOutput 兑换可以直接从给定输入量计算输出。
sqrtPriceLimitX96 是一个安全参数,它使用 Uniswap 的定点价格格式为你的兑换设置最差可接受价格。它通过在执行价格超出此限制时导致交易回滚来防止过度滑点。在生产环境中切勿将其设置为极端值(MIN_SQRT_RATIO 或 MAX_SQRT_RATIO),因为这会消除所有滑点保护。
BalanceDelta 包含 amount0 和 amount1,代表每个代币的净变化。负值表示代币离开池子(你卖出了它们),而正值表示代币进入池子(你买入了它们)。这个“收据”精确地告诉你兑换中交换了什么。
主要风险包括:如果价格限制过于宽松导致的滑点攻击,代币之间的小数位不匹配导致数量不正确,涉及 hooks 时的重入漏洞,以及对失败兑换的错误处理不足。始终验证输入,设置合理的滑点范围,了解附加的 hooks,并使用各种市场条件进行广泛测试。
本文中使用的关键术语快速参考:
| 术语 | 定义 |
|---|---|
| Automated Market Maker (AMM) | 使用数学公式定价资产的去中心化交易协议。 |
| Hooks | 在特定池子生命周期点执行自定义逻辑的外部智能合约。 |
| BalanceDelta | 由 Uniswap v4 兑换返回的结构体,包含代币余额的净变化。 |
| Exact Input Swap | 用户精确指定花费数量的兑换类型。 |
| Exact Output Swap | 用户精确指定接收数量的兑换类型。 |
| sqrtPriceX96 | 以 2^96 乘以价格平方根表示的定点数格式。 |
| Slippage | 交易的预期执行价格与实际执行价格之间的差异。 |
| Gas | 衡量执行以太坊交易所需计算量的单位。 |
- 原文链接: zealynx.io/blogs/uniswap...
- 登链社区 AI 助手,为大家转译优秀英文文章,如有翻译不通的地方,还请包涵~
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!