from web3Tools import *

def callV2V2Auto (balanceIn0, balanceOut0, fee0, balanceIn1, balanceOut1, fee1):
    amountIn  = callBestAmountV2V2(balanceIn0, balanceOut0, fee0, balanceIn1, balanceOut1, fee1)
    if amountIn < 1 :
        return {'amountIn': amountIn, 'amountMid': 0, 'amountOut': 0, 'profit': 0}
    amountMid = calAmountOutV2(amountIn, balanceIn0, balanceOut0, fee0)
    amountOut = calAmountOutV2(amountMid, balanceIn1, balanceOut1, fee1)
    profit = amountOut - amountIn
    return   {'amountIn': amountIn , 'amountMid': amountMid , 'amountOut': amountOut, 'profit': profit, 'profit-18': profit / 1e18}



def calBestAmountABCA(balanceIn0, balanceOut0, fee0, balanceIn1, balanceOut1, fee1, balanceIn2, balanceOut2, fee2):
    F0 = 1 - fee0 / 1e6
    F1 = 1 - fee1 / 1e6
    F2 = 1 - fee2 / 1e6
    B0, B1, B2 = balanceIn0, balanceIn1, balanceIn2
    S0, S1, S2 = balanceOut0, balanceOut1, balanceOut2
    amountIn = ((B0 * B1 * B2 * F0 * F1 * F2 * S0 * S1 * S2) ** 0.5 - B0 * B1 * B2)
    if (F0 * F1 * F2 * S0 * S1 + B2 * F0 * F1 * S0 + B1 * B2 * F0) == 0:
        printTime('0 err', balanceIn0, balanceOut0, fee0, balanceIn1, balanceOut1, fee1, balanceIn2, balanceOut2, fee2)
        return {'amountIn':0, 'amountOut0':0 , 'amountOut1':0, 'amountOut2':0,'profit':0}      
    amountIn = amountIn / (F0 * F1 * F2 * S0 * S1 + B2 * F0 * F1 * S0 + B1 * B2 * F0)
    if amountIn < 1 :
        amountIn = 0
    amountOut0 = calAmountOutV2(amountIn, balanceIn0, balanceOut0, fee0) - 2
    amountOut1 = calAmountOutV2(amountOut0, balanceIn1, balanceOut1, fee1) - 2
    amountOut2 = calAmountOutV2(amountOut1, balanceIn2, balanceOut2, fee2) - 2
    profit = amountOut2 - amountIn
    return {'amountIn':amountIn * 1.00001, 'amountOut0':amountOut0 * 1.000006 , 'amountOut1':amountOut1 * 1.000003, 'amountOut2':amountOut2,'profit':profit}



def calBestAmountV2V2(balanceIn0, balanceOut0, fee0, balanceIn1, balanceOut1, fee1):

    F0, F1 = fee0, fee1
    F0 = 1 - F0 / 1e6
    F1 = 1 - F1 / 1e6
    B0, S0 = balanceIn0, balanceOut0
    B1, S1 = balanceIn1, balanceOut1
    if F0 * F1 * S0 + B1 * F0 == 0 :
        return {'amountIn':0, 'amountOut0':0, 'amountOut1':0, 'profit':0}
    I = ((B0 * B1 * F0 * F1 * S0 * S1) ** 0.5 - B0 * B1) / (F0 * F1 * S0 + B1 * F0)
    if I < 1 :
        I = 0
    amountOut0 = calAmountOutV2(I, balanceIn0, balanceOut0, fee0)
    amountOut1 = calAmountOutV2(amountOut0, balanceIn1, balanceOut1, fee1)
    profit = amountOut1 - I
    return {'amountIn':I * 1.00001, 'amountOut0':amountOut0 * 1.000005, 'amountOut1':amountOut1, 'profit':profit}

def calAmountOutV2(amountIn, balanceIn, balanceOut, fee):
    if amountIn < 1 :
        return 0
    afterFee = (1e6 - fee) / 100
    amountInWithFee = amountIn * (afterFee)
    numerator = amountInWithFee * (balanceOut)
    denominator = balanceIn * (1e4) + (amountInWithFee)
    amountOut = numerator / denominator - 1
    return amountOut

def calAmountInV2(amountOut, balanceIn, balanceOut, fee):
    fee = fee / 1000000
    k = balanceIn * balanceOut
    amountIn =  k / (balanceOut - amountOut) - balanceIn
    amountIn = amountIn / (1 - fee)
    return amountIn

