0

In Uniswap V3, getting different prices of 1 WETH to USDC while swapping and adding liquidity, potentially because of fees and other contributing factors.

Swap

enter image description here

Add Liquidity

enter image description here

Programmatically, I'm calculating the amounts using this JS code:

const JSBI = require('jsbi')
const { ethers } = require("ethers");
const { TickMath, FullMath } = require('@uniswap/v3-sdk')
const UniswapV3FactoryABI = require('./abis/UniswapV3Factory.json');
const UniswapV3PoolABI = require('./abis/UniswapV3Pool.json');
const RPC_URL = "JSON_RPC_URL"
const WETH = "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2"; // quote
const USDC = "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48"; // base
const provider = new ethers.JsonRpcProvider(RPC_URL);

const uniswapV3Factory = "0x1F98431c8aD98523631AE4a59f267346ea31F984";

const factoryContract = new ethers.Contract(
    uniswapV3Factory,
    UniswapV3FactoryABI,
    provider
);

async function getCurrentTick(fee) {

    const poolAddress = await factoryContract.getPool(WETH, USDC, fee);
    const poolContract = new ethers.Contract(
        poolAddress,
        UniswapV3PoolABI,
        provider
    );

    console.log(poolAddress);

    const slot0 = await poolContract.slot0();
    const currentTick = parseInt(slot0.tick);

    return currentTick;
}

async function main(
    inputAmount,
    baseTokenDecimals,
    quoteTokenDecimals,
    fee
) {

    const currentTick = await getCurrentTick(fee);
    // console.log(currentTick)
    const sqrtRatioX96 = TickMath.getSqrtRatioAtTick(currentTick);

    const ratioX192 = JSBI.multiply(sqrtRatioX96, sqrtRatioX96);

    const baseAmount = JSBI.BigInt(inputAmount * (10 ** baseTokenDecimals));

    const shift = JSBI.leftShift(JSBI.BigInt(1), JSBI.BigInt(192));

    const quoteAmount = FullMath.mulDivRoundingUp(ratioX192, baseAmount, shift);
    console.log(`${inputAmount} USDC: `, (quoteAmount.toString() / (10 ** quoteTokenDecimals)), 'WETH');
    console.log(`${inputAmount} WETH: `, inputAmount / (quoteAmount.toString() / (10 ** quoteTokenDecimals)), 'USDC');

}

const fee = 100;
const amount = 1;

main(amount, 6, 18, fee);

But, the result neither matches with the pricing of the Swap nor with the pricing of the Add Liquidity.

So, how to calculate the prices with precision that Uniswap V3 is using both in case of Swap and Add Liquidity, and how the factors like price range (i.e., given as a selection in the UI of Add Liquidity) contribute in this calculation ?

Thanks.

1
  • 1
    Oh, now I see where the confusion between sqrtPriceX96 and sqrtRatioX96 comes from.
    – kfx
    Apr 13 at 16:21

1 Answer 1

2

Replace sqrtRatioX96 with this:

const sqrtPriceX96 = JSBI.BigInt(slot0.sqrtPriceX96);

Explanation: your code is correct except in one minor detail: it computes the price ratio (sqrtRatioX96) from the current tick of the pool. This is not 100% accurate and can be improved by using the sqrtPriceX96 variable of the pool's smart contract.

Many different prices can correspond to the same tick. For example, if the current tick of the pool is 0 then the price is approximately 1.0, and if the current tick is 1, then the pool's price is approximately 1.0001, but Uniswap is also able to track a large number of more precise prices between these two values using the sqrtPriceX96 variable. (Most of the time converting the pool's sqrtPriceX96 to a tick gives the pool's current tick, but even that is not always true.)

Edit regarding the price range. The token amounts when adding liquidity depend not only on the current price of the pool, but also on the range endpoints. Uniswap v3 does not required that both tokens are added in balanced proportions. Unlike in v2, the current price corresponds to virtual reserves of the pool, not to the real token reserves in pool's contract. The real and virtual reserves are different, unless only full range position are used (and even then likely a bit different, because of small rounding & math errors).

3
  • Thanks a lot for the answer, @kfx. Using sqrtPriceX96, it's giving precise results when comparing on the UI giving low price as 0 and high price as . So, can you please also address the second part of the question i.e., how the price range (i.e., given as a selection as low price and high price in the UI of Add Liquidity) contribute a change while doing this calculation ? Apr 13 at 16:52
  • Can you please throw some light on how the price range selection would affect the calculation, so that I can mark your answer as accepted? Thanks. Apr 19 at 19:03
  • Thanks a lot, @kfx. Apr 24 at 13:52

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.