lp-lib.js 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263
  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 = 2000
  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.r0Str = r0s[relativeIndex]
  140. lp.r0 = parseInt(lp.r0Str)
  141. lp.r1Str = r1s[relativeIndex]
  142. lp.r1 = parseInt(lp.r1Str)
  143. }
  144. }
  145. }
  146. putSwapPrice(lpList) {
  147. for (const lp of lpList) {
  148. const r0RealAmount = parseInt(lp.r0) / Math.pow(10, parseInt(lp.decimals0))
  149. const r1RealAmount = parseInt(lp.r1) / Math.pow(10, parseInt(lp.decimals1))
  150. // 计算0换1的价格和1换0的价格
  151. lp.in1Token0OutToken1 = r1RealAmount / r0RealAmount // 一个token0 = in1Token0OutToken1个token1
  152. lp.in1Token1OutToken0 = r0RealAmount / r1RealAmount // 一个token1 = in1Token1OutToken0个token0
  153. }
  154. }
  155. getBaseTokenCovertEthValue(maxValueLpMap) {
  156. const baseTokenConvertEthValueMap = {}
  157. for (const baseTokenAddress of baseTokenAddressList) {
  158. const isEthToken = ethTokenAddressList.indexOf(baseTokenAddress) !== -1
  159. // eth兑换eth价格当然是1:1了
  160. if (isEthToken) {
  161. baseTokenConvertEthValueMap[baseTokenAddress] = 1
  162. } else {
  163. // 在ETH token中查找适合兑换的
  164. for (const ethTokenAddress of ethTokenAddressList) {
  165. const maxValueLp = maxValueLpMap[this.getKey(baseTokenAddress, ethTokenAddress)]
  166. if (!maxValueLp) continue
  167. const token0 = maxValueLp.token0
  168. const token1 = maxValueLp.token1
  169. if (baseTokenAddress === token0) {
  170. baseTokenConvertEthValueMap[baseTokenAddress] = maxValueLp.in1Token0OutToken1
  171. } else if (baseTokenAddress === token1) {
  172. baseTokenConvertEthValueMap[baseTokenAddress] = maxValueLp.in1Token1OutToken0
  173. }
  174. }
  175. }
  176. }
  177. return baseTokenConvertEthValueMap
  178. }
  179. getKey(hash0, hash1) {
  180. if (hash0 < hash1) {
  181. return hash0 + hash1
  182. }
  183. else {
  184. return hash1 + hash0
  185. }
  186. }
  187. putLpValue(lpList) {
  188. }
  189. getMaxValueLpMap(lpList) {
  190. const maxValueLpMap = {}
  191. for (const lp of lpList) {
  192. const key = lp.token0 + lp.token1
  193. const isExists = !!maxValueLpMap[key]
  194. const isMoreThanNowMax = isExists && maxValueLpMap[key].r0 < lp.r0
  195. if (!isExists) maxValueLpMap[key] = lp
  196. if (isMoreThanNowMax) maxValueLpMap[key] = lp
  197. }
  198. return maxValueLpMap
  199. }
  200. async getLpList(version) {
  201. let requestRst = undefined
  202. if (version === LpLib.VERSION.UNIV2) {
  203. requestRst = await this.v2LpModel.findByPaginate(1, debug.isDev() ? 200 : 10000000)
  204. } else if (version === LpLib.VERSION.UNIV3) {
  205. requestRst = await this.v3LpModel.findByPaginate(1, debug.isDev() ? 200 : 10000000)
  206. } else {
  207. throw Error(`Unknown lp version: ${version}.`)
  208. }
  209. if (requestRst.state) {
  210. return requestRst.data
  211. } else {
  212. logger.error(requestRst.msg)
  213. return []
  214. }
  215. }
  216. }