Esempio n. 1
0
    def get_basic_contract_info(self, address: ChecksumEthAddress) -> Dict[str, Any]:
        """
        Query a contract address and return basic information as:
        - Decimals
        - name
        - symbol
        if it is provided in the contract. This method may raise:
        - BadFunctionCallOutput: If there is an error calling a bad address
        """
        cache = self.contract_info_cache.get(address)
        if cache is not None:
            return cache

        properties = ('decimals', 'symbol', 'name')
        info: Dict[str, Any] = {}

        contract = EthereumContract(address=address, abi=ERC20TOKEN_ABI, deployed_block=0)
        try:
            # Output contains call status and result
            output = multicall_2(
                ethereum=self,
                require_success=False,
                calls=[(address, contract.encode(method_name=prop)) for prop in properties],
            )
        except RemoteError:
            # If something happens in the connection the output should have
            # the same length as the tuple of properties
            output = [(False, b'')] * len(properties)
        try:
            decoded = [
                contract.decode(x[1], method_name)[0]  # pylint: disable=E1136
                if x[0] and len(x[1]) else None
                for (x, method_name) in zip(output, properties)
            ]
        except OverflowError as e:
            # This can happen when contract follows the ERC20 standard methods
            # but name and symbol return bytes instead of string. UNIV1 LP is in this case
            log.error(
                f'{address} failed to decode as ERC20 token. Trying UNIV1 LP token. {str(e)}',
            )
            contract = EthereumContract(address=address, abi=UNIV1_LP_ABI, deployed_block=0)
            decoded = [
                contract.decode(x[1], method_name)[0]  # pylint: disable=E1136
                if x[0] and len(x[1]) else None
                for (x, method_name) in zip(output, properties)
            ]
            log.debug(f'{address} was succesfuly decoded as ERC20 token')

        for prop, value in zip(properties, decoded):
            if isinstance(value, bytes):
                value = value.rstrip(b'\x00').decode()
            info[prop] = value

        self.contract_info_cache[address] = info
        return info
Esempio n. 2
0
def pairs_from_ethereum(ethereum: EthereumManager) -> Dict[str, Any]:
    """Detect the uniswap v2 pool tokens by using an ethereum node"""
    contracts_file = Path(__file__).resolve().parent / 'contracts.json'
    with contracts_file.open('r') as f:
        contracts = json.loads(f.read())

    univ2factory = EthereumContract(
        address=contracts['UNISWAPV2FACTORY']['address'],
        abi=contracts['UNISWAPV2FACTORY']['abi'],
        deployed_block=0,  # whatever
    )
    pairs_num = univ2factory.call(ethereum, 'allPairsLength')
    chunks = list(get_chunks([[x] for x in range(pairs_num)], n=500))
    pairs = []
    for idx, chunk in enumerate(chunks):
        print(f'Querying univ2 pairs chunk {idx + 1} / {len(chunks)}')
        result = multicall_specific(ethereum, univ2factory, 'allPairs', chunk)
        try:
            pairs.extend([deserialize_ethereum_address(x[0]) for x in result])
        except DeserializationError:
            print(
                'Error deserializing address while fetching uniswap v2 pool tokens'
            )
            sys.exit(1)

    return pairs
Esempio n. 3
0
    def get_basic_contract_info(self, address: ChecksumEthAddress) -> Dict[str, Any]:
        """
        Query a contract address and return basic information as:
        - Decimals
        - name
        - symbol
        if it is provided in the contract. This method may raise:
        - BadFunctionCallOutput: If there is an error calling a bad address
        """
        properties = ('decimals', 'symbol', 'name')
        info: Dict[str, Any] = {}

        contract = EthereumContract(address=address, abi=ERC20TOKEN_ABI, deployed_block=0)
        try:
            # Output contains call status and result
            output = multicall_2(
                ethereum=self,
                require_success=False,
                calls=[(address, contract.encode(method_name=prop)) for prop in properties],
            )
        except RemoteError:
            # If something happens in the connection the output should have
            # the same length as the tuple of properties
            output = [(False, b'')] * len(properties)

        decoded = [
            contract.decode(x[1], method_name)[0]  # pylint: disable=E1136
            if x[0] and len(x[1]) else None
            for (x, method_name) in zip(output, properties)
        ]

        for prop, value in zip(properties, decoded):
            info[prop] = value

        return info
