import time from calcTools import * from osTools import * from web3Tools import * class MaxProfitUtils: # 判断两个数组是否有交集 @staticmethod def has_intersection(arr1, arr2): # 1.这段感觉会慢 return len(set(arr1) & set(arr2)) > 0 # 若二维数组中所有数组与指定数组没有交集,返回True # 2.已经判断没有交集的路径,就不需要再去互相判断了。 @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 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]): # 3.这段会很卡 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'): 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 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 = readText('aba' + i + i) array = array + iiArray array = array + readText('aba' + WETHW + WETH) array = array + readText('aba' + WETH + WETHW) return array def readABCA(): array = [] for i in baseTokenInfo: i = i.lower() iiArray = readText('abca' + i + i) array = array + iiArray array = array + readText('abca' + WETHW + WETH) array = array + readText('abca' + WETH + WETHW) return array topLpInfo = readText('topLP') lpRouterInfo = readABCA() lpRouterInfo = lpRouterInfo + readABA() printTime('v3 start') v3CCAddress = '0xB4971D0dda22359Ad86867362b7FC3206eA0d86B' dataUsed = [] nowBlockLpRes = {} initBlock = 15879488 while True: try: blockNumber = initBlock + 1 if blockNumber == initBlock: time.sleep(0.1) continue printTime(blockNumber, 'start') initBlock = blockNumber baseGasPrice = getBaseGasPrice(blockNumber) * 1.5 if baseGasPrice < 1.6 * 1e9: baseGasPrice = 1.6 * 1e9 nowBlockLpRes = {} # 找出有关该LP的路由 lpRouterArray = lpRouterInfo # findLpRouter(nowBlockLpTradeI, lpRouterInfo) # 过滤需要获取余额的路由 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(blockNumber, 'lpRouterArray long time') tradeInfo = calRouterProfit(lpRouter) if tradeInfo: type = tradeInfo['type'] if type == 'type2': result = callABA(tradeInfo['lp0'], tradeInfo['lp1'], tradeInfo['tokenIn0'], tradeInfo['tokenLoan'], tradeInfo['fee0'], tradeInfo['fee1'], tradeInfo['indexIn0'], tradeInfo['indexIn1'], 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 tradeInfo['lp1'] =='0xd75ea151a61d06868e31f8988d28dfe5e9df57b4': # printTime(result) 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() for tradeInfoI in tradeInfoArray: if time.time() - initForTime > 60: printTime(blockNumber, 'tradeInfoArray long time') 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() initTradeList = tradeList tradeList = sorted(tradeList, key=lambda k: k['profit'], reverse=True) params = [] 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) 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 profit = profit - gasUsed * baseGasPrice if profit < tradeMinProfit: continue totalProfit = totalProfit + profit bestIndex.append(i) printTime(blockNumber, 'batch', time.time() - initbestTradeListTime, ' s totalProfit', totalProfit/1e18) bestTradeList = [] for index in bestIndex: bestTradeList.append(tradeList[index]) for i in bestTradeList: printTime(i['data']['tokenIn']) # 最优解法 initbestTradeListTime = time.time() newMinProfit = tradeMinProfit while len(tradeList) > 50: newMinProfit = newMinProfit * 1.1 newTradeList = [] for tradeI in tradeList: if tradeI['profit'] > newMinProfit: newTradeList.append(tradeI) tradeList = newTradeList bestTradeList = MaxProfitUtils.max_profit_path_list(tradeList) totalProfit = 0 for tradeI in bestTradeList: totalProfit = totalProfit + tradeI['profit'] printTime(blockNumber, 'best ', time.time() - initbestTradeListTime, ' s totalProfit', totalProfit/1e18) for i in bestTradeList: printTime(i['data']['tokenIn']) printTime('-' * 50) except BaseException as err: printTime(traceback.format_exc()) printTime('loop', ) time.sleep(1)