

# #import traceback
# import aiohttp
# import os
# import pandas as pd
# #import web3
# from web3.middleware import async_geth_poa_middleware
# from web3 import AsyncWeb3, AsyncHTTPProvider, Web3
# import asyncio
from base_class import BaseVariableFunction
from base_class import *
# #import pathlib
# #import json
# #import time

old_print = print


def timestamped_print(*args, **kwargs):
    old_print(datetime.datetime.utcnow().replace(
        microsecond=0), *args, **kwargs)


print = timestamped_print

baseclass = BaseVariableFunction(__file__)
base_library = baseclass.open_base_library()
biaoshi = base_library["biaoshi"]
baseclass.getbiaoshipath(biaoshi=biaoshi)


print('\n'*5)

print(f"{'{:<6}'.format('ENTER')} {baseclass.scriptfilename} ----------------NOTE-----------NOTE---------------")

token01_abi = [{"constant": True, "inputs": [], "name": "token0", "outputs": [{"internalType": "address", "name": "", "type": "address"}], "payable": False, "stateMutability": "view", "type": "function"}, {
    "constant": True, "inputs": [], "name": "token1", "outputs": [{"internalType": "address", "name": "", "type": "address"}], "payable": False, "stateMutability": "view", "type": "function"}]


httpProvider_arr = baseclass.httpProvider_arr
httpProvider_idx = 0


print('httpProvider=', httpProvider_arr[0])
async_w3 = None
async_w3 = AsyncWeb3(AsyncWeb3.AsyncHTTPProvider(httpProvider_arr[0]))
# w3.middleware_onion.inject(async_geth_poa_middleware,  "poa",layer=0)
async_w3.middleware_onion.inject(
    async_geth_poa_middleware, "poa", layer=0)


async def savemintLpAddress():
    global mintlpaddress_info_obj
    with (baseclass.librarydata_path/"mintLpAddress.json").open(mode='w', encoding='utf-8') as f:
        json.dump(mintlpaddress_info_obj, f)
    print('savemintLpAddress_obj_() success')


async def getmintLPtoken01(token_lp_address):
    # print(`gettoken01_() lp_address=${lp_address}`)
    # 验证是否是lp_address 如果是 获取token0 和token1
    global async_w3
    global mintlpaddress_info_obj
    token0 = None
    token1 = None
    try:
        checksum_token_lp_address = Web3.to_checksum_address(token_lp_address)
        LP_Contract = async_w3.eth.contract(
            address=checksum_token_lp_address, abi=token01_abi)
        token0, token1 = await asyncio.gather(*[LP_Contract.functions.token0().call(), LP_Contract.functions.token1().call()])
        token0 = token0.lower()
        token1 = token1.lower()
        mintlpaddress_info_obj[token_lp_address]['islp'] = True
        mintlpaddress_info_obj[token_lp_address]['token0'] = token0
        mintlpaddress_info_obj[token_lp_address]['token1'] = token1
        return token_lp_address

    except aiohttp.ClientResponseError as exc:
        if exc.message == 'Forbidden':
            raise Exception("Forbidden")
        elif 'Too Many Requests' in exc.message :
            raise Exception("LimitExceeded")

        else:
            print(
                f"ClientResponseError token_lp_address= {token_lp_address} {str(exc)} \n", end='')
            print("exc=",exc)
            raise
    except ValueError as exc:
        if "limit exceeded" in exc.args[0].get("message"):
            raise Exception("LimitExceeded")
        else:
            print(
                f"ValueError token_lp_address= {token_lp_address} errmsg={str(exc)} \n", end='')
            raise
    except asyncio.TimeoutError as exc:
        raise Exception("AsyncioTimeout")
    except web3.exceptions.ContractCustomError as exc:
        print(
            f"ContractCustomError token_lp_address= {token_lp_address} errmsg={str(exc)} \n", end='')
        print(traceback.format_exc())
        return token_lp_address
    
    except Exception as e:
        print(f"token_lp_address= {token_lp_address} errmsgg={str(e)}")

        if ("execution reverted" in str(e)
                or "Error happened while trying to execute a function inside a smart contract" in str(e)
                or "Parameter decoding error: Returned values aren't valid, did it run Out of Gas? You might also see this error if you are not using the correct ABI for the contract you are retrieving data from, requesting data from a block number that does not exist, or querying a node which is not fully synced." in str(e)
                or "Could not decode contract function call to token0 with return data:" in str(e)
                or "Could not decode contract function call to token1 with return data:" in str(e)
                ):
            mintlpaddress_info_obj[token_lp_address]['islp'] = False
            mintlpaddress_info_obj[token_lp_address]['token0'] = False
            mintlpaddress_info_obj[token_lp_address]['token1'] = False
            return token_lp_address
        else:
            print('token_lp_address=' + token_lp_address)
            print("except Exception as e:=",e)
            print(traceback.format_exc())
            raise


