使用 ethersjs 给 uniswap v3 添加流动性。

2024-04-27by@tim&@ChatGPT.Model: GPT-4
tim

ethersjs 给 uniswap v3 添加流动性。 ABI:

ABI: ```[{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"positions","outputs":[{"internalType":"uint96","name":"nonce","type":"uint96"},{"internalType":"address","name":"operator","type":"address"},{"internalType":"address","name":"token0","type":"address"},{"internalType":"address","name":"token1","type":"address"},{"internalType":"uint24","name":"fee","type":"uint24"},{"internalType":"int24","name":"tickLower","type":"int24"},{"internalType":"int24","name":"tickUpper","type":"int24"},{"internalType":"uint128","name":"liquidity","type":"uint128"},{"internalType":"uint256","name":"feeGrowthInside0LastX128","type":"uint256"},{"internalType":"uint256","name":"feeGrowthInside1LastX128","type":"uint256"},{"internalType":"uint128","name":"tokensOwed0","type":"uint128"},{"internalType":"uint128","name":"tokensOwed1","type":"uint128"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"},{"internalType":"uint256","name":"deadline","type":"uint256"},{"internalType":"uint8","name":"v","type":"uint8"},{"internalType":"bytes32","name":"r","type":"bytes32"},{"internalType":"bytes32","name":"s","type":"bytes32"}],"name":"selfPermitIfNecessary","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"burn","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"totalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"uint256","name":"index","type":"uint256"}],"name":"tokenOfOwnerByIndex","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"PERMIT_TYPEHASH","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"components":[{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"uint128","name":"liquidity","type":"uint128"},{"internalType":"uint256","name":"amount0Min","type":"uint256"},{"internalType":"uint256","name":"amount1Min","type":"uint256"},{"internalType":"uint256","name":"deadline","type":"uint256"}],"internalType":"struct INonfungiblePositionManager.DecreaseLiquidityParams","name":"params","type":"tuple"}],"name":"decreaseLiquidity","outputs":[{"internalType":"uint256","name":"amount0","type":"uint256"},{"internalType":"uint256","name":"amount1","type":"uint256"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"getApproved","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"components":[{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"uint256","name":"amount0Desired","type":"uint256"},{"internalType":"uint256","name":"amount1Desired","type":"uint256"},{"internalType":"uint256","name":"amount0Min","type":"uint256"},{"internalType":"uint256","name":"amount1Min","type":"uint256"},{"internalType":"uint256","name":"deadline","type":"uint256"}],"internalType":"struct INonfungiblePositionManager.IncreaseLiquidityParams","name":"params","type":"tuple"}],"name":"increaseLiquidity","outputs":[{"internalType":"uint128","name":"liquidity","type":"uint128"},{"internalType":"uint256","name":"amount0","type":"uint256"},{"internalType":"uint256","name":"amount1","type":"uint256"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount0Owed","type":"uint256"},{"internalType":"uint256","name":"amount1Owed","type":"uint256"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"uniswapV3MintCallback","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"nonce","type":"uint256"},{"internalType":"uint256","name":"expiry","type":"uint256"},{"internalType":"uint8","name":"v","type":"uint8"},{"internalType":"bytes32","name":"r","type":"bytes32"},{"internalType":"bytes32","name":"s","type":"bytes32"}],"name":"selfPermitAllowed","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"bytes","name":"_data","type":"bytes"}],"name":"safeTransferFrom","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"approve","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"operator","type":"address"}],"name":"isApprovedForAll","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"token0","type":"address"},{"internalType":"address","name":"token1","type":"address"},{"internalType":"uint24","name":"fee","type":"uint24"},{"internalType":"uint160","name":"sqrtPriceX96","type":"uint160"}],"name":"createAndInitializePoolIfNecessary","outputs":[{"internalType":"address","name":"pool","type":"address"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"bytes[]","name":"data","type":"bytes[]"}],"name":"multicall","outputs":[{"internalType":"bytes[]","name":"results","type":"bytes[]"}],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"refundETH","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"baseURI","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"pure","type":"function"},{"inputs":[{"components":[{"internalType":"address","name":"token0","type":"address"},{"internalType":"address","name":"token1","type":"address"},{"internalType":"uint24","name":"fee","type":"uint24"},{"internalType":"int24","name":"tickLower","type":"int24"},{"internalType":"int24","name":"tickUpper","type":"int24"},{"internalType":"uint256","name":"amount0Desired","type":"uint256"},{"internalType":"uint256","name":"amount1Desired","type":"uint256"},{"internalType":"uint256","name":"amount0Min","type":"uint256"},{"internalType":"uint256","name":"amount1Min","type":"uint256"},{"internalType":"address","name":"recipient","type":"address"},{"internalType":"uint256","name":"deadline","type":"uint256"}],"internalType":"struct INonfungiblePositionManager.MintParams","name":"params","type":"tuple"}],"name":"mint","outputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"uint128","name":"liquidity","type":"uint128"},{"internalType":"uint256","name":"amount0","type":"uint256"},{"internalType":"uint256","name":"amount1","type":"uint256"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"amountMinimum","type":"uint256"},{"internalType":"address","name":"recipient","type":"address"}],"name":"sweepToken","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256","name":"index","type":"uint256"}],"name":"tokenByIndex","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"ownerOf","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"WETH9","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"nonce","type":"uint256"},{"internalType":"uint256","name":"expiry","type":"uint256"},{"internalType":"uint8","name":"v","type":"uint8"},{"internalType":"bytes32","name":"r","type":"bytes32"},{"internalType":"bytes32","name":"s","type":"bytes32"}],"name":"selfPermitAllowedIfNecessary","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"amountMinimum","type":"uint256"},{"internalType":"address","name":"recipient","type":"address"}],"name":"unwrapWETH9","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"bytes4","name":"interfaceId","type":"bytes4"}],"name":"supportsInterface","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"DOMAIN_SEPARATOR","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"transferFrom","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"},{"internalType":"uint256","name":"deadline","type":"uint256"},{"internalType":"uint8","name":"v","type":"uint8"},{"internalType":"bytes32","name":"r","type":"bytes32"},{"internalType":"bytes32","name":"s","type":"bytes32"}],"name":"selfPermit","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"components":[{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"address","name":"recipient","type":"address"},{"internalType":"uint128","name":"amount0Max","type":"uint128"},{"internalType":"uint128","name":"amount1Max","type":"uint128"}],"internalType":"struct INonfungiblePositionManager.CollectParams","name":"params","type":"tuple"}],"name":"collect","outputs":[{"internalType":"uint256","name":"amount0","type":"uint256"},{"internalType":"uint256","name":"amount1","type":"uint256"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"uint256","name":"deadline","type":"uint256"},{"internalType":"uint8","name":"v","type":"uint8"},{"internalType":"bytes32","name":"r","type":"bytes32"},{"internalType":"bytes32","name":"s","type":"bytes32"}],"name":"permit","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"operator","type":"address"},{"internalType":"bool","name":"approved","type":"bool"}],"name":"setApprovalForAll","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"tokenURI","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"factory","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"}]```
Avatar
Avatar

