Use js to interact with Uniswap V3 (1) - Create liquidity

Network Configuration

goerli testnet

Use Infura to obtain the RPC of the goerli test network and find a way to get some water.

Contracts used

Uniswap V3 Contract
Contract name Contract address
UniswapV3Factory 0x1F98431c8aD98523631AE4a59f267346ea31F984
SwapRouter 0xE592427A0AEce92De3Edee1F18E0157C05861564
NonfungiblePositionManager 0xC36442b4a4522E871399CD717aBDD847Ab11FE88
WETH9 0xB4FBF271143F4FBf7B91A5ded31805e42b2208d6

Create a liquidity pool

token sorting

Guaranteed token0 < token1

Weth packing

If you want to create XXX/ETH liquidity and need to package ETH into WETH, please use the WETH9 contract for packaging. The contract address of this WETH9 can be obtained from the WETH9 parameter of the NonfungiblePositionManager contract.

const { ethers, BigNumber : BN } = require('ethers');
const provider = new ethers.providers.JsonRpcProvider(process.env.GOERLI_TEST_RPC);
const wallet = new ethers.Wallet(process.env.DEPLOYER_PK, provider);
const WETH9 = {
    address:'0xB4FBF271143F4FBf7B91A5ded31805e42b2208d6',
    abi:'',
    decimals:18,
}

//Create Contract for WETH9
const contract = new ethers.Contract(WETH9.address, WETH9.abi, wallet);
(async () =>{
    //eg. you want wrap 1 ether to weth
    const amount = ethers.utils.parseUnits("1", WETH9.decimals);
    await contract.deposit({value:amount});
})()

Authorized contract consumption limit

WETH does not need to authorize a consumption limit. Other Tokens need to check the allowance to determine whether a new limit needs to be authorized.

const contract = new ethers.Contract(token.address, token.abi, wallet);
const tokenAllowance = await contract.allowance(owner, spender);
if(tokenAllowance < yourDesiredAmount){
    await contract.approve(spender, yourDesiredAmount);
}

Create Pool

core methods

NonfungiblePositionManager.sol > createAndInitializePoolIfNecessary()

Pass in parameters

There is nothing to say about the address of token0 and the address of token1. Note that they are sorted tokens.

There are several fee options to choose from: 500, 3000, and 10,000, and the corresponding tickerSpacing are 10, 60, and 200 respectively.

The last parameter price is the square root of the price of token0 relative to token1 in Q96.64 format, that is

How many token1 can be exchanged for one token0, and then multiply the square root of this value by 2 to the power of 96. The code is implemented as follows

const bn = require('bignumber.js');
const {BigNumber, BigNumberish} = require("ethers");

bn.config({ EXPONENTIAL_AT: 999999, DECIMAL_PLACES: 40 })
function encodePriceSqrt(reserve1, reserve0) {
    return BigNumber.from(
        new bn(reserve1.toString())
            .div(reserve0.toString())
            .sqrt()
            .multipliedBy(new bn(2).pow(96))
            .integerValue(3)
            .toString()
    )
}

//eg. 
const sqrtPriceX96 = encodePriceSqrt(`token1's amount`, `token0's amount`);

mint liquidity

core methods

NonfungiblePositionManager.sol > mint()

The incoming parameter is an object

const param = {
        token0:token0.address,
        token1:token1.address,
        fee:fee,
        tickLower:tickLower,
        tickUpper:tickUpper,
        amount0Desired:token0.amount,
        amount1Desired:token1.amount,
        amount0Min:token0.amount.mul(90).div(100),
        amount1Min:token1.amount.mul(90).div(100),
        recipient:wallet.address,
        deadline:deadline,
        sqrtPriceX96:sqrtPriceX96.toString(),
    };

The first 3 parameters are nothing to explain.

The key is the 4th and 5th ticks. This tick requires a special method to handle. In theory, the calculation of this tick only requires the following formula:

tick=\log_{1.0001}{price}

However, the resulting tick needs to be evenly divided by tickerSpacing , otherwise the call will be reverted without any useful error information, as shown below:

 So a method is needed to convert:

function priceToTick(price, tickSpacing) {
    let tick = Math.floor(Math.log(price) / Math.log(1.0001));
    for (let i = 1; i < tickSpacing; i++){
        if((tick - i) % tickSpacing === 0){
            tick = tick - i;
            break;
        }
        if((tick + i) % tickSpacing === 0){
            tick = tick + i;
            break;
        }
    }
    return tick;
}

Then execute the mint method. At this point, the flow creation is completed

Guess you like

Origin blog.csdn.net/bbandxq521/article/details/130847376