import { web3 } from "hardhat"; import History from "../interface/history"; import contracts from "../../config/contracts"; import logger from "../../utils/logger"; import {BigNumber} from "ethers"; import {replaceAll} from "hardhat/internal/util/strings"; import history from "../interface/history"; import Time from "../../utils/time"; import MemoryUtils from "../../utils/memory"; const ierc20abi = require('../../abi/IERC20_ABI.json') export class LpGenerate { tokenInstance: any = {} lpInstance: any = {} memoryUtils: MemoryUtils = new MemoryUtils() async handlePosition(routerObj: any) { const block = routerObj.type === 'univ2' ? 'UNIV2DEX' : 'UNIV3DEX' const hash = routerObj.type === 'univ2' ? routerObj.factory : routerObj.router + '_router' try { // 拉取当前pull状态信息 const pullState = await History.findByHashOrBlockOrDataVague(block, hash, '', 0, 200) let nowPosition = 0 // 没有状态信息的情况 if (pullState.data.length == 0) { await History.appendOrUpdate(block, hash, {"next": 0}) } else { nowPosition = pullState.data[0].dataObj.next } return nowPosition } catch (e) { logger.error(`Pull state error, router: ${routerObj.router}, factory: ${routerObj.factory}`) logger.error(e) } return 0 } async getV2PoolByPosition(routerObj: any, position: number, v2ToolBy410: any) { try { const info = await v2ToolBy410.methods.getPairIdInfo(routerObj.factory, position).call() const symbol0 = info['2'].replace(/[^A-Za-z0-9]+/g, '').substring(0, 10) const symbol1 = info['6'].replace(/[^A-Za-z0-9]+/g, '').substring(0, 10) const name = `${routerObj.name}_${symbol0}_${symbol1}` const sum2 = replaceAll(BigNumber.from(info['1']).add(BigNumber.from(info['5']))._hex, '0x0', '0x') const lp = { LP: info['0'], decimals0: info['3'], decimals1: info['7'], factory: routerObj.factory, feei: routerObj.fee, id: position, name: name, r0: info['4'], r1: info['8'], router: routerObj.router, sum2: sum2, symbol0: symbol0, symbol1: symbol1, token0: info['1'], token1: info['5'], isEthW: routerObj.type == 'ETHW' } return lp } catch (e) { logger.info(e) } return undefined } async getV3Pool(routerObj: any, position: number, v3Tool: any, positionManager: any) { try { const positionInfo = await positionManager.methods.positions(position).call() const info = await v3Tool.methods.getMoreInfo(positionInfo.token0, positionInfo.token1, positionInfo.fee).call() const symbol0 = info.symbol0.replace(/[^A-Za-z0-9 ]+/g, '').substring(0, 10) const symbol1 = info.symbol1.replace(/[^A-Za-z0-9 ]+/g, '').substring(0, 10) const name = `${routerObj.name}_${symbol0}_${symbol1}` const sum2 = replaceAll(BigNumber.from(positionInfo.token0).add(BigNumber.from(positionInfo.token1))._hex, '0x0', '0x') const lp = { LP: info.lp, decimals0: info.decimals0, decimals1: info.decimals1, factory: routerObj.factory, feei: positionInfo.fee, id: position, name: name, r0: info.r0, r1: info.r1, router: routerObj.router, sum2: sum2, symbol0: symbol0, symbol1: symbol1, token0: positionInfo.token0, token1: positionInfo.token1 } return lp } catch (e) {} return undefined } async handleToken(pool: any, zero: boolean) { const token = { 'address': zero ? pool.token0 : pool.token1, 'symbol': zero ? pool.symbol0 : pool.symbol1, 'decimals': zero ? pool.decimals0 : pool.decimals1, 'name': 'xxxxxxxx' } let tokenObj = this.tokenInstance[token.address] if (!tokenObj) { tokenObj = new web3.eth.Contract(ierc20abi, token.address) this.tokenInstance[token.address] = tokenObj } token.name = await tokenObj.methods.name().call() token.name = token.name.replace(/[^A-Za-z0-9 ]+/g, '').substring(0, 37) return token } async saveToken(lp: any, type='token') { try { const token = await this.handleToken(lp, true) // token的hash不同时为lp的情况才更新 if (!this.lpInstance[token.address.toLowerCase()]) { await history.appendOrUpdate(type, token.address, token) } } catch (e) {} try { const token = await this.handleToken(lp, false) // token的hash不同时为lp的情况才更新 if (!this.lpInstance[token.address.toLowerCase()]) { await history.appendOrUpdate(type, token.address, token) } } catch (e) {} } async saveLp(lp: any, type='0') { // 本地lp实例存档 this.lpInstance[lp.LP.toLowerCase()] = lp await History.appendOrUpdate(type, lp.LP, lp) } parseFactory(router: any, factoryAbi: any, factoryAddress: string) { if (router.factoryObj == null) { router.factoryObj = new web3.eth.Contract(factoryAbi, factoryAddress) } return router.factoryObj } parsePositionManager(router: any, positionManagerAbi: any) { if (router.positionManager == null) { router.positionManager = new web3.eth.Contract(positionManagerAbi, router.position) } return router.positionManager } // 二分法查length async getPositionLength(pm: any, low: number) { let high = 1_000_000 let checkNumber = low let prevLow: number = low let prevHigh: number = high async function check(checkNumber: number) { async function final_check(checkNumber: number) { try { await pm.methods.positions(checkNumber).call() return true } catch (e) { return false } } // [,,,,,c(,,,,,,,)] let too_low = false for (let i = 1; i <= 5; i++) { too_low = too_low || (await final_check(checkNumber + i)) } if (too_low) return 'too_low' // [,,,,,(c,,,,,,,)] let too_high = true for (let i = 0; i < 5; i++) { too_high = too_high && (!await final_check(checkNumber + i)) } if (too_high) return 'too_high' return 'ok' } let count = 60 while (--count > 0) { let checkRst = await check(checkNumber) // logger.debug(`[${low}, ${high}], c: ${checkNumber}, ${checkRst}. [${++count}]`) if (checkRst == 'ok') { break } else { let tempHigh = high let tempLow = low if (checkNumber == low) { if (checkRst == 'too_high') { high = tempLow low = prevLow prevHigh = tempHigh prevLow = tempLow checkNumber = high } else if (checkRst == 'too_low') { low = low + Math.floor((high - low) / 2) prevLow = tempLow checkNumber = low } } else if (checkNumber == high) { if (checkRst == 'too_high') { high = low + Math.floor((high - low) / 2) prevHigh = tempHigh checkNumber = high } else if (checkRst == 'too_low') { low = tempHigh high = prevHigh prevHigh = tempHigh prevLow = tempLow checkNumber = low } } } } // logger.debug(`Found value, is: ${checkNumber}`) return checkNumber } async handleLp(position: number, pairsLength: number, routerObj: any, v2ToolBy410: any, v3Tool: any, v3PositionManager: any) { // 1. 获取lp let lp = undefined if (routerObj.type === 'univ2') { lp = await this.getV2PoolByPosition(routerObj, position, v2ToolBy410) } else if (routerObj.type === 'univ3') { // v3是从1开始 lp = await this.getV3Pool(routerObj, position + 1, v3Tool, v3PositionManager) } if (lp) { // 2. 保存Lp信息 await this.saveLp(lp, 'lp') // 3. 保存Lp的Token到Token表 await this.saveToken(lp, 'token') } if (lp) { logger.debug(`${position + 1} / ${pairsLength}, ${lp.name}-${lp.LP}-${routerObj.chain}`) } else { logger.debug(`lp get filed. ${position + 1} / ${pairsLength}, ${routerObj.name}-${routerObj.router}-${routerObj.chain}`) } } async run() { logger.debug('Pull lp start.') // 初始化410 v2工具箱 const v2ToolBy410Abi = require('../../abi/410_V2_TOOLS.json') const v2ToolBy410 = new web3.eth.Contract(v2ToolBy410Abi, contracts.V2_TOOLS_BY_410) const v2FactoryAbi = require('../../abi/UNIV2_FACTORY_ABI.json') // 初始化v3相关 const positionManagerAbi = require('../../abi/UNIV3_POSITION_MANAGER_ABI.json') const v3ToolAbi = require('../../artifacts/contracts/V3Tool.sol/V3Tool.json').abi const v3Tool = new web3.eth.Contract(v3ToolAbi, contracts.V3_TOOLS) // router const routerList: Array = require('../../config/router-list.json') while (true) { for (const routerObj of routerList) { try { let pairsLength = 0 const factoryAddress = routerObj.factory // 获取当前pull状态 let position = await this.handlePosition(routerObj) let v2Factory = undefined let v3PositionManager = undefined // v2,v3分开处理 if (routerObj.type === 'univ2') { // 获取工厂实例 v2Factory = this.parseFactory(routerObj, v2FactoryAbi, factoryAddress) // 获取当前pairsLength pairsLength = await v2Factory.methods.allPairsLength().call() } else if (routerObj.type === 'univ3') { // 获取positionManager v3PositionManager = this.parsePositionManager(routerObj, positionManagerAbi) pairsLength = await this.getPositionLength(v3PositionManager, position) } // 如果有未获取的池子 const haveNewLp = position < pairsLength // 打印信息 if (haveNewLp) { logger.debug(`Router address: ${routerObj.router}`) if (routerObj.type === 'univ2') { logger.debug(`factory: ${factoryAddress}, ${position} / ${pairsLength}.`) } else { logger.debug(`position: ${routerObj.position}, ${position} / ${pairsLength}.`) } } // 挨个处理lp信息 for (; position < pairsLength; position++) { await this.handleLp(position, pairsLength, routerObj, v2ToolBy410, v3Tool, v3PositionManager) } if (haveNewLp) { if (routerObj.type === 'univ2') { await History.appendOrUpdate('UNIV2DEX', routerObj.factory, {"next": position}) } else if (routerObj.type === 'univ3') { await History.appendOrUpdate('UNIV3DEX', routerObj.router + '_router', {"next": position}) } logger.debug('') logger.debug('') logger.debug('') } } catch (e) { logger.debug(e) } } await Time.delay(12000) } } } async function main() { await new LpGenerate().run() } main().catch((error) => { console.error(error); process.exitCode = 1; })