Updates for Web3.py v6
Handling Some Backwards-Incompatible Changes
I’ve learned a few things about web3.py along the way. First, I highly recommend reading the official documentation Migrating your code from v5 to v6 page.
It is mostly straightforward, but there are some subtle differences that you should be aware of.
get_transaction Return Values
A subtle difference to be sure, but version v5 and v6 have differing output from the
In v5, the
data fields are returned as a hex-formatted string. In v6, they are
HexBytes, which is a wrapped bytes object that displays as hex by default. Be careful when directly comparing a hex string with a
HexBytes object, since they don’t behave like you’d expect!
If you need to do string comparisons or provide a string input to a function, but you have a
HexBytes object, convert it with the
>>> HexBytes('0x069420')=='0x069420' False >>> HexBytes('0x069420').hex()=='0x069420' True
Type and Chain ID
In v5, the transactio fields
'chainID' are presented as hex-formatted strings. In v6, they are converted to integers. If you’re doing a check like this:
if transaction["type"] == '0x0': do_something()
Please update it to:
if transaction["type"] == 0: do_something()
Or make it compatible with both versions by accepting both types:
if transaction["type"] in (0, '0x0'): do_something()
Or doing an inline conversion in the comparison:
if int(transaction["type"]) == 0: do_something()
Web3.py version 5 used a mixture of
camelCase for function names. The most commonly used camel case functions in degenbot were
After reading the code behind web3.py’s
to_checksum_address, it became clear that web3.py is just wrapping a function provided by the eth-utils module. It’s quite simple to just replace the use of
eth_utils.to_checksum_address, and I’ve done this throughout the code base.
If you are checksumming addresses inside your scripts, be sure to make this change.
Brownie handles both lower-case and checksummed addresses automatically, but web3.py will throw warnings and exceptions if you attempt to use a lower-case address without special arguments. Addresses within degenbot functions and methods are checksummed before use, but you may run into issues if you do comparisons in your client code without considering this.
My goal is to support web3.py v5 until the transition away from Brownie can be made complete. To do this effectively, I need to handle both versions of web3.py by default.
Python allows you to retrieve an attribute of a particular object with the
getattr function. Since everything in Python is an object, I can quickly identify which version of web3.py the script has loaded by calling
getattr on the module name.
For example, if I want to use the
is_connected) method on a
Web3 object, I can check for both attributes:
for method_name in ("is_connected", "isConnected"): try: connected_method = getattr(w3, method_name) except AttributeError: pass else: break
And now I have a reference to the method that I can use directly:
if connected_method() == False: raise ValueError("Web3 object is not connected.")
I use this technique in a few places in the code base. I’ve ordered the method names so that the v6 method appears first, since this the preferred condition.
Which Functions Are Affected?
This may not be an exhaustive list, but I’ve identified these:
Here are some Github links that include more detail on these changes:
Changes to decode_function_input
This one tripped me up for a while. The documentation for v5 says that decode_function_input returns a dictionary of values for the function parameters, however I’ve confirmed that sometimes it returns a tuple instead.
Here’s an example of a nested multicall, decoded from a transaction I wrote about in Part IV of the Transaction Prediction series. Skipping some steps required to unwrap the multiple encodings, I decode the final payload with web3.py v5: