예제 #1
0
def encode_abi(w3: "Web3",
               abi: ABIFunction,
               arguments: Sequence[Any],
               data: Optional[HexStr] = None) -> HexStr:
    argument_types = get_abi_input_types(abi)

    if not check_if_arguments_can_be_encoded(abi, w3.codec, arguments, {}):
        raise TypeError(
            "One or more arguments could not be encoded to the necessary "
            f"ABI type.  Expected types are: {', '.join(argument_types)}")

    normalizers = [
        abi_ens_resolver(w3),
        abi_address_to_hex,
        abi_bytes_to_bytes,
        abi_string_to_text,
    ]
    normalized_arguments = map_abi_data(
        normalizers,
        argument_types,
        arguments,
    )
    encoded_arguments = w3.codec.encode_abi(
        argument_types,
        normalized_arguments,
    )

    if data:
        return to_hex(HexBytes(data) + encoded_arguments)
    else:
        return encode_hex(encoded_arguments)
예제 #2
0
def encode_abi(web3, abi, arguments, data=None):
    argument_types = get_abi_input_types(abi)

    if not check_if_arguments_can_be_encoded(abi, arguments, {}):
        raise TypeError(
            "One or more arguments could not be encoded to the necessary "
            "ABI type.  Expected types are: {0}".format(
                ', '.join(argument_types), ))

    normalizers = [
        abi_ens_resolver(web3),
        abi_address_to_hex,
        abi_bytes_to_bytes,
        abi_string_to_text,
    ]
    normalized_arguments = map_abi_data(
        normalizers,
        argument_types,
        arguments,
    )
    encoded_arguments = eth_abi_encode_abi(
        argument_types,
        normalized_arguments,
    )

    if data:
        return to_hex(HexBytes(data) + encoded_arguments)
    else:
        return encode_hex(encoded_arguments)
예제 #3
0
    def _decode_transaction(self, data: Union[bytes, str]) -> Tuple[str, List[Tuple[str, str, Any]]]:
        """
        Decode tx data
        :param data: Tx data as `hex string` or `bytes`
        :return: Tuple with the `function name` and a List of sorted tuples with
        the `name` of the argument, `type` and `value`
        :raises: CannotDecode if data cannot be decoded. You should catch this exception when using this function
        :raises: UnexpectedProblemDecoding if there's an unexpected problem decoding (it shouldn't happen)
        """

        if not data:
            raise CannotDecode(data)

        data = HexBytes(data)
        selector, params = data[:4], data[4:]
        if selector not in self.supported_fn_selectors:
            raise CannotDecode(data.hex())

        try:
            contract_fn = self.supported_fn_selectors[selector]
            names = get_abi_input_names(contract_fn.abi)
            types = get_abi_input_types(contract_fn.abi)
            decoded = self.dummy_w3.codec.decode_abi(types, cast(HexBytes, params))
            normalized = map_abi_data(BASE_RETURN_NORMALIZERS, types, decoded)
            values = map(self._parse_decoded_arguments, normalized)
        except ValueError as exc:
            raise UnexpectedProblemDecoding from exc

        return contract_fn.fn_name, list(zip(names, types, values))
예제 #4
0
def test_finds_function_with_matching_args(web3, arguments, expected_types):
    Contract = web3.eth.contract(abi=MULTIPLE_FUNCTIONS)

    abi = Contract._find_matching_fn_abi('a', arguments)
    assert abi['name'] == 'a'
    assert len(abi['inputs']) == len(expected_types)
    assert set(get_abi_input_types(abi)) == set(expected_types)
def test_finds_function_with_matching_args(web3, arguments, expected_types):
    Contract = web3.eth.contract(abi=MULTIPLE_FUNCTIONS)

    abi = Contract._find_matching_fn_abi('a', arguments)
    assert abi['name'] == 'a'
    assert len(abi['inputs']) == len(expected_types)
    assert set(get_abi_input_types(abi)) == set(expected_types)
예제 #6
0
def test_finds_function_with_matching_args_deprecation_warning(w3):
    Contract = w3.eth.contract(abi=MULTIPLE_FUNCTIONS)

    with pytest.warns(DeprecationWarning):
        abi = Contract._find_matching_fn_abi('a', [''])
        assert abi['name'] == 'a'
        assert len(abi['inputs']) == len(['bytes32'])
        assert get_abi_input_types(abi) == ['bytes32']
