| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313 |
- 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 Time from "../../utils/time";
- import MemoryUtils from "../../utils/memory";
- // 初始化410 v2工具箱
- const v2ToolBy410Abi = require('../../abi/410_V2_TOOLS.json')
- const v2ToolBy410 = new web3.eth.Contract(v2ToolBy410Abi, contracts.V2_TOOLS_BY_410)
- // basetoken
- const baseTokenMap = require('../../config/base-token.json')
- const baseTokenAddressList = Object.keys(baseTokenMap)
- const ethTokenAddressList = baseTokenAddressList.filter(baseTokenAddress => {
- return baseTokenMap[baseTokenAddress].name.indexOf('ETH') != -1
- })
- export class LpMaintenance {
- tokenAssembly: any = {}
- memoryUtils: MemoryUtils = new MemoryUtils()
- // 计算价格要用
- allMaxValueLpGroupBySum: any = {}
- baseTokenConvertEthValueMap: any = {}
- async saveLp(lp: any, type='0') {
- await History.appendOrUpdate(type, lp.LP, lp)
- }
- getLpEthValue(lp: any) {
- const token0 = lp.token0.toLowerCase()
- const token1 = lp.token1.toLowerCase()
- // 1.两token之一能直接与ethw或wethw做直接池子的
- // 2.两token之一能直接与baseToken做池子的
- // 这两种情况直接带入baseTokenConvertEthValueMap计算
- for (const baseTokenAddress of baseTokenAddressList) {
- const token0AndBaseTokenSum = this.getHexSum(token0, baseTokenAddress.toLowerCase())
- const token0AndBaseTokenLp = this.allMaxValueLpGroupBySum[token0AndBaseTokenSum]
- if (token0AndBaseTokenLp) {
- const priceMap: any = {}
- if (token0AndBaseTokenLp.token0.toLowerCase() == token0) {
- priceMap['0'] = token0AndBaseTokenLp.in1Token0OutToken1 * this.baseTokenConvertEthValueMap[baseTokenAddress]
- } else {
- priceMap['0'] = token0AndBaseTokenLp.in1Token1OutToken0 * this.baseTokenConvertEthValueMap[baseTokenAddress]
- }
- priceMap['1'] = 0
- return priceMap
- }
- const token1AndBaseTokenSum = this.getHexSum(token1, baseTokenAddress.toLowerCase())
- const token1AndBaseTokenLp = this.allMaxValueLpGroupBySum[token1AndBaseTokenSum]
- if (token1AndBaseTokenLp) {
- const priceMap: any = {}
- if (token1AndBaseTokenLp.token0.toLowerCase() == token1) {
- priceMap['1'] = token1AndBaseTokenLp.in1Token0OutToken1 * this.baseTokenConvertEthValueMap[baseTokenAddress]
- } else {
- priceMap['1'] = token1AndBaseTokenLp.in1Token1OutToken0 * this.baseTokenConvertEthValueMap[baseTokenAddress]
- }
- priceMap['0'] = 0
- return priceMap
- }
- // lp.LP.toLowerCase() === '0x23103F8a0A9B8585762Ab3cD79e1667A1f5C163a'.toLowerCase()
- }
- // 3.两token都不能与baseToken做池子的
- // 这种情况则视为lp类型池子
- return {'0': 0, '1': 0}
- }
- async checkLpType(lp: any) {
- // lp过滤后类型表
- const filterTypeList = [
- { type: 'topLp', limit: 10 },
- { type: 'normalLp', limit: 1 },
- { type: 'lp', limit: 0 },
- ]
- // token折合成eth的价格,分三种情况探讨
- // 0: token0与eth的价格
- // 1: token1与eth的价格
- let tokenConvertEthPrice: any = this.getLpEthValue(lp)
- // 从高到低遍历过滤条件
- const decimals0 = parseInt(lp.decimals0)
- const decimals1 = parseInt(lp.decimals1)
- const realAmount0 = parseInt(lp.r0) * tokenConvertEthPrice['0'] / Math.pow(10, decimals0)
- const realAmount1 = parseInt(lp.r1) * tokenConvertEthPrice['1'] / Math.pow(10, decimals1)
- for (const filterType of filterTypeList) {
- const amountLimit = filterType.limit
- if (realAmount0 >= amountLimit || realAmount1 >= amountLimit) {
- return filterType.type
- }
- }
- }
- async checkTokenByAddress(tokenAddress: any, newType: string) {
- const tokenDbOBj = this.tokenAssembly[tokenAddress]
- if (tokenDbOBj && this.needUpdate(tokenDbOBj.type, newType)) {
- logger.debug(`token:${tokenAddress},${tokenDbOBj.type}->${newType}.`)
- tokenDbOBj.type = newType
- await History.appendOrUpdate(tokenDbOBj.type, tokenDbOBj.hash, tokenDbOBj.data)
- }
- }
- async handleLp(lp: any, oldType: any) {
- // 过滤Lp
- let lpType: any = await this.checkLpType(lp)
- let tokenType = 'token'
- if (lpType != 'lp') tokenType = replaceAll(lpType, 'Lp', '') + 'Token'
- // 一共12w个池子,非ethw的垃圾池子就不拉了
- if (lpType == 'lp' && !lp.isEthW) {
- lpType = 'ethLp'
- }
- if (lpType != oldType) {
- // 保存变更后的Lp
- await this.saveLp(lp, lpType)
- logger.debug(`lp:${lp.LP},${oldType}->${lpType}.`)
- }
- await this.checkTokenByAddress(lp.token0, tokenType)
- await this.checkTokenByAddress(lp.token1, tokenType)
- }
- generateAddressListByLpList (lpList: any) {
- const lpAddressList: any = []
- for (const lp of lpList) {
- lp.dataObj = JSON.parse(lp.data)
- lpAddressList.push(lp.dataObj.LP)
- }
- return lpAddressList
- }
- updateLocalLpListR0R1(lpList: any, r0s: any, r1s: any) {
- for (let index = 0; index < lpList.length; index++) {
- lpList[index].dataObj.r0 = r0s[index]
- lpList[index].dataObj.r1 = r1s[index]
- }
- }
- putAllLpGroupBySum(lpList: any) {
- this.allMaxValueLpGroupBySum = {}
- for (const lpDbObj of lpList) {
- const lp = lpDbObj.dataObj
- lp.sum2 = this.getHexSum(lp.token0, lp.token1)
- const notExist = !this.allMaxValueLpGroupBySum[lp.sum2]
- const moreValueThanNowMax = !notExist && (parseInt(lp.r0) > this.allMaxValueLpGroupBySum[lp.sum2].r0)
- if (notExist || moreValueThanNowMax) {
- const r0RealAmount = parseInt(lp.r0) / Math.pow(10, parseInt(lp.decimals0))
- const r1RealAmount = parseInt(lp.r1) / Math.pow(10, parseInt(lp.decimals1))
- // 计算0换1的价格和1换0的价格
- lp.in1Token0OutToken1 = r1RealAmount / r0RealAmount // 一个token0 = in1Token0OutToken1个token1
- lp.in1Token1OutToken0 = r0RealAmount / r1RealAmount // 一个token1 = oneForZeroPrice个token0
- this.allMaxValueLpGroupBySum[lp.sum2] = lp
- }
- }
- }
- needUpdate(oldType: string, newType: string) {
- const tokenTypeMap: any = {
- 'token': 1,
- 'normalToken': 2,
- 'topToken': 3
- }
- return tokenTypeMap[newType] > tokenTypeMap[oldType]
- }
- async pullAllToken(lpList: any) {
- for (const lpDbObj of lpList) {
- const lp = lpDbObj.dataObj
- const lpType = lpDbObj.block
- let tokenType = 'token'
- if (lpType != 'lp') tokenType = replaceAll(lpType, 'Lp', '') + 'Token'
- if (!this.tokenAssembly[lp.token0]) {
- const token0Rst = await History.findByHash(lp.token0)
- const token0DbObj = token0Rst.data[0]
- if (token0DbObj) {
- this.tokenAssembly[lp.token0] = {
- type: token0DbObj.block,
- hash: token0DbObj.hash,
- data: JSON.parse(token0DbObj.data)
- }
- }
- }
- if (!this.tokenAssembly[lp.token1]) {
- const token1Rst = await History.findByHash(lp.token1)
- const token1DbObj = token1Rst.data[0]
- if (token1DbObj) {
- this.tokenAssembly[lp.token1] = {
- type: token1DbObj.block,
- hash: token1DbObj.hash,
- data: JSON.parse(token1DbObj.data)
- }
- }
- }
- }
- }
- calcBaseTokenCovertEthValue() {
- for (const baseTokenAddress of baseTokenAddressList) {
- const isEthToken = ethTokenAddressList.indexOf(baseTokenAddress) != -1
- // eth兑换eth价格当然是1:1啦
- if (isEthToken) {
- this.baseTokenConvertEthValueMap[baseTokenAddress] = 1
- } else {
- // 在ETH token中查找适合兑换的
- for (const ethTokenAddress of ethTokenAddressList) {
- const maxValueLp = this.allMaxValueLpGroupBySum[this.getHexSum(baseTokenAddress, ethTokenAddress)]
- if (!maxValueLp) continue
- const token0 = maxValueLp.token0.toLowerCase()
- const token1 = maxValueLp.token1.toLowerCase()
- if (baseTokenAddress.toLowerCase() == token0) {
- this.baseTokenConvertEthValueMap[baseTokenAddress] = maxValueLp.in1Token0OutToken1
- } else if (baseTokenAddress.toLowerCase() == token1) {
- this.baseTokenConvertEthValueMap[baseTokenAddress] = maxValueLp.in1Token1OutToken0
- }
- }
- }
- }
- }
- getHexSum(hex0: string, hex1: string) {
- return replaceAll(BigNumber.from(hex0).add(BigNumber.from(hex1))._hex, '0x0', '0x')
- }
- async run() {
- logger.debug('LP maintenance start.')
- while (true) {
- await Time.delay(12000)
- this.memoryUtils.logWithMemoryOnMemoryChange('a loop...')
- try {
- const topLpPullRst = await History.findByBlock('topLp')
- if (!topLpPullRst.state) continue
- const topLpList = topLpPullRst.data
- const lpPullRst = await History.findByBlock('lp')
- if (!lpPullRst.state) continue
- const lpList = lpPullRst.data
- const normalLpPullRst = await History.findByBlock('normalLp')
- if (!normalLpPullRst.state) continue
- const normalLpList = normalLpPullRst.data
- // const ethLpPullRst = await History.findByBlock('ethLp')
- // if (!ethLpPullRst.state) continue
- // const ethLpList = ethLpPullRst.data
- const ethLpList: any = []
- const allTypeLpList: any = topLpList.concat(lpList).concat(normalLpList).concat(ethLpList)
- // const allTypeLpList: any = [].concat(normalLpList).concat(ethLpList)
- const lpAddressList: any = this.generateAddressListByLpList(allTypeLpList)
- // lp按sum分类
- this.putAllLpGroupBySum(allTypeLpList)
- // 获取所有lp的未拉取的token
- await this.pullAllToken(allTypeLpList)
- // 集中拉取r0,r1并更新本地的
- const size = 2000
- for (let from = 0; from < allTypeLpList.length; from += size) {
- // logger.debug(`${from}, ${allTypeLpList.length}`)
- await Time.delay(168)
- const lpR0R1Info: any = await v2ToolBy410.methods.getPairSBalance(lpAddressList.slice(from, from + size)).call()
- const r0s = lpR0R1Info.amounts0
- const r1s = lpR0R1Info.amounts1
- this.updateLocalLpListR0R1(allTypeLpList.slice(from, from + size), r0s, r1s)
- }
- this.calcBaseTokenCovertEthValue()
- // 将lp类型有变动的全部更新
- // logger.debug(`${allTypeLpList.length}`)
- for (const lpDbObj of allTypeLpList) {
- await this.handleLp(lpDbObj.dataObj, lpDbObj.block)
- }
- } catch (e) {
- logger.debug(e)
- }
- }
- }
- }
- async function main() {
- await new LpMaintenance().run()
- }
- main().catch((error) => {
- console.error(error);
- process.exitCode = 1;
- })
|