Closed Netherdrake closed 6 years ago
Calling an ERC20 balanceOf will blow up if balance is 0.
balanceOf
_______________________________ test_historic_view_token_balance _______________________________ web3 = <web3.main.Web3 object at 0x7f2029a5b860> address = '0xFBCe7c17608eBd5640313eCf4d2ff09B6726bAB9', normalizers = () function_identifier = 'balanceOf' transaction = {'to': '0xFBCe7c17608eBd5640313eCf4d2ff09B6726bAB9'}, block_id = 5876634 contract_abi = [{'constant': True, 'inputs': [], 'name': 'totalSupply', 'outputs': [{'name': '', 'type': 'uint256'}], ...}, {'constan...s'}, {'name': 'guy', 'type': 'address'}], 'name': 'allowance', 'outputs': [{'name': '', 'type': 'uint256'}], ...}, ...] fn_abi = {'constant': True, 'inputs': [{'name': 'src', 'type': 'address'}], 'name': 'balanceOf', 'outputs': [{'name': '', 'type': 'uint256'}], ...} args = ('0xaAF3FFEE9d4C976aA8d0CB1bb84c3C90ee6E9118',), kwargs = {} call_transaction = {'data': '0x70a08231000000000000000000000000aaf3ffee9d4c976aa8d0cb1bb84c3c90ee6e9118', 'to': '0xFBCe7c17608eBd5640313eCf4d2ff09B6726bAB9'} return_data = HexBytes('0x'), output_types = ['uint256'], is_missing_code_error = False msg = "Could not decode contract function call balanceOf return data b'' for output_types ['uint256']" def call_contract_function( web3, address, normalizers, function_identifier, transaction, block_id=None, contract_abi=None, fn_abi=None, *args, **kwargs): """ Helper function for interacting with a contract function using the `eth_call` API. """ call_transaction = prepare_transaction( address, web3, fn_identifier=function_identifier, contract_abi=contract_abi, fn_abi=fn_abi, transaction=transaction, fn_args=args, fn_kwargs=kwargs, ) if block_id is None: return_data = web3.eth.call(call_transaction) else: return_data = web3.eth.call(call_transaction, block_identifier=block_id) if fn_abi is None: fn_abi = find_matching_fn_abi(contract_abi, function_identifier, args, kwargs) output_types = get_abi_output_types(fn_abi) try: > output_data = decode_abi(output_types, return_data) ../../../.pyenv/versions/3.6.5/envs/general/lib/python3.6/site-packages/web3/contract.py:1363: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ types = ['uint256'], data = HexBytes('0x') def decode_abi(types, data): if not is_bytes(data): raise TypeError("The `data` value must be of bytes type. Got {0}".format(type(data))) decoders = [ registry.get_decoder(type_str) for type_str in types ] decoder = TupleDecoder(decoders=decoders) stream = ContextFramesBytesIO(data) > return decoder(stream) ../../../.pyenv/versions/3.6.5/envs/general/lib/python3.6/site-packages/eth_abi/abi.py:96: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = <eth_abi.decoding.TupleDecoder object at 0x7f2033b002b0> stream = <eth_abi.decoding.ContextFramesBytesIO object at 0x7f2029cb2150> def __call__(self, stream): > return self.decode(stream) ../../../.pyenv/versions/3.6.5/envs/general/lib/python3.6/site-packages/eth_abi/decoding.py:118: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ args = (<eth_abi.decoding.TupleDecoder object at 0x7f2033b002b0>, <eth_abi.decoding.ContextFramesBytesIO object at 0x7f2029cb2150>) kwargs = {} @functools.wraps(fn) def inner(*args, **kwargs): > return callback(fn(*args, **kwargs)) ../../../.pyenv/versions/3.6.5/envs/general/lib/python3.6/site-packages/eth_utils/functional.py:22: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = <eth_abi.decoding.TupleDecoder object at 0x7f2033b002b0> stream = <eth_abi.decoding.ContextFramesBytesIO object at 0x7f2029cb2150> @to_tuple def decode(self, stream): for decoder in self.decoders: > yield decoder(stream) ../../../.pyenv/versions/3.6.5/envs/general/lib/python3.6/site-packages/eth_abi/decoding.py:164: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = <eth_abi.decoding.UnsignedIntegerDecoder object at 0x7f2029a73518> stream = <eth_abi.decoding.ContextFramesBytesIO object at 0x7f2029cb2150> def __call__(self, stream): > return self.decode(stream) ../../../.pyenv/versions/3.6.5/envs/general/lib/python3.6/site-packages/eth_abi/decoding.py:118: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = <eth_abi.decoding.UnsignedIntegerDecoder object at 0x7f2029a73518> stream = <eth_abi.decoding.ContextFramesBytesIO object at 0x7f2029cb2150> def decode(self, stream): > raw_data = self.read_data_from_stream(stream) ../../../.pyenv/versions/3.6.5/envs/general/lib/python3.6/site-packages/eth_abi/decoding.py:186: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = <eth_abi.decoding.UnsignedIntegerDecoder object at 0x7f2029a73518> stream = <eth_abi.decoding.ContextFramesBytesIO object at 0x7f2029cb2150> def read_data_from_stream(self, stream): data = stream.read(self.data_byte_size) if len(data) != self.data_byte_size: raise InsufficientDataBytes( "Tried to read {0} bytes. Only got {1} bytes".format( self.data_byte_size, > len(data), ) ) E eth_abi.exceptions.InsufficientDataBytes: Tried to read 32 bytes. Only got 0 bytes ../../../.pyenv/versions/3.6.5/envs/general/lib/python3.6/site-packages/eth_abi/decoding.py:279: InsufficientDataBytes The above exception was the direct cause of the following exception: def test_historic_view_token_balance(): assert ETH_CHAIN == 'kovan', 'This test was designed for Kovan chain.' address = '0xaAF3FFEE9d4C976aA8d0CB1bb84c3C90ee6E9118' > assert view_token_balance(address, block_num=5876634) == 0 tests/core/test_eth.py:36: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ src/core/eth.py:101: in view_token_balance return instance.functions.balanceOf(address).call(block_identifier=block_num) ../../../.pyenv/versions/3.6.5/envs/general/lib/python3.6/site-packages/web3/contract.py:1106: in call **self.kwargs _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ web3 = <web3.main.Web3 object at 0x7f2029a5b860> address = '0xFBCe7c17608eBd5640313eCf4d2ff09B6726bAB9', normalizers = () function_identifier = 'balanceOf' transaction = {'to': '0xFBCe7c17608eBd5640313eCf4d2ff09B6726bAB9'}, block_id = 5876634 contract_abi = [{'constant': True, 'inputs': [], 'name': 'totalSupply', 'outputs': [{'name': '', 'type': 'uint256'}], ...}, {'constan...s'}, {'name': 'guy', 'type': 'address'}], 'name': 'allowance', 'outputs': [{'name': '', 'type': 'uint256'}], ...}, ...] fn_abi = {'constant': True, 'inputs': [{'name': 'src', 'type': 'address'}], 'name': 'balanceOf', 'outputs': [{'name': '', 'type': 'uint256'}], ...} args = ('0xaAF3FFEE9d4C976aA8d0CB1bb84c3C90ee6E9118',), kwargs = {} call_transaction = {'data': '0x70a08231000000000000000000000000aaf3ffee9d4c976aa8d0cb1bb84c3c90ee6e9118', 'to': '0xFBCe7c17608eBd5640313eCf4d2ff09B6726bAB9'} return_data = HexBytes('0x'), output_types = ['uint256'], is_missing_code_error = False msg = "Could not decode contract function call balanceOf return data b'' for output_types ['uint256']" def call_contract_function( web3, address, normalizers, function_identifier, transaction, block_id=None, contract_abi=None, fn_abi=None, *args, **kwargs): """ Helper function for interacting with a contract function using the `eth_call` API. """ call_transaction = prepare_transaction( address, web3, fn_identifier=function_identifier, contract_abi=contract_abi, fn_abi=fn_abi, transaction=transaction, fn_args=args, fn_kwargs=kwargs, ) if block_id is None: return_data = web3.eth.call(call_transaction) else: return_data = web3.eth.call(call_transaction, block_identifier=block_id) if fn_abi is None: fn_abi = find_matching_fn_abi(contract_abi, function_identifier, args, kwargs) output_types = get_abi_output_types(fn_abi) try: output_data = decode_abi(output_types, return_data) except DecodingError as e: # Provide a more helpful error message than the one provided by # eth-abi-utils is_missing_code_error = ( return_data in ACCEPTABLE_EMPTY_STRINGS and web3.eth.getCode(address) in ACCEPTABLE_EMPTY_STRINGS ) if is_missing_code_error: msg = ( "Could not transact with/call contract function, is contract " "deployed correctly and chain synced?" ) else: msg = ( "Could not decode contract function call {} return data {} for " "output_types {}".format( function_identifier, return_data, output_types ) ) > raise BadFunctionCallOutput(msg) from e E web3.exceptions.BadFunctionCallOutput: Could not decode contract function call balanceOf return data b'' for output_types ['uint256'] ../../../.pyenv/versions/3.6.5/envs/general/lib/python3.6/site-packages/web3/contract.py:1385: BadFunctionCallOutput
Actually, not an issue. I was mistakenly querying a block before erc20 creation.
What was wrong?
Calling an ERC20
balanceOf
will blow up if balance is 0.