Esempio n. 4
0
    def find_yearn_price(
        self,
        token: EthereumToken,
    ) -> Optional[Price]:
        """
        Query price for a yearn vault v2 token using the pricePerShare method
        and the price of the underlying token.
        """
        assert self._ethereum is not None, 'Inquirer ethereum manager should have been initialized'  # noqa: E501

        maybe_underlying_token = GlobalDBHandler().fetch_underlying_tokens(
            token.ethereum_address)
        if maybe_underlying_token is None or len(maybe_underlying_token) != 1:
            log.error(f'Yearn vault token {token} without an underlying asset')
            return None

        underlying_token = EthereumToken(maybe_underlying_token[0].address)
        underlying_token_price = self.find_usd_price(underlying_token)
        # Get the price per share from the yearn contract
        contract = EthereumContract(
            address=token.ethereum_address,
            abi=YEARN_VAULT_V2_ABI,
            deployed_block=0,
        )
        try:
            price_per_share = contract.call(self._ethereum, 'pricePerShare')
            return Price(price_per_share * underlying_token_price /
                         10**token.decimals)
        except (RemoteError, BlockchainQueryError) as e:
            log.error(
                f'Failed to query pricePerShare method in Yearn v2 Vault. {str(e)}'
            )

        return None
Esempio n. 5
0
def _decode_ens_contract(params: EnsContractParams, result_encoded: Any) -> ChecksumEthAddress:
    contract = EthereumContract(address=params.address, abi=params.abi, deployed_block=0)
    result = contract.decode(  # pylint: disable=E1136
        result=result_encoded,
        method_name=params.method_name,
        arguments=params.arguments,
    )[0]
    return string_to_ethereum_address(result)
Esempio n. 6
0
 def __init__(
     self,
     ethereum_manager: 'EthereumManager',
     msg_aggregator: MessagesAggregator,
 ) -> None:
     self.ethereum = ethereum_manager
     self.msg_aggregator = msg_aggregator
     self.contract = EthereumContract(
         address=ZERION_ADAPTER_ADDRESS,
         abi=ZERION_ABI,
         deployed_block=1586199170,
     )
Esempio n. 7
0
 def __init__(
     self,
     ethereum_manager: 'EthereumManager',
     database: 'DBHandler',
     premium: Optional[Premium],
     msg_aggregator: MessagesAggregator,
 ) -> None:
     self.ethereum = ethereum_manager
     self.database = database
     self.premium = premium
     self.msg_aggregator = msg_aggregator
     self.rewards_contract = EthereumContract(
         address=PICKLE_DILL_REWARDS.address,
         abi=PICKLE_DILL_REWARDS.abi,
         deployed_block=PICKLE_DILL_REWARDS.deployed_block,
     )
     self.dill_contract = EthereumContract(
         address=PICKLE_DILL.address,
         abi=PICKLE_DILL.abi,
         deployed_block=PICKLE_DILL.deployed_block,
     )
