[Ethereum] How exactly do you decode input data using web3.py using the decode_function_input

web3.py

My goal is to decode the input data placed in a transaction on Ethereum. My current example is to look at the MKR-ETH contract on Uniswap. Normally you can see how much ETH goes into a transaction, but when the ETH value is 0, it implies some MKR is being deposited in the contract for ETH. The goal is to know what amount of MKR is being sent hopefully be decoding the input being sent. I am able to pull some information from mkr-contract instance but fails to use the decode_function_input

#!/usr/bin/env python3

from web3 import Web3
import json

w3 = Web3(Web3.HTTPProvider("https://mainnet.infura.io/v3/4ac548169d6b4cf1ac1a92ed1b85767c", request_kwargs={'timeout': 60}))
mkr_token_address = '0x9f8F72aA9304c8B593d555F12eF6589cC3A579A2'
mkr_contract_address = '0x2C4Bd064b998838076fa341A83d007FC2FA50957'

data='[{"name": "Transfer", "inputs": [{"type": "address", "name": "_from", "indexed": true}, {"type": "address", "name": "_to", "indexed": true}, {"type": "uint256", "name": "_value", "indexed": false}], "anonymous": false, "type": "event"}, {"name": "Approval", "inputs": [{"type": "address", "name": "_owner", "indexed": true}, {"type": "address", "name": "_spender", "indexed": true}, {"type": "uint256", "name": "_value", "indexed": false}], "anonymous": false, "type": "event"}, {"name": "__init__", "outputs": [], "inputs": [{"type": "bytes32", "name": "_name"}, {"type": "bytes32", "name": "_symbol"}, {"type": "uint256", "name": "_decimals"}, {"type": "uint256", "name": "_supply"}], "constant": false, "payable": false, "type": "constructor"}, {"name": "deposit", "outputs": [], "inputs": [], "constant": false, "payable": true, "type": "function", "gas": 74279}, {"name": "withdraw", "outputs": [{"type": "bool", "name": "out"}], "inputs": [{"type": "uint256", "name": "_value"}], "constant": false, "payable": false, "type": "function", "gas": 108706}, {"name": "totalSupply", "outputs": [{"type": "uint256", "name": "out"}], "inputs": [], "constant": true, "payable": false, "type": "function", "gas": 543}, {"name": "balanceOf", "outputs": [{"type": "uint256", "name": "out"}], "inputs": [{"type": "address", "name": "_owner"}], "constant": true, "payable": false, "type": "function", "gas": 745}, {"name": "transfer", "outputs": [{"type": "bool", "name": "out"}], "inputs": [{"type": "address", "name": "_to"}, {"type": "uint256", "name": "_value"}], "constant": false, "payable": false, "type": "function", "gas": 74698}, {"name": "transferFrom", "outputs": [{"type": "bool", "name": "out"}], "inputs": [{"type": "address", "name": "_from"}, {"type": "address", "name": "_to"}, {"type": "uint256", "name": "_value"}], "constant": false, "payable": false, "type": "function", "gas": 110600}, {"name": "approve", "outputs": [{"type": "bool", "name": "out"}], "inputs": [{"type": "address", "name": "_spender"}, {"type": "uint256", "name": "_value"}], "constant": false, "payable": false, "type": "function", "gas": 37888}, {"name": "allowance", "outputs": [{"type": "uint256", "name": "out"}], "inputs": [{"type": "address", "name": "_owner"}, {"type": "address", "name": "_spender"}], "constant": true, "payable": false, "type": "function", "gas": 1025}, {"name": "name", "outputs": [{"type": "bytes32", "name": "out"}], "inputs": [], "constant": true, "payable": false, "type": "function", "gas": 723}, {"name": "symbol", "outputs": [{"type": "bytes32", "name": "out"}], "inputs": [], "constant": true, "payable": false, "type": "function", "gas": 753}, {"name": "decimals", "outputs": [{"type": "uint256", "name": "out"}], "inputs": [], "constant": true, "payable": false, "type": "function", "gas": 783}]'

abi = json.loads(data)

mkr_contract = w3.eth.contract(address=mkr_token_address, abi=abi)

amount_mkr_tokens = mkr_contract.functions.balanceOf(mkr_contract_address).call()
amount_eth_tokens = w3.eth.getBalance(mkr_contract_address)
print(amount_mkr_tokens/10**18)
print(amount_eth_tokens/10**18)
print(f'current ratio of eth to mkr is {amount_eth_tokens/amount_mkr_tokens}')
tx_hash = '0x127a3ab5de9177d924b4379810626f225f86987815e96a2b88175a74a3f16835'
transaction = w3.eth.getTransaction(tx_hash)
print(mkr_contract.decode_function_input(transaction.input))

The output of the code is:

3605.1715355318133
11960.30071335804
current ratio of eth to mkr is 3.317539982627686
Traceback (most recent call last):
  File "stack_exchange_example.py", line 23, in <module>
    print(mkr_contract.decode_function_input(transaction.input))
  File "/home/mitakash/anaconda3/envs/hummingbot/lib/python3.6/site-packages/web3/utils/decorators.py", line 14, in _wrapper
    return self.method(obj, *args, **kwargs)
  File "/home/mitakash/anaconda3/envs/hummingbot/lib/python3.6/site-packages/web3/contract.py", line 692, in decode_function_input
    func = self.get_function_by_selector(selector)
  File "/home/mitakash/anaconda3/envs/hummingbot/lib/python3.6/site-packages/web3/utils/decorators.py", line 14, in _wrapper
    return self.method(obj, *args, **kwargs)
  File "/home/mitakash/anaconda3/envs/hummingbot/lib/python3.6/site-packages/web3/contract.py", line 686, in get_function_by_selector
    return get_function_by_identifier(fns, 'selector')
  File "/home/mitakash/anaconda3/envs/hummingbot/lib/python3.6/site-packages/web3/contract.py", line 1541, in get_function_by_identifier
    'Could not find any function with matching {0}'.format(identifier)
ValueError: Could not find any function with matching selector

I don't understand what I am doing wrong. I am following the instructions from the following link:
https://web3py.readthedocs.io/en/stable/contracts.html#utils

I believe I don't fully understand how "contract" was created in the instructions. Any help would be appreciated!

Best Answer

Looking at your code, I think the problem is with the ABI that you are loading into the contract object.

Your data which you use for the abi appears to be from an ERC-20 contract. This is correct for the maker_token_address. However, when I look at that transaction on etherscan, it appears as though it is actually for a Uniswap MKR contract with the function signature of ethToTokenSwapOutput.

To fix this you will need a second contract object with the Uniswap ABI and address, then you should be able to decode that input.

Related Topic