What’s New in the Web3.py v4 Beta

Jason Carver
4 min readJan 25, 2018

--

The first beta of Web3.py version 4 was released in mid-November, with five more releases since. We are nearing a stable release. A major version bump means some backwards-incompatible changes, and some brand new features enabled by those changes. Read on for an overview.

How to Install

Can’t wait to play with it? Install now with pip. Pip won’t install beta versions by default, so use the --pre flag to get v4 now:

pip install --pre web3

Python ≥ 3.5

One of the more significant changes is that Python 3.5 is now required. Is your project still stuck on py2? Upgrade your code today. It will go faster than you think, with 2to3.

Python 3 provides many useful features and libraries, plus it disambiguates bytes and str , which paves the way for…

More Intuitive Argument and Return Types

Global Features

If you browse the functions that return a hex string in v3, you will find that most of them return a bytes-like object in v4. The HexBytes class in the example below is a subclass of the built-in bytes type, and so can be used everywhere that bytes is.

>>> from web3.auto import w3>>> block_hash = w3.eth.getBlock(4359745).hash
HexBytes('0x03087766bf68e78671d1ea436ae087da74a12761dac020011a9eddc4900bf13b')
# get the first byte:
>>> block_hash[0]
3
# show how many bytes are in the hash
>>> len(block_hash)
32
# hex-encode the hash
>>> w3.toHex(block_hash)
'0x03087766bf68e78671d1ea436ae087da74a12761dac020011a9eddc4900bf13b'
# cast back to the basic `bytes` type
>>> bytes(block_hash)
b"\x03\x08wf\xbfh\xe7\x86q\xd1\xeaCj\xe0\x87\xdat\xa1'a\xda\xc0 \x01\x1a\x9e\xdd\xc4\x90\x0b\xf1;"

Contract Arguments

If a contract returns an ABI “bytes” type, then you will get a python bytes value. If it returns an ABI “string” type, then you will get a python str. Similarly, arguments to contract functions will appropriately correspond.

If a supplied argument type does not exactly match the corresponding ABI type, an attempt will be made to interpret it. For example:

  • a bytes value sent to an ABI string type will be UTF-8 decoded
  • a str value sent to an ABI bytes type will be interpreted as hex

Contract Functions

The v3 API for contract functions was a bit counterintuitive, having you specify what to do with the function before specifying the function, like: contract.call().balanceOf(...). We flipped these, using a format much like web3.js will have in v1: contract.functions.balanceOf(...).call().

Sign Messages and Transactions with Private Keys

You can now sign messages and raw transactions, as well as verify them, all without any client connection.

This module is pending a security audit, so production use is imprudent for now

Here is an example of signing a message:

>>> from web3 import Account, Web3>>> msg = "I♥SF"
>>> key = b"\xb2..."
>>> Account.sign(message_text=msg, private_key=key)
{'message': b'I\xe2\x99\xa5SF',
'messageHash': HexBytes('0x1476abb745d423bf09273f1afd887d951181d25adc66c4834a70491911b7f750'),
...
'signature': HexBytes('0xe6ca9bba58c88611fad66a6ce8f996908195593807c4b38bd528d2cff09d4eb33e5bfbbf4d3e39b1a2fd816a7680c19ebebaf3a141b239934ad43cb33fcec8ce1c')}

There are quite a few other private key features. See the web3.eth.account docs for more.

Preparing Transactions for Signing

In v3, there was no great way to create a contract transaction object. This wasn’t critical, because there wasn’t much else to do besides broadcast that transaction, which was already easy. In v4, however, we might want to sign that transaction object locally before broadcasting. So the buildTransaction() option was added, like so:

>>> from web3.auto import w3
>>> token = w3.eth.contract(abi=...)
>>> transfer = token.functions.transfer("ethereumfoundation.eth", 1).buildTransaction()
>>> signed = w3.eth.account.signTransaction(transfer, key)
>>> w3.eth.sendRawTransaction(signed.rawTransaction)

More Predictable Filters

In v3, filters never quite worked the way people wanted. The standard JSON-RPC API only supports polling for updates to filters. So to simulate a callback mechanism, Web3.py had to be opinionated about threading, and support multiple approaches (like stdlib or gevent). It was cumbersome and prone to failures. Those failures were difficult to debug and catch at runtime.

In v4, Web3.py puts the choice and responsibility on the user to add a monitoring thread and call get_new_entries() at the time that is appropriate for your application. This allows you to catch whatever Exception might be raised, and debug your filters. The new system is more reliable and easier to trace what’s happening, but there is more room to improve. So watch for more filtering updates in v4 and on into v5.

Ethereum Name Service, Everywhere

Anywhere that you can input an address, v4 accepts a name. Web3.py will then look up the address for you. For more detail about the Ethereum Name Service, see this article about how ENS fits into Web3.py.

For example, we can get the balance of the address at “ethereumfoundation.eth”:

>>> from web3.auto import w3
>>> w3.eth.getBalance('ethereumfoundation.eth')
2963803006730275571720

Automated Initialization

It’s not difficult to guess what parameters are needed to connect to common clients. Nevertheless, in v3, it was necessary to specify which provider to use, like:

from web3 import Web3, IPCProvider
w3 = Web3(IPCProvider())

Perhaps you’ve noticed from previous examples that in v4, this is achievable with a one-liner:

from web3.auto import w3

Additionally, you can initialize Web3 without any provider, which has the same effect:

from web3 import Web3
w3 = Web3()

Automatic provider detection can guess at common IPC and HTTP connection settings for production networks. It also checks if you set an environment variable.

Checksum Addresses Everywhere

EIP 55 defines a checksum mechanism for hex addresses, which flips some of the hex characters to upper case. That mechanism has been optionally supported in Web3.py for a while; it is now required for all addresses. All-lower-case hex addresses will be rejected as invalid (except in the rare case where the checksum produces an all-lower-case hex address).

In addition, checksum addresses are now returned from all methods. This has the advantage that address equality testing can simply use addr1 == addr2 because there is exactly one valid representation of an EIP 55 address.

Gas Price Estimation

Thanks to https://ethgasstation.info/ for posting their algorithm. We incorporated a version of it into a new gas price estimation backend in Web3.py. Opt into it this way:

from web3 import Web3, middleware
from web3.gas_strategies.time_based import medium_gas_price_strategy

w3 = Web3()
w3.eth.setGasPriceStrategy(medium_gas_price_strategy)

w3.middleware_stack.add(middleware.time_based_cache_middleware)
w3.middleware_stack.add(middleware.latest_block_based_cache_middleware)
w3.middleware_stack.add(middleware.simple_cache_middleware)
w3.eth.generateGasPrice()

You will certainly want those (new!) caching middlewares, because it makes a lot of calls to your node. Only set this up if getting a better price estimate automatically is critical, and worth the wait to you. One test clocked the first estimate as taking ~50 seconds.

More…

To see the full list of changes, visit the v4 Release Notes.

--

--