async def main():
    print("enter main")
    global async_w3
    global httpProvider_idx
    global batchSize, forbidden_sleeptime

    # 获取所有没有获取token0 token1的lp 获取lp的token0 token1

    mintlpaddress_info_values_list = list(mintlpaddress_info_obj.values())
    mintlpaddress_set = set()
    # mintlpaddress_info_values_list=mintlpaddress_info_values_list[3000:5000]
    for mintaddress_info_obj in mintlpaddress_info_values_list:
        lpaddress = mintaddress_info_obj['lpaddress']
        if mintaddress_info_obj.get('islp') is None:
            mintlpaddress_set.add(lpaddress)
        elif  mintaddress_info_obj.get('islp') == False :
            del mintlpaddress_info_obj[lpaddress]
    mintlpaddress_list = list(mintlpaddress_set)
    while len(mintlpaddress_list) > 0:
        time.sleep(0.1)
        print("token_lp_address_list remainder", len(mintlpaddress_list))
        cur_success_list = []
        cur_batch_list = mintlpaddress_list[0: batchSize]
        tasks = [asyncio.create_task(getmintLPtoken01(
            token_lp_address)) for token_lp_address in cur_batch_list]
        done, pending = await asyncio.wait(tasks, timeout=20, return_when="ALL_COMPLETED")
        await savemintLpAddress()
        exc_set = set()
        for done_task in done:
            exc = done_task.exception()
            if not exc:
                cur_done_token_lp_address = done_task.result()
                cur_success_list.append(cur_done_token_lp_address)
            elif str(exc) == "Forbidden":
                exc_set.add(str(exc))
                continue
            elif str(exc) == "LimitExceeded":
                exc_set.add(str(exc))
                continue
            elif str(exc) == "AsyncioTimeout":
                exc_set.add(str(exc))
                continue
            else:
                print(f"done error= {exc}", type(exc),exc)
                raise
        mintlpaddress_list = list(
            set(mintlpaddress_list)-set(cur_success_list))
        pending_len = len(pending)
        cur_success_len = len(cur_success_list)
        cur_success_len_per = cur_success_len / len(cur_batch_list)
        if pending_len > 0:
            for pending_task in pending:
                pending_task.cancel()
        # 如果有超时超过30% 需要重新连接w3 并长时间等待
        if pending_len/len(cur_batch_list) > 0.3:
            print(
                f"pending_len_per={pending_len/len(cur_batch_list)} exc_set={exc_set} httpProvider_idx={httpProvider_idx}")
            httpProvider_idx = (httpProvider_idx +
                                1) % len( httpProvider_arr)
            print(
                f" httpProvider={httpProvider_arr[httpProvider_idx]}")
            async_w3 = AsyncWeb3(AsyncWeb3.AsyncHTTPProvider(
                httpProvider_arr[httpProvider_idx]))
            async_w3.middleware_onion.inject(
                async_geth_poa_middleware, "poa", layer=0)
            await asyncio.sleep(10)
        # 如果成功率较低  重新连接w3 并普通时间等待
        elif (cur_success_len_per == 0):
            print(
                f"cur_success_len_per={cur_success_len_per} exc_set={exc_set} httpProvider_idx={httpProvider_idx}")
            httpProvider_idx = (httpProvider_idx +
                                1) % len(httpProvider_arr )
            print(
                f" httpProvider={httpProvider_arr[httpProvider_idx]}")
            if batchSize >= 10:
                batchSize = int(batchSize/2)
            if forbidden_sleeptime <= 12:
                forbidden_sleeptime = int(forbidden_sleeptime+5)
            await asyncio.sleep(forbidden_sleeptime)
            async_w3 = AsyncWeb3(AsyncWeb3.AsyncHTTPProvider(
                httpProvider_arr[0]))
            async_w3.middleware_onion.inject(
                async_geth_poa_middleware, "poa", layer=0)
            await asyncio.sleep(5)
        elif (cur_success_len_per < 0.7):
            print(
                f"cur_success_len_per={cur_success_len_per} exc_set={exc_set} httpProvider_idx={httpProvider_idx}")
            httpProvider_idx = (httpProvider_idx +
                                1) % len(httpProvider_arr )
            print(
                f" httpProvider={httpProvider_arr[httpProvider_idx]}")
            async_w3 = AsyncWeb3(AsyncWeb3.AsyncHTTPProvider(
                httpProvider_arr[httpProvider_idx]))
            async_w3.middleware_onion.inject(
                async_geth_poa_middleware, "poa", layer=0)
            if batchSize >= 10:
                batchSize = int(batchSize/2)
            await asyncio.sleep(8)
        elif (cur_success_len_per == 1):
            batchSize = batchSize_init
            forbidden_sleeptime = forbidden_sleeptime_init

    await savemintLpAddress()
    return
mintlpaddress_info_obj = {}
batchSize_init = 30
forbidden_sleeptime_init = 12
forbidden_sleeptime = forbidden_sleeptime_init
batchSize = batchSize_init
with (baseclass.librarydata_path/"mintLpAddress.json").open(mode='r', encoding='utf8') as f:
    mintlpaddress_info_obj = json.load(f)

asyncio.run(main())
print(f"{'{:<6}'.format('END')} {baseclass.scriptfilename} ----------------NOTE-----------NOTE---------------")