要使用 ethers.js 和给定的 ABI 在 Uniswap v3 上添加流动性,你可以按照以下步骤操作:

步骤 1: 初始化 ethers.js 和合约

首先,你需要安装 ethers.js 库(如果尚未安装):

npm install ethers

然后,你可以用以下代码初始化 ethers.js 和 Uniswap v3 的 NonfungiblePositionManager 合约:

const { ethers } = require('ethers');

// 连接到以太坊网络(这里使用的是主网,你可以根据需要更改为测试网或其他网络)
const provider = new ethers.providers.JsonRpcProvider('https://mainnet.infura.io/v3/YOUR_INFURA_API_KEY');

// Uniswap v3 NonfungiblePositionManager 的地址
const positionManagerAddress = '0xC36442b4a4522E871399CD717aBDD847Ab11FE88';

// ABI
const abi = [...] // 使用你提供的 ABI

// 初始化合约
const positionManager = new ethers.Contract(positionManagerAddress, abi, provider);

步骤 2: 准备交易和钱包

你需要一个签名者(通常是你的钱包),以便发送交易:

const privateKey = '你的私钥';
const wallet = new ethers.Wallet(privateKey, provider);
const positionManagerWithSigner = positionManager.connect(wallet);

步骤 3: 添加流动性

你需要决定以下参数:

  • token0token1 的地址
  • 费用等级(例如 500300010000
  • 价格范围的上下限(tickLowertickUpper
  • 想要添加的每种代币的数量(amount0Desiredamount1Desired
  • 最小接受数量(amount0Minamount1Min
  • 截止时间(deadline

然后,你可以调用 mint 函数:

const mintParams = {
    token0: '0x...', // token0 地址
    token1: '0x...', // token1 地址
    fee: 3000,
    tickLower: -60000,
    tickUpper: 60000,
    amount0Desired: ethers.utils.parseUnits('1.0', 18),  // 代币数量需要根据代币的小数位数来调整
    amount1Desired: ethers.utils.parseUnits('500.0', 6),
    amount0Min: ethers.utils.parseUnits('0.9', 18),
    amount1Min: ethers.utils.parseUnits('450.0', 6),
    recipient: wallet.address,
    deadline: Math.floor(Date.now() / 1000) + (24 * 60 * 60) // 现在时间 + 24小时
};

const tx = await positionManagerWithSigner.mint(mintParams);
console.log('Transaction submitted:', tx.hash);

await tx.wait();
console.log('Transaction confirmed:', tx.hash);

确保所有参数都正确无误,并且你的钱包中有足够的 ETH 来支付交易费用。

步骤 4: 确认交易

经过一段时间后,交易应该会被网络确认。你可以检查区块链上的状态,确认流动性已成功添加。

以上是在 Uniswap v3 上添加流动性的基本步骤。在实际操作中,你可能还需要考虑其他因素,如滑点保护、交易费用优化等。

tim

如何确定 amount0Desired,amount1Desired,amount0Min和amount1Min

Avatar
Avatar

在 Uniswap v3 中添加流动性时,确定 amount0Desiredamount1Desiredamount0Minamount1Min 需要对交易对的当前市场价格以及你设定的价格范围(通过 tickLowertickUpper 表示)有一定了解。以下是确定这些参数的一些步骤和考虑因素。

步骤 1: 理解价格范围和当前市场价格

  1. 当前市场价格:了解交易对当前的市场价格,这是设定流动性范围的基础。
  2. 价格范围 (tickLowertickUpper):这定义了你希望提供流动性的价格区间。你应该根据预期的市场波动和你的风险偏好来设定这个范围。

步骤 2: 计算流动性

Uniswap v3 允许你在指定的价格范围内提供非均匀的流动性。这意味着你可以选择在某个特定的价格区间内集中流动性。流动性的计算需要依据下列公式:

[ \text{liquidity} = \frac{\text{amount of tokens}}{\text{price range per token unit}} ]

  • 对于 token0 (例如 ETH):
    [ \text{liquidity} = \frac{\text{amount0Desired}}{\text{tickUpper} - \text{current tick}} ]

  • 对于 token1 (例如 USDC):
    [ \text{liquidity} = \frac{\text{amount1Desired}}{\text{current tick} - \text{tickLower}} ]

步骤 3: 设定 Desired 和 Minimum 量

  • Desired Amounts (amount0Desired, amount1Desired): 这是你愿意投入的最大代币数量。通常基于你愿意承担的投资额。
  • Minimum Amounts (amount0Min, amount1Min): 这是在价格滑动或其他因素影响交易执行时你能接受的最小代币数量。通常设定为 Desired Amount 的一个百分比,如90%。

步骤 4: 使用实际数据模拟

在决定具体的数字之前,可以使用 Uniswap v3 的模拟器或其他 DeFi 分析工具来评估不同设置下的潜在收益和风险。

实用提示:

  • 滑点保护: 确保你的 amountMin 足够低以防止在价格变动时交易失败,但又足够高以保护你不会在不利的价格下交易。
  • 费用级别: 不同的费用级别(例如 0.05%,0.30%,1.00%)适用于不同的预期交易频率和波动性。选择适合你的资产对和策略的费用级别。
  • 考虑未来的价格变动: 考虑市场可能的走势并相应地调整你的 tickLowertickUpper

在实际操作之前,建议详细了解 Uniswap v3 的机制和测试不同的策略配置。