| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309 |
- const logger = require("../../utils/logger");
- const debug = require('./../../utils/debug')
- const BaseModel = require("../../model/base-model");
- const V2ToolAbi = require('../../abi/UNIV2_TOOLS_ABI.json')
- const V3ToolAbi = require('../../abi/UNIV3_TOOLS_ABI.json')
- const FactoryLib = require("./factory-lib");
- const TokenLib = require("./token-lib");
- // basetoken
- const baseTokenMap = require('../../config/base-token.json')
- const baseTokenAddressList = Object.keys(baseTokenMap)
- const ethTokenAddressList = baseTokenAddressList.filter(baseTokenAddress => {
- return baseTokenMap[baseTokenAddress].name.indexOf('ETH') !== -1
- })
- module.exports = class LpLib {
- static LEVEL = {
- NOT: 'not',
- DISCARD: 'discard',
- INIT: 'init',
- NORMAL: 'normal',
- TOP: 'top'
- }
- static VERSION = {
- UNIV2: 'univ2',
- UNIV3: 'univ3'
- }
- constructor(web3, chain) {
- this.web3 = web3
- this.chain = chain
- this.factoryLib = new FactoryLib(this.web3, this.chain)
- this.tokenLib = new TokenLib(this.web3, this.chain)
-
- this.v2LpModel = new BaseModel(this.chain.id, BaseModel.MODULES.V2_LP)
- this.v3LpModel = new BaseModel(this.chain.id, BaseModel.MODULES.V3_LP)
- // 初始化V2工具箱
- this.v2Tool = new this.web3.eth.Contract(V2ToolAbi, this.chain.v2ToolAddress)
- // 初始化V3工具箱
- this.v3Tool = new this.web3.eth.Contract(V3ToolAbi, this.chain.v3ToolAddress)
- }
- async saveLp(factory, lp) {
- let saveRst = undefined
- if (factory.version === 'univ2') {
- saveRst = await this.v2LpModel.appendOrUpdate(lp)
- } else if (factory.version === 'univ3') {
- saveRst = await this.v3LpModel.appendOrUpdate(lp)
- } else {
- throw Error(`Unknown factory version: ${factory.version}, hash is: ${factory.hash}.`)
- }
- if (!saveRst.state) throw Error(saveRst.msg)
- }
- getEffectiveLp(factory, position, lp) {
- // lp对象标准化
- lp.symbol0 = this.tokenLib.getEffectiveSymbol(lp.symbol0)
- lp.symbol1 = this.tokenLib.getEffectiveSymbol(lp.symbol1)
- lp.name = `${factory.name}_${lp.symbol0}_${lp.symbol1}`
- lp.router = factory.router
- lp.factory = factory.hash
- lp.fee = factory.fee
- lp.positionId = position
- lp.r0Str = String(lp.r0)
- lp.r1Str = String(lp.r1)
- lp.level = LpLib.LEVEL.INIT
- // lp各种地址最小化
- lp.hash = lp.hash.toLowerCase()
- lp.token0 = lp.token0.toLowerCase()
- lp.token1 = lp.token1.toLowerCase()
- // 字符化数字
- lp.decimals0 = parseInt(lp.decimals0)
- lp.decimals1 = parseInt(lp.decimals1)
- // 链信息
- lp.chainId = this.chain.id
- return lp
- }
- async getV2Pool(factory, position) {
- const info = await this.v2Tool.methods.getPairIdInfo(factory.hash, position).call()
- const lp = {
- hash: info['0'],
- decimals0: info['3'],
- decimals1: info['7'],
- r0: info['4'],
- r1: info['8'],
- symbol0: info['2'],
- symbol1: info['6'],
- token0: info['1'],
- token1: info['5']
- }
- return this.getEffectiveLp(factory, position, lp)
- }
- async getV3Pool(factory, position) {
- const positionManager = this.factoryLib.getPositionManager(factory.positionManager)
- const positionInfo = await positionManager.methods.positions(position).call()
- const info = await this.v3Tool.methods.getMoreInfo(positionInfo.token0, positionInfo.token1, positionInfo.fee).call()
- const lp = {
- hash: info.lp,
- decimals0: info.decimals0,
- decimals1: info.decimals1,
- factory: factory.hash,
- feei: positionInfo.fee,
- id: position,
- r0: info.r0,
- r1: info.r1,
- router: factory.router,
- token0: positionInfo.token0,
- token1: positionInfo.token1,
- symbol0: info.symbol0,
- symbol1: info.symbol1
- }
- return this.getEffectiveLp(factory, position, lp)
- }
- async getLpByPosition(factory, position) {
- if (factory.version === LpLib.VERSION.UNIV2) {
- return await this.getV2Pool(factory, position)
- } else if (factory.version === LpLib.VERSION.UNIV3) {
- return await this.getV3Pool(factory, position)
- } else {
- throw Error(`Unknown factory version: ${factory.version}, hash is: ${factory.hash}.`)
- }
- }
- getHashListByLpList(lpList) {
- const hashList = []
- for (const lp of lpList) {
- hashList.push(lp.hash)
- }
- return hashList
- }
- async putR0AndR1(lpList) {
- // 生成所有lp的hash
- const hashList = this.getHashListByLpList(lpList)
- // 以段为单位,更新r0、r1。每一段有SIZE个元素
- const SIZE = 2000
- for (let from = 0; from < hashList.length; from += SIZE) {
- logger.debug(`${from}, ${hashList.length}`)
- // 拉取该段所有r0,r1
- const lpR0R1Info = await this.v2Tool.methods.getPairSBalance(hashList.slice(from, from + SIZE)).call()
- const r0s = lpR0R1Info.amounts0
- const r1s = lpR0R1Info.amounts1
- // 更新到lp中
- for (let index = from; index < from + SIZE && index < hashList.length; index++) {
- const lp = lpList[index]
- const relativeIndex = index % SIZE
- lp.r0Str = r0s[relativeIndex]
- lp.r0 = parseInt(lp.r0Str)
- lp.r1Str = r1s[relativeIndex]
- lp.r1 = parseInt(lp.r1Str)
- }
- }
- }
- putSwapPrice(lpList) {
- for (const lp of lpList) {
- const r0RealAmount = parseInt(lp.r0) / Math.pow(10, parseInt(lp.decimals0))
- const r1RealAmount = parseInt(lp.r1) / Math.pow(10, parseInt(lp.decimals1))
- // 计算0换1的价格和1换0的价格
- lp.in1Token0OutToken1 = r1RealAmount / r0RealAmount // 一个token0 = in1Token0OutToken1个token1
- lp.in1Token1OutToken0 = r0RealAmount / r1RealAmount // 一个token1 = in1Token1OutToken0个token0
- }
- }
- getBaseTokenCovertEthValue(maxValueLpMap) {
- const baseTokenConvertEthValueMap = {}
- for (const baseTokenAddress of baseTokenAddressList) {
- const isEthToken = ethTokenAddressList.indexOf(baseTokenAddress) !== -1
- // eth兑换eth价格当然是1:1了
- if (isEthToken) {
- baseTokenConvertEthValueMap[baseTokenAddress] = 1
- } else {
- // 在ETH token中查找适合兑换的
- for (const ethTokenAddress of ethTokenAddressList) {
- const maxValueLp = maxValueLpMap[this.getKey(baseTokenAddress, ethTokenAddress)]
- if (!maxValueLp) continue
- const token0 = maxValueLp.token0
- const token1 = maxValueLp.token1
- if (baseTokenAddress === token0) {
- baseTokenConvertEthValueMap[baseTokenAddress] = maxValueLp.in1Token0OutToken1
- } else if (baseTokenAddress === token1) {
- baseTokenConvertEthValueMap[baseTokenAddress] = maxValueLp.in1Token1OutToken0
- }
- }
- }
- }
- return baseTokenConvertEthValueMap
- }
- getKey(hash0, hash1) {
- if (hash0 < hash1) {
- return hash0 + hash1
- }
- else {
- return hash1 + hash0
- }
- }
- getLpEthValue(lp, baseTokenConvertEthValueMap, maxValueLpMap) {
- const token0 = lp.token0
- const token1 = lp.token1
- if (ethTokenAddressList.indexOf(token0) !== -1 || ethTokenAddressList.indexOf(token1) !== -1) {
- return {
- '0': ethTokenAddressList.indexOf(token0) !== -1 ? 1 : 0,
- '1': ethTokenAddressList.indexOf(token1) !== -1 ? 1 : 0
- }
- }
- // 1.两token之一能直接与ethw或wethw做直接池子的
- // 2.两token之一能直接与baseToken做池子的
- // 这两种情况直接带入baseTokenConvertEthValueMap计算
- for (const baseTokenAddress of baseTokenAddressList) {
- const token0AndBaseKey = this.getKey(token0, baseTokenAddress)
- const token0AndBaseTokenLp = maxValueLpMap[token0AndBaseKey]
- if (token0AndBaseTokenLp) {
- const priceMap = {}
- if (token0AndBaseTokenLp.token0 === token0) {
- priceMap['0'] = token0AndBaseTokenLp.in1Token0OutToken1 * baseTokenConvertEthValueMap[baseTokenAddress]
- } else {
- priceMap['0'] = token0AndBaseTokenLp.in1Token1OutToken0 * baseTokenConvertEthValueMap[baseTokenAddress]
- }
- priceMap['1'] = 0
- return priceMap
- }
- const token1AndBaseKey = this.getKey(token1, baseTokenAddress)
- const token1AndBaseTokenLp = maxValueLpMap[token1AndBaseKey]
- if (token1AndBaseTokenLp) {
- const priceMap = {}
- if (token1AndBaseTokenLp.token0 === token1) {
- priceMap['1'] = token1AndBaseTokenLp.in1Token0OutToken1 * baseTokenConvertEthValueMap[baseTokenAddress]
- } else {
- priceMap['1'] = token1AndBaseTokenLp.in1Token1OutToken0 * baseTokenConvertEthValueMap[baseTokenAddress]
- }
- priceMap['0'] = 0
- return priceMap
- }
- }
- // 3.两token都不能与baseToken做池子的
- // 这种情况则视为lp类型池子
- return {'0': 0, '1': 0}
- }
- getMaxValueLpMap(lpList) {
- const maxValueLpMap = {}
- for (const lp of lpList) {
- const key = lp.token0 + lp.token1
- const isExists = !!maxValueLpMap[key]
- const isMoreThanNowMax = isExists && maxValueLpMap[key].r0 < lp.r0
- if (!isExists) maxValueLpMap[key] = lp
- if (isMoreThanNowMax) maxValueLpMap[key] = lp
- }
- return maxValueLpMap
- }
- async getLpList(version) {
- let requestRst = undefined
- if (version === LpLib.VERSION.UNIV2) {
- requestRst = await this.v2LpModel.findByPaginate(1, debug.isDev() ? 200 : 10000000)
- } else if (version === LpLib.VERSION.UNIV3) {
- requestRst = await this.v3LpModel.findByPaginate(1, debug.isDev() ? 200 : 10000000)
- } else {
- throw Error(`Unknown lp version: ${version}.`)
- }
- if (requestRst.state) {
- return requestRst.data
- } else {
- logger.error(requestRst.msg)
- return []
- }
- }
- }
|