1

I'm trying to create a bot that will send me new LPS notifications on Uniswap V3 and in the future more dexes. I want to contain in the message specific data like amount of token and ETH in the pool, then count price from it, FDV etc.

First of all I tried to notify me about the pair information that will include just the amount of pooled token and pooled ETH. But I'm not getting the correct numbers, it is very confusing and difficult to do it. Isn't there a way to fetch directly the amount of the tokens from the pool contract? If not, then how to calculate the values correctly?

uniswapV3PoolStateABI.json:

[
    {
        "constant": true,
        "inputs": [],
        "name": "slot0",
        "outputs": [
            { "name": "sqrtPriceX96", "type": "uint160" },
            { "name": "tick", "type": "int24" },
            { "name": "observationIndex", "type": "uint16" },
            { "name": "observationCardinality", "type": "uint16" },
            { "name": "observationCardinalityNext", "type": "uint16" },
            { "name": "feeProtocol", "type": "uint8" },
            { "name": "unlocked", "type": "bool" }
        ],
        "type": "function"
    },
    {
        "constant": true,
        "inputs": [],
        "name": "feeGrowthGlobal0X128",
        "outputs": [{ "name": "", "type": "uint256" }],
        "type": "function"
    },
    {
        "constant": true,
        "inputs": [],
        "name": "feeGrowthGlobal1X128",
        "outputs": [{ "name": "", "type": "uint256" }],
        "type": "function"
    },
    {
        "constant": true,
        "inputs": [],
        "name": "protocolFees",
        "outputs": [
            { "name": "token0", "type": "uint128" },
            { "name": "token1", "type": "uint128" }
        ],
        "type": "function"
    },
    {
        "constant": true,
        "inputs": [],
        "name": "liquidity",
        "outputs": [{ "name": "", "type": "uint128" }],
        "type": "function"
    },
    {
        "constant": true,
        "inputs": [{ "name": "tick", "type": "int24" }],
        "name": "ticks",
        "outputs": [
            { "name": "liquidityGross", "type": "uint128" },
            { "name": "liquidityNet", "type": "int128" },
            { "name": "feeGrowthOutside0X128", "type": "uint256" },
            { "name": "feeGrowthOutside1X128", "type": "uint256" },
            { "name": "tickCumulativeOutside", "type": "int56" },
            { "name": "secondsPerLiquidityOutsideX128", "type": "uint160" },
            { "name": "secondsOutside", "type": "uint32" },
            { "name": "initialized", "type": "bool" }
        ],
        "type": "function"
    },
    {
        "constant": true,
        "inputs": [{ "name": "wordPosition", "type": "int16" }],
        "name": "tickBitmap",
        "outputs": [{ "name": "", "type": "uint256" }],
        "type": "function"
    },
    {
        "constant": true,
        "inputs": [{ "name": "key", "type": "bytes32" }],
        "name": "positions",
        "outputs": [
            { "name": "_liquidity", "type": "uint128" },
            { "name": "feeGrowthInside0LastX128", "type": "uint256" },
            { "name": "feeGrowthInside1LastX128", "type": "uint256" },
            { "name": "tokensOwed0", "type": "uint128" },
            { "name": "tokensOwed1", "type": "uint128" }
        ],
        "type": "function"
    }
]

Code:

const IUniswapV3PoolStateABI = require('./uniswapV3PoolStateABI.json')
const Web3 = require('web3').default;
const web3 = new Web3(new Web3.providers.WebsocketProvider(`wss://base-mainnet.blastapi.io/${blastApi}`));

//Log Training data
const trainigdata = {
  address: '0x33128a8fc17869897dce68ed026d694621f6fdfd',
  topics: [
    '0x783cca1c0412dd0d695e784568c96da2e9c22ff989357a2e8b1d9b2b4e6b7118',
    '0x0000000000000000000000003fe81f3bb452534eb23d3ed7b1161cd31d6853ba',
    '0x0000000000000000000000004200000000000000000000000000000000000006',
    '0x0000000000000000000000000000000000000000000000000000000000002710'
  ],
  data: '0x00000000000000000000000000000000000000000000000000000000000000c800000000000000000000000016f56e849787d801aee730074df1e3d37307a1c0',
  blockNumber: 2625294n,
  transactionHash: '0xf07374ffb64fca391b00c32f0d4469cbe760a834aae7e1f64c23738e975e79fd',
  transactionIndex: 16n,
  blockHash: '0xeba5103d304dd471a0ddf3c04e74d944f5e8e2b6f5b856ed64a963482f6586bf',
  logIndex: 38n,
  removed: false,
  returnValues: {
    '0': '0x8544FE9D190fD7EC52860abBf45088E81Ee24a8c',
    '1': '0x4200000000000000000000000000000000000006',
    '2': 10000n,
    '3': 200n,
    '4': '0x16F56E849787d801aEe730074Df1E3D37307a1c0',
    __length__: 5,
    token0: '0x8544FE9D190fD7EC52860abBf45088E81Ee24a8c',
    token1: '0x4200000000000000000000000000000000000006',
    fee: 10000n,
    tickSpacing: 200n,
    pool: '0xe6e16fa8f4c2b9f56a3378b227bede63940a657c'
  },
  event: 'PoolCreated',
  signature: '0x783cca1c0412dd0d695e784568c96da2e9c22ff989357a2e8b1d9b2b4e6b7118',
  raw: {
    data: '0x00000000000000000000000000000000000000000000000000000000000000c800000000000000000000000016f56e849787d801aee730074df1e3d37307a1c0',
    topics: [
      '0x783cca1c0412dd0d695e784568c96da2e9c22ff989357a2e8b1d9b2b4e6b7118',
      '0x0000000000000000000000003fe81f3bb452534eb23d3ed7b1161cd31d6853ba',
      '0x0000000000000000000000004200000000000000000000000000000000000006',
      '0x0000000000000000000000000000000000000000000000000000000000002710'
    ]
  }
}