예제 #7
0
def decode_log(log_data, events):
    topic_inputs = get_indexed_event_inputs(log_data['abi'])
    topic_types = get_abi_input_types({'inputs': topic_inputs})
    topic_names = get_abi_input_names({'inputs': topic_inputs})
    topic_data = [HexBytes(topic) for topic in log_data['topics'][1:]]
    decoded_topics = [
        decode_single(topic_type, topic_data)
        for topic_type, topic_data in zip(topic_types, topic_data)
    ]
    data_inputs = exclude_indexed_event_inputs(log_data['abi'])
    data_types = get_abi_input_types({'inputs': data_inputs})
    data_names = get_abi_input_names({'inputs': data_inputs})
    decoded_data = decode_abi(data_types, HexBytes(log_data['data']))
    return dict(
        itertools.chain(
            zip(topic_names, decoded_topics),
            zip(data_names, decoded_data),
        ))
    def decode_function_input(self, data):
        data = HexBytes(data)
        selector, params = data[:4], data[4:]
        func = self.get_function_by_selector(selector)

        names = get_abi_input_names(func.abi)
        types = get_abi_input_types(func.abi)

        decoded = self.web3.codec.decode_abi(types, params)
        normalized = map_abi_data(BASE_RETURN_NORMALIZERS, types, decoded)

        return func, dict(zip(names, normalized))
예제 #9
0
def decode_func_with_fallback(abis_to_try, data):
    for abi in abis_to_try:
        try:
            selector, params = data[:4], data[4:]
            names = get_abi_input_names(abi)
            types = get_abi_input_types(abi)
            decoded = w3.codec.decode_abi(types, params)
            normalized = map_abi_data(BASE_RETURN_NORMALIZERS, types, decoded)
            return abi['name'], dict(zip(names, normalized))
        except DecodingError:
            logger.debug('trying fallback fn input decoder')
    raise DecodingError('could not decode fn input')
예제 #10
0
def encode_abi(web3, abi, arguments, data=None):
    argument_types = get_abi_input_types(abi)

    if not check_if_arguments_can_be_encoded(abi, arguments, {}):
        raise TypeError(
            "One or more arguments could not be encoded to the necessary "
            "ABI type.  Expected types are: {0}".format(
                ', '.join(argument_types),
            )
        )

    try:
        normalizers = [
            abi_ens_resolver(web3),
            abi_address_to_hex,
            abi_bytes_to_bytes,
            abi_string_to_text,
        ]
        normalized_arguments = map_abi_data(
            normalizers,
            argument_types,
            arguments,
        )
        encoded_arguments = eth_abi_encode_abi(
            argument_types,
            normalized_arguments,
        )
    except EncodingError as e:
        raise TypeError(
            "One or more arguments could not be encoded to the necessary "
            "ABI type: {0}".format(str(e))
        )

    if data:
        return to_hex(HexBytes(data) + encoded_arguments)
    else:
        return encode_hex(encoded_arguments)
예제 #11
0
    def _decode_data(
        self,
        data: Union[bytes, str],
        address: Optional[ChecksumAddress] = None
    ) -> Tuple[str, List[Tuple[str, str, Any]]]:
        """
        Decode tx data

        :param data: Tx data as `hex string` or `bytes`
        :param address: contract address in case of ABI colliding
        :return: Tuple with the `function name` and a List of sorted tuples with
            the `name` of the argument, `type` and `value`
        :raises: CannotDecode if data cannot be decoded. You should catch this exception when using this function
        :raises: UnexpectedProblemDecoding if there's an unexpected problem decoding (it shouldn't happen)
        """

        if not data:
            raise CannotDecode(data)

        data = HexBytes(data)
        params = data[4:]
        fn_abi = self.get_abi_function(data, address)
        if not fn_abi:
            raise CannotDecode(data.hex())
        try:
            names = get_abi_input_names(fn_abi)
            types = get_abi_input_types(fn_abi)
            decoded = self.dummy_w3.codec.decode_abi(types,
                                                     cast(HexBytes, params))
            normalized = map_abi_data(BASE_RETURN_NORMALIZERS, types, decoded)
            values = map(self._parse_decoded_arguments, normalized)
        except (ValueError, DecodingError) as exc:
            logger.warning("Cannot decode %s", data.hex())
            raise UnexpectedProblemDecoding(data) from exc

        return fn_abi["name"], list(zip(names, types, values))
