lpMaintenance.ts 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340
  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. import {replaceAll} from "hardhat/internal/util/strings";
  7. import Time from "../../utils/time";
  8. import MemoryUtils from "../../utils/memory";
  9. // 初始化410 v2工具箱
  10. const v2ToolBy410Abi = require('../../abi/410_V2_TOOLS.json')
  11. const v2ToolBy410 = new web3.eth.Contract(v2ToolBy410Abi, contracts.V2_TOOLS_BY_410)
  12. // basetoken
  13. const baseTokenMap = require('../../config/base-token.json')
  14. const baseTokenAddressList = Object.keys(baseTokenMap)
  15. const ethTokenAddressList = baseTokenAddressList.filter(baseTokenAddress => {
  16. return baseTokenMap[baseTokenAddress].name.indexOf('ETH') != -1
  17. })
  18. export class LpMaintenance {
  19. tokenAssembly: any = {}
  20. memoryUtils: MemoryUtils = new MemoryUtils()
  21. prevUpdateEthLpHours: number = -1
  22. // 计算价格要用
  23. allMaxValueLpGroupBySum: any = {}
  24. baseTokenConvertEthValueMap: any = {}
  25. async saveLp(lp: any, type='0') {
  26. await History.appendOrUpdate(type, lp.LP, lp)
  27. }
  28. getLpEthValue(lp: any) {
  29. const token0 = lp.token0.toLowerCase()
  30. const token1 = lp.token1.toLowerCase()
  31. // 1.两token之一能直接与ethw或wethw做直接池子的
  32. // 2.两token之一能直接与baseToken做池子的
  33. // 这两种情况直接带入baseTokenConvertEthValueMap计算
  34. for (const baseTokenAddress of baseTokenAddressList) {
  35. const token0AndBaseTokenSum = this.getHexSum(token0, baseTokenAddress.toLowerCase())
  36. const token0AndBaseTokenLp = this.allMaxValueLpGroupBySum[token0AndBaseTokenSum]
  37. if (token0AndBaseTokenLp) {
  38. const priceMap: any = {}
  39. if (token0AndBaseTokenLp.token0.toLowerCase() == token0) {
  40. priceMap['0'] = token0AndBaseTokenLp.in1Token0OutToken1 * this.baseTokenConvertEthValueMap[baseTokenAddress]
  41. } else {
  42. priceMap['0'] = token0AndBaseTokenLp.in1Token1OutToken0 * this.baseTokenConvertEthValueMap[baseTokenAddress]
  43. }
  44. priceMap['1'] = 0
  45. return priceMap
  46. }
  47. const token1AndBaseTokenSum = this.getHexSum(token1, baseTokenAddress.toLowerCase())
  48. const token1AndBaseTokenLp = this.allMaxValueLpGroupBySum[token1AndBaseTokenSum]
  49. if (token1AndBaseTokenLp) {
  50. const priceMap: any = {}
  51. if (token1AndBaseTokenLp.token0.toLowerCase() == token1) {
  52. priceMap['1'] = token1AndBaseTokenLp.in1Token0OutToken1 * this.baseTokenConvertEthValueMap[baseTokenAddress]
  53. } else {
  54. priceMap['1'] = token1AndBaseTokenLp.in1Token1OutToken0 * this.baseTokenConvertEthValueMap[baseTokenAddress]
  55. }
  56. priceMap['0'] = 0
  57. return priceMap
  58. }
  59. // lp.LP.toLowerCase() === '0x23103F8a0A9B8585762Ab3cD79e1667A1f5C163a'.toLowerCase()
  60. }
  61. // 3.两token都不能与baseToken做池子的
  62. // 这种情况则视为lp类型池子
  63. return {'0': 0, '1': 0}
  64. }
  65. async checkLpType(lp: any) {
  66. // lp过滤后类型表
  67. const filterTypeList = [
  68. { type: 'topLp', limit: 10 },
  69. { type: 'normalLp', limit: 1 },
  70. { type: 'lp', limit: 0 },
  71. ]
  72. // token折合成eth的价格,分三种情况探讨
  73. // 0: token0与eth的价格
  74. // 1: token1与eth的价格
  75. let tokenConvertEthPrice: any = this.getLpEthValue(lp)
  76. // 从高到低遍历过滤条件
  77. const decimals0 = parseInt(lp.decimals0)
  78. const decimals1 = parseInt(lp.decimals1)
  79. const realAmount0 = parseInt(lp.r0) * tokenConvertEthPrice['0'] / Math.pow(10, decimals0)
  80. const realAmount1 = parseInt(lp.r1) * tokenConvertEthPrice['1'] / Math.pow(10, decimals1)
  81. for (const filterType of filterTypeList) {
  82. const amountLimit = filterType.limit
  83. if (realAmount0 >= amountLimit || realAmount1 >= amountLimit) {
  84. return filterType.type
  85. }
  86. }
  87. }
  88. async checkTokenByAddress(tokenAddress: any, newType: string) {
  89. const tokenDbOBj = this.tokenAssembly[tokenAddress]
  90. if (tokenDbOBj && this.needUpdate(tokenDbOBj.type, newType)) {
  91. logger.debug(`token:${tokenAddress},${tokenDbOBj.type}->${newType}.`)
  92. tokenDbOBj.type = newType
  93. await History.appendOrUpdate(tokenDbOBj.type, tokenDbOBj.hash, tokenDbOBj.data)
  94. }
  95. }
  96. async handleLp(lp: any, oldType: any) {
  97. // 过滤Lp
  98. let lpType: any = await this.checkLpType(lp)
  99. let tokenType = 'token'
  100. if (lpType != 'lp') tokenType = replaceAll(lpType, 'Lp', '') + 'Token'
  101. // 一共12w个池子,非ethw的垃圾池子就不拉了
  102. if (lpType == 'lp' && !lp.isEthW) {
  103. lpType = 'ethLp'
  104. }
  105. if (lpType != oldType) {
  106. // 保存变更后的Lp
  107. await this.saveLp(lp, lpType)
  108. logger.debug(`lp:${lp.LP}(${lp.r0}, ${lp.r1}),${oldType}->${lpType}.`)
  109. }
  110. await this.checkTokenByAddress(lp.token0, tokenType)
  111. await this.checkTokenByAddress(lp.token1, tokenType)
  112. }
  113. generateAddressListByLpList (lpList: any) {
  114. const lpAddressList: any = []
  115. for (const lp of lpList) {
  116. lp.dataObj = JSON.parse(lp.data)
  117. lpAddressList.push(lp.dataObj.LP)
  118. }
  119. return lpAddressList
  120. }
  121. updateLocalLpListR0R1(lpList: any, r0s: any, r1s: any) {
  122. for (let index = 0; index < lpList.length; index++) {
  123. const lp = lpList[index].dataObj
  124. if ((lp.symbol0 === 'ERR' && parseInt(lp.decimals0) === 0)
  125. || (lp.symbol1 === 'ERR' && parseInt(lp.decimals1) === 0)) {
  126. lp.r0 = 0
  127. lp.r1 = 0
  128. } else {
  129. lp.r0 = r0s[index]
  130. lp.r1 = r1s[index]
  131. }
  132. }
  133. }
  134. putAllLpGroupBySum(lpList: any) {
  135. this.allMaxValueLpGroupBySum = {}
  136. for (const lpDbObj of lpList) {
  137. const lp = lpDbObj.dataObj
  138. lp.sum2 = this.getHexSum(lp.token0, lp.token1)
  139. const notExist = !this.allMaxValueLpGroupBySum[lp.sum2]
  140. const moreValueThanNowMax = !notExist && (parseInt(lp.r0) > this.allMaxValueLpGroupBySum[lp.sum2].r0)
  141. if (notExist || moreValueThanNowMax) {
  142. const r0RealAmount = parseInt(lp.r0) / Math.pow(10, parseInt(lp.decimals0))
  143. const r1RealAmount = parseInt(lp.r1) / Math.pow(10, parseInt(lp.decimals1))
  144. // 计算0换1的价格和1换0的价格
  145. lp.in1Token0OutToken1 = r1RealAmount / r0RealAmount // 一个token0 = in1Token0OutToken1个token1
  146. lp.in1Token1OutToken0 = r0RealAmount / r1RealAmount // 一个token1 = oneForZeroPrice个token0
  147. this.allMaxValueLpGroupBySum[lp.sum2] = lp
  148. }
  149. }
  150. }
  151. needUpdate(oldType: string, newType: string) {
  152. const tokenTypeMap: any = {
  153. 'token': 1,
  154. 'normalToken': 2,
  155. 'topToken': 3
  156. }
  157. return tokenTypeMap[newType] > tokenTypeMap[oldType]
  158. }
  159. async pullAllToken(lpList: any) {
  160. for (const lpDbObj of lpList) {
  161. const lp = lpDbObj.dataObj
  162. const lpType = lpDbObj.block
  163. let tokenType = 'token'
  164. if (lpType != 'lp') tokenType = replaceAll(lpType, 'Lp', '') + 'Token'
  165. if (!this.tokenAssembly[lp.token0]) {
  166. const token0Rst = await History.findByHash(lp.token0)
  167. const token0DbObj = token0Rst.data[0]
  168. if (token0DbObj) {
  169. this.tokenAssembly[lp.token0] = {
  170. type: token0DbObj.block,
  171. hash: token0DbObj.hash,
  172. data: JSON.parse(token0DbObj.data)
  173. }
  174. }
  175. }
  176. if (!this.tokenAssembly[lp.token1]) {
  177. const token1Rst = await History.findByHash(lp.token1)
  178. const token1DbObj = token1Rst.data[0]
  179. if (token1DbObj) {
  180. this.tokenAssembly[lp.token1] = {
  181. type: token1DbObj.block,
  182. hash: token1DbObj.hash,
  183. data: JSON.parse(token1DbObj.data)
  184. }
  185. }
  186. }
  187. }
  188. }
  189. calcBaseTokenCovertEthValue() {
  190. for (const baseTokenAddress of baseTokenAddressList) {
  191. const isEthToken = ethTokenAddressList.indexOf(baseTokenAddress) != -1
  192. // eth兑换eth价格当然是1:1啦
  193. if (isEthToken) {
  194. this.baseTokenConvertEthValueMap[baseTokenAddress] = 1
  195. } else {
  196. // 在ETH token中查找适合兑换的
  197. for (const ethTokenAddress of ethTokenAddressList) {
  198. const maxValueLp = this.allMaxValueLpGroupBySum[this.getHexSum(baseTokenAddress, ethTokenAddress)]
  199. if (!maxValueLp) continue
  200. const token0 = maxValueLp.token0.toLowerCase()
  201. const token1 = maxValueLp.token1.toLowerCase()
  202. if (baseTokenAddress.toLowerCase() == token0) {
  203. this.baseTokenConvertEthValueMap[baseTokenAddress] = maxValueLp.in1Token0OutToken1
  204. } else if (baseTokenAddress.toLowerCase() == token1) {
  205. this.baseTokenConvertEthValueMap[baseTokenAddress] = maxValueLp.in1Token1OutToken0
  206. }
  207. }
  208. }
  209. }
  210. }
  211. getHexSum(hex0: string, hex1: string) {
  212. return replaceAll(BigNumber.from(hex0).add(BigNumber.from(hex1))._hex, '0x0', '0x')
  213. }
  214. needToCheckEthLp() {
  215. const hours = new Date().getHours()
  216. if (this.prevUpdateEthLpHours != hours) {
  217. this.prevUpdateEthLpHours = hours
  218. return true
  219. }
  220. return false
  221. }
  222. async run() {
  223. logger.debug('LP maintenance start.')
  224. while (true) {
  225. this.memoryUtils.logWithMemoryOnMemoryChange('a loop...')
  226. try {
  227. // toplp获取信息
  228. const topLpPullRst = await History.findByBlock('topLp')
  229. if (!topLpPullRst.state) continue
  230. const topLpList = topLpPullRst.data
  231. // lp获取信息
  232. const lpPullRst = await History.findByBlock('lp')
  233. if (!lpPullRst.state) continue
  234. const lpList = lpPullRst.data
  235. // normalLp获取息p
  236. const normalLpPullRst = await History.findByBlock('normalLp')
  237. if (!normalLpPullRst.state) continue
  238. const normalLpList = normalLpPullRst.data
  239. // 信息串联
  240. let allTypeLpList: any = topLpList.concat(lpList).concat(normalLpList)
  241. // 信息的ethLp获取需要是否
  242. const needToCheckEthLp = this.needToCheckEthLp()
  243. if (needToCheckEthLp) {
  244. const ethLpPullRst = await History.findByBlock('ethLp')
  245. if (!ethLpPullRst.state) continue
  246. const ethLpList = ethLpPullRst.data
  247. allTypeLpList = allTypeLpList.concat(ethLpList)
  248. }
  249. const lpAddressList: any = this.generateAddressListByLpList(allTypeLpList)
  250. // lp按sum分类
  251. this.putAllLpGroupBySum(allTypeLpList)
  252. // 获取所有lp的未拉取的token
  253. await this.pullAllToken(allTypeLpList)
  254. // 集中拉取r0,r1并更新本地的
  255. const size = needToCheckEthLp ? 200 : 2000
  256. for (let from = 0; from < allTypeLpList.length; from += size) {
  257. // logger.debug(`${from}, ${allTypeLpList.length}`)
  258. if (needToCheckEthLp) await Time.delay(168)
  259. const lpR0R1Info: any = await v2ToolBy410.methods.getPairSBalance(lpAddressList.slice(from, from + size)).call()
  260. const r0s = lpR0R1Info.amounts0
  261. const r1s = lpR0R1Info.amounts1
  262. this.updateLocalLpListR0R1(allTypeLpList.slice(from, from + size), r0s, r1s)
  263. }
  264. this.calcBaseTokenCovertEthValue()
  265. // 将lp类型有变动的全部更新
  266. // logger.debug(`${allTypeLpList.length}`)
  267. for (const lpDbObj of allTypeLpList) {
  268. await this.handleLp(lpDbObj.dataObj, lpDbObj.block)
  269. }
  270. } catch (e) {
  271. logger.debug(e)
  272. }
  273. await Time.delay(12000)
  274. }
  275. }
  276. }
  277. async function main() {
  278. await new LpMaintenance().run()
  279. }
  280. main().catch((error) => {
  281. console.error(error);
  282. process.exitCode = 1;
  283. })