Calculating Price Impact for Uniswap V3 Using Ethers

TLDR;

require("dotenv").config(); // to get alchemy api key
const fs = require("fs");
const { ethers } = require("ethers");
const { abi: QuoterV2ABI } = require("@uniswap/v3-periphery/artifacts/contracts/lens/QuoterV2.sol/QuoterV2.json");
const { abi: PoolABI } = require("@uniswap/v3-core/artifacts/contracts/UniswapV3Pool.sol/UniswapV3Pool.json");
const { abi: FactoryABI } = require("@uniswap/v3-core/artifacts/contracts/UniswapV3Factory.sol/UniswapV3Factory.json");

// You can find this online or grab it here https://gist.github.com/DieHard073055/b223a285040ef99dbea06c8eaa66d470
const ERC20ABI = JSON.parse(fs.readFileSync('./erc-20_abi.json', 'utf-8'));

// Quoter2 and factory of polygon mumbai
const QUOTER2_ADDRESS = "0x61fFE014bA17989E743c5F6cB21bF9697530B21e";
const FACTORY_ADDRESS = "0x1f98431c8ad98523631ae4a59f267346ea31f984";

// My own custom token called Trustless
const TRUSTLESS = "0x226b056c24fb131b86e3343be2f573c0e5407418";
// My own custom token called TrustlessUSDC
const USDC = "0x828a74eb32492fa9585b8136babd7c34a9a0ffd8";

// Using alchemy as provider
const providerUrl = `https://polygon-mumbai.g.alchemy.com/v2/${process.env.ALCHEMY_API_KEY}`;
const provider = new ethers.providers.JsonRpcProvider(providerUrl);

/**
 * Convert sqrt price to actual price
 */
const sqrtToPrice = (sqrt, decimals0, decimals1, token0IsInput = true) => {
    const numerator = sqrt ** 2;
    const denominator = 2 ** 192;
    let ratio = numerator / denominator;
    const shiftDecimals = Math.pow(10, decimals0 - decimals1);
    ratio *= shiftDecimals;
    return token0IsInput ? ratio : 1 / ratio;
};

/**
 * Main function to calculate price impact
 */
const main = async (tokenIn, tokenOut, fee, amountIn) => {
    const factory = new ethers.Contract(FACTORY_ADDRESS, FactoryABI, provider);
    const poolAddress = await factory.getPool(tokenIn, tokenOut, fee);
    const poolContract = new ethers.Contract(poolAddress, PoolABI, provider);
    const slot0 = await poolContract.slot0();
    const sqrtPriceX96 = slot0.sqrtPriceX96;
    const token0 = await poolContract.token0();
    const token1 = await poolContract.token1();
    const token0IsInput = tokenIn == token0;

    const tokenInContract = new ethers.Contract(tokenIn, ERC20ABI, provider);
    const tokenOutContract = new ethers.Contract(tokenOut, ERC20ABI, provider);
    const quoter = new ethers.Contract(QUOTER2_ADDRESS, QuoterV2ABI, provider);

    const Token0Decimals = await tokenInContract.decimals();
    const Token1Decimals = await tokenOutContract.decimals();
    const amountInStr = ethers.utils.parseUnits(amountIn, await tokenInContract.decimals()).toString();

    const quote = await quoter.callStatic.quoteExactInputSingle({
        tokenIn: tokenIn,
        tokenOut: tokenOut,
        fee: fee,
        amountIn: amountInStr,
        sqrtPriceLimitX96: '0'
    });

    const sqrtPriceX96After = quote.sqrtPriceX96After;
    const price = sqrtToPrice(sqrtPriceX96, Token0Decimals, Token1Decimals, token0IsInput);
    const priceAfter = sqrtToPrice(sqrtPriceX96After, Token0Decimals, Token1Decimals, token0IsInput);

    console.log("Price:", price);
    console.log("Price After:", priceAfter);

    const absChange = price - priceAfter;
    const percentChange = absChange / price;
    console.log("Price Impact:", (percentChange * 100).toFixed(3), "%");
};

// Buy 1000 USDC worth of Trustless Token.
// Please dont use 3000 in production for fees
main(USDC, TRUSTLESS, "3000", "1000");

// Output below is the impact on price after buying 1000 USDC worth of trustless token.
/*******
Output:

❯ node getPriceImpact.js
Price: 45.02915980779544
Price After: 27.571883689985125
Price Impact: 38.769 %

*/

Introduction

In the dynamic world of decentralized finance, understanding the nuances of liquidity pools and their behavior is crucial. One aspect that always intrigued me was the price impact of trades within these pools. To delve deeper into this, I decided to set up my own liquidity pool on Uniswap, specifically on the Polygon Mumbai testnet.

To make the experiment more hands-on, I went ahead and minted my own tokens: Trustless and Trustless USDC. After seeding these tokens into the liquidity pool, the next step was to gauge the potential price impact of a trade before actually executing it. This preemptive measure is invaluable, especially when transacting programmatically on platforms like Uniswap.

The code shared in this article serves as a tool to estimate this price impact, offering insights that can be pivotal for traders and developers alike.

Setting up the Environment

If you want to test out the code above please make sure you have done the following steps.

  • Make sure you signup to Alchemy using my referral link https://alchemy.com/?r=99df4af9e92fdd0f and get yourself a free api key to query polygon pos.

  • Install nodejs and required deps

    • npm install ethers @uniswap/v3-periphery @uniswap/v3-core
  • Add your alchemy api key to your .env file.

    • ALCHEMY_API_KEY="YOUR API KEY FROM ALCHEMY"

You are set, now run app with node your_code.js << it rhymes

Conclusion

Understanding price impact is essential for both traders and developers working with decentralized exchanges. This script provides a straightforward way to calculate the price impact on Uniswap V3. As the DeFi space evolves, tools like these will become increasingly valuable for making informed decisions.