예제 #12
0
    def mock_requests_get(url, *args, **kwargs):
        if 'etherscan.io/api?module=account&action=balance&address' in url:
            addr = url[67:109]
            value = eth_map[addr].get('ETH', '0')
            response = f'{{"status":"1","message":"OK","result":{value}}}'

        elif 'etherscan.io/api?module=account&action=balancemulti' in url:
            queried_accounts = []
            length = 72
            # process url and get the accounts
            while True:
                if len(url) < length:
                    break
                potential_address = url[length:length + 42]
                if 'apikey=' in potential_address:
                    break
                queried_accounts.append(potential_address)
                length += 43

            accounts = []
            for addr in queried_accounts:
                value = eth_map[addr].get('ETH', '0')
                accounts.append({'account': addr, 'balance': eth_map[addr]['ETH']})
            response = f'{{"status":"1","message":"OK","result":{json.dumps(accounts)}}}'

        elif 'api.etherscan.io/api?module=account&action=tokenbalance' in url:
            token_address = url[80:122]
            msg = 'token address missing from test mapping'
            assert token_address in CONTRACT_ADDRESS_TO_TOKEN, msg
            response = '{"status":"1","message":"OK","result":"0"}'
            token = CONTRACT_ADDRESS_TO_TOKEN[token_address]
            account = url[131:173]
            value = eth_map[account].get(token.identifier, 0)
            response = f'{{"status":"1","message":"OK","result":"{value}"}}'
        elif 'api.etherscan.io/api?module=account&action=txlistinternal&' in url:
            if 'transactions' in original_queries:
                return original_requests_get(url, *args, **kwargs)
            # By default when mocking, don't query for transactions
            response = '{"status":"1","message":"OK","result":[]}'
        elif 'api.etherscan.io/api?module=account&action=txlist&' in url:
            if 'transactions' in original_queries:
                return original_requests_get(url, *args, **kwargs)
            # By default when mocking, don't query for transactions
            response = '{"status":"1","message":"OK","result":[]}'
        elif 'api.etherscan.io/api?module=logs&action=getLogs&' in url:
            if 'logs' in original_queries:
                return original_requests_get(url, *args, **kwargs)
            # By default when mocking, don't query logs
            response = '{"status":"1","message":"OK","result":[]}'
        elif 'api.etherscan.io/api?module=block&action=getblocknobytime&' in url:
            if 'blocknobytime' in original_queries:
                return original_requests_get(url, *args, **kwargs)
            # By default when mocking don't query blocknobytime
            response = '{"status":"1","message":"OK","result":"1"}'
        elif f'api.etherscan.io/api?module=proxy&action=eth_call&to={ZERION_ADAPTER_ADDRESS}' in url:  # noqa: E501
            if 'zerion' in original_queries:
                return original_requests_get(url, *args, **kwargs)

            web3 = Web3()
            contract = web3.eth.contract(address=ZERION_ADAPTER_ADDRESS, abi=ZERION_ABI)
            if 'data=0xc84aae17' in url:  # getBalances
                data = url.split('data=')[1]
                if '&apikey' in data:
                    data = data.split('&apikey')[0]

                fn_abi = contract._find_matching_fn_abi(
                    fn_identifier='getBalances',
                    args=['address'],
                )
                input_types = get_abi_input_types(fn_abi)
                output_types = get_abi_output_types(fn_abi)
                decoded_input = web3.codec.decode_abi(input_types, bytes.fromhex(data[10:]))
                # TODO: This here always returns empty response. If/when we want to
                # mock it for etherscan, this is where we do it
                args = []
                result = '0x' + web3.codec.encode_abi(output_types, [args]).hex()
                response = f'{{"jsonrpc":"2.0","id":1,"result":"{result}"}}'
            elif 'data=0x85c6a7930' in url:  # getProtocolBalances
                data = url.split('data=')[1]
                if '&apikey' in data:
                    data = data.split('&apikey')[0]

                fn_abi = contract._find_matching_fn_abi(
                    fn_identifier='getProtocolBalances',
                    args=['address', ['some', 'protocol', 'names']],
                )
                input_types = get_abi_input_types(fn_abi)
                output_types = get_abi_output_types(fn_abi)
                decoded_input = web3.codec.decode_abi(input_types, bytes.fromhex(data[10:]))
                # TODO: This here always returns empty response. If/when we want to
                # mock it for etherscan, this is where we do it
                args = []
                result = '0x' + web3.codec.encode_abi(output_types, [args]).hex()
                response = f'{{"jsonrpc":"2.0","id":1,"result":"{result}"}}'
            elif 'data=0x3b692f52' in url:  # getProtocolNames
                data = url.split('data=')[1]
                if '&apikey' in data:
                    data = data.split('&apikey')[0]

                fn_abi = contract._find_matching_fn_abi(
                    fn_identifier='getProtocolNames',
                )
                input_types = get_abi_input_types(fn_abi)
                output_types = get_abi_output_types(fn_abi)
                decoded_input = web3.codec.decode_abi(input_types, bytes.fromhex(data[10:]))
                # TODO: This here always returns empty response. If/when we want to
                # mock it for etherscan, this is where we do it
                args = []
                result = '0x' + web3.codec.encode_abi(output_types, [args]).hex()
                response = f'{{"jsonrpc":"2.0","id":1,"result":"{result}"}}'
            else:
                raise AssertionError(f'Unexpected etherscan call during tests: {url}')
        elif 'api.etherscan.io/api?module=proxy&action=eth_call&to=0xB6456b57f03352bE48Bf101B46c1752a0813491a' in url:  # noqa: E501  # ADEX Staking contract
            if 'adex_staking' in original_queries:
                return original_requests_get(url, *args, **kwargs)

            if 'data=0x447b15f4' in url:  # a mocked share value
                response = '{"jsonrpc":"2.0","id":1,"result":"0x0000000000000000000000000000000000000000000000000fc4a48782d85b51"}'  # noqa: E501
            else:
                raise AssertionError(f'Unknown call to Adex Staking pool during tests: {url}')
        elif f'api.etherscan.io/api?module=proxy&action=eth_call&to={ETH_MULTICALL.address}' in url:  # noqa: E501
            web3 = Web3()
            contract = web3.eth.contract(address=ETH_MULTICALL.address, abi=ETH_MULTICALL.abi)
            if 'b6456b57f03352be48bf101b46c1752a0813491a' in url:
                multicall_purpose = 'adex_staking'
            elif '5f3b5dfeb7b28cdbd7faba78963ee202a494e2a2' in url:
                multicall_purpose = 'vecrv'
            else:
                raise AssertionError('Unknown multicall in mocked tests')
            if 'data=0x252dba42' in url:  # aggregate
                if multicall_purpose == 'adex_staking':
                    if 'adex_staking' in original_queries:
                        return original_requests_get(url, *args, **kwargs)

                    if 'mocked_adex_staking_balance' in extra_flags:
                        # mock adex staking balance for a single account
                        response = '{"jsonrpc": "2.0", "id": 1, "result": "0x0000000000000000000000000000000000000000000000000000000000bb45aa000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000152982285d2e4d5aeaa9"}'  # noqa: E501
                        return MockResponse(200, response)

                data = url.split('data=')[1]
                if '&apikey' in data:
                    data = data.split('&apikey')[0]

                fn_abi = contract.functions.abi[1]
                assert fn_abi['name'] == 'aggregate', 'Abi position of multicall aggregate changed'
                input_types = get_abi_input_types(fn_abi)
                output_types = get_abi_output_types(fn_abi)
                decoded_input = web3.codec.decode_abi(input_types, bytes.fromhex(data[10:]))
                # For now the only mocked multicall is 32 bytes for multicall balance
                # of both veCRV and adex staking pool.
                # When there is more we have to figure out a way to differentiate
                # between them in mocking. Just return empty response here
                # all pylint ignores below due to https://github.com/PyCQA/pylint/issues/4114
                args = [1, [b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' for x in decoded_input[0]]]  # pylint: disable=unsubscriptable-object  # noqa: E501
                result = '0x' + web3.codec.encode_abi(output_types, args).hex()
                response = f'{{"jsonrpc":"2.0","id":1,"result":"{result}"}}'
            else:
                raise AssertionError('Unexpected etherscan multicall during tests: {url}')

        elif f'api.etherscan.io/api?module=proxy&action=eth_call&to={ETH_SCAN.address}' in url:
            if 'ethscan' in original_queries:
                return original_requests_get(url, *args, **kwargs)

            web3 = Web3()
            contract = web3.eth.contract(address=ETH_SCAN.address, abi=ETH_SCAN.abi)
            if 'data=0xdbdbb51b' in url:  # Eth balance query
                data = url.split('data=')[1]
                if '&apikey' in data:
                    data = data.split('&apikey')[0]

                fn_abi = contract._find_matching_fn_abi(
                    fn_identifier='etherBalances',
                    args=[list(eth_map.keys())],
                )
                input_types = get_abi_input_types(fn_abi)
                output_types = get_abi_output_types(fn_abi)
                decoded_input = web3.codec.decode_abi(input_types, bytes.fromhex(data[10:]))
                args = []
                for account_address in decoded_input[0]:  # pylint: disable=unsubscriptable-object  # noqa: E501
                    account_address = deserialize_ethereum_address(account_address)
                    args.append(int(eth_map[account_address]['ETH']))
                result = '0x' + web3.codec.encode_abi(output_types, [args]).hex()
                response = f'{{"jsonrpc":"2.0","id":1,"result":"{result}"}}'
            elif 'data=0x06187b4f' in url:  # Multi token multiaddress balance query
                data = url.split('data=')[1]
                if '&apikey' in data:
                    data = data.split('&apikey')[0]
                # not really the given args, but we just want the fn abi
                args = [list(eth_map.keys()), list(eth_map.keys())]
                fn_abi = contract._find_matching_fn_abi(
                    fn_identifier='tokensBalances',
                    args=args,
                )
                input_types = get_abi_input_types(fn_abi)
                output_types = get_abi_output_types(fn_abi)
                decoded_input = web3.codec.decode_abi(input_types, bytes.fromhex(data[10:]))
                args = []
                for account_address in decoded_input[0]:  # pylint: disable=unsubscriptable-object  # noqa: E501
                    account_address = deserialize_ethereum_address(account_address)
                    x = []
                    for token_address in decoded_input[1]:  # pylint: disable=unsubscriptable-object  # noqa: E501
                        token_address = deserialize_ethereum_address(token_address)
                        value_to_add = 0
                        for given_asset, value in eth_map[account_address].items():
                            given_asset = _get_token(given_asset)
                            if given_asset is None:
                                # not a token
                                continue
                            if token_address != given_asset.ethereum_address:
                                continue
                            value_to_add = int(value)
                            break
                        x.append(value_to_add)
                    args.append(x)

                result = '0x' + web3.codec.encode_abi(output_types, [args]).hex()
                response = f'{{"jsonrpc":"2.0","id":1,"result":"{result}"}}'

            elif 'data=0xe5da1b68' in url:  # Multi token balance query
                data = url.split('data=')[1]
                if '&apikey' in data:
                    data = data.split('&apikey')[0]
                # not really the given args, but we just want the fn abi
                args = ['str', list(eth_map.keys())]
                fn_abi = contract._find_matching_fn_abi(
                    fn_identifier='tokensBalance',
                    args=args,
                )
                input_types = get_abi_input_types(fn_abi)
                output_types = get_abi_output_types(fn_abi)
                decoded_input = web3.codec.decode_abi(input_types, bytes.fromhex(data[10:]))
                args = []
                account_address = deserialize_ethereum_address(decoded_input[0])  # pylint: disable=unsubscriptable-object  # noqa: E501
                x = []
                for token_address in decoded_input[1]:  # pylint: disable=unsubscriptable-object  # noqa: E501
                    token_address = deserialize_ethereum_address(token_address)
                    value_to_add = 0
                    for given_asset, value in eth_map[account_address].items():
                        given_asset = _get_token(given_asset)
                        if given_asset is None:
                            # not a token
                            continue

                        if token_address != given_asset.ethereum_address:
                            continue
                        value_to_add = int(value)
                        break
                    args.append(value_to_add)

                result = '0x' + web3.codec.encode_abi(output_types, [args]).hex()
                response = f'{{"jsonrpc":"2.0","id":1,"result":"{result}"}}'
            else:
                raise AssertionError(f'Unexpected etherscan call during tests: {url}')

        else:
            return original_requests_get(url, *args, **kwargs)

        return MockResponse(200, response)
예제 #13
0
파일: blockchain.py 프로젝트: jbrit/rotki
    def mock_requests_get(url, *args, **kwargs):
        if 'etherscan.io/api?module=account&action=balance&address' in url:
            addr = url[67:109]
            value = eth_map[addr].get('ETH', '0')
            response = f'{{"status":"1","message":"OK","result":{value}}}'

        elif 'etherscan.io/api?module=account&action=balancemulti' in url:
            queried_accounts = []
            length = 72
            # process url and get the accounts
            while True:
                if len(url) < length:
                    break
                potential_address = url[length:length + 42]
                if 'apikey=' in potential_address:
                    break
                queried_accounts.append(potential_address)
                length += 43

            accounts = []
            for addr in queried_accounts:
                value = eth_map[addr].get('ETH', '0')
                accounts.append({
                    'account': addr,
                    'balance': eth_map[addr]['ETH']
                })
            response = f'{{"status":"1","message":"OK","result":{json.dumps(accounts)}}}'

        elif 'api.etherscan.io/api?module=account&action=tokenbalance' in url:
            token_address = url[80:122]
            msg = 'token address missing from test mapping'
            assert token_address in CONTRACT_ADDRESS_TO_TOKEN, msg
            response = '{"status":"1","message":"OK","result":"0"}'
            token = CONTRACT_ADDRESS_TO_TOKEN[token_address]
            account = url[131:173]
            value = eth_map[account].get(token.identifier, 0)
            response = f'{{"status":"1","message":"OK","result":"{value}"}}'

        elif f'api.etherscan.io/api?module=proxy&action=eth_call&to={ETH_SCAN_ADDRESS}' in url:
            web3 = Web3()
            contract = web3.eth.contract(address=ETH_SCAN_ADDRESS,
                                         abi=ETH_SCAN_ABI)
            if 'data=0xdbdbb51b' in url:  # Eth balance query
                data = url.split('data=')[1]
                if '&apikey' in data:
                    data = data.split('&apikey')[0]

                fn_abi = contract._find_matching_fn_abi(
                    fn_identifier='etherBalances',
                    args=[list(eth_map.keys())],
                )
                input_types = get_abi_input_types(fn_abi)
                output_types = get_abi_output_types(fn_abi)
                decoded_input = web3.codec.decode_abi(input_types,
                                                      bytes.fromhex(data[10:]))
                args = []
                for account_address in decoded_input[0]:
                    account_address = to_checksum_address(account_address)
                    args.append(int(eth_map[account_address]['ETH']))
                result = '0x' + web3.codec.encode_abi(output_types,
                                                      [args]).hex()
                response = f'{{"jsonrpc":"2.0","id":1,"result":"{result}"}}'
            elif 'data=0x06187b4f' in url:  # Multi token balance query
                data = url.split('data=')[1]
                if '&apikey' in data:
                    data = data.split('&apikey')[0]
                # not really the given args, but we just want the fn abi
                args = [list(eth_map.keys()), list(eth_map.keys())]
                fn_abi = contract._find_matching_fn_abi(
                    fn_identifier='tokensBalances',
                    args=args,
                )
                input_types = get_abi_input_types(fn_abi)
                output_types = get_abi_output_types(fn_abi)
                decoded_input = web3.codec.decode_abi(input_types,
                                                      bytes.fromhex(data[10:]))
                args = []
                for account_address in decoded_input[0]:
                    account_address = to_checksum_address(account_address)
                    x = []
                    for token_address in decoded_input[1]:
                        token_address = to_checksum_address(token_address)
                        value_to_add = 0
                        for given_asset, value in eth_map[
                                account_address].items():
                            if not isinstance(given_asset, EthereumToken):
                                continue
                            if token_address != given_asset.ethereum_address:
                                continue
                            value_to_add = int(value)
                            break
                        x.append(value_to_add)
                    args.append(x)

                result = '0x' + web3.codec.encode_abi(output_types,
                                                      [args]).hex()
                response = f'{{"jsonrpc":"2.0","id":1,"result":"{result}"}}'

            else:
                raise AssertionError(
                    f'Unexpected etherscan call during tests: {url}')

        else:
            return original_requests_get(url, *args, **kwargs)

        return MockResponse(200, response)
예제 #14
0
def parse_args_to_matching_types_for_function(args, function_abi):
    types = get_abi_input_types(function_abi)
    return [parse_arg_to_matching_type(arg, type) for arg, type in zip(args, types)]
예제 #15
0
파일: blockchain.py 프로젝트: zhiiker/rotki
    def mock_requests_get(url, *args, **kwargs):
        if 'etherscan.io/api?module=account&action=balance&address' in url:
            addr = url[67:109]
            value = eth_map[addr].get('ETH', '0')
            response = f'{{"status":"1","message":"OK","result":{value}}}'

        elif 'etherscan.io/api?module=account&action=balancemulti' in url:
            queried_accounts = []
            length = 72
            # process url and get the accounts
            while True:
                if len(url) < length:
                    break
                potential_address = url[length:length + 42]
                if 'apikey=' in potential_address:
                    break
                queried_accounts.append(potential_address)
                length += 43

            accounts = []
            for addr in queried_accounts:
                value = eth_map[addr].get('ETH', '0')
                accounts.append({
                    'account': addr,
                    'balance': eth_map[addr]['ETH']
                })
            response = f'{{"status":"1","message":"OK","result":{json.dumps(accounts)}}}'

        elif 'api.etherscan.io/api?module=account&action=tokenbalance' in url:
            token_address = url[80:122]
            msg = 'token address missing from test mapping'
            assert token_address in CONTRACT_ADDRESS_TO_TOKEN, msg
            response = '{"status":"1","message":"OK","result":"0"}'
            token = CONTRACT_ADDRESS_TO_TOKEN[token_address]
            account = url[131:173]
            value = eth_map[account].get(token.identifier, 0)
            response = f'{{"status":"1","message":"OK","result":"{value}"}}'
        elif 'api.etherscan.io/api?module=account&action=txlistinternal&' in url:
            # By default when mocking, don't query for transactions
            response = '{"status":"1","message":"OK","result":[]}'
        elif 'api.etherscan.io/api?module=account&action=txlist&' in url:
            # By default when mocking, don't query for transactions
            response = '{"status":"1","message":"OK","result":[]}'
        elif 'api.etherscan.io/api?module=logs&action=getLogs&' in url:
            if 'logs' in original_queries:
                return original_requests_get(url, *args, **kwargs)
            # By default when mocking, don't query logs
            response = '{"status":"1","message":"OK","result":[]}'
        elif 'api.etherscan.io/api?module=block&action=getblocknobytime&' in url:
            if 'blocknobytime' in original_queries:
                return original_requests_get(url, *args, **kwargs)
            # By default when mocking don't query blocknobytime
            response = '{"status":"1","message":"OK","result":"1"}'
        elif f'api.etherscan.io/api?module=proxy&action=eth_call&to={ZERION_ADAPTER_ADDRESS}' in url:  # noqa: E501
            if 'zerion' in original_queries:
                return original_requests_get(url, *args, **kwargs)

            web3 = Web3()
            contract = web3.eth.contract(address=ZERION_ADAPTER_ADDRESS,
                                         abi=ZERION_ABI)
            if 'data=0xc84aae17' in url:  # getBalances
                data = url.split('data=')[1]
                if '&apikey' in data:
                    data = data.split('&apikey')[0]

                fn_abi = contract._find_matching_fn_abi(
                    fn_identifier='getBalances',
                    args=['address'],
                )
                input_types = get_abi_input_types(fn_abi)
                output_types = get_abi_output_types(fn_abi)
                decoded_input = web3.codec.decode_abi(input_types,
                                                      bytes.fromhex(data[10:]))
                # TODO: This here always returns empty response. If/when we want to
                # mock it for etherscan, this is where we do it
                args = []
                result = '0x' + web3.codec.encode_abi(output_types,
                                                      [args]).hex()
                response = f'{{"jsonrpc":"2.0","id":1,"result":"{result}"}}'
            else:
                raise AssertionError(
                    f'Unexpected etherscan call during tests: {url}')
        elif f'api.etherscan.io/api?module=proxy&action=eth_call&to={ETH_SCAN.address}' in url:
            if 'ethscan' in original_queries:
                return original_requests_get(url, *args, **kwargs)

            web3 = Web3()
            contract = web3.eth.contract(address=ETH_SCAN.address,
                                         abi=ETH_SCAN.abi)
            if 'data=0xdbdbb51b' in url:  # Eth balance query
                data = url.split('data=')[1]
                if '&apikey' in data:
                    data = data.split('&apikey')[0]

                fn_abi = contract._find_matching_fn_abi(
                    fn_identifier='etherBalances',
                    args=[list(eth_map.keys())],
                )
                input_types = get_abi_input_types(fn_abi)
                output_types = get_abi_output_types(fn_abi)
                decoded_input = web3.codec.decode_abi(input_types,
                                                      bytes.fromhex(data[10:]))
                args = []
                for account_address in decoded_input[0]:
                    account_address = to_checksum_address(account_address)
                    args.append(int(eth_map[account_address]['ETH']))
                result = '0x' + web3.codec.encode_abi(output_types,
                                                      [args]).hex()
                response = f'{{"jsonrpc":"2.0","id":1,"result":"{result}"}}'
            elif 'data=0x06187b4f' in url:  # Multi token multiaddress balance query
                data = url.split('data=')[1]
                if '&apikey' in data:
                    data = data.split('&apikey')[0]
                # not really the given args, but we just want the fn abi
                args = [list(eth_map.keys()), list(eth_map.keys())]
                fn_abi = contract._find_matching_fn_abi(
                    fn_identifier='tokensBalances',
                    args=args,
                )
                input_types = get_abi_input_types(fn_abi)
                output_types = get_abi_output_types(fn_abi)
                decoded_input = web3.codec.decode_abi(input_types,
                                                      bytes.fromhex(data[10:]))
                args = []
                for account_address in decoded_input[0]:
                    account_address = to_checksum_address(account_address)
                    x = []
                    for token_address in decoded_input[1]:
                        token_address = to_checksum_address(token_address)
                        value_to_add = 0
                        for given_asset, value in eth_map[
                                account_address].items():
                            given_asset = _get_token(given_asset)
                            if given_asset is None:
                                # not a token
                                continue
                            if token_address != given_asset.ethereum_address:
                                continue
                            value_to_add = int(value)
                            break
                        x.append(value_to_add)
                    args.append(x)

                result = '0x' + web3.codec.encode_abi(output_types,
                                                      [args]).hex()
                response = f'{{"jsonrpc":"2.0","id":1,"result":"{result}"}}'

            elif 'data=0xe5da1b68' in url:  # Multi token balance query
                data = url.split('data=')[1]
                if '&apikey' in data:
                    data = data.split('&apikey')[0]
                # not really the given args, but we just want the fn abi
                args = ['str', list(eth_map.keys())]
                fn_abi = contract._find_matching_fn_abi(
                    fn_identifier='tokensBalance',
                    args=args,
                )
                input_types = get_abi_input_types(fn_abi)
                output_types = get_abi_output_types(fn_abi)
                decoded_input = web3.codec.decode_abi(input_types,
                                                      bytes.fromhex(data[10:]))
                args = []
                account_address = to_checksum_address(decoded_input[0])
                x = []
                for token_address in decoded_input[1]:
                    token_address = to_checksum_address(token_address)
                    value_to_add = 0
                    for given_asset, value in eth_map[account_address].items():
                        given_asset = _get_token(given_asset)
                        if given_asset is None:
                            # not a token
                            continue

                        if token_address != given_asset.ethereum_address:
                            continue
                        value_to_add = int(value)
                        break
                    args.append(value_to_add)

                result = '0x' + web3.codec.encode_abi(output_types,
                                                      [args]).hex()
                response = f'{{"jsonrpc":"2.0","id":1,"result":"{result}"}}'
            else:
                raise AssertionError(
                    f'Unexpected etherscan call during tests: {url}')

        else:
            return original_requests_get(url, *args, **kwargs)

        return MockResponse(200, response)