Esempio n. 8
0
    def get_pool_price(
        self,
        pool_addr: ChecksumEthAddress,
        block_identifier: BlockIdentifier = 'latest',
    ) -> PoolPrice:
        """
        Returns the units of token1 that one token0 can buy
        """
        pool_contract = EthereumContract(
            address=pool_addr,
            abi=UNISWAP_V3_POOL_ABI,
            deployed_block=UNISWAP_FACTORY_DEPLOYED_BLOCK,
        )
        calls = [
            (
                pool_contract.address,
                pool_contract.encode(method_name='slot0'),
            ),
            (
                pool_contract.address,
                pool_contract.encode(method_name='token0'),
            ),
            (
                pool_contract.address,
                pool_contract.encode(method_name='token1'),
            ),
        ]
        output = multicall(
            ethereum=self.eth_manager,
            calls=calls,
            require_success=True,
            block_identifier=block_identifier,
        )
        token_0 = EthereumToken(
            to_checksum_address(pool_contract.decode(output[1], 'token0')[0]),  # noqa: E501 pylint:disable=unsubscriptable-object
        )
        token_1 = EthereumToken(
            to_checksum_address(pool_contract.decode(output[2], 'token1')[0]),  # noqa: E501 pylint:disable=unsubscriptable-object
        )
        sqrt_price_x96, _, _, _, _, _, _ = pool_contract.decode(
            output[0], 'slot0')
        decimals_constant = 10**(token_0.decimals - token_1.decimals)

        price = FVal(
            (sqrt_price_x96 * sqrt_price_x96) / 2**(192) * decimals_constant)

        if ZERO == price:
            raise DefiPoolError(
                f'Uniswap pool for {token_0}/{token_1} has price 0')

        return PoolPrice(price=price, token_0=token_0, token_1=token_1)
Esempio n. 9
0
    def contract_or_none(name: str) -> Optional[EthereumContract]:
        """Gets details of an ethereum contract from the contracts json file

        Returns None if missing
        """
        contract = EthereumConstants().contracts.get(name, None)
        if contract is None:
            return None

        return EthereumContract(
            address=contract['address'],
            abi=contract['abi'],
            deployed_block=contract['deployed_block'],
        )
Esempio n. 10
0
    def get_pool_price(
        self,
        pool_addr: ChecksumEthAddress,
        block_identifier: BlockIdentifier = 'latest',
    ) -> PoolPrice:
        """
        Returns the units of token1 that one token0 can buy
        """
        pool_contract = EthereumContract(
            address=pool_addr,
            abi=UNISWAP_V2_LP_ABI,
            deployed_block=10000835,  # Factory deployment block
        )
        calls = [
            (
                pool_contract.address,
                pool_contract.encode(method_name='getReserves'),
            ),
            (
                pool_contract.address,
                pool_contract.encode(method_name='token0'),
            ),
            (
                pool_contract.address,
                pool_contract.encode(method_name='token1'),
            ),
        ]
        output = multicall(
            ethereum=self.eth_manager,
            calls=calls,
            require_success=True,
            block_identifier=block_identifier,
        )
        token_0 = EthereumToken(
            to_checksum_address(pool_contract.decode(output[1], 'token0')[0]),  # noqa: E501 pylint:disable=unsubscriptable-object
        )
        token_1 = EthereumToken(
            to_checksum_address(pool_contract.decode(output[2], 'token1')[0]),  # noqa: E501 pylint:disable=unsubscriptable-object
        )
        reserve_0, reserve_1, _ = pool_contract.decode(output[0],
                                                       'getReserves')
        decimals_constant = 10**(token_0.decimals - token_1.decimals)

        if ZERO in (reserve_0, reserve_1):
            raise DefiPoolError(
                f'Uniswap pool for {token_0}/{token_1} has asset with no reserves'
            )

        price = FVal((reserve_1 / reserve_0) * decimals_constant)
        return PoolPrice(price=price, token_0=token_0, token_1=token_1)
Esempio n. 11
0
def uniswap_lp_token_balances(
    userdb: 'DBHandler',
    address: ChecksumEthAddress,
    ethereum: 'EthereumManager',
    lp_addresses: List[ChecksumEthAddress],
    known_assets: Set[EthereumToken],
    unknown_assets: Set[EthereumToken],
) -> List[LiquidityPool]:
    """Query uniswap token balances from ethereum chain

    The number of addresses to query in one call depends a lot on the node used.
    With an infura node we saw the following:
    500 addresses per call took on average 43 seconds for 20450 addresses
    2000 addresses per call took on average 36 seconds for 20450 addresses
    4000 addresses per call took on average 32.6 seconds for 20450 addresses
    5000 addresses timed out a few times
    """
    zerion_contract = EthereumContract(
        address=ZERION_ADAPTER_ADDRESS,
        abi=ZERION_ABI,
        deployed_block=1586199170,
    )
    if NodeName.OWN in ethereum.web3_mapping:
        chunks = list(get_chunks(lp_addresses, n=4000))
        call_order = [NodeName.OWN]
    else:
        chunks = list(get_chunks(lp_addresses, n=700))
        call_order = ethereum.default_call_order(skip_etherscan=True)

    balances = []
    for chunk in chunks:
        result = zerion_contract.call(
            ethereum=ethereum,
            method_name='getAdapterBalance',
            arguments=[
                address, '0x4EdBac5c8cb92878DD3fd165e43bBb8472f34c3f', chunk
            ],
            call_order=call_order,
        )

        for entry in result[1]:
            balances.append(
                _decode_result(userdb, entry, known_assets, unknown_assets))

    return balances
