lpMaintenance.ts 8.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260
  1. import { web3 } from "hardhat";
  2. import History from "../interface/history";
  3. import contracts from "../../config/contracts";
  4. import logger from "../../utils/logger";
  5. import {BigNumber} from "ethers";
  6. // 初始化410 v2工具箱
  7. const v2ToolBy410Abi = require('../../abi/410_V2_TOOLS.json')
  8. const v2ToolBy410 = new web3.eth.Contract(v2ToolBy410Abi, contracts.V2_TOOLS_BY_410)
  9. // basetoken
  10. const baseTokenMap = require('../../config/base-token.json')
  11. const baseTokenAddressList = Object.keys(baseTokenMap)
  12. const ethTokenAddressList = baseTokenAddressList.filter(baseTokenAddress => {
  13. return baseTokenMap[baseTokenAddress].name.indexOf('ETH') != -1
  14. })
  15. export class LpMaintenance {
  16. tokenInstance: any = {}
  17. maxMemoryOfByte: number = 0
  18. maxMemoryChanged: boolean = true
  19. // 计算价格要用
  20. allMaxValueLpGroupBySum: any = {}
  21. baseTokenConvertEthValueMap: any = {}
  22. async saveLp(lp: any, type='0') {
  23. await History.appendOrUpdate(type, lp.LP, lp)
  24. }
  25. getLpEthValue(lp: any) {
  26. const token0 = lp.token0.toLowerCase()
  27. const token1 = lp.token1.toLowerCase()
  28. // 1.两token之一能直接与ethw或wethw做直接池子的
  29. // 2.两token之一能直接与baseToken做池子的
  30. // 这两种情况直接带入baseTokenConvertEthValueMap计算
  31. for (const baseTokenAddress of baseTokenAddressList) {
  32. const token0AndBaseTokenLp = this.allMaxValueLpGroupBySum[this.getHexSum(token0, baseTokenAddress.toLowerCase())]
  33. if (token0AndBaseTokenLp) {
  34. return {
  35. '0': token0AndBaseTokenLp.zeroToOnePrice * this.baseTokenConvertEthValueMap[baseTokenAddress],
  36. '1': 1 / token0AndBaseTokenLp.zeroToOnePrice * this.baseTokenConvertEthValueMap[baseTokenAddress]
  37. }
  38. }
  39. const token1AndBaseTokenLp = this.allMaxValueLpGroupBySum[this.getHexSum(token1, baseTokenAddress.toLowerCase())]
  40. if (token1AndBaseTokenLp) {
  41. return {
  42. '0': 1 / token1AndBaseTokenLp.oneToZeroPrice * this.baseTokenConvertEthValueMap[baseTokenAddress],
  43. '1': token1AndBaseTokenLp.oneToZeroPrice * this.baseTokenConvertEthValueMap[baseTokenAddress]
  44. }
  45. }
  46. if (ethTokenAddressList.indexOf(baseTokenAddress.toLowerCase()) == -1) {
  47. logger.debug(`${JSON.stringify(lp)}`)
  48. logger.debug(`0, ${token0AndBaseTokenLp.zeroToOnePrice}, ${this.baseTokenConvertEthValueMap[baseTokenAddress]}`)
  49. logger.debug(`1, ${token1AndBaseTokenLp.zeroToOnePrice}, ${this.baseTokenConvertEthValueMap[baseTokenAddress]}`)
  50. }
  51. }
  52. // 3.两token都不能与baseToken做池子的
  53. // 这种情况则视为lp类型池子
  54. return {'0': 0, '1': 0}
  55. }
  56. async checkLpType(lp: any) {
  57. // lp过滤后类型表
  58. const filterTypeList = [
  59. { type: 'topLp', limit: 1 },
  60. { type: 'normalLp', limit: 0.1 },
  61. { type: 'lp', limit: 0 },
  62. ]
  63. // token折合成eth的价格
  64. let tokenConvertEthPrice: any = {
  65. '0': 1,
  66. '1': 1
  67. }
  68. // token0和token1都不是eth的,分三种情况探讨
  69. if (ethTokenAddressList.indexOf(lp.token0.toLowerCase()) == -1
  70. && ethTokenAddressList.indexOf(lp.token1.toLowerCase()) == -1) {
  71. tokenConvertEthPrice = this.getLpEthValue(lp)
  72. }
  73. // 从高到低遍历过滤条件
  74. const decimals0 = parseInt(lp.decimals0)
  75. const decimals1 = parseInt(lp.decimals0)
  76. const realAmount0 = parseInt(lp.r0) * tokenConvertEthPrice['0'] / Math.pow(10, -decimals0)
  77. const realAmount1 = parseInt(lp.r1) * tokenConvertEthPrice['1'] / Math.pow(10, -decimals1)
  78. for (const filterType of filterTypeList) {
  79. const amountLimit = filterType.limit
  80. if (realAmount0 >= amountLimit || realAmount1 >= amountLimit) {
  81. return filterType.type
  82. }
  83. }
  84. }
  85. async handleLp(lp: any, oldType: any) {
  86. // 过滤Lp
  87. let lpType = await this.checkLpType(lp)
  88. // 一共12w个池子,非ethw的垃圾池子就不拉了
  89. if (lpType == 'lp' && !lp.isEthW) {
  90. lpType = 'ethLp'
  91. }
  92. if (lpType != oldType) {
  93. // 保存变更之后的的Token
  94. // await this.saveToken(lp, lpType)
  95. // 保存变更后的Lp
  96. await this.saveLp(lp, lpType)
  97. logger.debug(`lp:${lp.LP},${oldType}->${lpType}.`)
  98. }
  99. }
  100. showMemory (mainInfo: string) {
  101. const memoryUsage = process.memoryUsage()
  102. if (this.maxMemoryOfByte < memoryUsage.rss) {
  103. this.maxMemoryOfByte = memoryUsage.rss
  104. this.maxMemoryChanged = true
  105. }
  106. if (this.maxMemoryChanged) {
  107. logger.debug(`${mainInfo} ${this.format(memoryUsage.rss)}/${this.format(this.maxMemoryOfByte)}`)
  108. this.maxMemoryChanged = false
  109. }
  110. }
  111. format (bytes: any) {
  112. return (bytes / 1024 / 1024).toFixed(2) + ' MB';
  113. }
  114. generateAddressListByLpList (lpList: any) {
  115. const lpAddressList: any = []
  116. for (const lp of lpList) {
  117. lp.dataObj = JSON.parse(lp.data)
  118. lpAddressList.push(lp.dataObj.LP)
  119. }
  120. return lpAddressList
  121. }
  122. updateLocalLpListR0R1(lpList: any, r0s: any, r1s: any) {
  123. for (let index = 0; index < lpList.length; index++) {
  124. lpList[index].dataObj.r0 = r0s[index]
  125. lpList[index].dataObj.r1 = r1s[index]
  126. }
  127. }
  128. putAllLpGroupBySum(lpList: any) {
  129. this.allMaxValueLpGroupBySum = {}
  130. for (const lpDbObj of lpList) {
  131. const lp = lpDbObj.dataObj
  132. const notExist = !this.allMaxValueLpGroupBySum[lp.sum2]
  133. const moreValueThanNowMax = !notExist && (parseInt(lp.r0) > this.allMaxValueLpGroupBySum[lp.sum2].r0)
  134. if (notExist || moreValueThanNowMax) {
  135. const r0RealAmount = parseInt(lp.r0) / Math.pow(10, parseInt(lp.decimals0))
  136. const r1RealAmount = parseInt(lp.r1) / Math.pow(10, parseInt(lp.decimals1))
  137. // 计算0换1的价格和1换0的价格
  138. lp.zeroToOnePrice = r1RealAmount / r0RealAmount // 一个token0 = zeroToOnePrice个token1
  139. lp.oneToZeroPrice = r0RealAmount / r1RealAmount // 一个token1 = oneForZeroPrice个token0
  140. this.allMaxValueLpGroupBySum[lp.sum2] = lp
  141. }
  142. }
  143. }
  144. calcBaseTokenCovertEthValue() {
  145. for (const baseTokenAddress of baseTokenAddressList) {
  146. const isEthToken = ethTokenAddressList.indexOf(baseTokenAddress) != -1
  147. // eth兑换eth价格当然是1:1啦
  148. if (isEthToken) {
  149. this.baseTokenConvertEthValueMap[baseTokenAddress] = 1
  150. } else {
  151. // 在ETH token中查找适合兑换的
  152. for (const ethTokenAddress of ethTokenAddressList) {
  153. const maxValueLp = this.allMaxValueLpGroupBySum[this.getHexSum(baseTokenAddress, ethTokenAddress)]
  154. if (!maxValueLp) continue
  155. const token0 = maxValueLp.token0.toLowerCase()
  156. const token1 = maxValueLp.token1.toLowerCase()
  157. if (baseTokenAddress.toLowerCase() == token0) {
  158. this.baseTokenConvertEthValueMap[baseTokenAddress] = maxValueLp.zeroToOnePrice
  159. } else if (baseTokenAddress.toLowerCase() == token1) {
  160. this.baseTokenConvertEthValueMap[baseTokenAddress] = maxValueLp.oneToZeroPrice
  161. }
  162. }
  163. }
  164. }
  165. }
  166. getHexSum(hex0: string, hex1: string) {
  167. return BigNumber.from(hex0).add(BigNumber.from(hex1))._hex
  168. }
  169. async run() {
  170. logger.debug('LP maintenance start.')
  171. while (true) {
  172. this.showMemory('a loop...')
  173. try {
  174. const topLpPullRst = await History.findByBlock('topLp')
  175. if (!topLpPullRst.state) continue
  176. const topLpList = topLpPullRst.data
  177. const lpPullRst = await History.findByBlock('lp')
  178. if (!lpPullRst.state) continue
  179. const lpList = lpPullRst.data
  180. const normalLpPullRst = await History.findByBlock('normalLp')
  181. if (!normalLpPullRst.state) continue
  182. const normalLpList = normalLpPullRst.data
  183. const ethLpPullRst = await History.findByBlock('ethLp')
  184. if (!ethLpPullRst.state) continue
  185. const ethLpList = ethLpPullRst.data
  186. // const ethLpList: any = []
  187. const allTypeLpList: any = topLpList.concat(lpList).concat(normalLpList).concat(ethLpList)
  188. const lpAddressList: any = this.generateAddressListByLpList(allTypeLpList)
  189. // lp按sum分类
  190. this.putAllLpGroupBySum(allTypeLpList)
  191. // logger.debug(`${lpAddressList.length}`)
  192. // 集中拉取r0,r1并更新本地的
  193. const size = 2000
  194. for (let from = 0; from < allTypeLpList.length; from += size) {
  195. // logger.debug(`${from}, ${allTypeLpList.length}`)
  196. const lpR0R1Info: any = await v2ToolBy410.methods.getPairSBalance(lpAddressList.slice(from, from + size)).call()
  197. const r0s = lpR0R1Info.amounts0
  198. const r1s = lpR0R1Info.amounts1
  199. this.updateLocalLpListR0R1(allTypeLpList.slice(from, from + size), r0s, r1s)
  200. }
  201. this.calcBaseTokenCovertEthValue()
  202. // 将lp类型有变动的全部更新
  203. for (const lpDbObj of allTypeLpList) {
  204. await this.handleLp(lpDbObj.dataObj, lpDbObj.block)
  205. }
  206. } catch (e) {}
  207. }
  208. }
  209. }
  210. async function main() {
  211. await new LpMaintenance().run()
  212. }
  213. main().catch((error) => {
  214. console.error(error);
  215. process.exitCode = 1;
  216. })