from  web3Tools import  *
from calcTools  import *


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 findLpArrayRouter(lpArray, lpRouter256Info):
    routerArray = []
    sumUsed = {}
    for lpAddress in lpArray:
        if lpAddress not in lpRouter256Info:
            continue
        lpRouter = lpRouter256Info[lpAddress]
        for lpRouterI in lpRouter:
            sumValue = ''
            same = False
            for lpi in lpRouterI:
                sumValue = sumValue + lpi['lp'] + lpi['inToken']
                if lpi['lp'] == lpAddress:
                    same = True
            if same:
                sumUsed[sumValue] = lpRouterI
    for key in sumUsed:
        routerArray.append(sumUsed[key])
    return  routerArray



def calLpInTradeInfo(tradeInfo):
    lpArray = []
    for i in tradeInfo:
        if i['to'] in topLpInfo and i['to'] not in lpArray:
            lpArray.append(i['to'])
        if i['from'] in topLpInfo and i['from'] not in lpArray:
            lpArray.append(i['from'])  
    return lpArray  


def gethashInfo(hsResult):
    try:
        hashInfo = {}
        hashInfo['fromAddress'] = hsResult['from']
        hashInfo['toAddress']= hsResult['to']
        hashInfo['inputData'] = hsResult['input']
        hashInfo['function'] = hashInfo['inputData'][:10]
        hashInfo['value'] = int(hsResult['value'], 16)
        hashInfo['nonce'] = int(hsResult['nonce'], 16)  # 对比nonce
        hashInfo['hash'] = hsResult['hash']           
        hashInfo['gasLimit'] = int(hsResult['gas'], 16)
        hashInfo['type'] = int(hsResult['type'],16)
        hashInfo['gasPrice'] = int(hsResult['gasPrice'], 16)
        if hashInfo['type'] == 2:
            hashInfo['maxFeePerGas'] = int(hsResult['maxFeePerGas'], 16)  
            hashInfo['maxPriorityFeePerGas'] = int(hsResult['maxPriorityFeePerGas'], 16)
        return hashInfo
    except:
        return False

def getLpArrayResAfterTrade(fromAddress, toAddress, inputData, value,  lpArray, block = 'latest'):
    paramsRes = {}
    paramsRes['to'] = toolsAddress
    paramsRes['data'] = '0xd94ccee1' + encodeInput(0x20, lpArray) 
    params0 = {}
    params0['from'] = fromAddress
    params0['to'] = toAddress
    params0['data'] = inputData
    params0['value'] = hex(int(value))
    r = batchCall([params0, paramsRes], block)
    result = r['result'][1]['Return']
    result = w3.debug.decodeOutput(result)
    info = {}
    length = len(lpArray)
    for index in range(0, length):
        lpAddress = lpArray[index]
        r0 = result[index + 3]
        r1 = result[index + 4 + length]
        info[lpAddress] = [r0, r1]
    return info


def calRouterProfit(router):
    # 整理LP信息
    for lpInfo in router:
        lpAddress = lpInfo['lp']
        r0 = nowBlockLpRes[lpAddress][0]
        r1 = nowBlockLpRes[lpAddress][1]
        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 getABAInput(lp0, lp1, tokenIn0, tokenLoan, fee0, fee1, lp0InIndex, lp1InIndex, minProfit):
    function = '0x5105a538'
    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
    return inputData


def callABAPending(tradeInfo, hashInfo):
    tradeInputData = getABAInput(tradeInfo['lp0'], tradeInfo['lp1'],
                tradeInfo['tokenIn0'], tradeInfo['tokenLoan'],
                tradeInfo['fee0'], tradeInfo['fee1'],
                tradeInfo['indexIn0'], tradeInfo['indexIn1'],
                baseTokenInfo[tradeInfo['tokenIn0']]['profitMin'])
    gasCalInputData  = '0x4327f0db' + encodeInput(v5Address, 0x40, (len(tradeInputData) - 2)/2 , tradeInputData)
    
    paramsTrade = {}
    paramsTrade['from'] = myAddress
    paramsTrade['to'] = '0xEAf90BcB75e7B92900678c4aBD80883b414bEDD6'
    paramsTrade['data'] = gasCalInputData

    paramsHash = {}
    paramsHash['from'] = hashInfo['fromAddress']
    paramsHash['to'] = hashInfo['toAddress']
    paramsHash['data'] = hashInfo['inputData']
    paramsHash['value'] = hex(hashInfo['value'])

    result = batchCall([paramsHash,paramsTrade])
    result = result['result'][1]['Return']
    info = w3.debug.decodeOutput(result)   
    r = {}
    r['gasUsed'] = info[1]
    r['info'] = info[3:]
    returnInfo = {'type':2, 'lp0': tradeInfo['lp0'], 'lpLoan':tradeInfo['lp1'], 'tokenIn':tradeInfo['tokenIn0'], 'tokenLoan':tradeInfo['tokenLoan'], 'profit':0, 'sumValue':0}
    if len(r['info']) != 23 or r['info'][-1] == 0:
        return returnInfo
    sumValue = r['info'][3] + r['info'][4] + r['info'][5] + r['info'][6] + r['info'][7] + r['info'][8]
    returnInfo['sumValue'] = sumValue
    returnInfo['profit'] = r['info'][-1]
    returnInfo['gasUsed'] = r['gasUsed']
    returnInfo['inputData'] = tradeInputData
    return returnInfo



