lp-lib.js 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340
  1. const logger = require("../../utils/logger");
  2. const debug = require('./../../utils/debug')
  3. const BaseModel = require("../../model/base-model");
  4. const V2ToolAbi = require('../../abi/UNIV2_TOOLS_ABI.json')
  5. const V3ToolAbi = require('../../abi/UNIV3_TOOLS_ABI.json')
  6. const FactoryLib = require("./factory-lib");
  7. const TokenLib = require("./token-lib");
  8. // basetoken
  9. const baseTokenMap = require('../../config/base-token.json')
  10. const baseTokenAddressList = Object.keys(baseTokenMap)
  11. const ethTokenAddressList = baseTokenAddressList.filter(baseTokenAddress => {
  12. return baseTokenMap[baseTokenAddress].name.indexOf('ETH') !== -1
  13. })
  14. module.exports = class LpLib {
  15. static LEVEL = {
  16. NOT: 'not',
  17. DISCARD: 'discard',
  18. INIT: 'init',
  19. NORMAL: 'normal',
  20. TOP: 'top'
  21. }
  22. static VERSION = {
  23. UNIV2: 'univ2',
  24. UNIV3: 'univ3'
  25. }
  26. constructor(web3, chain) {
  27. this.web3 = web3
  28. this.chain = chain
  29. this.factoryLib = new FactoryLib(this.web3, this.chain)
  30. this.tokenLib = new TokenLib(this.web3, this.chain)
  31. this.v2LpModel = new BaseModel(this.chain.id, BaseModel.MODULES.V2_LP)
  32. this.v3LpModel = new BaseModel(this.chain.id, BaseModel.MODULES.V3_LP)
  33. // 初始化V2工具箱
  34. this.v2Tool = new this.web3.eth.Contract(V2ToolAbi, this.chain.v2ToolAddress)
  35. // 初始化V3工具箱
  36. this.v3Tool = new this.web3.eth.Contract(V3ToolAbi, this.chain.v3ToolAddress)
  37. }
  38. async saveLp(factory, lp) {
  39. let saveRst = undefined
  40. if (factory.version === 'univ2') {
  41. saveRst = await this.v2LpModel.appendOrUpdate(lp)
  42. } else if (factory.version === 'univ3') {
  43. saveRst = await this.v3LpModel.appendOrUpdate(lp)
  44. } else {
  45. throw Error(`Unknown factory version: ${factory.version}, hash is: ${factory.hash}.`)
  46. }
  47. if (!saveRst.state) throw Error(saveRst.msg)
  48. }
  49. getEffectiveLp(factory, position, lp) {
  50. // lp对象标准化
  51. lp.symbol0 = this.tokenLib.getEffectiveSymbol(lp.symbol0)
  52. lp.symbol1 = this.tokenLib.getEffectiveSymbol(lp.symbol1)
  53. lp.name = `${factory.name}_${lp.symbol0}_${lp.symbol1}`
  54. lp.router = factory.router
  55. lp.factory = factory.hash
  56. lp.fee = factory.fee
  57. lp.positionId = position
  58. lp.r0Str = String(lp.r0)
  59. lp.r1Str = String(lp.r1)
  60. lp.level = LpLib.LEVEL.INIT
  61. // lp各种地址最小化
  62. lp.hash = lp.hash.toLowerCase()
  63. lp.token0 = lp.token0.toLowerCase()
  64. lp.token1 = lp.token1.toLowerCase()
  65. // 字符化数字
  66. lp.decimals0 = parseInt(lp.decimals0)
  67. lp.decimals1 = parseInt(lp.decimals1)
  68. // 链信息
  69. lp.chainId = this.chain.id
  70. return lp
  71. }
  72. async getV2Pool(factory, position) {
  73. const info = await this.v2Tool.methods.getPairIdInfo(factory.hash, position).call()
  74. const lp = {
  75. hash: info['0'],
  76. decimals0: info['3'],
  77. decimals1: info['7'],
  78. r0: info['4'],
  79. r1: info['8'],
  80. symbol0: info['2'],
  81. symbol1: info['6'],
  82. token0: info['1'],
  83. token1: info['5']
  84. }
  85. return this.getEffectiveLp(factory, position, lp)
  86. }
  87. async getV3Pool(factory, position) {
  88. const positionManager = this.factoryLib.getPositionManager(factory.positionManager)
  89. const positionInfo = await positionManager.methods.positions(position).call()
  90. const info = await this.v3Tool.methods.getMoreInfo(positionInfo.token0, positionInfo.token1, positionInfo.fee).call()
  91. const lp = {
  92. hash: info.lp,
  93. decimals0: info.decimals0,
  94. decimals1: info.decimals1,
  95. factory: factory.hash,
  96. feei: positionInfo.fee,
  97. id: position,
  98. r0: info.r0,
  99. r1: info.r1,
  100. router: factory.router,
  101. token0: positionInfo.token0,
  102. token1: positionInfo.token1,
  103. symbol0: info.symbol0,
  104. symbol1: info.symbol1
  105. }
  106. return this.getEffectiveLp(factory, position, lp)
  107. }
  108. async getLpByPosition(factory, position) {
  109. if (factory.version === LpLib.VERSION.UNIV2) {
  110. return await this.getV2Pool(factory, position)
  111. } else if (factory.version === LpLib.VERSION.UNIV3) {
  112. return await this.getV3Pool(factory, position)
  113. } else {
  114. throw Error(`Unknown factory version: ${factory.version}, hash is: ${factory.hash}.`)
  115. }
  116. }
  117. getHashListByLpList(lpList) {
  118. const hashList = []
  119. for (const lp of lpList) {
  120. hashList.push(lp.hash)
  121. }
  122. return hashList
  123. }
  124. async putR0AndR1(lpList) {
  125. // 生成所有lp的hash
  126. const hashList = this.getHashListByLpList(lpList)
  127. // 以段为单位,更新r0、r1。每一段有SIZE个元素
  128. const SIZE = 600
  129. for (let from = 0; from < hashList.length; from += SIZE) {
  130. logger.debug(`${from}, ${hashList.length}`)
  131. // 拉取该段所有r0,r1
  132. const lpR0R1Info = await this.v2Tool.methods.getPairSBalance(hashList.slice(from, from + SIZE)).call()
  133. const r0s = lpR0R1Info.amounts0
  134. const r1s = lpR0R1Info.amounts1
  135. // 更新到lp中
  136. for (let index = from; index < from + SIZE && index < hashList.length; index++) {
  137. const lp = lpList[index]
  138. const relativeIndex = index % SIZE
  139. lp.appendTimestamp = undefined
  140. lp.updateTimestamp = undefined
  141. lp.r0Str = r0s[relativeIndex]
  142. lp.r0 = parseInt(lp.r0Str)
  143. lp.r1Str = r1s[relativeIndex]
  144. lp.r1 = parseInt(lp.r1Str)
  145. if (lp.r0 === 404 && lp.r1 === 404) {
  146. lp.r0Str = '0'
  147. lp.r0 = 0
  148. lp.r1Str = '0'
  149. lp.r1 = 0
  150. lp.level = LpLib.LEVEL.NOT
  151. }
  152. }
  153. }
  154. }
  155. putSwapPrice(lpList) {
  156. for (const lp of lpList) {
  157. const r0RealAmount = lp.r0 / Math.pow(10, parseInt(lp.decimals0))
  158. const r1RealAmount = lp.r1 / Math.pow(10, parseInt(lp.decimals1))
  159. // 计算0换1的价格和1换0的价格
  160. lp.token0ToToken1Price = r1RealAmount / r0RealAmount // 一个token0 = token0ToToken1Price个token1
  161. lp.token1ToToken0Price = r0RealAmount / r1RealAmount // 一个token1 = token1ToToken0Price个token0
  162. }
  163. }
  164. getBaseTokenCovertEthValue(maxValueLpMap) {
  165. const baseTokenConvertEthValueMap = {}
  166. for (const baseTokenAddress of baseTokenAddressList) {
  167. const isEthToken = ethTokenAddressList.indexOf(baseTokenAddress) !== -1
  168. // eth兑换eth价格当然是1:1了
  169. if (isEthToken) {
  170. baseTokenConvertEthValueMap[baseTokenAddress] = 1
  171. } else {
  172. // 在ETH token中查找适合兑换的
  173. for (const ethTokenAddress of ethTokenAddressList) {
  174. const maxValueLp = maxValueLpMap[this.getKey(baseTokenAddress, ethTokenAddress)]
  175. if (!maxValueLp) continue
  176. const token0 = maxValueLp.token0
  177. const token1 = maxValueLp.token1
  178. if (baseTokenAddress === token0) {
  179. baseTokenConvertEthValueMap[baseTokenAddress] = maxValueLp.token0ToToken1Price
  180. } else if (baseTokenAddress === token1) {
  181. baseTokenConvertEthValueMap[baseTokenAddress] = maxValueLp.token1ToToken0Price
  182. }
  183. }
  184. }
  185. }
  186. return baseTokenConvertEthValueMap
  187. }
  188. getKey(hash0, hash1) {
  189. if (hash0 < hash1) {
  190. return hash0 + hash1
  191. }
  192. else {
  193. return hash1 + hash0
  194. }
  195. }
  196. getLpEthValue(lp, baseTokenConvertEthValueMap, maxValueLpMap) {
  197. const r0 = lp.r0
  198. const r1 = lp.r1
  199. const token0 = lp.token0
  200. const token1 = lp.token1
  201. const decimals0 = lp.decimals0
  202. const decimals1 = lp.decimals1
  203. if (r0 === 0 && r1 === 0) {
  204. return 0
  205. }
  206. // 1.两token之一能直接与ethw或wethw做直接池子的
  207. if (ethTokenAddressList.indexOf(token0) !== -1 || ethTokenAddressList.indexOf(token1) !== -1) {
  208. if (ethTokenAddressList.indexOf(token0) !== -1) {
  209. const ethToken = baseTokenMap[token0]
  210. return 2 * r0 / (10 ** ethToken.decimals)
  211. } else if (ethTokenAddressList.indexOf(token1) !== -1) {
  212. const ethToken = baseTokenMap[token1]
  213. return 2 * r1 / (10 ** ethToken.decimals)
  214. }
  215. }
  216. // 2.两token之一能直接与baseToken做池子的 带入maxValueLpMap计算
  217. // 转换数量: token到base token
  218. function valueConvertTokenToBaseToken(lpLib, reserve, tokenHash, decimals, baseTokenAddress) {
  219. const tokenAndBaseKey = lpLib.getKey(tokenHash, baseTokenAddress)
  220. const tokenAndBaseTokenLp = maxValueLpMap[tokenAndBaseKey]
  221. const realAmount = reserve / Math.pow(10, decimals)
  222. if (tokenAndBaseTokenLp) {
  223. if (tokenAndBaseTokenLp.token0 === tokenHash) {
  224. const token0ToBaseTokenPrice = tokenAndBaseTokenLp.token0ToToken1Price
  225. return isNaN(token0ToBaseTokenPrice) ? 0 : token0ToBaseTokenPrice * realAmount
  226. } else {
  227. const token1ToBaseTokenPrice = tokenAndBaseTokenLp.token1ToToken0Price
  228. return isNaN(token1ToBaseTokenPrice) ? 0 : token1ToBaseTokenPrice * realAmount
  229. }
  230. }
  231. return 0
  232. }
  233. for (const baseTokenAddress of baseTokenAddressList) {
  234. const baseTokenToEthPrice = baseTokenConvertEthValueMap[baseTokenAddress]
  235. if (!baseTokenToEthPrice) continue
  236. // 如果token0跟baseToken能组成池子
  237. const r0ConvertToBaseTokenRealAmount = valueConvertTokenToBaseToken(this, r0, token0, decimals0, baseTokenAddress)
  238. if (r0ConvertToBaseTokenRealAmount !== 0) {
  239. return r0ConvertToBaseTokenRealAmount * baseTokenToEthPrice
  240. }
  241. // 如果token1跟baseToken能组成池子
  242. const r1ConvertToBaseTokenRealAmount = valueConvertTokenToBaseToken(this, r1, token1, decimals1, baseTokenAddress)
  243. if (r1ConvertToBaseTokenRealAmount !== 0) {
  244. return r1ConvertToBaseTokenRealAmount * baseTokenToEthPrice
  245. }
  246. }
  247. // 3.两token都不能与baseToken做池子的
  248. // 这种情况则视为lp类型池子
  249. return 0
  250. }
  251. getMaxValueLpMap(lpList) {
  252. const maxValueLpMap = {}
  253. for (const lp of lpList) {
  254. const key = lp.token0 + lp.token1
  255. const isExists = !!maxValueLpMap[key]
  256. const isMoreThanNowMax = isExists && maxValueLpMap[key].r0 < lp.r0
  257. if (!isExists) maxValueLpMap[key] = lp
  258. if (isMoreThanNowMax) maxValueLpMap[key] = lp
  259. }
  260. return maxValueLpMap
  261. }
  262. async getLpList(version) {
  263. let requestRst = undefined
  264. if (version === LpLib.VERSION.UNIV2) {
  265. requestRst = await this.v2LpModel.findByPaginate(1, debug.isDev() ? 200 : 10000000)
  266. } else if (version === LpLib.VERSION.UNIV3) {
  267. requestRst = await this.v3LpModel.findByPaginate(1, debug.isDev() ? 200 : 10000000)
  268. } else {
  269. throw Error(`Unknown lp version: ${version}.`)
  270. }
  271. if (requestRst.state) {
  272. return requestRst.data
  273. } else {
  274. logger.error(requestRst.msg)
  275. return []
  276. }
  277. }
  278. }