Using ABI in Web3.py – How to Use ABI in Python When solc Doesn’t Work in Web3.py?

contract-invocationweb3.py

I have a smart contract that I deployed to the Ropsten test network with truffle (using Javascript). I want to be able to send transactions to it from my server, which is written in Python. I've been trying to follow: http://web3py.readthedocs.io/en/stable/web3.eth.html#web3.eth.Eth.contract

from web3 import Web3, HTTPProvider
import json

provider = HTTPProvider('https://ropsten.infura.io/<Redacted Key>')
w3 = Web3(provider)
assert w3.isConnected()

pdb.set_trace()
abi = str(<Here I am pasting my abi in.  How should I do this, since I didn't build the contract in Python?>
escrow = w3.eth.contract(address='0x8850259566e9d03a1524e35687db2c78d4003409', abi=abi)

… When I get to this line, the program crashes:

File "/usr/local/lib/python3.6/dist-packages/web3/eth.py", line 351, in contract
    ContractFactory = ContractFactoryClass.factory(self.web3, **kwargs)
  File "/usr/local/lib/python3.6/dist-packages/web3/contract.py", line 225, in factory
    normalizers=normalizers)
  File "/usr/local/lib/python3.6/dist-packages/web3/utils/datatypes.py", line 36, in __new__
    namespace)
  File "cytoolz/functoolz.pyx", line 236, in cytoolz.functoolz.curry.__call__
  File "cytoolz/functoolz.pyx", line 232, in cytoolz.functoolz.curry.__call__
  File "/usr/local/lib/python3.6/dist-packages/eth_utils/functional.py", line 22, in inner
    return callback(fn(*args, **kwargs))
  File "/usr/local/lib/python3.6/dist-packages/web3/utils/formatters.py", line 69, in apply_formatters_to_dict
    raise type(exc)("Could not format value %r as field %r" % (item, key)) from exc
TypeError: __init__() missing 2 required positional arguments: 'doc' and 'pos'

Best Answer

You need to provide the abi as a dictionary, not as a string. Since the ABI is json encoded, you need to parse it with json.loads or similar.

You should also read it directly from the contract json file that is generated by truffle. This makes sure you don't accidentally use an old ABI. Here is what I do:

from web3 import Web3, HTTPProvider
import json

with open("build/Contracts/YourContract.json") as f:
    info_json = json.load(f)
abi = info_json["abi"]

w3 = Web3(HTTPProvider("https://..."))
escrow = w3.eth.contract(address='0x8850259566e9d03a1524e35687db2c78d4003409', abi=abi)