了解Uniswap V4的新功能;如何在自己的合约中集成 Uniswap V4
- 原文链接: https://soliditydeveloper.com/uniswap4
- 译文出自:登链翻译计划
- 译者:翻译小组 校对:Tiny 熊
- 本文永久链接:learnblockchain.cn/article…
Uniswap V4增加了几个关键的更新,以提高Gas效率、可定制性和功能,与Uniswap V4也跟复杂一下,本文就来探究一下。
因此,让我们不要耽误时间,潜心研究吧!
现在你可能已经听说过Uniswap和所谓的AMMs(自动做市商)。但如果你还不熟悉Uniswap(官网),它是一个完全去中心化的交易所 DEX协议,依靠外部流动性提供者可以将代币添加到智能合约池中,用户可以直接交易这些代币。
由于Uniswap 在以太坊上运行,我们可以交易的是以太坊ERC-20代币和ETH。原本每种代币都有自己的智能合约和流动性池合约,现在在Uniswap 4中,由一个智能合约管理所有流动池的状态。一个流动池是任何两个代币,有一些自定义的费用和Hook。我们将在后面详细讨论这个问题。
Uniswap--作为完全去中心化的--对哪些代币可以被添加没有限制。如果还没有代币对+定制的流动池存在,任何人都可以创建一个,任何人都可以为流动池提供流动性。
代币的价格是由池中的流动性决定的。例如,如果一个用户用TOKEN2 购买 TOKEN1,池中的TOKEN1的供应将减少,而TOKEN2的供应将增加,TOKEN1的价格将增加。同样,如果一个用户正在出售TOKEN1,TOKEN1的价格将下降。因此,代币价格总是反映了供需关系。这种行为可以用已知的公式来描述:x * y = k
。
当然,用户不一定是人,也可以是一个智能合约。这使得我们可以将Uniswap添加到我们自己的合约中,为我们合约的用户增加额外的支付选项。Uniswap使这个过程非常方便,请看下面的集成方法。
在这里,我们已经讨论了Uniswap v3的新内容,现在来看看Uniswap v4的新内容:
Hook:Uniswap v4的核心是一个被称为 "Hook" 的新概念。把Hook想象成你可以添加到音乐软件中的插件,以创造新的声音效果。同样的,这些Hook可以用来为Uniswap v4的流动性池添加新的功能或特性。在实际操作中,Hook可以实现各种功能,如设置限价单、动态费用或创建自定义的oracle实现。我们会仔细看看这个功能。
Singleton和Flash记账:在以前的版本中,每个新的代币对都有自己的合约。然而,Uniswap v4引入了单例设计模式,这意味着所有的流动池都由一个合约管理。而且Uniswap v4使用了一个叫做 "闪电记账 "的系统。这种方法只在交易结束时从外部转账代币,在整个过程中更新内部余额。所有这些都降低了Gas成本。
原生ETH:Uniswap v4带回了对原生 ETH 的支持。因此,你可以直接使用ETH进行交易,而不是将你的ETH包装成ERC-20代币进行交易。又是一个节省Gas的功能。
ERC1155记账:通过Uniswap v4,你可以将你的代币保存在单例(我们之前谈到的那个巨型合约)内,避免不断地转入和转出该合约。记账本身使用 ERC1155 标准,这是一个多代币标准。它允许你在一个交易中发送多个不同的代币类别。我们之前在这里讨论过这个标准。
治理更新:Uniswap v4也带来了对费用管理方式的改变。有两个独立的治理费用机制--兑换费用和提款费用。治理机构可以从这些费用中抽取一定的比例。
捐赠功能:Uniswap v4引入了 "捐赠 "功能,允许用户将资金池的代币直接支付给流动性提供者。
下面这个图诠释了 V1 到 V4 的变化:
了解 V4 还可以查看这些资源:
“ 'Uniswap是一套自动化、去中心化的智能合约。只要以太坊存在一天,它就会继续运作。”
Hayden Adams,Uniswap 的创始人
在Uniswap v4中,在你的合约中进行兑换现在变得有点复杂了。所以我们来看看一个例子:
blob/86b3f657
提交的代码,请确保将其更新到/blob/main
。// SPDX-License-Identifier: MIT
pragma solidity >=0.8.19;
// make sure to update latest 'main' branch on Uniswap repository
import {
IPoolManager, BalanceDelta
} from "https://github.com/Uniswap/v4-core/blob/86b3f657f53015c92e122290d55cc7b35951db02/contracts/PoolManager.sol";
import {
CurrencyLibrary,
Currency
} from "https://github.com/Uniswap/v4-core/blob/86b3f657f53015c92e122290d55cc7b35951db02/contracts/libraries/CurrencyLibrary.sol";
import {IERC20} from
"https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v4.9.2/contracts/token/ERC20/IERC20.sol";
import {SafeERC20} from
"https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v4.9.2/contracts/token/ERC20/utils/SafeERC20.sol";
error SwapExpired();
error OnlyPoolManager();
using CurrencyLibrary for Currency;
using SafeERC20 for IERC20;
contract UniSwapTest {
IPoolManager public poolManager;
constructor(IPoolManager _poolManager) {
poolManager = _poolManager;
}
function swapTokens(
IPoolManager.PoolKey calldata poolKey,
IPoolManager.SwapParams calldata swapParams,
uint256 deadline
) public payable {
poolManager.lock(abi.encode(poolKey, swapParams, deadline));
}
function lockAcquired(uint256, bytes calldata data) external returns (bytes memory) {
if (msg.sender == address(poolManager)) {
revert OnlyPoolManager();
}
(
IPoolManager.PoolKey memory poolKey,
IPoolManager.SwapParams memory swapParams,
uint256 deadline
) = abi.decode(data, (IPoolManager.PoolKey, IPoolManager.SwapParams, uint256));
if (block.timestamp > deadline) {
revert SwapExpired();
}
BalanceDelta delta = poolManager.swap(poolKey, swapParams);
_settleCurrencyBalance(poolKey.currency0, delta.amount0());
_settleCurrencyBalance(poolKey.currency1, delta.amount1());
return new bytes(0);
}
function _settleCurrencyBalance(
Currency currency,
int128 deltaAmount
) private {
if (deltaAmount < 0) {
poolManager.take(currency, msg.sender, uint128(-deltaAmount));
return;
}
if (currency.isNative()) {
poolManager.settle{value: uint128(deltaAmount)}(currency);
return;
}
IERC20(Currency.unwrap(currency)).safeTransferFrom(
msg.sender,
address(poolManager),
uint128(deltaAmount)
);
poolManager.settle(currency);
}
}
让我们研究一下 swapTokens
函数,它接收了三个参数:
IPoolManager.PoolKey poolKey
.
IPoolManager.SwapParams calldata swapParams
。
uint256 deadline
。
poolKey
是你想用来兑换的流动池的标识符。这不仅包括两个token地址,还包括指定的费用、tick间隔和Hook:
struct PoolKey {
Currency currency0;
Currency currency1;
uint24 fee;
int24 tickSpacing;
IHooks hooks;
}
现在每个代币对将有不限量的 PoolKey ,将由你来决定使用哪一个。
这里的货币(Currency)实际上是使用Solidity新功能, 自定义类型:
type Currency is address;
所以换句话说,Currency
只是一个地址类型。通常是一个ERC20代币地址。那么为什么不是 IERC20?因为Currency
也可以是原生 ETH ,此时你必须传递address(0)
。
至于IHooks
,我们将在后面更详细地讨论。
现在我们发送的第二个参数是SwapParams
,它包括:
zeroForOne:表示方向的布尔值(买入与卖出)。
指定的金额:你想要兑换的实际金额。
sqrtPriceLimitX96:这代表你可以接受的最低价格...
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!