使用js与Uniswap V3进行交互实战(一)-创建流动性

网络配置

goerli测试网

使用Infura获取goerli测试网的RPC,想办法搞点水。

使用到的合约

Uniswap V3合约
合约名称 合约地址
UniswapV3Factory 0x1F98431c8aD98523631AE4a59f267346ea31F984
SwapRouter 0xE592427A0AEce92De3Edee1F18E0157C05861564
NonfungiblePositionManager 0xC36442b4a4522E871399CD717aBDD847Ab11FE88
WETH9 0xB4FBF271143F4FBf7B91A5ded31805e42b2208d6

创建流动性池

token排序

保证token0 < token1

Weth打包

如果要创建XXX/ETH流动性需要将ETH打包成WETH,请使用WETH9合约进行打包,这个WETH9的合约地址可以从NonfungiblePositionManager合约的WETH9参数中得到。

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});
})()

授权合约消费额度

WETH无需授权消费额度,其他的Token需要检查allowance来确定是否需要授权新的额度

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);
}

创建Pool

核心方法

NonfungiblePositionManager.sol > createAndInitializePoolIfNecessary()

传入参数

token0的address、token1的address没有好说的,注意是排序后的token。

fee有几个可供选择500、3000、10000,对应的tickerSpacing分别为10、60、200。

最后一个参数price是token0相对于token1的价格的平方根的Q96.64格式,即

一个token0可以兑换多少个token1,然后将这个数值的平方根乘以2的96次方,代码实现如下

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流动性

核心方法

NonfungiblePositionManager.sol > mint()

传入参数为一个对象

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(),
    };

前3个参数没什么好解释的。

关键是第4、5个,这个tick需要一个专门的方法来处理,理论上,这个tick的计算只需下面的公式即可:

tick=\log_{1.0001}{price}

但是得出的tick需要被tickerSpacing整除才可以,否则调用会被revert掉,且没有任何有用的报错信息,如下图:

 因此需要一个方法进行转换:

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;
}

然后执行mint方法即可。至此流动创建完成

猜你喜欢

转载自blog.csdn.net/bbandxq521/article/details/130847376