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 swapPath from "../interface/swapPath"; import history from "../interface/history"; const ierc20abi = require('../../abi/IERC20_ABI.json') export class PullAndPush { fromHead: boolean = false isFirst: boolean = false constructor (_fromHead: boolean, _isFirst: boolean) { this.fromHead = _fromHead this.isFirst = _isFirst } async handlePosition(router: String, factory: String) { try { // 拉取当前pull状态信息 const pullState = await History.findByHashOrBlockOrDataVague('UNIV2DEX', factory, '', 0, 200) let nowPosition = 0 // 没有状态信息的情况 if (pullState.data.length == 0) { const appendRst = await History.appendOrUpdate('UNIV2DEX', factory, {"now": nowPosition}) logger.debug(appendRst) } else { nowPosition = pullState.data[0].dataObj.now } return nowPosition } catch (e) { logger.error(`Pull state error, router: ${router}, factory: ${factory}`) logger.error(e) } return 0 } async getPoolByPosition(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' } // 更新一次position await History.appendOrUpdate('UNIV2DEX', routerObj.factory, {"now": position}) return lp } catch (e) {} return undefined } async saveLpToSwapPath(lp: any) { const lpSum2Query = await swapPath.findBySumValueAndLevel(lp.sum2, '2') let lpSum2List = [] if (!this.isFirst && lpSum2Query.state) { lpSum2List = JSON.parse(lpSum2Query.data.data) } lpSum2List.push(lp) const insertRst = await swapPath.appendOrUpdate(lp.sum2, '2', lpSum2List) logger.debug(`save path: ${lp.name}: ${insertRst.msg}`) } 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' } const tokenObj = new web3.eth.Contract(ierc20abi, token.address) 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) const saveRst = await history.appendOrUpdate(type, token.address, token) logger.debug(`${token.name} ${saveRst.msg}`) } catch (e) {} try { const token = await this.handleToken(lp, false) const saveRst = await history.appendOrUpdate(type, token.address, token) logger.debug(`${token.name} ${saveRst.msg}`) } catch (e) {} } async saveLp(lp: any, type='0') { const insertRst = await History.appendOrUpdate(type, lp.LP, lp) logger.debug(`insert lp:${insertRst.msg}, hash: ${lp.LP}.}`) } async filterLp(lp: any) { if (lp.token0.toLowerCase() === '0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2') { return parseInt(lp.r0) > 20 * 1e18 } else if (lp.token1.toLowerCase() === '0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2') { return parseInt(lp.r1) > 20 * 1e18 } return true } parseFactory(router: any, factoryAbi: any, factoryAddress: any) { if (router.factoryObj == null) { router.factoryObj = new web3.eth.Contract(factoryAbi, factoryAddress) } return router.factoryObj } async run() { logger.debug('Pull v2 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') const routerList: Array = require('../../config/router-list.json') while (true) { for (const routerObj of routerList) { if (routerObj.type === 'univ3') continue const v2FactoryAddress = routerObj.factory // 获取工厂实例 const v2Factory = this.parseFactory(routerObj, v2FactoryAbi, v2FactoryAddress) // 获取当前pull状态 let position = this.fromHead ? routerObj.fromPosition : await this.handlePosition(routerObj.router, v2FactoryAddress) // 获取当前pairsLength const pairsLength = await v2Factory.methods.allPairsLength().call() // 如果有未获取的池子 const haveNewLp = position + 1 < pairsLength // 打印信息 if (haveNewLp) { logger.debug(`Router address: ${routerObj.router}`) logger.debug(`factory: ${v2FactoryAddress}, ${position + 1} / ${pairsLength}.`) } // 挨个处理lp信息 while (position + 1 < pairsLength) { logger.debug(`${position + 1} / ${pairsLength}`) // 1. 获取lp const lp = await this.getPoolByPosition(routerObj, position, v2ToolBy410) // 2. 保存Lp信息 await this.saveLp(lp, '0') // 3. 保存Lp的Token到Token表 await this.saveToken(lp, 'token') // 4. 过滤Lp if (await this.filterLp(lp)) { // 5. 将过滤后的Lp保存到二阶表 await this.saveLpToSwapPath(lp) // 6. 保存筛选之后的的Token到TopToken表 await this.saveToken(lp, 'topToken') // 7. 保存过滤后的Lp到TopLp await this.saveLp(lp, 'topLp') } position++ } if (haveNewLp) { logger.debug('') logger.debug('') logger.debug('') } } if (this.fromHead && this.isFirst) { this.isFirst = false } } } }