initTime = time.time()
pendingInitTime = time.time()   
topLpInfo  = readAny('topLP')
lpRouterInfo = readAny('abcd')
lpRouter256Info = readAny('256Router')
r = w3.provider.make_request('eth_newPendingTransactionFilter',[])
filterId = r['result']
printTime(filterId)
nowBlockLpRes = {}
dataUsed = []
while True:
    try:
    
        hsArry = w3.provider.make_request('eth_getFilterChanges', [filterId])
        hsArry = hsArry['result']
        nowBlockLpRes = {}
        blockNumber = w3.eth.blockNumber
        baseGasPrice = getBaseGasPrice(blockNumber) * 1.5
        if baseGasPrice < 3 * 1e9:
            baseGasPrice = 3 * 1e9

        if hsArry:
                      
            for hsI in hsArry:
                tradeInfoArray = []
                #printTime('hsI OK')
                hsResult = w3.new.getTransaction(hsI)
                #printTime('hsResult OK')

                hashInfoExp = {
                    'blockHash': None, 
                    'blockNumber': None, 
                    'from': '0x7a5ddf8a2a924e4faa9d76b97b1363c864062df5', 
                    'gas': '0x5208', 
                    'gasPrice': 
                    '0x5968f26e', 
                    'maxFeePerGas': '0x5968f26e', 
                    'maxPriorityFeePerGas': '0x59682f00', 
                    'hash': '0xe5eb2fb4ce91e96501e6ee6423198f3395341bab744cdad7eb2e354ac9fec8ac', 
                    'input': '0x', 
                    'nonce': '0x3', 
                    'to': '0x92be6de0ae6d89a2378795553ecb4cdb820e0d7b', 
                    'transactionIndex': None,
                    'value': '0x2386f26fc10000', 
                    'type': '0x2', 
                    }

                hashInfo = gethashInfo(hsResult)
                if not hashInfo:
                    continue
                if hashInfo['function'] not in swapFunction:
                    continue
                if hashInfo['gasPrice'] > baseGasPrice * 5 or hashInfo['gasPrice'] > 20 * 1e9:
                    continue

                #printTime(hsI)
                traceResult = w3.debug.traceCall(hashInfo['fromAddress'], hashInfo['toAddress'], hashInfo['inputData'], hashInfo['value'])
                #printTime('traceCall OK')
                if not traceResult:
                    continue
                if 'error' in traceResult:
                    continue
                if 'calls' not in traceResult:
                    continue
                tradeInfo = w3.debug.transferDecodeFromCalldata(traceResult['calls'])
                #printTime('transferDecodeFromCalldata OK')
                if not tradeInfo:
                    continue
                lpArray = calLpInTradeInfo(tradeInfo)
                if not lpArray:
                    continue
                #printTime('calLpInTradeInfo OK', len(lpArray))
                lpRouterArray = findLpArrayRouter(lpArray, lpRouter256Info)
                if not lpRouterArray:
                    continue
                #printTime('findLpArrayRouter OK', len(lpRouterArray))
                onlyLp = calOnlyLpInRouterArray(lpRouterArray)  
                #printTime('calOnlyLpInRouterArray OK', len(onlyLp))
                # 获取余额
                lpResArray = getLpArrayResAfterTrade(hashInfo['fromAddress'], hashInfo['toAddress'], hashInfo['inputData'], hashInfo['value'], onlyLp)
                # 余额进行缓存
                for lpResI in lpResArray:
                    nowBlockLpRes[lpResI] = lpResArray[lpResI]
                #printTime('RES OK')
                #计算每一个路由的利润
                
                for lpRouter in lpRouterArray:
                    tradeInfo = calRouterProfit(lpRouter)
                    if tradeInfo:
                        type = tradeInfo['type']
                        if type == 'type2':
                            result = callABAPending(tradeInfo, hashInfo)                   
                            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'] * int(hsResult['gasPrice'],16)
                            result['maxPrice'] = result['ethProfit'] / result['gasUsed']
                            if result['tureProfit'] < tradeMinProfit:
                                continue
                            tradeInfoArray.append(result)
                           
            maxProfitInfo = {'tureProfit' : 0}
            for tradeInfoI in tradeInfoArray:
                if tradeInfoI['tureProfit'] > maxProfitInfo['tureProfit']:
                    maxProfitInfo = tradeInfoI

            if maxProfitInfo['tureProfit'] > 0:
                printTime(maxProfitInfo['lp0'], maxProfitInfo['lpLoan'], maxProfitInfo['tureProfit'] / 1e18,  hashInfo['hash'])  
                tradeParams = {}
                tradeParams['to'] = v5Address
                tradeParams['data'] = maxProfitInfo['inputData']            
                tradeParams['gas'] = hex(int(1e6))
                tradeParams['value'] = hex(0)
                tradeParams['type'] = hsResult['type']

                if tradeParams['type'] == '0x2':
                    tradeParams['maxFeePerGas'] = hsResult['maxFeePerGas']
                    tradeParams['maxPriorityFeePerGas'] = hsResult['maxPriorityFeePerGas']
                else:
                    tradeParams['gasPrice'] = hsResult['gasPrice']

                params = {}
                params['method'] = 'sendTransaction'
                params['params'] = tradeParams
                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))
                        printTime('-' * 50)
                except:
                    hs = 'err seed'   

        pendingInitTime = time.time()   
        time.sleep(0.0005)
    except BaseException as err:
        printTime(traceback.format_exc())
        printTime('loop', err)
        time.sleep(20)
        try:
            r = w3.provider.make_request('eth_newPendingTransactionFilter',[])
            filterId = r['result']
        except:
            time.sleep(10)





