# UniSwap V3的池子和价格计算

By [kuimale](https://paragraph.com/@kuimale) · 2023-06-27

---

UniSwap V3已经出来好几年了, 因为对defi的友好, 现在逐渐有很多项目由V2迁移到了v3. 我的自动交易机器人在前段时间也支持了V3版本的交易.

今天来讲讲V3里面新的api如何获取池子和计算价格.

### 获取v3项目的池子

我们以常见的池子类型为例, 例如USDC/USDT/WETH.

首先去ethscan上面拿到Factory V3和Pair V3的ABI, 还需要额外的erc20Abi(这是为了后续获取池子信息时使用), 然后拿到Factory的Contract对象.

    improt Web3 from 'web3'
    import erc20Abi from 'xxxx' // 你的ABI存放位置
    import pairAbiV3 from 'xxxx' // 你的ABI存放位置
    import factoryAbiV3 from 'xxxx' // 你的ABI存放位置
    
    const contract = new Web3.eth.Contract(factoryAbiV3, contractAddress)
    

以上, 我们拿到了V3版本的factory contract对象, V3中获取池子的核心方法是\`getPool\`.

且V3中一个最重要的核心就是引入了fee的概念, 它可以根据币价浮动的风险, 使池子可以收取不同的手续费.

我们在获取池子的时候, 一定要把fee考虑进去.

目前, uniswap的fee共有上三档: 0.05%/0.3%/1%, 对应的费率值为500/3000/10000.

关于fee的具体内容这里不做更多的讲解. 而我们要获取v3池子的核心点就是使用不同的池子类型的token1合约去遍历不同的fee, 然后使用计算出的liquidity来确定最活跃的池子.

    const fees = [500, 3000, 10000]
    const USDTAddress = '0xdac17f958d2ee523a2206206994597c13d831ec7'
    const USDCAddress = '0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48'
    const WETHAddress = '0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2'
    
    // 获取最活跃的池子
    // tAddr为erc20代币合约地址
    async function getMaxLiq(lp, tAddr) {
        let pool: string = ''
        let tempPool: string = ''
        let liquidity: string = ''
        let tempLiquidity: string = ''
    
        for(let fee of fees) {
            tempPool = await contract.methods.getPool(lp, tAddr, fee).call()
            if(tempPool.toLowerCase() !== zeroAddr) {
                const poolContract = new Web3.eth.Contract(tempPool, pairAbiV3)
                tempLiquidity = await poolContract.methods.liquidity().call()
                // 比较两个池子的大小
                if (COMPARE_BIGNUM(tempLiquidity, liquidity) >= 1) {
                    pool = tempPool
                    liquidity = tempLiquidity
                }
            }
        }
            
        return { pool, liquidity }
    }
    

现在使用getMaxLiq方法, 我们获取到了USDT/USDC/WETH这几种池子类型下最活跃的池子.

基于获取到的活跃池子信息, 我们使用pool来获取完整的池子信息. 这里我以获取USDC池子信息为例:

    const usdcPool = await getMaxLiq(USDCAddress)
    
    // pair contract
    const pairContract = new Web3.eth.Contract(usdcPool.pool, pairAbiV3)
    // get fee level
    const fee = await pairContract.methods.fee().call()
    // get token0 address
    const t0Addr = await pairContract.methods.token0().call()
    // get token1 address
    const t1Addr = await pairContract.methods.token1().call()
    // get t0 and t1 contract
    const t0Contract = new Web3.eth.Contract(t0Addr, erc20Abi)
    const t1Contract = new Web3.eth.Contract(t1Addr, erc20Abi)
    // get decimals
    const t0Decimals = await t0Contract.methods.decimals().call()
    const t1Decimals = await t1Contract.methods.decimals().call()
    // 获取池中代币份额
    const t0Amount = await t0Contract.methods.balance(usdcPool.pool).call()
    const t1Amount = await t1Contract.methods.balance(usdcPool.pool).call()
    // 获取token0和token1代币symbol
    const t0Symbol = await t0Contract.methods.symbol().call()
    const t1Symbol = await t1Contract.methods.symbol().call()
    

以上, 我们拿到了池子的诸多信息, 不过还有一个需要注意的地方, 就是token0和token1的顺序问题.

接上面的例子:

    let params = {}
    if(t0Addr.toLowerCase() === USDCAddress.toLowerCase()) {
        params.lpName = `${t1Symbol}/${t0Symbol}`
        // BIG_TRANSFER自定义方法, 将wei数据转换为可读数据
        params.count = BIG_TRANSFER(t0Amount, t0Decimals, 'from')
        params.tokenCount = BIG_TRANSFER(t1Amount, t1Decimals, 'from')
    }
    

至此, 池子的主要信息我们都拿到了. 接下来说一说价格的计算.

### V3池子的价格计算

不同于V2的x \* y = k公式, V3的价格计算复杂很多, 这里不做详细描述, 说一说计算的方法:

    const poolContract = new Web3.eth.Contract(usdcPool.pool, pairAbiV3)
    const sqrtPriceX96 = (await poolContract.methods.slot0().call()).sqrtPriceX96
    const price = sqrtPriceX96 ** 2 / 2 ** 192
    

以上, 我们拿到了常规价格, 为什么是常规价格? 因为真实的价格还需要引入token0和token1的decimals共同计算得到(针对WETH).

    // WETH池
    const price = WETHPrice / (sqrtPriceX96 ** 2 / 2 ** 192) / Math.pow(10, t0Decimals - t1Decimals)
    

两种价格计算方式, 如果目标池子非WETH池, 那么第一种获取价格的方式救能计算出准确价格, 如果池子时WETH池, 那么需要使用第二种来获取准确的价格.

---

*Originally published on [kuimale](https://paragraph.com/@kuimale/uniswap-v3)*