Esempio n. 12
0
    def __init__(  # pylint: disable=super-init-not-called
            self,
            ethereum_manager: 'EthereumManager',
            base_tools: 'BaseDecoderTools',
            msg_aggregator: 'MessagesAggregator',  # pylint: disable=unused-argument
    ) -> None:
        self.base = base_tools
        self.ethereum = ethereum_manager

        dir_path = os.path.dirname(os.path.realpath(__file__))
        with open(os.path.join(dir_path, 'data', 'contracts.json'), 'r') as f:
            contracts = json.loads(f.read())

        self.contract = EthereumContract(
            address=contracts['DXDAOMESA']['address'],
            abi=contracts['DXDAOMESA']['abi'],
            deployed_block=contracts['DXDAOMESA']['deployed_block'],
        )
Esempio n. 13
0
    def get_pool(
        self,
        token_0: EthereumToken,
        token_1: EthereumToken,
    ) -> List[str]:
        result = multicall_specific(
            ethereum=self.eth_manager,
            contract=UNISWAP_V3_FACTORY,
            method_name='getPool',
            arguments=[[
                token_0.ethereum_address,
                token_1.ethereum_address,
                fee,
            ] for fee in (3000, 500, 10000)],
        )

        # get liquidity for each pool and choose the pool with the highest liquidity
        best_pool, max_liquidity = to_checksum_address(result[0][0]), 0
        for query in result:
            if query[0] == ZERO_ADDRESS:
                continue
            pool_address = to_checksum_address(query[0])
            pool_contract = EthereumContract(
                address=pool_address,
                abi=UNISWAP_V3_POOL_ABI,
                deployed_block=UNISWAP_FACTORY_DEPLOYED_BLOCK,
            )
            pool_liquidity = pool_contract.call(
                ethereum=self.eth_manager,
                method_name='liquidity',
                arguments=[],
                call_order=None,
            )
            if pool_liquidity > max_liquidity:
                best_pool = pool_address

        return [best_pool]
