| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437 |
- import time
- from calcTools import *
- from osTools import *
- from web3Tools import *
- class MaxProfitUtils:
- # 判断两个数组是否有交集
- @staticmethod
- def has_intersection(arr1, arr2):
- return len(set(arr1) & set(arr2)) > 0
- # 若二维数组中所有数组与指定数组没有交集,返回True
- @staticmethod
- def all_has_no_intersection(swap_details_list, check_swap_details):
- for swap_details in swap_details_list:
- if MaxProfitUtils.has_intersection(swap_details['swapPath'], check_swap_details['swapPath']):
- return False
- return True
- # 求和swap详情list的利润并返回
- @staticmethod
- def get_profit_sum(swap_details_list):
- profit_sum = 0
- for swap_details in swap_details_list:
- profit_sum += swap_details['profit']
- return profit_sum
- @staticmethod
- def get_max_profit_swap_details_list(x, y):
- return x if MaxProfitUtils.get_profit_sum(x) > MaxProfitUtils.get_profit_sum(y) else y
- # 得出指定数据结构中的最大利润path
- @staticmethod
- def max_profit_path_list(origin_swap_details_list, level=0, swap_details_list=None):
- # first level
- if time.time() - initbestTradeListTime>60:
- printTime('max_profit_path_list loong')
- if swap_details_list is None:
- swap_details_list = []
- # last level
- # >=
- if level == len(origin_swap_details_list):
- return swap_details_list
- # 此层的swap_path与当前的swap_path_list内的所有swap_path都没有交集的话,将此层的加入为x路径
- x = []
- if MaxProfitUtils.all_has_no_intersection(swap_details_list, origin_swap_details_list[level]):
- new_swap_details_list = json.loads(json.dumps(swap_details_list))
- new_swap_details_list.append(origin_swap_details_list[level])
- x = MaxProfitUtils.max_profit_path_list(origin_swap_details_list, level+1, new_swap_details_list)
- # 不将此层加入的y路径
- y = MaxProfitUtils.max_profit_path_list(origin_swap_details_list, level+1, swap_details_list)
- return MaxProfitUtils.get_max_profit_swap_details_list(x, y)
- def calOnlyLpInRouterArray(routerArray):
- onlyLpArray = []
- for router in routerArray:
- for lpInfo in router:
- lp = lpInfo['lp']
- if lp in onlyLpArray:
- continue
- if lp in nowBlockLpRes:
- continue
- onlyLpArray.append(lp)
- return onlyLpArray
- def calRouterProfit(router):
- # 整理LP信息
- for lpInfo in router:
- lpAddress = lpInfo['lp']
- r0 = nowBlockLpRes[lpAddress][0]
- r1 = nowBlockLpRes[lpAddress][1]
- if lpAddress not in topLpInfo:
- return False
- lpInfo['fee'] = topLpInfo[lpAddress]['fee']
- if lpInfo['inToken'] < lpInfo['outToken']:
- lpInfo['rIn'] = r0
- lpInfo['rOut'] = r1
- else:
- lpInfo['rIn'] = r1
- lpInfo['rOut'] = r0
- # ABA
- if len(router) == 2:
- tokenLoan = router[1]['outToken']
- tokenIn0 = router[0]['inToken']
- tokenLoanETHPrice = baseTokenInfo[tokenLoan]['price'] * 10 ** baseTokenInfo[WETH]['decimals'] / 10 ** baseTokenInfo[tokenLoan]['decimals']
- # WETH - A - WETH
- tradeInfo = calBestAmountV2V2(router[0]['rIn'], router[0]['rOut'], router[0]['fee'], router[1]['rIn'], router[1]['rOut'], router[1]['fee'] )
- profit = tradeInfo['profit']
- tradeInfo['ethProfit'] = profit * tokenLoanETHPrice
- #printTime(router, amountIn0, amountOut0, amountOut1, profit)
- if tradeInfo['ethProfit'] < tradeMinProfit:
- return False
- else:
- tradeInfo['lp0'] = router[0]['lp']
- tradeInfo['lp1'] = router[1]['lp']
- tradeInfo['fee0'] = router[0]['fee']
- tradeInfo['fee1'] = router[1]['fee']
- tradeInfo['indexIn0'] = router[0]['indexIn']
- tradeInfo['indexIn1'] = router[1]['indexIn']
- tradeInfo['tokenLoan'] = tokenLoan
- tradeInfo['tokenIn0'] = tokenIn0
- tradeInfo['tradeToken0'] = router[0]['outToken']
- tradeInfo['type'] = 'type2'
- return tradeInfo
- # ABCA
- elif len(router) == 3:
- tokenLoan = router[2]['outToken']
- tokenIn0 = router[0]['inToken']
- tokenLoanETHPrice = baseTokenInfo[tokenLoan]['price'] * 10 ** baseTokenInfo[WETH]['decimals'] / 10 ** baseTokenInfo[tokenLoan]['decimals']
- tradeInfo = calBestAmountABCA(router[0]['rIn'], router[0]['rOut'], router[0]['fee'],
- router[1]['rIn'], router[1]['rOut'], router[1]['fee'],
- router[2]['rIn'], router[2]['rOut'], router[2]['fee'])
- profit = tradeInfo['profit']
- tradeInfo['ethProfit'] = profit * tokenLoanETHPrice
- #printTime(router, amountIn0, amountOut0, amountOut1, amountOut2, profit)
- if tradeInfo['ethProfit'] < tradeMinProfit:
- return False
- else:
- tradeInfo['lp0'] = router[0]['lp']
- tradeInfo['lp1'] = router[1]['lp']
- tradeInfo['lp2'] = router[2]['lp']
- tradeInfo['fee0'] = router[0]['fee']
- tradeInfo['fee1'] = router[1]['fee']
- tradeInfo['fee2'] = router[2]['fee']
- tradeInfo['indexIn0'] = router[0]['indexIn']
- tradeInfo['indexIn1'] = router[1]['indexIn']
- tradeInfo['indexIn2'] = router[2]['indexIn']
- tradeInfo['tokenLoan'] = tokenLoan
- tradeInfo['tokenIn0'] = tokenIn0
- tradeInfo['tradeToken0'] = router[0]['outToken']
- tradeInfo['tradeToken1'] = router[1]['outToken']
- tradeInfo['type'] = 'type3'
- return tradeInfo
- def callABCA(lp0, lp1, lpLoan, tokenIn0, tokenLoan, fee0, fee1, fee2, lp0InIndex, lp1InIndex, lp2InIndex, minProfit, block = 'latest'):
- function = '0x1d650c35'
- inputData = encodeInput(lp0, lp1, lpLoan, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- fee0, fee1, fee2, lp0InIndex, lp1InIndex, lp2InIndex,
- tokenIn0, tokenLoan, minProfit, 0)
- inputData = function + inputData
- r = gasCal(myAddress, v3CCAddress, inputData, block)
- tradeInfo = {'type':3, 'lp0': lp0, 'lp1': lp1, 'lpLoan':lpLoan, 'tokenIn':tokenIn0, 'tokenLoan':tokenLoan, 'profit':0, 'sumValue':0}
- if len(r['info']) != 23 or r['info'][-1] == 0:
- return tradeInfo
- sumValue = r['info'][3] + r['info'][4] + r['info'][5] + r['info'][6] + r['info'][7] + r['info'][8]
- tradeInfo['sumValue'] = sumValue
- tradeInfo['profit'] = r['info'][-1]
- tradeInfo['gasUsed'] = r['gasUsed']
- tradeInfo['inputData'] = inputData
- return tradeInfo
- def callABA(lp0, lp1, tokenIn0, tokenLoan, fee0, fee1, lp0InIndex, lp1InIndex, minProfit, block = 'latest'):
- # estimasGas 不能用
- function = '0xa628df6d'
- inputData = encodeInput(lp0, lp1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- fee0, fee1, 0, lp0InIndex, lp1InIndex, 0,
- tokenIn0, tokenLoan, minProfit, 0)
- inputData = function + inputData
- r = gasCal(myAddress, v3CCAddress, inputData, block)
-
- tradeInfo = {'type':2, 'lp0': lp0, 'lpLoan':lp1, 'tokenIn':tokenIn0, 'tokenLoan':tokenLoan, 'profit':0, 'sumValue':0}
- if len(r['info']) != 23 or r['info'][-1] == 0:
- return tradeInfo
- #路径中所有R0R1的和
- sumValue = r['info'][3] + r['info'][4] + r['info'][5] + r['info'][6] + r['info'][7] + r['info'][8]
- tradeInfo['sumValue'] = sumValue
- tradeInfo['profit'] = r['info'][-1]
- tradeInfo['gasUsed'] = r['gasUsed']
- tradeInfo['inputData'] = inputData
- return tradeInfo
- def readABA():
- array = []
- for i in baseTokenInfo:
- i = i.lower()
- iiArray = readAny('aba' + i + i)
- array = array + iiArray
- array = array + readAny('aba' + WETHW + WETH)
- array = array + readAny('aba' + WETH + WETHW)
- return array
- def readABCA():
- array = []
- for i in baseTokenInfo:
- i = i.lower()
- iiArray = readAny('abca' + i + i)
- array = array + iiArray
- array = array + readAny('abca' + WETHW + WETH)
- array = array + readAny('abca' + WETH + WETHW)
- return array
- # 读取LP,LP怎么来?
- topLpInfo = readAny('topLP')
- # 读取路径,路径怎么来?
- lpRouterInfo = readABCA()
- lpRouterInfo = lpRouterInfo + readABA()
- printTime('v3 start')
- v3CCAddress = '0xB4971D0dda22359Ad86867362b7FC3206eA0d86B'
- dataUsed = []
- nowBlockLpRes = {}
- # 自定义开始区块号码
- initBlock = w3.eth.blockNumber
- while True:
- try:
- if time.time() % 60 > 50 and time.time() % 60 < 55:
- # 读取LP
- topLpInfo = readAny('topLP')
- # 读取路径
- lpRouterInfo = readABCA()
- lpRouterInfo = lpRouterInfo + readABA()
- blockNumber = w3.eth.blockNumber
- if blockNumber == initBlock:
- time.sleep(0.1)
- continue
- initBlock = blockNumber
- # 算出基础gas
- baseGasPrice = getBaseGasPrice(blockNumber) * 1.22
- if baseGasPrice < 1.6 * 1e9:
- baseGasPrice = 1.6 * 1e9
- nowBlockLpRes = {}
- # 找出有关该LP的路径 == 因为不限制时间,并且速度够快。可能等于所有路径。
- lpRouterArray = lpRouterInfo # findLpRouter(nowBlockLpTradeI, lpRouterInfo)
- # 过滤重复LP。返回一次所有LP ([ABC][ABD])> [ABCD]
- lpOnlyRouterArray = calOnlyLpInRouterArray(lpRouterArray)
- # printTime(len(lpOnlyRouterArray), len(lpRouterArray))
- # 获取余额
- lpResArray = w3.dex.getPairSBalance(lpOnlyRouterArray, blockNumber)
- # 余额进行缓存
- for lpResI in lpResArray:
- nowBlockLpRes[lpResI] = lpResArray[lpResI]
- #计算每一个路径的利润
- tradeInfoArray = []
- initForTime = time.time()
- for lpRouter in lpRouterArray:
- if time.time() - initForTime > 60:
- printTime('lpRouterArray long time')
- #计算收入,通过本地R0R1,计算的
- tradeInfo = calRouterProfit(lpRouter)
- if tradeInfo:
- type = tradeInfo['type']
- if type == 'type2':
- #计算利润 = 收入 - gas支出,通过智能合约计算
- result = callABA(tradeInfo['lp0'], tradeInfo['lp1'],
- tradeInfo['tokenIn0'], tradeInfo['tokenLoan'],
- tradeInfo['fee0'], tradeInfo['fee1'],
- tradeInfo['indexIn0'], tradeInfo['indexIn1'],
- baseTokenInfo[tradeInfo['tokenIn0']]['profitMin'], blockNumber)
-
- # result['profit'] 是收入
- if result['profit'] < tradeMinProfit:
- continue
-
- sumValue = result['sumValue']
- #过滤幽灵LP, 其实可以通过eth call可以排除,但是很耗时间。
-
- if sumValue in dataUsed:
- continue
- tokenLoanETHPrice = baseTokenInfo[tradeInfo['tokenLoan']]['price'] * 10 ** baseTokenInfo[WETH]['decimals'] / 10 ** \
- baseTokenInfo[tradeInfo['tokenLoan']]['decimals']
- result['ethProfit'] = result['profit'] * tokenLoanETHPrice
- result['ethProfit-E18'] = result['profit'] * tokenLoanETHPrice / 1e18
- # result['tureProfit'] 才是利润
- result['tureProfit'] = result['ethProfit'] - result['gasUsed'] * baseGasPrice
- result['maxPrice'] = result['ethProfit'] / result['gasUsed']
- if result['tureProfit'] < tradeMinProfit:
- continue
- #有利润就加入交易数组
- tradeInfoArray.append(result)
- if type == 'type3':
- result = callABCA(tradeInfo['lp0'], tradeInfo['lp1'], tradeInfo['lp2'],
- tradeInfo['tokenIn0'], tradeInfo['tokenLoan'],
- tradeInfo['fee0'], tradeInfo['fee1'], tradeInfo['fee2'],
- tradeInfo['indexIn0'], tradeInfo['indexIn1'], tradeInfo['indexIn2'],
- baseTokenInfo[tradeInfo['tokenIn0']]['profitMin'], blockNumber)
- if result['profit'] < tradeMinProfit:
- continue
- sumValue = result['sumValue']
- if sumValue in dataUsed:
- continue
- tokenLoanETHPrice = baseTokenInfo[tradeInfo['tokenLoan']]['price'] * 10 ** baseTokenInfo[WETH][
- 'decimals'] / 10 ** \
- baseTokenInfo[tradeInfo['tokenLoan']]['decimals']
- result['ethProfit'] = result['profit'] * tokenLoanETHPrice
- result['ethProfit-E18'] = result['profit'] * tokenLoanETHPrice / 1e18
- result['tureProfit'] = result['ethProfit'] - result['gasUsed'] * baseGasPrice
- result['maxPrice'] = result['ethProfit'] / result['gasUsed']
- if result['tureProfit'] < tradeMinProfit:
- continue
- tradeInfoArray.append(result)
- if len(tradeInfoArray) != 0 :
- #printTime('trade List' ,blockNumber, tradeInfoArray)
- tradeList = []
- initForTime = time.time()
- #交易详细信息数组转化为只有 LP:利润的信息。然后所有交易信息用tradeInfoI复制一次。
- for tradeInfoI in tradeInfoArray:
- if tradeInfoI['type'] == 2:
- tradeList.append({"swapPath": [tradeInfoI['lp0'], tradeInfoI['lpLoan']], "profit": tradeInfoI['tureProfit'], 'data':tradeInfoI})
- if tradeInfoI['type'] == 3:
- tradeList.append(
- {"swapPath": [tradeInfoI['lp0'], tradeInfoI['lp1'],tradeInfoI['lpLoan']], "profit": tradeInfoI['tureProfit'], 'data':tradeInfoI})
-
- initbestTradeListTime = time.time( )
- # 进行利润排序,从大到小
- tradeList = sorted(tradeList, key=lambda k: k['profit'], reverse=True)
- params = []
- # 进行input的encode
- for info in tradeList:
- paramsI = {}
- paramsI['from'] = myAddress
- paramsI['to'] = '0xEAf90BcB75e7B92900678c4aBD80883b414bEDD6'
- tradeInputData = info['data']['inputData']
- paramsI['data'] = '0x4327f0db' + encodeInput(v4Address, 0x40, (len(tradeInputData) - 2) / 2, tradeInputData)
- params.append(paramsI)
- #BATCH CALL暴力解LP ,按顺序,进行一次模拟交易。是一直叠加态
- batchCallResult = batchCall(params, blockNumber)
- batchCallResult = decodeBatchCall(batchCallResult['result'])
- totalProfit = 0
- length = len(tradeList)
- bestIndex = []
- for i in range(0, length):
- profit = batchCallResult[i][-1]
- if profit > 0:
- tokenLoan = batchCallResult[i][-3]
- gasUsed = batchCallResult[i][1]
- tokenLoanETHPrice = baseTokenInfo[tokenLoan]['price'] * 10 ** baseTokenInfo[WETH][
- 'decimals'] / 10 ** \
- baseTokenInfo[tokenLoan]['decimals']
- profit = profit * tokenLoanETHPrice
- # 最大gasPrice
- maxPrice = profit / gasUsed
- profit = profit - gasUsed * baseGasPrice
- tradeList[i]['data']['maxPrice'] = maxPrice
- tradeList[i]['data']['profit'] = profit
- if profit < tradeMinProfit:
- continue
- totalProfit = totalProfit + profit
- # 成功的位置做key
- bestIndex.append(i)
- printTime(blockNumber, 'batch', time.time() - initbestTradeListTime, 's , totalProfit', totalProfit/1e18)
- # 过滤要下单的路径
- bestTradeList = []
- for index in bestIndex:
- bestTradeList.append(tradeList[index])
- tradeTimes = 0
- initForTime = time.time()
- # 根据路径下单
- for bestTradeI in bestTradeList:
- if time.time() - initForTime > 60:
- printTime('bestTradeList long time')
- printTime(blockNumber, 'bset', bestTradeI['swapPath'], bestTradeI['profit']/1e18)
- sumValue = bestTradeI['data']['sumValue']
- inputData = bestTradeI['data']['inputData']
- nowGasPrice = bestTradeI['data']['maxPrice'] * 0.2
- if nowGasPrice < baseGasPrice *1.2:
- nowGasPrice = baseGasPrice * 1.2
- if nowGasPrice > baseGasPrice * 1.8:
- nowGasPrice = baseGasPrice * 1.8
- # 为了后面gas war,做传参。
- gasLimit = 1e6 + bestTradeI['data']['maxPrice'] / 1e9 + 1
- if tradeTimes > 0:
- gasLimit = 1e6
-
- if gasLimit > 1e6 + 2000:
- gasLimit = 1e6 + 2000
-
- params = {}
- params['method'] = 'send'
- params['params'] = {'toAddress': v3CCAddress, 'inputData': inputData,
- 'gasPrice': nowGasPrice, 'gasLimit': gasLimit}
- try:
- hs = requests.post(url='http://127.0.0.1:410/operation', json=params).text
- if len(hs)>50:
- printTime(blockNumber, 'https://www.oklink.com/en/ethw/tx/' + str(hs))
- dataUsed.append(sumValue)
- tradeTimes = tradeTimes + 1
- except:
- hs = 'err seed'
- printTime('-' * 50)
- except BaseException as err:
- printTime(traceback.format_exc())
- printTime('loop', )
- time.sleep(20)
-
|