import re
import time
import json
import redis
import random
import aiohttp
import requests
from web3 import Web3
from web3.eth import AsyncEth
import datetime
import itertools
import eth_utils
import numpy as np
from lxml import etree
from fake_useragent import UserAgent
from aredis import StrictRedis, ConnectionPool
from web3._utils.abi import get_abi_output_types, map_abi_data
from web3._utils.normalizers import BASE_RETURN_NORMALIZERS

uni256Max = 2**256


class RedisConnSingleton(object):
    _instance = None

    def __new__(cls, *args, **kwargs):
        """
        redis connection

        :param args:
        :param kwargs:
        """
        if cls._instance is None:
            cls._instance = redis.ConnectionPool(max_connections=1000, host='114.132.50.141', port=6379, db=0, password='lhj123456')
        return cls._instance


class RedisConnectionPoolSingleton(object):
    _instance = None

    def __new__(cls, *args, **kwargs):
        """
        aredis connection

        :param args:
        :param kwargs:
        """
        if cls._instance is None:
            cls._instance = ConnectionPool(max_connections=1000, host='114.132.50.141', port=6379, db=0, password='lhj123456')
        return cls._instance


class RedisCon:
    @staticmethod
    def RedisConn():
        """
        Redis connect

        :return:
        """
        redis_con = redis.Redis(connection_pool=RedisConnSingleton())
        return redis_con

    @staticmethod
    def delete_task_data(redis_key, key):
        """
        redis hdel

        :param redis_key:
        :param key:
        :return:
        """
        redis_con = redis.Redis(connection_pool=RedisConnSingleton())
        try:
            redis_con.hdel(redis_key, key)
        except Exception as e:
            print("Redis hdel error", e)

    @staticmethod
    def save_data(redis_key, key, value):
        """
        redis hset

        :param redis_key:
        :param key:
        :param value:
        :return:
        """
        redis_con = redis.Redis(connection_pool=RedisConnSingleton())
        try:
            redis_con.hset(redis_key, key, value)
        except Exception as e:
            print("Redis hset error", e)

    @staticmethod
    def query_contrast(redis_key, key):
        """
        redis hget

        :param redis_key:
        :param key:
        :return:
        """
        redis_con = redis.Redis(connection_pool=RedisConnSingleton())
        try:
            res = redis_con.hget(redis_key, key)
            return res
        except Exception as e:
            print("Redis hget error:", e)

    @staticmethod
    def task_list(key, value):
        """
        redis rpush

        :param key:
        :param value:
        :return:
        """
        redis_con = redis.Redis(connection_pool=RedisConnSingleton())
        try:
            redis_con.rpush(key, value)
        except Exception as e:
            print("Redis rpush error:", e)

    @staticmethod
    def get_task(key):
        """
        redis lpop

        :param key:
        :return:
        """
        redis_con = redis.Redis(connection_pool=RedisConnSingleton())
        try:
            task = redis_con.lpop(key)
            return task
        except Exception as e:
            print("Redis lpop error:", e)

    @staticmethod
    def del_task(key, value):
        """
        redis lrem

        :param key:
        :param value:
        :return:
        """
        redis_con = redis.Redis(connection_pool=RedisConnSingleton())
        try:
            redis_con.lrem(key, 0, value)
        except Exception as e:
            print("Redis lrem error:", e)

    @staticmethod
    def ex_data(key, v_time, value):
        """
        redis setex

        :param key:
        :param v_time:
        :param value:
        :return:
        """
        redis_con = redis.Redis(connection_pool=RedisConnSingleton())
        try:
            redis_con.setex(key, v_time, value)
        except Exception as e:
            print("Redis setex error:", e)

    @staticmethod
    def get_task_num(key):
        """
        redis llen

        :param key:
        :return:
        """
        redis_con = redis.Redis(connection_pool=RedisConnSingleton())
        try:
            num = redis_con.llen(key)
            if num is not None:
                return num
            else:
                return 0
        except Exception as e:
            print("Redis llen error:", e)