Esempio n. 14
0
    def find_curve_pool_price(
        self,
        lp_token: EthereumToken,
    ) -> Optional[Price]:
        """
        1. Obtain the pool for this token
        2. Obtain prices for assets in pool
        3. Obtain the virtual price for share and the balances of each
        token in the pool
        4. Calc the price for a share

        Returns the price of 1 LP token from the pool
        """
        assert self._ethereum is not None, 'Inquirer ethereum manager should have been initialized'  # noqa: E501

        pools = get_curve_pools()
        if lp_token.ethereum_address not in pools:
            return None
        pool = pools[lp_token.ethereum_address]
        tokens = []
        # Translate addresses to tokens
        try:
            for asset in pool.assets:
                if asset == '0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE':
                    tokens.append(A_WETH)
                else:
                    tokens.append(EthereumToken(asset))
        except UnknownAsset:
            return None

        # Get price for each token in the pool
        prices = []
        for token in tokens:
            price = self.find_usd_price(token)
            if price == Price(ZERO):
                log.error(
                    f'Could not calculate price for {lp_token} due to inability to '
                    f'fetch price for {token}.', )
                return None
            prices.append(price)

        # Query virtual price of LP share and balances in the pool for each token
        contract = EthereumContract(
            address=pool.pool_address,
            abi=CURVE_POOL_ABI,
            deployed_block=0,
        )
        calls = [(pool.pool_address,
                  contract.encode(method_name='get_virtual_price'))]
        calls += [(pool.pool_address,
                   contract.encode(method_name='balances', arguments=[i]))
                  for i in range(len(pool.assets))]
        output = multicall_2(
            ethereum=self._ethereum,
            require_success=False,
            calls=calls,
        )

        # Check that the output has the correct structure
        if not all([len(call_result) == 2 for call_result in output]):
            log.debug(
                f'Failed to query contract methods while finding curve pool price. '
                f'Not every outcome has length 2. {output}', )
            return None
        # Check that all the requests were successful
        if not all([contract_output[0] for contract_output in output]):
            log.debug(
                f'Failed to query contract methods while finding curve price. {output}'
            )
            return None
        # Deserialize information obtained in the multicall execution
        data = []
        # https://github.com/PyCQA/pylint/issues/4739
        virtual_price_decoded = contract.decode(output[0][1],
                                                'get_virtual_price')  # pylint: disable=unsubscriptable-object  # noqa: E501
        if not _check_curve_contract_call(virtual_price_decoded):
            log.debug(
                f'Failed to decode get_virtual_price while finding curve price. {output}'
            )
            return None
        data.append(FVal(virtual_price_decoded[0]))  # pylint: disable=unsubscriptable-object
        for i in range(len(pool.assets)):
            amount_decoded = contract.decode(output[i + 1][1],
                                             'balances',
                                             arguments=[i])
            if not _check_curve_contract_call(amount_decoded):
                log.debug(
                    f'Failed to decode balances {i} while finding curve price. {output}'
                )
                return None
            # https://github.com/PyCQA/pylint/issues/4739
            amount = amount_decoded[0]  # pylint: disable=unsubscriptable-object
            normalized_amount = token_normalized_value_decimals(
                amount, tokens[i].decimals)
            data.append(normalized_amount)

        # Prices and data should verify this relation for the following operations
        if len(prices) != len(data) - 1:
            log.debug(
                f'Length of prices {len(prices)} does not match len of data {len(data)} '
                f'while querying curve pool price.', )
            return None
        # Total number of assets price in the pool
        total_assets_price = sum(map(operator.mul, data[1:], prices))
        if total_assets_price == 0:
            log.error(
                f'Curve pool price returned unexpected data {data} that lead to a zero price.',
            )
            return None

        # Calculate weight of each asset as the proportion of tokens value
        weights = map(lambda x: data[x + 1] * prices[x] / total_assets_price,
                      range(len(tokens)))
        assets_price = FVal(sum(map(operator.mul, weights, prices)))
        return (assets_price * FVal(data[0])) / (10**lp_token.decimals)
Esempio n. 15
0
    def find_uniswap_v2_lp_price(
        self,
        token: EthereumToken,
    ) -> Optional[Price]:
        """
        Calculate the price for a uniswap v2 LP token. That is
        value = (Total value of liquidity pool) / (Current suply of LP tokens)
        We need:
        - Price of token 0
        - Price of token 1
        - Pooled amount of token 0
        - Pooled amount of token 1
        - Total supply of of pool token
        """
        assert self._ethereum is not None, 'Inquirer ethereum manager should have been initialized'  # noqa: E501

        address = token.ethereum_address
        contract = EthereumContract(address=address,
                                    abi=UNISWAP_V2_LP_ABI,
                                    deployed_block=0)
        methods = [
            'token0', 'token1', 'totalSupply', 'getReserves', 'decimals'
        ]
        try:
            output = multicall_2(
                ethereum=self._ethereum,
                require_success=True,
                calls=[(address, contract.encode(method_name=method))
                       for method in methods],
            )
        except RemoteError as e:
            log.error(
                f'Remote error calling multicall contract for uniswap v2 lp '
                f'token {token.ethereum_address} properties: {str(e)}', )
            return None

        # decode output
        decoded = []
        for (method_output, method_name) in zip(output, methods):
            if method_output[0] and len(method_output[1]) != 0:
                decoded_method = contract.decode(method_output[1], method_name)
                if len(decoded_method) == 1:
                    # https://github.com/PyCQA/pylint/issues/4739
                    decoded.append(decoded_method[0])  # pylint: disable=unsubscriptable-object
                else:
                    decoded.append(decoded_method)
            else:
                log.debug(
                    f'Multicall to Uniswap V2 LP failed to fetch field {method_name} '
                    f'for token {token.ethereum_address}', )
                return None

        try:
            token0 = EthereumToken(decoded[0])
            token1 = EthereumToken(decoded[1])
        except UnknownAsset:
            return None

        try:
            token0_supply = FVal(decoded[3][0] * 10**-token0.decimals)
            token1_supply = FVal(decoded[3][1] * 10**-token1.decimals)
            total_supply = FVal(decoded[2] * 10**-decoded[4])
        except ValueError as e:
            log.debug(
                f'Failed to deserialize token amounts for token {address} '
                f'with values {str(decoded)}. f{str(e)}', )
            return None
        token0_price = self.find_usd_price(token0)
        token1_price = self.find_usd_price(token1)

        if ZERO in (token0_price, token1_price):
            log.debug(
                f'Couldnt retrieve non zero price information for tokens {token0}, {token1} '
                f'with result {token0_price}, {token1_price}', )
        numerator = (token0_supply * token0_price +
                     token1_supply * token1_price)
        share_value = numerator / total_supply
        return Price(share_value)