def callV2V2Info(lp0, lp1, fee0, fee1, tokenIn, tokenOut, profit, blockNumber = 'latest'):
    try:
        function = '0x0ad5a3af'
        inputData = function + encodeInput(lp0, lp1, fee0, fee1, tokenIn, tokenOut, profit)
        result = web3Call(myAddress,v2v3CCAddress, inputData, blockNumber)
        info = w3.debug.decodeOutput(result)

        info = {"lpTradeRIn": info[4], "lpTradeROut": info[5], "lpLoanRIn": info[2], "lpLoanROut": info[3],
         "amountLoan": info[6], "midAmount": info[7], "outAmount": info[8], "profit": info[9], "profit-18": info[9] / 1e18, 'data':inputData}
        return info
    except BaseException as err:
        #print(err)
        return {"profit": 0, 'err':str(err)}


def getV2V2SwapInput(lp0, lp1, fee0, fee1, tokenIn, tokenOut, profit):
    function = '0x0ad5a3af'
    inputData = function + encodeInput(lp0, lp1, fee0, fee1, tokenIn, tokenOut, profit)
    return inputData


def callV2EEV2Info(lp0, lp1, fee0, fee1, tokenLp0Out, tokenLp1In, tokenTrade, profit, blockNumber):
    try:
        function = '0xa98eb068'
        inputData = function + encodeInput(lp0, lp1, fee0, fee1, tokenLp0Out, tokenLp1In, tokenTrade, profit)
        result = web3Call(myAddress, v2v3CCAddress, inputData, blockNumber)
        info = w3.debug.decodeOutput(result)
        info = {"lpTradeRIn": info[2], "lpTradeROut": info[3], "lpLoanRIn": info[0], "lpLoanROut": info[1],
         "amountLoan": info[6], "midAmount": info[7], "outAmount": info[5], "profit": info[-1], "profit-18": info[-1] / 1e18, 'data':inputData}
        return info

    except BaseException as err:
        #print(err)
        return {"profit": 0, 'err':str(err)}


def getV2EEV2SwapInput(lp0, lp1, fee0, fee1, tokenLp0Out, tokenLp1In, tokenTrade, profit):
    function = '0xa98eb068'
    inputData = function + encodeInput(lp0, lp1, fee0, fee1, tokenLp0Out, tokenLp1In, tokenTrade, profit)
    return inputData



def callV2V3Info(v2Pair, v2Fee, v3fee, tokenIn, tokenOut, amountInV2, blockNumber):
    try:
        function = '0x69e7fd5c'
        inputData = function + encodeInput(v2Pair, v2Fee, v3fee, tokenIn, tokenOut, amountInV2)
        result = web3Call(myAddress,v2v3Math, inputData, blockNumber)
        info = w3.debug.decodeOutput(result)
        info = {"amountInV2": info[0], "amountOutV2_amountInV3": info[1], "outAmountV3": info[2], "profit": info[3],
                "profit-18": info[3] / 1e18}
        return info
    except BaseException as err:
        print(err)
        return {"profit": 0, 'err':str(err)}

def getV2V3SwapInput(poolV3, pairV2, tokenInV3_tokenOutV2, tokenOutV3_tokenInV2, amountInV3_amountOutV2, amountInV2, profit):
    function = '0x1530b468'
    inputData = function + encodeInput(poolV3, pairV2, tokenInV3_tokenOutV2, tokenOutV3_tokenInV2, amountInV3_amountOutV2, amountInV2, profit)
    return inputData


def simulateSwap(inputdata, gasPrice ,blockNumber):
    r = mev.simulate(v2v3CC, [inputdata], blockNumber)
    data = {}
    data['profit'] = 0
    if not r :
        data['profit'] = 0
        return data

    if 'error' in r['results'][0]:
        printTime('sumilate err', r)
        return False

    data['gasUsed'] = r['results'][0]['gasUsed']
    value = r['results'][0]['value']
    profit = int(value[-60:],16)
    data['profit'] =  profit
    data['profit-E18'] = profit / 1e18
    data['baseGasPrice'] = gasPrice
    data['baseGasPrice-E9'] = gasPrice / 1e9
    data['costBase'] = gasPrice * data['gasUsed']
    return data

def gasCal(fomrAdd, toAdd, data, blockNumber = 'latest'):
    inputData  = '0x4327f0db' + encodeInput(toAdd, 0x40, (len(data) - 2)/2 , data)
    info = web3Call(fomrAdd, '0xEAf90BcB75e7B92900678c4aBD80883b414bEDD6', inputData, blockNumber)
    info = w3.debug.decodeOutput(info)
    result = {}
    result['gasUsed'] = info[1]
    result['info'] = info[3:]
    return result



printTime('CALC:OK')
printTime(calAmountOutV2(14120103689241891864, 31975927096398017590445, 222178467759,  3000)  - 97773396 ,  )
