lpMaintenance.ts 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318
  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 history from "../interface/history";
  8. const ierc20abi = require('../../abi/IERC20_ABI.json')
  9. export class LpMaintenance {
  10. fromHead: boolean = false
  11. isFirst: boolean = false
  12. tokenInstance: any = {}
  13. maxMemoryOfByte: number = 0
  14. constructor (_fromHead: boolean, _isFirst: boolean) {
  15. this.fromHead = _fromHead
  16. this.isFirst = _isFirst
  17. }
  18. async getV2PoolByPosition(routerObj: any, position: number, v2ToolBy410: any) {
  19. try {
  20. const info = await v2ToolBy410.methods.getPairIdInfo(routerObj.factory, position).call()
  21. const symbol0 = info['2'].replace(/[^A-Za-z0-9]+/g, '').substring(0, 10)
  22. const symbol1 = info['6'].replace(/[^A-Za-z0-9]+/g, '').substring(0, 10)
  23. const name = `${routerObj.name}_${symbol0}_${symbol1}`
  24. const sum2 = replaceAll(BigNumber.from(info['1']).add(BigNumber.from(info['5']))._hex, '0x0', '0x')
  25. const lp = {
  26. LP: info['0'],
  27. decimals0: info['3'],
  28. decimals1: info['7'],
  29. factory: routerObj.factory,
  30. feei: routerObj.fee,
  31. id: position,
  32. name: name,
  33. r0: info['4'],
  34. r1: info['8'],
  35. router: routerObj.router,
  36. sum2: sum2,
  37. symbol0: symbol0,
  38. symbol1: symbol1,
  39. token0: info['1'],
  40. token1: info['5'],
  41. isEthW: routerObj.type == 'ETHW'
  42. }
  43. // 更新一次position
  44. // await History.appendOrUpdate('UNIV2DEX', routerObj.factory, {"now": position + 1})
  45. return lp
  46. } catch (e) {}
  47. return undefined
  48. }
  49. async getV3Pool(routerObj: any, position: number, v3Tool: any, positionManager: any) {
  50. try {
  51. const positionInfo = await positionManager.methods.positions(position).call()
  52. const info = await v3Tool.methods.getMoreInfo(positionInfo.token0, positionInfo.token1, positionInfo.fee).call()
  53. const symbol0 = info.symbol0.replace(/[^A-Za-z0-9 ]+/g, '').substring(0, 10)
  54. const symbol1 = info.symbol1.replace(/[^A-Za-z0-9 ]+/g, '').substring(0, 10)
  55. const name = `${routerObj.router.slice(2, 4) + routerObj.router.slice(-2)}_${symbol0}_${symbol1}`
  56. const sum2 = replaceAll(BigNumber.from(positionInfo.token0).add(BigNumber.from(positionInfo.token1))._hex, '0x0', '0x')
  57. const lp = {
  58. LP: info.lp,
  59. decimals0: info.decimals0,
  60. decimals1: info.decimals1,
  61. factory: routerObj.factory,
  62. feei: positionInfo.fee,
  63. id: position,
  64. name: name,
  65. r0: info.r0,
  66. r1: info.r1,
  67. router: routerObj.router,
  68. sum2: sum2,
  69. symbol0: symbol0,
  70. symbol1: symbol1,
  71. token0: positionInfo.token0,
  72. token1: positionInfo.token1
  73. }
  74. // await History.appendOrUpdate('UNIV3DEX', routerObj.router + '_router', {"now": position + 1})
  75. return lp
  76. } catch (e) {}
  77. return undefined
  78. }
  79. async handleToken(pool: any, zero: boolean) {
  80. const token = {
  81. 'address': zero ? pool.token0 : pool.token1,
  82. 'symbol': zero ? pool.symbol0 : pool.symbol1,
  83. 'decimals': zero ? pool.decimals0 : pool.decimals1,
  84. 'name': 'xxxxxxxx'
  85. }
  86. let tokenObj = this.tokenInstance[token.address]
  87. if (!tokenObj) {
  88. tokenObj = new web3.eth.Contract(ierc20abi, token.address)
  89. this.tokenInstance[token.address] = tokenObj
  90. }
  91. token.name = await tokenObj.methods.name().call()
  92. token.name = token.name.replace(/[^A-Za-z0-9 ]+/g, '').substring(0, 37)
  93. return token
  94. }
  95. async saveToken(lp: any, type='token') {
  96. try {
  97. const token = await this.handleToken(lp, true)
  98. // const saveRst = await history.appendOrUpdate(type, token.address, token)
  99. // logger.debug(`${token.name} ${saveRst.msg}`)
  100. await history.appendOrUpdate(type, token.address, token)
  101. } catch (e) {}
  102. try {
  103. const token = await this.handleToken(lp, false)
  104. // const saveRst = await history.appendOrUpdate(type, token.address, token)
  105. // logger.debug(`${token.name} ${saveRst.msg}`)
  106. await history.appendOrUpdate(type, token.address, token)
  107. } catch (e) {}
  108. }
  109. async saveLp(lp: any, type='0') {
  110. // const insertRst = await History.appendOrUpdate(type, lp.LP, lp)
  111. // logger.debug(`insert lp:${insertRst.msg}, hash: ${lp.LP}.}`)
  112. await History.appendOrUpdate(type, lp.LP, lp)
  113. }
  114. async filterLp(lp: any) {
  115. // const filterSizeNormal = 1
  116. const filterSizeTop = 10
  117. // WETH
  118. if (lp.token0.toLowerCase() === '0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2') {
  119. return parseInt(lp.r0) > filterSizeTop * 1e18
  120. } else if (lp.token1.toLowerCase() === '0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2') {
  121. return parseInt(lp.r1) > filterSizeTop * 1e18
  122. }
  123. // ETH
  124. if (lp.token0.toLowerCase() === '0xaf3ccfd9b59b36628cc2f659a09d6440795b2520') {
  125. return parseInt(lp.r0) > filterSizeTop * 1e18
  126. } else if (lp.token1.toLowerCase() === '0xaf3ccfd9b59b36628cc2f659a09d6440795b2520') {
  127. return parseInt(lp.r1) > filterSizeTop * 1e18
  128. }
  129. // WETHW
  130. if (lp.token0.toLowerCase() === '0x7bf88d2c0e32de92cdaf2d43ccdc23e8edfd5990') {
  131. return parseInt(lp.r0) > filterSizeTop * 1e18
  132. } else if (lp.token1.toLowerCase() === '0x7bf88d2c0e32de92cdaf2d43ccdc23e8edfd5990') {
  133. return parseInt(lp.r1) > filterSizeTop * 1e18
  134. }
  135. return true
  136. }
  137. parseFactory(router: any, factoryAbi: any, factoryAddress: string) {
  138. if (router.factoryObj == null) {
  139. router.factoryObj = new web3.eth.Contract(factoryAbi, factoryAddress)
  140. }
  141. return router.factoryObj
  142. }
  143. parsePositionManager(router: any, positionManagerAbi: any) {
  144. if (router.positionManager == null) {
  145. router.positionManager = new web3.eth.Contract(positionManagerAbi, router.position)
  146. }
  147. return router.positionManager
  148. }
  149. async checkPosition(pm: any, position: any) {
  150. try {
  151. await pm.methods.positions(position).call()
  152. return true
  153. } catch (e) {
  154. return false
  155. }
  156. }
  157. // 二分法查length
  158. async getPositionLength(pm: any) {
  159. let low = 300_000
  160. let high = 1_000_000
  161. let lastLow = low
  162. let lastHigh = high
  163. let checkNumber = low
  164. // TODO 二分法算法
  165. // while (true) {
  166. //
  167. // }
  168. return checkNumber
  169. }
  170. async handleLp(position: number, pairsLength: number,
  171. routerObj: any, v2ToolBy410: any, v3Tool: any, v3PositionManager: any) {
  172. // 获取lp
  173. let lp = undefined
  174. if (routerObj.type === 'univ2') {
  175. lp = await this.getV2PoolByPosition(routerObj, position, v2ToolBy410)
  176. } else if (routerObj.type === 'univ3') {
  177. // v3是从1开始
  178. lp = await this.getV3Pool(routerObj, position + 1, v3Tool, v3PositionManager)
  179. }
  180. if (lp) {
  181. // 保存Lp信息
  182. await this.saveLp(lp, '0')
  183. // 保存Lp的Token到Token表
  184. await this.saveToken(lp, 'token')
  185. // 过滤Lp
  186. if (await this.filterLp(lp)) {
  187. // 保存筛选之后的的Token到TopToken表
  188. await this.saveToken(lp, 'topToken')
  189. // 保存过滤后的Lp到TopLp
  190. await this.saveLp(lp, 'topLp')
  191. }
  192. }
  193. const memoryUsage = process.memoryUsage()
  194. if (this.maxMemoryOfByte < memoryUsage.rss) {
  195. this.maxMemoryOfByte = memoryUsage.rss
  196. }
  197. if (lp) {
  198. logger.debug(`${position + 1} / ${pairsLength}, ${lp.name}-${lp.LP}-${routerObj.chain}`
  199. + ` ${this.format(memoryUsage.rss)}/${this.format(this.maxMemoryOfByte)}`)
  200. } else {
  201. logger.debug(`lp get filed. ${position + 1} / ${pairsLength}, ${routerObj.name}-${routerObj.router}-${routerObj.chain}`
  202. + ` ${this.format(memoryUsage.rss)}/${this.format(this.maxMemoryOfByte)}`)
  203. }
  204. }
  205. format (bytes: any) {
  206. return (bytes / 1024 / 1024).toFixed(2) + ' MB';
  207. }
  208. async run() {
  209. logger.debug('Pull lp start.')
  210. // 初始化410 v2工具箱
  211. const v2ToolBy410Abi = require('../../abi/410_V2_TOOLS.json')
  212. const v2ToolBy410 = new web3.eth.Contract(v2ToolBy410Abi, contracts.V2_TOOLS_BY_410)
  213. const v2FactoryAbi = require('../../abi/UNIV2_FACTORY_ABI.json')
  214. // 初始化v3相关
  215. const positionManagerAbi = require('../../abi/UNIV3_POSITION_MANAGER_ABI.json')
  216. const v3ToolAbi = require('../../artifacts/contracts/V3Tool.sol/V3Tool.json').abi
  217. const v3Tool = new web3.eth.Contract(v3ToolAbi, contracts.V3_TOOLS)
  218. // router
  219. const routerList: Array<any> = require('../../config/router-list.json')
  220. while (true) {
  221. for (const routerObj of routerList) {
  222. let pairsLength = 0
  223. const factoryAddress = routerObj.factory
  224. // 获取当前pull状态
  225. let position = routerObj.fromPosition
  226. let v2Factory = undefined
  227. let v3PositionManager = undefined
  228. // v2,v3分开处理
  229. if (routerObj.type === 'univ2') {
  230. // 获取工厂实例
  231. v2Factory = this.parseFactory(routerObj, v2FactoryAbi, factoryAddress)
  232. // 获取当前pairsLength
  233. pairsLength = await v2Factory.methods.allPairsLength().call()
  234. } else if (routerObj.type === 'univ3') {
  235. // 获取positionManager
  236. v3PositionManager = this.parsePositionManager(routerObj, positionManagerAbi)
  237. pairsLength = await this.getPositionLength(v3PositionManager)
  238. }
  239. // log该router信息
  240. logger.debug(`Router address: ${routerObj.router}`)
  241. if (routerObj.type === 'univ2') {
  242. logger.debug(`factory: ${factoryAddress}, ${position + 1} / ${pairsLength}.`)
  243. } else {
  244. logger.debug(`position: ${routerObj.position}, ${position + 1} / ${pairsLength}.`)
  245. }
  246. // 挨个处理lp信息
  247. for (; position < pairsLength; position++) {
  248. await this.handleLp(position, pairsLength, routerObj, v2ToolBy410, v3Tool, v3PositionManager)
  249. }
  250. logger.debug('')
  251. logger.debug('')
  252. logger.debug('')
  253. }
  254. if (this.fromHead && this.isFirst) {
  255. this.isFirst = false
  256. this.fromHead = false
  257. }
  258. }
  259. }
  260. }
  261. async function main() {
  262. await new LpMaintenance(true, true).run()
  263. }
  264. main().catch((error) => {
  265. console.error(error);
  266. process.exitCode = 1;
  267. })