Esempio n. 16
0
    def get_positions(
        self,
        addresses_list: List[ChecksumEthAddress],
    ) -> Dict[ChecksumEthAddress, Trove]:
        contract = EthereumContract(
            address=LIQUITY_TROVE_MANAGER.address,
            abi=LIQUITY_TROVE_MANAGER.abi,
            deployed_block=LIQUITY_TROVE_MANAGER.deployed_block,
        )
        # make a copy of the list to avoid modifications in the list that is passed as argument
        addresses = list(addresses_list)
        proxied_addresses = self._get_accounts_having_proxy()
        proxies_to_address = {v: k for k, v in proxied_addresses.items()}
        addresses += proxied_addresses.values()

        calls = [(LIQUITY_TROVE_MANAGER.address,
                  contract.encode(method_name='Troves', arguments=[x]))
                 for x in addresses]
        outputs = multicall_2(
            ethereum=self.ethereum,
            require_success=False,
            calls=calls,
        )

        data: Dict[ChecksumEthAddress, Trove] = {}
        eth_price = Inquirer().find_usd_price(A_ETH)
        lusd_price = Inquirer().find_usd_price(A_LUSD)
        for idx, output in enumerate(outputs):
            status, result = output
            if status is True:
                try:
                    trove_info = contract.decode(result,
                                                 'Troves',
                                                 arguments=[addresses[idx]])
                    trove_is_active = bool(trove_info[3])  # pylint: disable=unsubscriptable-object
                    if not trove_is_active:
                        continue
                    collateral = deserialize_asset_amount(
                        token_normalized_value_decimals(trove_info[1], 18),  # noqa: E501 pylint: disable=unsubscriptable-object
                    )
                    debt = deserialize_asset_amount(
                        token_normalized_value_decimals(trove_info[0], 18),  # noqa: E501 pylint: disable=unsubscriptable-object
                    )
                    collateral_balance = AssetBalance(
                        asset=A_ETH,
                        balance=Balance(
                            amount=collateral,
                            usd_value=eth_price * collateral,
                        ),
                    )
                    debt_balance = AssetBalance(
                        asset=A_LUSD,
                        balance=Balance(
                            amount=debt,
                            usd_value=lusd_price * debt,
                        ),
                    )
                    # Avoid division errors
                    collateralization_ratio: Optional[FVal]
                    liquidation_price: Optional[FVal]
                    if debt > 0:
                        collateralization_ratio = eth_price * collateral / debt * 100
                    else:
                        collateralization_ratio = None
                    if collateral > 0:
                        liquidation_price = debt * lusd_price * FVal(
                            MIN_COLL_RATE) / collateral
                    else:
                        liquidation_price = None

                    account_address = addresses[idx]
                    if account_address in proxies_to_address:
                        account_address = proxies_to_address[account_address]
                    data[account_address] = Trove(
                        collateral=collateral_balance,
                        debt=debt_balance,
                        collateralization_ratio=collateralization_ratio,
                        liquidation_price=liquidation_price,
                        active=trove_is_active,
                        trove_id=trove_info[4],  # pylint: disable=unsubscriptable-object
                    )
                except DeserializationError as e:
                    self.msg_aggregator.add_warning(
                        f'Ignoring Liquity trove information. '
                        f'Failed to decode contract information. {str(e)}.', )
        return data