//Training
async function Training() {
  try {
  let isToken;
  const ETH = '0x4200000000000000000000000000000000000006'
  const tokenA = trainigdata.returnValues.token0;
  const tokenB = trainigdata.returnValues.token1;
  const pooladdress = trainigdata.returnValues.pool;
  console.log('Pool', pooladdress);
  //Consts for Liquidty pool
  const poolContract = new web3.eth.Contract(IUniswapV3PoolStateABI, pooladdress);
  const poolState = await poolContract.methods.slot0().call();
  const sqrtPriceX96 = poolState.sqrtPriceX96;
  const liquidity = await poolContract.methods.liquidity().call();
  console.log('sqrtPriceX96', sqrtPriceX96);
  console.log('Liquidity:', liquidity);
  //Check which token is not ETH 
  if (tokenA === ETH) {
    isToken = tokenB;
  } else if (tokenB === ETH) {
    isToken = tokenA;
  }
  let pooledETH, pooledToken;
  //Check if tokenB is token
  if (isToken === tokenB) {
    pooledETH = calculatePooledAmount(sqrtPriceX96, liquidity, true);
    pooledToken = calculatePooledAmount(sqrtPriceX96, liquidity, false);
  } else {
    pooledETH = calculatePooledAmount(sqrtPriceX96, liquidity, false);
    pooledToken = calculatePooledAmount(sqrtPriceX96, liquidity, true);
  }
  console.log('Pooled ETH:', pooledETH);
  console.log('PooledToken', pooledToken);
  const tokenDetails = await getTokenDetails(isToken);
  const tokenName = tokenDetails.name;
  const tokenSymbol = tokenDetails.symbol;
  const totalSupply = tokenDetails.totalSupply;
  const tokenDecimals = tokenDetails.decimals;
  //Format Number function
  function formatNumber(num) {
    return (num % 1 === 0) ? num.toString() : num.toFixed(2);
  }
  
  const readablePooledETH = formatNumber(Number(BigInt(pooledETH)) / 1e18); //Format to number with 2 decimals
  const readablePooledToken = formatNumber(Number(BigInt(pooledToken)) / (10 ** Number(tokenDecimals))); //Format to number with 2 decimals
  
  console.log(readablePooledETH);
  console.log(readablePooledToken);
  bot.sendMessage(rasmont, `\nThis is message from test function \n\nPair data test on <b>Base</b> <b><a href='https://dexscreener.com/base/${pooladdress}'>${tokenName} - ${tokenSymbol} / WETH</a></b> \nDEX: <a href='https://dexscreener.com/base/uniswap'>Base / Uniswap</a> \n<a href='https://basescan.org/address/${isToken}'>Token CA:</a> <code>${isToken}</code> \n\nLiquidity pool data: \nPooled ETH: ${readablePooledETH} \nPooled ${tokenSymbol}: ${readablePooledToken}`, { parse_mode: 'HTML', disable_web_page_preview: true });
  } catch (error) {
  console.error('Error with training function:', error);
  }
}

Training();

function calculatePooledAmount(sqrtPriceX96, liquidity, isToken0) {
  const sqrtPrice = BigInt(sqrtPriceX96);
  const liquidityBigInt = BigInt(liquidity);

  if (isToken0) {
    // Calculate pooled amount for token0
    const pooledToken0 = (liquidityBigInt * sqrtPrice) / (2n**96n);
    return pooledToken0.toString();
  } else {
    // Calculate pooled amount for token1
    const pooledToken1 = (liquidityBigInt * (2n**96n)) / sqrtPrice;
    return pooledToken1.toString();
  }
}

async function getTokenDetails(tokenAddress) {
  try {
    const tokenContract = new web3.eth.Contract(abi, tokenAddress);
    const name = await tokenContract.methods.name().call();
    const symbol = await tokenContract.methods.symbol().call();
    const totalSupply = await tokenContract.methods.totalSupply().call();
    const decimals = await tokenContract.methods.decimals().call();
    return { name, symbol, totalSupply, decimals };
  } catch (error) {
    console.error('Error fetching token details:', error);
  }
}

When I log the values, the number is different... I use this pair for testing, the number are almost close, I would like to know how to get the actual/correct numbers..

Logs:

Pool 0xe6e16fa8f4c2b9f56a3378b227bede63940a657c
Bot started!
sqrtPriceX96 732417619370237412602984603026781n
Liquidity: 406719590872925364556632n
Pooled ETH: 43996273425429661525
PooledToken 3759882660976173724396741633
44.00 //ETH
3759882660.98 //Token

Current data: 5.15 //ETH in Pool 3,300,486,897 //Token in Pool

I tried several approaches and I never got the correct numbers.. it always differs.

1 Answer 1

0

The simplest way is to call balanceOf on both tokens (token contracts) from the pool and giving pool's address as a parameter. This way you'll see how many of each tokens your pool owns (in your case Toshi and WETH)

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.