DEV Community

Rodrigo Burgos
Rodrigo Burgos

Posted on • Updated on

Storing liquidity events from Uniswap V3 into mongoDB with ethers.js

Image description

This code is using the ethers library to interact with a smart contract on the Goerli Ethereum test network. The smart contract is an instance of Nonfungiblepositionmanager, and its ABI (Application Binary Interface) and address are imported from a JSON file.

The code also uses the MongoClient from the mongodb library to connect to a MongoDB database and store data.

import { ethers } from 'ethers'
import { MongoClient, ServerApiVersion  } from 'mongodb'
import { IIncreaseLiquidityArgs, IIncreaseLiquidity } from './interfaces'


const Nonfungiblepositionmanager = require('./artifacts/Nonfungiblepositionmanager.json')
const ERC20 = require('./artifacts/ERC20.json')

const uri = `mongodb+srv://<username>:<password><dbName>.hkje2xt.mongodb.net/?retryWrites=true&w=majority`
//@ts-ignore
const mongoClient = new MongoClient(uri, { useNewUrlParser: true, useUnifiedTopology: true, serverApi: ServerApiVersion.v1 });

const goerliWssProvider = new ethers.providers.WebSocketProvider("wss://eth-mainnet.g.alchemy.com/v2/<apiKey>")

const NFPM = new ethers.Contract(
    '0xC36442b4a4522E871399CD717aBDD847Ab11FE88',
    Nonfungiblepositionmanager.abi,
    goerliWssProvider
    )
Enter fullscreen mode Exit fullscreen mode

The code has two event listeners, one for the “IncreaseLiquidity” event and one for the “DecreaseLiquidity” event. When each event is emitted, the storeUntoDb function is called and data from the event is stored in the MongoDB database. The code also uses the WebSocketProvider from ethers to listen for new blocks on the Goerli network, and when a new block is mined, the increaseLiquidityListener and decreaseLiquidityListener functions are called to check for events emitted in that block.

const decreaseLiquidityListener = async (blockNumber) => {
    try {
            const decreaseLiquidityEvent = await NFPM.queryFilter(
                'DecreaseLiquidity',
                blockNumber - 1,
                blockNumber                
            )
            storeUntoDb(decreaseLiquidityEvent, blockNumber)
    } catch (error) {
        console.log(error)
    }
}

const increaseLiquidityListener = async (blockNumber) => {
    try {
            const increaseLiquidityEvent = await NFPM.queryFilter(
                'IncreaseLiquidity',
                blockNumber - 1,
                blockNumber                
            )
            storeUntoDb(increaseLiquidityEvent, blockNumber)
    } catch (error) {
        console.log(error)
    } 
}
Enter fullscreen mode Exit fullscreen mode

The function storeUntoDb is called with the event data and the block number, it connects to the MongoDB, it then retrieves the token information, symbol, dec

const storeUntoDb = async (event: IIncreaseLiquidity[] | any, blockNumber: number) => {
    try {
        if(event.length > 0){
                await mongoClient.connect()
                const position = await NFPM.positions(Number(event[0].args.tokenId._hex))
                const token0 = new ethers.Contract(position.token0, ERC20, goerliWssProvider)
                const token1 = new ethers.Contract(position.token1, ERC20, goerliWssProvider)
                const symbol0 = await token0.symbol()
                const symbol1 = await token1.symbol()
                const decimals0 = await token0.decimals()
                const decimals1 = await token1.decimals()
                const account = await NFPM.ownerOf(Number(event[0].args.tokenId._hex))
                    const tvlCollection = await mongoClient.db("tangle-db").collection("liquidity-transactions")
                    const result = await tvlCollection.insertOne({ 
                        block: blockNumber,
                        eventName: event[0].event,
                        token0Address: position.token0,
                        token1Address: position.token1,
                        symbol0: symbol0,
                        symbol1: symbol1,
                        amount0: (Number(event[0].args.amount0._hex) / (10 ** decimals0)),
                        amount1: (Number(event[0].args.amount1._hex) / (10 ** decimals1)),
                        account: account,
                        time: Date.now()
                    })
                    console.log(result)      
        }
    } catch (error) {
        console.log(error)
    }    
}
Enter fullscreen mode Exit fullscreen mode