Esempio n. 17
0
def _encode_ens_contract(params: EnsContractParams) -> str:
    contract = EthereumContract(address=params.address, abi=params.abi, deployed_block=0)
    return contract.encode(method_name=params.method_name, arguments=params.arguments)
Esempio n. 18
0
def find_uniswap_v2_lp_price(
    ethereum: 'EthereumManager',
    token: EthereumToken,
    token_price_func: Callable,
    token_price_func_args: List[Any],
    block_identifier: BlockIdentifier,
) -> Optional[Price]:
    """
    Calculate the price for a uniswap v2 LP token. That is
    value = (Total value of liquidity pool) / (Current suply of LP tokens)
    We need:
    - Price of token 0
    - Price of token 1
    - Pooled amount of token 0
    - Pooled amount of token 1
    - Total supply of of pool token
    """
    address = token.ethereum_address
    contract = EthereumContract(address=address,
                                abi=UNISWAP_V2_LP_ABI,
                                deployed_block=0)
    methods = ['token0', 'token1', 'totalSupply', 'getReserves', 'decimals']
    multicall_method = multicall_2  # choose which multicall to use
    if isinstance(block_identifier, int):
        if block_identifier <= 7929876:
            log.error(
                f'No multicall contract at the block {block_identifier}. Uniswap v2 LP '
                f'query failed. Should implement direct queries', )
            return None

        if block_identifier <= 12336033:
            multicall_method = multicall

    try:
        output = multicall_method(
            ethereum=ethereum,
            require_success=True,
            calls=[(address, contract.encode(method_name=method))
                   for method in methods],
            block_identifier=block_identifier,
        )
    except RemoteError as e:
        log.error(
            f'Remote error calling multicall contract for uniswap v2 lp '
            f'token {token.ethereum_address} properties: {str(e)}', )
        return None

    # decode output
    decoded = []
    for (method_output, method_name) in zip(output, methods):
        call_success = True
        if multicall_method == multicall_2:
            call_success = method_output[0]
            call_result = method_output[1]
        else:
            call_result = method_output  # type: ignore
        if call_success and len(call_result) != 0:
            decoded_method = contract.decode(call_result, method_name)
            if len(decoded_method) == 1:
                # https://github.com/PyCQA/pylint/issues/4739
                decoded.append(decoded_method[0])  # pylint: disable=unsubscriptable-object
            else:
                decoded.append(decoded_method)
        else:
            log.debug(
                f'Multicall to Uniswap V2 LP failed to fetch field {method_name} '
                f'for token {token.ethereum_address}', )
            return None

    try:
        token0 = EthereumToken(decoded[0])
        token1 = EthereumToken(decoded[1])
    except UnknownAsset:
        return None

    try:
        token0_supply = FVal(decoded[3][0] * 10**-token0.decimals)
        token1_supply = FVal(decoded[3][1] * 10**-token1.decimals)
        total_supply = FVal(decoded[2] * 10**-decoded[4])
    except ValueError as e:
        log.debug(
            f'Failed to deserialize token amounts for token {address} '
            f'with values {str(decoded)}. f{str(e)}', )
        return None
    token0_price = token_price_func(token0, *token_price_func_args)
    token1_price = token_price_func(token1, *token_price_func_args)

    if ZERO in (token0_price, token1_price):
        log.debug(
            f'Couldnt retrieve non zero price information for tokens {token0}, {token1} '
            f'with result {token0_price}, {token1_price}', )
    numerator = (token0_supply * token0_price + token1_supply * token1_price)
    share_value = numerator / total_supply
    return Price(share_value)