我正在开发一个名为 UniSwap V3 合约的项目。我已经阅读了白皮书、文档以及网上能找到的所有内容来解决这个问题,但一无所获。所以我来这里寻求帮助,不仅是为了解决问题本身,也是为了充分理解我在文档中遗漏的地方。
首先是初始化池。当我尝试创建一个新位置时,我发现函数 min() 上的注释指出:
/// @notice Creates a new position wrapped in a NFT
/// @dev Call this when the pool does exist and is initialized. Note that if the pool is created but not initialized
/// a method does not exist, i.e. the pool is assumed to be initialized.
/// @param params The params necessary to mint a position, encoded as `MintParams` in calldata
/// @return tokenId The ID of the token that represents the minted position
/// @return liquidity The amount of liquidity for this position
/// @return amount0 The amount of token0
/// @return amount1 The amount of token1
function mint(MintParams calldata params)
external
payable
returns (
uint256 tokenId,
uint128 liquidity,
uint256 amount0,
uint256 amount1
);
但是,文档中没有任何地方可以找到我应该如何初始化这个池。幸运的是,我发现了一个 github 项目演示了这个功能:
// Creates the new pool
pool = IUniswapV3Pool(
v3Factory.createPool(address(token0), address(token1), fee)
);
/*
Calculates the initial price (in sqrtPriceX96 format)
https://docs.uniswap.org/sdk/guides/fetching-prices
sqrtPriceX96 = sqrt(price) * 2 ** 96
*/
// Lets set the price to be 1000 token0 = 1 token1
uint160 sqrtPriceX96 = encodePriceSqrt(1, 1000);
pool.initialize(sqrtPriceX96);
但是,当我使用这段代码来初始化池时,它从未按预期运行。我会解释如何做。
预期功能是将一对 n ETH 和 m MING 发送到 Uniswap。以下是我的实现方法:
pool = IUniswapV3Pool(
v3Factory.createPool(Address.WETH, addressOfMing, fee)
);
// for testing purpose
// only 1 to 1 worked.
// uint160 sqrtPriceX96 = encodePriceSqrt(1, 1);
uint160 sqrtPriceX96 = encodePriceSqrt(amountOfMing, amountOfETH);
pool.initialize(sqrtPriceX96);
tickSpacing = pool.tickSpacing();
(uint256 _tokenId, , , ) =
mintNewPosition(amountOfETH, amountOfMing);
这是 mintNewPosition() 的实现:
IERC20 ming = IERC20(addressOfMing);
console.log("amount to mint new position: %s -> %s", amountOfETH, amountOfMing);
require(
ming.balanceOf(address(this)) >= amountOfMing,
"insufficient Ming"
);
require(
weth.balanceOf(address(this)) >= amountOfETH,
"insufficient WETH"
);
// Approve the position manager
TransferHelper.safeApprove(Address.WETH, address(nonfungiblePositionManager), amountOfETH);
TransferHelper.safeApprove(addressOfMing, address(nonfungiblePositionManager), amountOfMing);
// Get tick spacing
(, int24 curTick, , , , , ) = pool.slot0();
curTick = curTick - (curTick % tickSpacing);
int24 lowerTick = curTick - (tickSpacing * 2);
int24 upperTick = curTick + (tickSpacing * 2);
require(curTick % tickSpacing == 0, 'tick error');
INonfungiblePositionManager.MintParams memory params =
INonfungiblePositionManager.MintParams({
token0: pool.token0(),
token1: pool.token1(),
fee: poolFee,
tickLower: lowerTick,
tickUpper: upperTick,
amount0Desired: amountOfETH,
amount1Desired: amountOfMing,
amount0Min: 0,
amount1Min: 0,
recipient: address(this),
deadline: block.timestamp
});
// Note that the pool defined by DAI/USDC and fee tier 0.3% must already be
// created and initialized in order to mint
(tokenId, liquidity, amount0, amount1) = nonfungiblePositionManager.mint(params);
console.log("new position minted: %s, %s", amount0, amount1);
据我了解,当我调用 pool.initialize(sqrtPriceX96) 时,它决定了 MING 对于 ETH 的价格。因此,在使用相同数量的 ETH/MING 对调用 mint() 时,返回的 ETH/MING 数量应该相同,因为在此过程中没有滑点。
但是,这就是我得到的:
amount to mint new position: 3000000000000000000 -> 222222222222222000000000000000000
new position minted: 3000000000000000000, 25559352082992436084
我做错了什么吗?
----------------------------------------更新1--------------------- ---- 我在库 PoolAddress 中找到: require(key.token0 < key.token1) therefore I edited the following code in the pool creation:
if(Address.WETH < addressOfMing){
console.log("init pool");
pool = IUniswapV3Pool(
v3Factory.createPool(Address.WETH, addressOfMing, fee)
);
uint160 sqrtPriceX96 = encodePriceSqrt(amountOfMing, amountOfETH);
pool.initialize(sqrtPriceX96);
tickSpacing = pool.tickSpacing();
(uint256 _tokenId, , , ) = mintNewPosition(amountOfETH, amountOfMing);
tokenId = _tokenId;
}else{
console.log("init pool reversed");
pool = IUniswapV3Pool(
v3Factory.createPool(addressOfMing, Address.WETH, fee)
);
uint160 sqrtPriceX96 = encodePriceSqrt(amountOfETH, amountOfMing);
pool.initialize(sqrtPriceX96);
tickSpacing = pool.tickSpacing();
(uint256 _tokenId, , , ) = mintNewPosition(amountOfMing, amountOfETH);
tokenId = _tokenId;
}
问题还是一样。然而,当我将 lowerTick&upperTick 增加 1000x
int24 lowerTick = curTick - (tickSpacing * 1000);
int24 upperTick = curTick + (tickSpacing * 1000);
我得到了比预期更好的结果。
amount to mint new position 222222222222222000000000000000000, 3000000000000000000
new position minted -> 222222222222221995864146677687271, 2999106664470400961
然而,它并没有按预期工作。只要价格在池初始化时确定,通过提供相同数量的代币对,上限和下限不应(极大)影响添加到流动性中的代币数量。
我和你有同样的问题,我必须根据你的发现自己编写代码才能找到初始价格
我已经用solidity编写了它,所以你可能需要在js中重新实现它以供你使用
// src/library/UniswapV3PricingHelper.sol
library UniswapV3PricingHelper {
uint256 private constant padding = 100;
// Constants
uint256 private constant Q96 = 2 ** 96;
function _sortTokens(
address _tokenA,
address _tokenB
) internal pure returns (address _sortedTokenA, address _sortedTokenB) {
require(_tokenA != address(0) && _tokenB != address(0), "Token addresses cannot be zero.");
// Sort the token addresses
(_sortedTokenA, _sortedTokenB) = _tokenA < _tokenB ? (_tokenA, _tokenB) : (_tokenB, _tokenA);
}
function getInitPrice(
address _tokenBase,
address _tokenSwap,
uint256 tokenBaseAmt,
uint256 tokenSwapAmt
) internal pure returns (address token0, address token1, uint160 initSQRTPrice) {
(token0, token1) = _sortTokens(_tokenBase, _tokenSwap);
if (token0 != _tokenBase) initSQRTPrice = encodePriceSqrt(tokenSwapAmt, tokenBaseAmt);
else initSQRTPrice = encodePriceSqrt(tokenBaseAmt, tokenSwapAmt);
}
// Encode price square root function
function encodePriceSqrt(uint256 reserve0, uint256 reserve1) public pure returns (uint160 sqrtPriceX96) {
require(reserve0 > 0 && reserve1 > 0, "Reserves must be positive");
uint256 ratio = Math.sqrt(((reserve1 * padding) / reserve0) / padding) * Q96;
sqrtPriceX96 = uint160(ratio);
}
}
在我的库中,_tokenbase 是基础资产,在我的例子中,它是 WETH,tokenSwap 是你想要与 weth 配对的代币,比如说 dai、usdc 或其他东西
此代码假设基础代币和交换代币均为 18 位小数,您需要处理任一代币低于 18 位小数的情况,并可能对金额进行四舍五入
@shinado 我正在经历一些与 UniswapV3 类似的问题,在尝试建立仓位时不断出现气体估计错误,想知道您是否可以在这里为我提供任何指导?