In the end we assign to the websocket and listen to every block, grab its blockNumber and pass through the event listeners who will inser fields in the collection whenever block which has liquidity being added or removed.

goerliWssProvider.on('block', async (blockNumber) => {
    await decreaseLiquidityListener(blockNumber)
    await increaseLiquidityListener(blockNumber)
})
Enter fullscreen mode Exit fullscreen mode

The whole code:

import { ethers } from 'ethers'
import { MongoClient, ServerApiVersion  } from 'mongodb'
import { IIncreaseLiquidityArgs, IIncreaseLiquidity } from './interfaces'


const Nonfungiblepositionmanager = require('./artifacts/Nonfungiblepositionmanager.json')
const ERC20 = require('./artifacts/ERC20.json')

const uri = `mongodb+srv://<username>:<password>@<db>.hkje2xt.mongodb.net/?retryWrites=true&w=majority`
//@ts-ignore
const mongoClient = new MongoClient(uri, { useNewUrlParser: true, useUnifiedTopology: true, serverApi: ServerApiVersion.v1 });

const goerliWssProvider = new ethers.providers.WebSocketProvider("wss://eth-mainnet.g.alchemy.com/v2/APY_KEY")

const NFPM = new ethers.Contract(
    '0xC36442b4a4522E871399CD717aBDD847Ab11FE88',
    Nonfungiblepositionmanager.abi,
    goerliWssProvider
    )

const storeUntoDb = async (event: IIncreaseLiquidity[] | any, blockNumber: number) => {
    try {
        if(event.length > 0){
                await mongoClient.connect()
                const position = await NFPM.positions(Number(event[0].args.tokenId._hex))
                const token0 = new ethers.Contract(position.token0, ERC20, goerliWssProvider)
                const token1 = new ethers.Contract(position.token1, ERC20, goerliWssProvider)
                const symbol0 = await token0.symbol()
                const symbol1 = await token1.symbol()
                const decimals0 = await token0.decimals()
                const decimals1 = await token1.decimals()
                const account = await NFPM.ownerOf(Number(event[0].args.tokenId._hex))
                    const tvlCollection = await mongoClient.db("tangle-db").collection("liquidity-transactions")
                    const result = await tvlCollection.insertOne({ 
                        block: blockNumber,
                        eventName: event[0].event,
                        token0Address: position.token0,
                        token1Address: position.token1,
                        symbol0: symbol0,
                        symbol1: symbol1,
                        amount0: (Number(event[0].args.amount0._hex) / (10 ** decimals0)),
                        amount1: (Number(event[0].args.amount1._hex) / (10 ** decimals1)),
                        account: account,
                        time: Date.now()
                    })
                    console.log(result)      
        }
    } catch (error) {
        console.log(error)
    }    
}

const decreaseLiquidityListener = async (blockNumber) => {
    try {
            const decreaseLiquidityEvent = await NFPM.queryFilter(
                'DecreaseLiquidity',
                blockNumber - 1,
                blockNumber                
            )
            storeUntoDb(decreaseLiquidityEvent, blockNumber)
    } catch (error) {
        console.log(error)
    }
}

const increaseLiquidityListener = async (blockNumber) => {
    try {
            const increaseLiquidityEvent = await NFPM.queryFilter(
                'IncreaseLiquidity',
                blockNumber - 1,
                blockNumber                
            )
            storeUntoDb(increaseLiquidityEvent, blockNumber)
    } catch (error) {
        console.log(error)
    } 
}

goerliWssProvider.on('block', async (blockNumber) => {
    await decreaseLiquidityListener(blockNumber)
    await increaseLiquidityListener(blockNumber)
    // createPoolListener(blockNumber)
})
Enter fullscreen mode Exit fullscreen mode

Pay me a coffe (all evm chains):
0xe25bf122bc1369754edEC5318AF399EB9D0857CB

Top comments (0)