class Helper:
    """公共基础类"""
    @staticmethod
    def getProxyLst():
        """获取代理列表"""
        proxy_lst = list()
        for i in range(5):
            try:
                response = requests.get("https://proxy.webshare.io/api/v2/proxy/list/download/pgewrawzvvqtzwdzjfdfswvkwcjasmofoucnfyyq/-/any/username/direct/-/").text
                a = response.split('\r\n')
                proxy_lst = a[:-1]
                proxy_lst = list(map(lambda x: x.split(":"), proxy_lst))
                proxy_lst = list(map(lambda x: x[2] + ':' + x[3] + '@' + x[0] + ':' + x[1], proxy_lst))
                if proxy_lst != []:
                    # print("代理获取成功！")
                    break
            except:
                # 等待 1s 重试
                time.sleep(2)
        RedisCon.save_data("chain:pLst", "proxy", json.dumps(proxy_lst))
        RedisCon.save_data("chain:pLst", "date", json.dumps(int(time.time())))
        return proxy_lst

    @staticmethod
    def updateProxyLst():
        status = 0
        proxy_date = RedisCon.query_contrast("chain:pLst", "date")
        if proxy_date:
            proxy_date = json.loads(proxy_date)
            if int(time.time()) - proxy_date > 1800:
                status = 1
        else:
            status = 1
        return status

    @staticmethod
    def getProxyList():
        proxy = RedisCon.query_contrast("chain:pLst", "proxy")
        if proxy:
            proxy = json.loads(proxy)
            # 代理获取失败，重新获取代理！
            if not proxy:
                proxy = Helper.getProxyLst()
        else:
            proxy = Helper.getProxyLst()
        return proxy

    @staticmethod
    def swap_str():
        """
        swap str

        :return:
        """
        return '0xd78ad95fa46c994b6551d0da85fc275fe613ce37657fb8d5e3d130840159d822'

    @staticmethod
    def tran_str():
        """
        transfer str

        :return:
        """
        return '0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef'

    @staticmethod
    async def query_hash(redis_key, key):
        """
        aredis hget

        :param redis_key:
        :param key:
        :return:
        """
        redis_con = StrictRedis(connection_pool=RedisConnectionPoolSingleton())
        try:
            res = await redis_con.hget(redis_key, key)
            return res
        except Exception as e:
            print("Redis hget error:", e)

    @staticmethod
    async def task_list(key, value):
        """
        aredis rpush

        :param key:
        :param value:
        :return:
        """
        redis_con = StrictRedis(connection_pool=RedisConnectionPoolSingleton())
        try:
            await redis_con.rpush(key, value)
        except Exception as e:
            print("Redis rpush error:", e)

    @staticmethod
    async def get_task(key):
        """
        aredis lpop

        :param key:
        :return:
        """
        redis_con = StrictRedis(connection_pool=RedisConnectionPoolSingleton())
        try:
            task = await redis_con.lpop(key)
            return task
        except Exception as e:
            print("Redis lpop error:", e)

    @staticmethod
    async def save_hash_data(redis_key, key, value):
        """
        aredis hset

        :param redis_key:
        :param key:
        :param value:
        :return:
        """
        redis_con = StrictRedis(connection_pool=RedisConnectionPoolSingleton())
        try:
            await redis_con.hset(redis_key, key, value)
        except Exception as e:
            print("Redis hset error", e)

    @staticmethod
    async def del_hash_data(redis_key, key):
        """
        aredis hdel

        :param redis_key:
        :param key:
        :return:
        """
        redis_con = StrictRedis(connection_pool=RedisConnectionPoolSingleton())
        try:
            await redis_con.hdel(redis_key, key)
        except Exception as e:
            print("Redis hdel error", e)

    @staticmethod
    async def get_string_value(name):
        """
        aredis get

        :param name:
        :return:
        """
        redis_con = StrictRedis(connection_pool=RedisConnectionPoolSingleton())
        try:
            data = await redis_con.get(name)
            return data
        except Exception as e:
            print("Redis get error", e)

    @staticmethod
    async def ex_data(key, time, value):
        """
        aredis setex

        :param key:
        :param time:
        :param value:
        :return:
        """
        redis_con = StrictRedis(connection_pool=RedisConnectionPoolSingleton())
        try:
            await redis_con.setex(key, time, value)
        except Exception as e:
            print("Redis setex error", e)

    @staticmethod
    async def get_functions_(fn_abi, transaction, web_async):
        """
        web3 get_functions_

        :param fn_abi:
        :param transaction:
        :param web_async:
        :return:
        """
        eth = AsyncEth(web_async)
        contract = await eth.call(transaction)
        output_types = get_abi_output_types(fn_abi)
        output_data = web_async.codec.decode_abi(output_types, contract)

        _normalizers = itertools.chain(
            BASE_RETURN_NORMALIZERS,
            (),
        )
        normalized_data = map_abi_data(_normalizers, output_types, output_data)
        # print(normalized_data)
        if len(normalized_data) == 1:
            return normalized_data[0]
        else:
            return normalized_data

    @staticmethod
    async def get_token_decimals(token, web_async):
        """
        web3 decimals

        :param token:
        :param web_async:
        :return:
        """
        fn_abi = {'inputs': [], 'name': 'decimals', 'outputs': [{'internalType': 'uint8', 'name': '', 'type': 'uint8'}],
                  'stateMutability': 'view', 'type': 'function'}
        transaction = {'to': f'{token}', 'data': '0x313ce567'}
        function = await Helper.get_functions_(fn_abi, transaction, web_async)
        # print("decimals", function)
        return function

    @staticmethod
    async def get_token_name(token, web_async):
        """
        web3 name

        :param token:
        :param web_async:
        :return:
        """
        fn_abi = {'inputs': [], 'name': 'name', 'outputs': [{'internalType': 'string', 'name': '', 'type': 'string'}],
                  'stateMutability': 'view', 'type': 'function'}
        transaction = {'to': f'{token}', 'data': '0x06fdde03'}
        function = await Helper.get_functions_(fn_abi, transaction, web_async)
        # print("name", function)
        return function

    @staticmethod
    async def get_token_supply(token, web_async):
        """
        web3 supply

        :param token:
        :param web_async:
        :return:
        """
        fn_abi = {'inputs': [], 'name': 'totalSupply',
                  'outputs': [{'internalType': 'uint256', 'name': '', 'type': 'uint256'}], 'stateMutability': 'view',
                  'type': 'function'}
        transaction = {'to': f'{token}', 'data': '0x18160ddd'}
        function = await Helper.get_functions_(fn_abi, transaction, web_async)
        # print("total supply", function)
        return function

    @staticmethod
    async def get_token_symbol(token, web_async):
        """
        web3 symbol

        :param token:
        :param web_async:
        :return:
        """
        fn_abi = {'inputs': [], 'name': 'symbol', 'outputs': [{'internalType': 'string', 'name': '', 'type': 'string'}],
                  'stateMutability': 'view', 'type': 'function'}
        transaction = {'to': f'{token}', 'data': '0x95d89b41'}
        function = await Helper.get_functions_(fn_abi, transaction, web_async)
        # print("symbol", function)
        return function

    @staticmethod
    async def get_token_balance(token, address, web_async):
        """
        web3 balanceOf

        :param token:
        :param address:
        :param web_async:
        :return:
        """
        fn_abi = {'inputs': [{'internalType': 'address', 'name': '', 'type': 'address'}], 'name': 'balanceOf',
                  'outputs': [{'internalType': 'uint256', 'name': '', 'type': 'uint256'}], 'stateMutability': 'view',
                  'type': 'function'}
        address = '0x70a08231000000000000000000000000' + (address.lower()[2:])
        transaction = {'to': f'{token}', 'data': address}
        function = await Helper.get_functions_(fn_abi, transaction, web_async)
        # print("token balance", function)
        return function

    @staticmethod
    def trans_address_n0x(address):
        """
        address type change

        :param address:
        :return:
        """
        address = address[2:]
        while len(address) < 64:
            address = '0' + address
        return address.lower()

    @staticmethod
    async def get_amounts_out_op(token0, token1, web_async, Router, token0_d):
        """
        get price rate - op

        :param token0:
        :param token1:
        :param web_async:
        :param Router:
        :param token0_d:
        :return:
        """
        fn_abi = {'inputs': [{'internalType': 'uint256', 'name': 'amountIn', 'type': 'uint256'},
                             {'internalType': 'address[]', 'name': 'path', 'type': 'address[]'}],
                  'name': 'getAmountsOut',
                  'outputs': [{'internalType': 'uint256[]', 'name': 'amounts', 'type': 'uint256[]'}],
                  'stateMutability': 'view', 'type': 'function'}
        str_0 = '0x9881fcb4'
        str_1 = Helper.trans_address_n0x(web_async.toHex(1 * (10 ** token0_d)))
        str_2 = '00000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000001'
        str_3 = Helper.trans_address_n0x(token0)
        str_4 = Helper.trans_address_n0x(token1)
        str_5 = "0000000000000000000000000000000000000000000000000000000000000001"
        data_str = str_0 + str_1 + str_2 + str_3 + str_4 + str_5
        transaction = {'to': f'{Router}', 'data': data_str}
        function = await Helper.get_functions_(fn_abi, transaction, web_async)
        # print("token amount", function)
        return function

    @staticmethod
    async def get_amounts_out(token0, token1, web_async, Router, token0_d):
        """
        get price rate

        :param token0:
        :param token1:
        :param web_async:
        :param Router:
        :param token0_d:
        :return:
        """
        fn_abi = {'inputs': [{'internalType': 'uint256', 'name': 'amountIn', 'type': 'uint256'},
                             {'internalType': 'address[]', 'name': 'path', 'type': 'address[]'}],
                  'name': 'getAmountsOut',
                  'outputs': [{'internalType': 'uint256[]', 'name': 'amounts', 'type': 'uint256[]'}],
                  'stateMutability': 'view', 'type': 'function'}
        str_0 = '0xd06ca61f'
        str_1 = Helper.trans_address_n0x(web_async.toHex(1 * (10 ** token0_d)))
        str_2 = '00000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000002'
        str_3 = Helper.trans_address_n0x(token0)
        str_4 = Helper.trans_address_n0x(token1)
        data_str = str_0 + str_1 + str_2 + str_3 + str_4

        transaction = {'to': f'{Router}', 'data': data_str}
        function = await Helper.get_functions_(fn_abi, transaction, web_async)
        # print("token amount", function)
        return function

    @staticmethod
    async def get_token_detail_web(token, chain):
        """
        get goPlus token detail

        :param token:
        :param chain:
        :return:
        """
        url = f'https://api.gopluslabs.io/api/v1/token_security/{chain}?contract_addresses={token}'
        async with aiohttp.ClientSession() as session:
            proxy = Helper.aiohttp_proxy_pool()
            headers = Helper.getHeaders()
            res = await session.get(url, headers=headers, proxy=proxy, timeout=10)
            res = await res.text()
            da = json.loads(res)
            return da['result'][token.lower()] if token.lower() in da['result'] else {}

    @staticmethod
    async def get_sell_radio(token, sell_num, token_d, address, web_async):
        """
        get sell radio

        :param token:
        :param sell_num:
        :param token_d:
        :param address:
        :param web_async:
        :return:
        """
        if address is not None:
            balance = await Helper.get_token_balance(token, address, web_async)
            balance = balance / (10 ** token_d)
            sell_radio = sell_num / (sell_num + balance)
            return {
                'radio': sell_radio,
                'balance': balance,
            }
        else:
            return {
                'radio': None,
                'balance': None,
            }

    @staticmethod
    async def balancer_sell_radio(token, token_d, tx_hash, token_a, sell_num, web_async):
        """
        balancer sell info

        :param token:
        :param token_d:
        :param tx_hash:
        :param token_a:
        :param sell_num:
        :param web_async:
        :return:
        """
        tran_str = Helper.tran_str()
        pool = "0x000000000000000000000000ba12222222228d8ba445958a75a0704d566bf2c8"
        receipt = await web_async.eth.get_transaction_receipt(tx_hash)
        logs = receipt['logs']
        address = None
        for log in logs:
            if log['address'] == token and log['topics'][0].hex() == tran_str and log['topics'][
                2].hex() == pool and eval(log['data']) == token_a:
                address = log['topics'][1].hex()
                address = web_async.toChecksumAddress(Helper.hex2bsc(address))
                break
            else:
                pass
        sell_radio, balance = None, None
        if address is not None:
            balance = await Helper.get_token_balance(token, address, web_async)
            balance = balance / (10 ** token_d)
            sell_radio = sell_num / (sell_num + balance)
        return {
            'address': address,
            'radio': sell_radio,
            'balance': balance
        }

    @staticmethod
    async def Beethoven_sell_radio(token, token_d, tx_hash, token_a, sell_num, web_async):
        """
        beethoven sell info

        :param token:
        :param token_d:
        :param tx_hash:
        :param token_a:
        :param sell_num:
        :param web_async:
        :return:
        """
        tran_str = Helper.tran_str()
        pool = "0x00000000000000000000000020dd72ed959b6147912c2e529f0a0c651c33c9ce"
        receipt = await web_async.eth.get_transaction_receipt(tx_hash)
        logs = receipt['logs']
        address = None
        for log in logs:
            if log['address'] == token and log['topics'][0].hex() == tran_str and log['topics'][
                2].hex() == pool and eval(log['data']) == token_a:
                address = log['topics'][1].hex()
                address = web_async.toChecksumAddress(Helper.hex2bsc(address))
                break
            else:
                pass
        sell_radio, balance = None, None
        if address is not None:
            balance = await Helper.get_token_balance(token, address, web_async)
            balance = balance / (10 ** token_d)
            sell_radio = sell_num / (sell_num + balance)
        return {
            'address': address,
            'radio': sell_radio,
            'balance': balance
        }

    @staticmethod
    async def get_new_holder(token, token_d, address, buy_num, web_async):
        """
        get new holder info

        :param token:
        :param token_d:
        :param address:
        :param buy_num:
        :param web_async:
        :return:
        """
        address = web_async.toChecksumAddress(address)
        balance = await Helper.get_token_balance(token, address, web_async)
        balance = balance / (10 ** token_d)
        if balance <= buy_num:
            new = 1
        else:
            new = 0
        return {
            'balance': balance,
            'new': new
        }

    @staticmethod
    async def balancer_new_holder(token, tx_hash, token_a, web_async):
        """
        balancer new holder info

        :param token:
        :param tx_hash:
        :param token_a:
        :param web_async:
        :return:
        """
        tran_str = Helper.tran_str()
        pool = "0x000000000000000000000000ba12222222228d8ba445958a75a0704d566bf2c8"
        receipt = await web_async.eth.get_transaction_receipt(tx_hash)
        logs = receipt['logs']
        address = None
        for log in logs:
            if log['address'] == token and log['topics'][0].hex() == tran_str and log['topics'][
                1].hex() == pool and eval(log['data']) == token_a:
                address = log['topics'][2].hex()
                address = web_async.toChecksumAddress(Helper.hex2bsc(address))
                break
            else:
                pass
        # print('address', address)
        new, balance = 0, 0
        if address is not None:
            balance = await Helper.get_token_balance(token, address, web_async)
            if balance == token_a:
                new = 1
        return {
            'balance': balance,
            'address': address,
            'new': new
        }

    @staticmethod
    async def Beethoven_new_holder(token, tx_hash, token_a, web_async):
        """
        beethoven new holder info

        :param token:
        :param tx_hash:
        :param token_a:
        :param web_async:
        :return:
        """
        tran_str = Helper.tran_str()
        pool = "0x00000000000000000000000020dd72ed959b6147912c2e529f0a0c651c33c9ce"
        receipt = await web_async.eth.get_transaction_receipt(tx_hash)
        logs = receipt['logs']
        address = None
        for log in logs:
            if log['address'] == token and log['topics'][0].hex() == tran_str and log['topics'][
                1].hex() == pool and eval(log['data']) == token_a:
                address = log['topics'][2].hex()
                address = web_async.toChecksumAddress(Helper.hex2bsc(address))
                break
            else:
                pass
        # print('address', address)
        new, balance = 0, 0
        if address is not None:
            balance = await Helper.get_token_balance(token, address, web_async)
            if balance == token_a:
                new = 1
        return {
            'balance': balance,
            'address': address,
            'new': new
        }

    @staticmethod
    async def sell_address(logs, token, pool, amount, web_async):
        """
        get sell address

        :param logs:
        :param token:
        :param pool:
        :param amount:
        :param web_async:
        :return:
        """
        tran_str = Helper.tran_str()
        address = None
        for log in logs:
            if log['address'] == token and log['topics'][0].hex() == tran_str and log['topics'][
                2].hex() == pool and eval(log['data']) == amount:
                address = log['topics'][1].hex()
                address = web_async.toChecksumAddress(Helper.hex2bsc(address))
                break
            else:
                pass
        if address is None:
            for log in logs:
                if log['address'] == token and log['topics'][0].hex() == tran_str and log['topics'][2].hex() == pool:
                    address = log['topics'][1].hex()
                    address = web_async.toChecksumAddress(Helper.hex2bsc(address))
                    break
                else:
                    pass
        return address

    @staticmethod
    def hex2bsc(address):
        """
        change hex-address

        :param address:
        :return:
        """
        address = '0x' + address[26:]
        return address

    @staticmethod
    def normalize_32_byte_hex_address(address):
        """
        change type toCheckAddress

        :param address:
        :return:
        """
        as_bytes = eth_utils.to_bytes(hexstr=address)
        return eth_utils.to_normalized_address(as_bytes[-20:])

    @staticmethod
    def checksumAddress(address, web_async):
        """
        web3 checkAddress

        :param address:
        :param web_async:
        :return:
        """
        address = Helper.normalize_32_byte_hex_address(address)
        return web_async.toChecksumAddress(address)

    @staticmethod
    async def wBalances(amount, p_token, p_token_symbol, pDecimals, wToken, Router, redisKey, wDecimals, web_async):
        """
        get wrap pool balance

        :param amount
        :param p_token
        :param p_token_symbol
        :param pDecimals
        :param wToken
        :param Router
        :param redisKey
        :param wDecimals
        :param web_async
        """
        redis_con = StrictRedis(connection_pool=RedisConnectionPoolSingleton())
        rate = await redis_con.get(f'{redisKey}:rate:' + p_token_symbol + '-W')
        opType = ['opt', 'can']
        if rate is None:
            if redisKey in opType:
                rate = await Helper.get_amounts_out_op(p_token, wToken, web_async, Router, pDecimals)
            else:
                rate = await Helper.get_amounts_out(p_token, wToken, web_async, Router, pDecimals)
            rate = rate[1] / (10 ** wDecimals)
            await redis_con.setex(f'{redisKey}:rate:' + p_token_symbol + '-W', 60, rate)
        else:
            rate = json.loads(rate)
        w_balances = amount * rate
        return w_balances

    @staticmethod
    def trans_address(address):
        """
        change address-hex

        :param address:
        :return:
        """
        address = address[2:]
        while len(address) < 64:
            address = '0' + address
        address = '0x' + address
        return address.lower()

    @staticmethod
    async def get_pool_swap(pair, web_async):
        """
        get pool swap type

        :param pair:
        :param web_async:
        :return:
        """
        status = 0
        pool_swap = ''
        info = await Helper.query_hash("taos:search:saved", pair)
        if info is not None:
            info = json.loads(info)
            if "swap" in info:
                pool_swap = info['swap']
                status = 1
        if status == 0:
            try:
                pool_swap = await Helper.get_token_name(pair, web_async)
            except:
                pool_swap = 'Error Contract'
        return {
            "swap": pool_swap,
            "status": status
        }

    @staticmethod
    async def pair_balance(p_token, p_token_d, pair_address, redisKey, swapType, p_amount, web_async):
        """
        get pair balance

        :param p_token
        :param p_token_d
        :param pair_address
        :param redisKey
        :param swapType
        :param p_amount
        :param web_async
        """
        status = 0
        balance = await Helper.query_hash(f"{redisKey}:balance", pair_address)
        if balance is None:
            status = 1
        else:
            balance = json.loads(balance)
            ts_before = Helper.get_time_stamp(balance['time'])
            ts = Helper.get_time_stamp(Helper.get_time())
            if ts - ts_before > 300:
                status = 1
            else:
                if swapType == "sell":
                    balance['p_balance'] -= p_amount
                else:
                    balance['p_balance'] += p_amount
                if balance['p_balance'] < 0:
                    status = 1
        if status == 1:
            p_token_balance = await Helper.get_token_balance(p_token, pair_address, web_async)
            balance = {
                'time': Helper.get_time(),
                'p_balance': (p_token_balance / (10 ** p_token_d))
            }
            await Helper.save_hash_data(f"{redisKey}:balance", pair_address, json.dumps(balance))
        return balance

    @staticmethod
    async def public2usd(pToken, pSymbol, usd, redisKey, Router, uDecimals, pDecimals, web_async):
        """
        get public token - usd rate

        :param pToken
        :param pSymbol
        :param usd
        :param redisKey
        :param Router
        :param uDecimals
        :param pDecimals
        :param web_async
        """
        busd_bsc = "0xe9e7CEA3DedcA5984780Bafc599bD69ADd087D56"         # BUSD (bsc)
        weth_bsc = "0x2170Ed0880ac9A755fd29B2688956BD959F933F8"         # ETH (bsc)
        wbtc_bsc = "0x7130d2A12B9BCbFAe4f2634d864A1Ee1Ce3Ead9c"         # BTCB (bsc)
        router_bsc = "0x10ED43C718714eb63d5aA57B78B54704E256024E"       # pancakeSwapV2Router
        redis_con = StrictRedis(connection_pool=RedisConnectionPoolSingleton())
        rate = await redis_con.get(f'{redisKey}:rate:' + pSymbol + '-U')
        opType = ['opt', 'can']
        chainErrorRouter = ['cro', 'aur', 'gno', 'opt', 'cel', 'met', 'ast']
        chainErrorToken = ['WETH', 'WBTC']
        if rate is None:
            if pSymbol == 'WETH' and redisKey in chainErrorRouter:
                bsc_rate = await redis_con.get('bsc:rate:WETH-U')
                if bsc_rate is None:
                    rate = await Helper.get_amounts_out(weth_bsc, busd_bsc, Helper.getWeb3Bsc(), router_bsc, 18)
                    uDecimals = 18
                    rate = rate[1] / (10 ** uDecimals)
                    await redis_con.setex('bsc:rate:WETH-U', 60, rate)
                else:
                    rate = json.loads(bsc_rate)
                return rate
            elif pSymbol == 'WBTC' and redisKey in chainErrorRouter:
                bsc_rate = await redis_con.get('bsc:rate:WBTC-U')
                if bsc_rate is None:
                    rate = await Helper.get_amounts_out(wbtc_bsc, busd_bsc, Helper.getWeb3Bsc(), router_bsc, 18)
                    uDecimals = 18
                    rate = rate[1] / (10 ** uDecimals)
                    await redis_con.setex('bsc:rate:WBTC-U', 60, rate)
                else:
                    rate = json.loads(bsc_rate)
                return rate
            elif redisKey in opType and pSymbol not in chainErrorToken:
                rate = await Helper.get_amounts_out_op(pToken, usd, web_async, Router, pDecimals)
            else:
                rate = await Helper.get_amounts_out(pToken, usd, web_async, Router, pDecimals)
            rate = rate[1] / (10 ** uDecimals)
            await redis_con.setex(f'{redisKey}:rate:' + pSymbol + '-U', 60, rate)
        else:
            rate = json.loads(rate)
        return rate

    @staticmethod
    async def redis_data(data, info, ts_, redisKey, public_token, chainId, stable, search_table, web_async):
        """
        taos save insert task list

        :param data:
        :param info:
        :param ts_:
        :param redisKey:
        :param public_token:
        :param chainId:
        :param stable:
        :param search_table:
        :param web_async:
        :return:
        """
        token = data['token']
        if info is None:
            try:
                supply = await Helper.get_token_supply(token, web_async) / (10 ** data['token_decimals'])
                symbol = await Helper.get_token_symbol(token, web_async)
                name = await Helper.get_token_name(token, web_async)
            except:
                da = await Helper.get_token_detail_web(token, chainId)
                name = da['token_name'] if "token_name" in da else ""
                symbol = da['token_symbol'] if "token_symbol" in da else ""
                supply = da['total_supply'] if "total_supply" in da else ""
            redis_token = {
                'name': name,
                'symbol': symbol,
                'supply': supply,
                'decimals': data['token_decimals'],
                'token': token
            }
            await Helper.save_hash_data(f'{redisKey}:tokens', token, json.dumps(redis_token))
        else:
            name = info['name']
            symbol = info['symbol']
            supply = info['supply']
        basic_info = {
            'name': name,
            'symbol': symbol,
            'supply': supply,
        }
        chainInfo = {
            'stable': stable,
            'search_table': search_table,
            'chain_id': chainId,
            'redis_key': redisKey
        }
        if chainId == 1 or chainId == 56:
            token_info = dict(**data, **basic_info)
            await Helper.task_list(f"{redisKey}:taos:tokens", json.dumps(token_info))
        else:
            token_info = dict(**data, **basic_info, **chainInfo)
            await Helper.task_list("taos:insert:tokens", json.dumps(token_info))

    @staticmethod
    def get_swap_amount(amount):
        """
        uniSwap v2 tx amount

        :param amount:
        :return:
        """
        if amount[0:2] == "0x":
            amount0In = eval('0x' + amount[2:66])
            amount1In = eval('0x' + amount[66:130])
        else:
            amount0In = eval('0x' + amount[0:64])
            amount1In = eval('0x' + amount[64:128])
        amount0Out = eval('0x' + amount[-128:-64])
        amount1Out = eval('0x' + amount[-64:])
        swap_info = {
            '0In': amount0In,
            '1In': amount1In,
            '0Out': amount0Out,
            '1Out': amount1Out
        }
        return swap_info

    @staticmethod
    def getUniswapV3Amount(amount):
        """
        uniSwap v3 tx amount

        :param amount:
        :return:
        """
        amount0 = eval('0x' + amount[2:66])
        amount1 = eval('0x' + amount[66:130])
        if amount0 > amount1:
            amount0 = -(uni256Max - amount0)
        else:
            amount1 = -(uni256Max - amount1)
        swapinfo = {
            "amount0": amount0,
            "amount1": amount1
        }
        return swapinfo

    @staticmethod
    def getBalancerAmount(amount):
        """
        balancer tx amount

        :param amount:
        :return:
        """
        amountIn = eval('0x' + amount[2:66])
        amountOut = eval('0x' + amount[66:130])
        return amountIn, amountOut

    @staticmethod
    async def get_usd_price_V2(p_token, p_token_d, p_token_symbol, price, usd_token, uToken, redisKey, Router, uDecimals, web_async):
        """
        get usd price

        :param p_token
        :param p_token_d
        :param p_token_symbol
        :param price
        :param usd_token
        :param web_async
        :param uToken
        :param redisKey
        :param Router
        :param uDecimals
        """
        if p_token in usd_token:
            usd_price = price
        else:
            rate = await Helper.public2usd(p_token, p_token_symbol, uToken, redisKey, Router, uDecimals, p_token_d,
                                           web_async)
            usd_price = rate * price
        return usd_price

    @staticmethod
    async def get_token_detail(p_token, p_token_d, p_token_symbol, pool_address, usd_token, wToken, wDecimals, uToken, uDecimals, redisKey, Router, swapType, p_amount, web_async):
        """
        get token info

        :param p_token
        :param p_token_d
        :param p_token_symbol
        :param pool_address
        :param usd_token
        :param wToken
        :param wDecimals
        :param uToken
        :param uDecimals
        :param redisKey
        :param Router
        :param swapType
        :param p_amount
        :param web_async
        """
        balance = await Helper.pair_balance(p_token, p_token_d, pool_address, redisKey, swapType, p_amount, web_async)
        p_balance = balance['p_balance']
        u_balance = await Helper.get_usd_price_V2(p_token, p_token_d, p_token_symbol, p_balance, usd_token, uToken,
                                                  redisKey, Router, uDecimals, web_async)
        if p_token == wToken:
            w_balance = p_balance
        else:
            w_balance = await Helper.wBalances(p_balance, p_token, p_token_symbol, p_token_d, wToken, Router, redisKey,
                                               wDecimals, web_async)
        detail = {
            'pair_balance': p_balance,
            'usd_balance': u_balance,
            'wbnb_balance': w_balance
        }
        return detail

    @staticmethod
    async def redisSaveList(pool, dataProcess, redisKey, public_token, chainId, stable, search_table, web_async):
        """
        redis task list

        :param pool
        :param dataProcess
        :param redisKey
        :param public_token
        :param chainId
        :param stable
        :param search_table
        :param web_async
        """
        data = dataProcess['data']
        token_detail = dataProcess['info']
        ts_ = Helper.get_time_stamp(data['time'])
        if 'p' in pool['r']:
            pass
        else:
            new = await Helper.query_hash(f'{redisKey}:open:recent', data['pool_address'])
            if new:
                new = json.loads(new)
                if 'time' in new:
                    ts = Helper.get_time_stamp(new['time'])
                    if ts_ - ts <= 86400:
                        data['new_token'] = 1
                    else:
                        data['new_token'] = 0
                else:
                    data['new_token'] = 1
                    new['time'] = data['time']
                    await Helper.save_hash_data(f'{redisKey}:open:recent', data['pool_address'], json.dumps(new))
            else:
                data['new_token'] = 0
            data['is100'] = 0
            holder_info = await Helper.query_hash(f'{redisKey}:holder:100', data['token'])
            data_ = {
                'chain': redisKey,
                'token': data['token']
            }
            if holder_info is not None:
                holder_info = json.loads(holder_info)
                holder_list = holder_info['holder_100']
                time_ = holder_info['date']
                stamp = Helper.get_time_stamp(time_)
                if ts_ - stamp <= 3600:
                    if (data['swap_address']).lower() in holder_list:
                        data['is100'] = 1
                else:
                    await Helper.task_list('chain:holderQueue', json.dumps(data_))
            else:
                await Helper.task_list('chain:holderQueue', json.dumps(data_))
        await Helper.redis_data(data, token_detail, ts_, redisKey, public_token, chainId, stable, search_table, web_async)

    @staticmethod
    def get_time():
        """
        get millisecond time

        :return:
        """
        ct = time.time()
        local_time = time.localtime(ct)
        data_head = time.strftime("%Y-%m-%d %H:%M:%S", local_time)
        data_secs = (ct - np.compat.long(ct)) * 1000
        time_stamp = "%s.%03d" % (data_head, data_secs)
        return time_stamp

    @staticmethod
    def get_time_add(s):
        """
        recent time add millisecond time

        :param s:
        :return:
        """
        ct = (Helper.get_stamp(Helper.get_time()) + s) / 1000
        local_time = time.localtime(ct)
        data_head = time.strftime("%Y-%m-%d %H:%M:%S", local_time)
        data_secs = (ct - np.compat.long(ct)) * 1000
        time_stamp = "%s.%03d" % (data_head, data_secs)
        return time_stamp

    @staticmethod
    def get_time_add_old(ts, s):
        """
        ts add millisecond time

        :param ts:
        :param s:
        :return:
        """
        ct = (int(ts) + s) / 1000
        local_time = time.localtime(ct)
        data_head = time.strftime("%Y-%m-%d %H:%M:%S", local_time)
        data_secs = (ct - np.compat.long(ct)) * 1000
        time_stamp = "%s.%03d" % (data_head, data_secs)
        return time_stamp

    @staticmethod
    def get_stamp(timeStr):
        """
        get millisecond timestamp

        :param timeStr:
        :return:
        """
        try:
            datetime_obj = datetime.datetime.strptime(timeStr, "%Y-%m-%d %H:%M:%S.%f")
        except ValueError:
            datetime_obj = datetime.datetime.strptime(timeStr, "%Y-%m-%d %H:%M:%S")
        obj_stamp = int(time.mktime(datetime_obj.timetuple()) * 1000.0 + datetime_obj.microsecond / 1000.0)
        return obj_stamp

    @staticmethod
    def get_time_stamp(date):
        """
        get seconds timestamp

        :param date:
        :return:
        """
        try:
            d = datetime.datetime.strptime(date, "%Y-%m-%d %H:%M:%S.%f")
        except:
            d = datetime.datetime.strptime(date, "%Y-%m-%d %H:%M:%S")
        t = d.timetuple()
        time_stamp = int(time.mktime(t))
        return time_stamp

    @staticmethod
    def get_all_proxy():
        """
        get rola all type proxy

        :return:
        """
        proxies_address = requests.get(
            'http://list.rola.info:8088/user_get_ip_list?token=JyJu6trbF4LguQxA1656402892061&type=datacenter&qty=1&time=5&country=&format=txt&protocol=http&filter=1').text
        async_proxy = f"http://{proxies_address}"
        proxy = {
            'https': f"http://{proxies_address}",
            'http': f"http://{proxies_address}",
        }
        return {
            "proxy": proxy,
            "async_proxy": async_proxy
        }

    @staticmethod
    def all_proxy_pool():
        """
        get random all type proxy

        :return:
        """
        proxy_lst = Helper.getProxyList()
        index = random.randint(0, 999)
        proxies_address = proxy_lst[index]
        async_proxy = f"http://{proxies_address}"
        proxy = {
            'https': f"http://{proxies_address}",
            'http': f"http://{proxies_address}",
        }
        return {
            "proxy": proxy,
            "async_proxy": async_proxy
        }

    @staticmethod
    def aiohttp_proxy_pool():
        """
        get aiohttp proxy

        :return:
        """
        proxy_lst = Helper.getProxyList()
        index = random.randint(0, 999)
        proxy = proxy_lst[index]
        proxies = f"http://{proxy}"
        return proxies

    @staticmethod
    def requests_proxy_pool():
        """
        get http proxy

        :return:
        """
        proxy_lst = Helper.getProxyList()
        index = random.randint(0, 999)
        proxy = proxy_lst[index]
        proxies = {
            'https': f"http://{proxy}",
            'http': f"http://{proxy}",
        }
        return proxies

    @staticmethod
    def get_radio(radio):
        """
        get radio

        :param radio:
        :return:
        """
        fen = "%.2f%%" % (radio * 100)
        return fen

    @staticmethod
    def getHeaders():
        """
        get http headers

        :return:
        """
        ua = UserAgent()
        headers = {
            'accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9',
            'accept-language': 'zh-CN,zh;q=0.9',
            'sec-ch-ua': '" Not A;Brand";v="99", "Chromium";v="99", "Google Chrome";v="99"',
            'sec-ch-ua-mobile': '?0',
            'sec-ch-ua-platform': '"Windows"',
            'sec-fetch-dest': 'iframe',
            'sec-fetch-mode': 'navigate',
            'sec-fetch-site': 'same-origin',
            'sec-fetch-user': '?1',
            'upgrade-insecure-requests': '1',
            'user-agent': ua.chrome
        }
        return headers

    @staticmethod
    async def request_page(url, proxy):
        """
        aiohttp get page

        :param url:
        :param proxy:
        :return:
        """
        # headers = Helper.getHeaders()
        async with aiohttp.ClientSession() as session:
            # response = await session.get(url, headers=headers, proxy=proxy, timeout=20)
            response = await session.get(url, proxy=proxy, timeout=20)
            return await response.text()

    @staticmethod
    async def getTroops(chain, token):
        """
        get bsc/eth troops info

        :param chain:
        :param token:
        :return:
        """
        if chain == 56:
            url = f'https://aywt3wreda.execute-api.eu-west-1.amazonaws.com/default/IsHoneypot?chain=bsc2&token={token}'
        else:
            url = f'https://aywt3wreda.execute-api.eu-west-1.amazonaws.com/default/IsHoneypot?chain=eth&token={token}'
        troops = dict()
        for i in range(5):
            try:
                proxy = Helper.aiohttp_proxy_pool()
                res = await Helper.request_page(url, proxy)
                troops = json.loads(res)
                break
            except Exception as e:
                print("get troops error！", e)
        return troops

    @staticmethod
    async def getHolderNum(chain, token):
        """
        get holder nums

        :param chain:
        :param token:
        :return:
        """
        if chain == 56:
            url = f"https://bscscan.com/token/generic-tokenholders2?m=normal&a={token}&s=8000000000000&sid=&p=1"
        else:
            url = f"https://cn.etherscan.com/token/generic-tokenholders2?m=normal&a={token}&s=888888888000000000&sid=&p=1"
        holder_num = 0
        for i in range(5):
            try:
                proxy = Helper.aiohttp_proxy_pool()
                res = await Helper.request_page(url, proxy)
                html = etree.HTML(res)
                holder_list = html.xpath('//*[@class="mb-2 mb-md-0"]/text()')[0].strip()
                holder_num = re.findall(r"\d+,?\d*", holder_list)[0]
                # 代币持仓人数
                holder_num = int(holder_num.replace(',', ''))
                break
            except Exception as e:
                print("get holder nums error!", e)
        return holder_num

    @staticmethod
    def getWeb3Bsc():
        """
        async bsc web3

        :return:
        """
        bsc = "https://rpc.ankr.com/bsc"
       #  proxy = Helper.aiohttp_proxy_pool()
        # request_kwargs={"proxy": proxy}
        web_async = Web3(Web3.AsyncHTTPProvider(bsc), modules={'eth': (AsyncEth,)},
                         middlewares=[])
        return web_async

    @staticmethod
    def getWeb3Eth():
        """
        async eth web3

        :return:
        """
        eth = "https://rpc.ankr.com/eth"
        proxy = Helper.aiohttp_proxy_pool()
        web_async = Web3(Web3.AsyncHTTPProvider(eth, request_kwargs={"proxy": proxy}), modules={'eth': (AsyncEth,)},
                         middlewares=[])
        return web_async

    @staticmethod
    def bscPoolType(token):
        """
        bsc public token

        :param token:
        :return:
        """
        if token == 'WBNB':
            return '0xbb4CdB9CBd36B01bD1cBaEBF2De08d9173bc095c'
        elif token == 'BUSD':
            return '0xe9e7CEA3DedcA5984780Bafc599bD69ADd087D56'
        elif token == 'USDT':
            return '0x55d398326f99059fF775485246999027B3197955'
        elif token == 'USDC':
            return '0x8AC76a51cc950d9822D68b83fE1Ad97B32Cd580d'
        elif token == 'BTCB':
            return '0x7130d2A12B9BCbFAe4f2634d864A1Ee1Ce3Ead9c'
        elif token == 'DAI':
            return '0x1AF3F329e8BE154074D8769D1FFa4eE058B1DBc3'
        elif token == 'ETH':
            return '0x2170Ed0880ac9A755fd29B2688956BD959F933F8'
        elif token == 'FIST':
            return '0xC9882dEF23bc42D53895b8361D0b1EDC7570Bc6A'
        elif token == 'DOGE':
            return '0xbA2aE424d960c26247Dd6c32edC70B295c744C43'
        elif token == 'Cake':
            return '0x0E09FaBB73Bd3Ade0a17ECC321fD13a19e81cE82'

    @staticmethod
    def bscPoolDecimal(token):
        """
        bsc pool decimals

        :param token:
        :return:
        """
        if token == 'WBNB':
            return 18
        elif token == 'BUSD':
            return 18
        elif token == 'USDT':
            return 18
        elif token == 'USDC':
            return 18
        elif token == 'BTCB':
            return 18
        elif token == 'DAI':
            return 18
        elif token == 'ETH':
            return 18
        elif token == 'FIST':
            return 6
        elif token == 'DOGE':
            return 8
        elif token == 'Cake':
            return 18

    @staticmethod
    def ethPoolType(token):
        """
        eth public token

        :param token:
        :return:
        """
        if token == 'WETH':
            return '0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2'
        elif token == 'USDT':
            return '0xdAC17F958D2ee523a2206206994597C13D831ec7'
        elif token == 'USDC':
            return '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48'
        elif token == 'BUSD':
            return '0x4Fabb145d64652a948d72533023f6E7A623C7C53'
        elif token == 'WBTC':
            return '0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599'
        elif token == 'DAI':
            return '0x6B175474E89094C44Da98b954EedeAC495271d0F'
        elif token == 'BNB':
            return '0xB8c77482e45F1F44dE1745F52C74426C631bDD52'

    @staticmethod
    def ethPoolDecimal(token):
        """
        eth pool decimals

        :param token:
        :return:
        """
        if token == 'WETH':
            return 18
        elif token == 'USDT':
            return 6
        elif token == 'USDC':
            return 6
        elif token == 'BUSD':
            return 18
        elif token == 'WBTC':
            return 8
        elif token == 'DAI':
            return 18
        elif token == 'BNB':
            return 18


if __name__ == '__main__':
    # a = Helper.updateProxyLst()
    # if a[0] == 0:
    #     print("代理未过期！")
    # else:
    #     proxyLst = a[1]
    #     print(proxyLst)
    print(Helper.getProxyLst())
