Welcome to the Python branch of my Web3 Development series. This is the first section, where we'll walk through some basic use cases of web3.py, Ethereum's Web3 library for Python. We'll create a short script to accomplish the following:
- Get the latest block
- Get the first transaction from the latest block
- Fetch the balances of the to and from address in that transaction
Prerequisites
If you run into any issues installing or configuring these prerequisites, please do not hesitate to comment below. I'll do my best to help and will probably update this section accordingly over time.
Prerequisites are mentioned in some capacity in the series introduction post. For our purposes today, they include:
- A Python3 development environment--at the bare minimum, the ability to edit and run a python script--which could include one of the following:
- RPC endpoint - see the series introduction post referenced above. This is a common requirement across all web3 development environments, so I'll try to just go in depth in one place.
How to Install Python and pip
Results may vary across operating systems, but here is a nice guide I found that walks through the most common cases (MacOS, Windows, and various Linux distros): https://howchoo.com/python/install-pip-python.
Setting Things Up
First, we'll install the web3.py library, which we'll be using to interact with the blockchain.
pip install web3
Now, web3 should be accessible from Python scripts, so we can begin coding. From here, we'll build the script out line by line. If you don't wish to build this script out line by line, you may optionally just download it from GitHub and follow along to see how it's put together.
Let's create a fresh Python file, first_contact.py, and top of it, import the Web3
object from web3
like so:
from web3 import Web3
The Web3
object is the root from which we can make the magic happen. To use it, we must create a new instance of it, but first, we'll need an RPC endpoint. If unsure, I've covered RPC endpoints to some extent in the Prerequisites section of the Series Introduction.
Let's store our RPC endpoint value in a variable, like so (replacing the URL with your endpoint):
RPC_ENDPOINT = "<YOUR_RPC_ENDPOINT_HERE>"
A new Web3
object expects a provider, which by default could be HTTP-, IPC-, or Websocket-based (see their docs on Built In Providers for details). For our purposes here, we'll use their HTTPProvider, which expects an RPC endpoint input. Convenient!
w3 = Web3(Web3.HTTPProvider(RPC_ENDPOINT))
First Contact
Now we have our own Web3
object, but more importantly, we have access to its eth
property, which we can use to talk to the blockchain.
ChainId
To demonstrate connectivity here, and for good fun, let's print the chainId
:
print(f"ChainID: {hex(web3.eth.chainId)}")
We make use of the built-in hex()
here to convert the decimal value provided by chainId to its hex representation. The string provided to print()
is a special string called an f-string that allows for quick inline formatting. For more information on f-strings, I quite like DigitalOcean's post about them.
If all is well, running first_contact.py now should produce something like the following:
$ python first_contact.py
ChainID: 0x1
Behind the scenes, web3.eth.chainId
does actually delegate to an RPC call, meaning you just talked to the blockchain!
Latest Block
Let's try something a bit more interesting.
block_latest = web3.eth.get_block("latest")
print(block_latest)
The above fetches the latest block to hit the chain. Returned by get_block()
and contained within block_latest
will be a sizable AttributeDict
, or more simply, a structure housing key-value pairs. The get_block() docs
provide a pretty good example output.
For now, we'll focus on two of the block's properties: number
and transactions
. Let's print those as well:
print(f"Latest block number: {block_latest['number']}")
print(f"Transaction count: {len(block_latest['transactions'])}")
Running first_contact.py again should produce something like:
ChainID: 0x1
AttributeDict({...})
Latest block number: 16152249
Transaction count: 90
The number
property represents the block's number, while transactions
is a list of transaction ids contained in the block (hence us using len()
to print the transaction count).
Transactions
Since we already have a block with transactions, let's explore one of them.
transaction = web3.eth.get_transaction(block_latest["transactions"][0])
print(transaction)
Again, if you check out the get_transaction()
docs, you'll find a good output example. Each transaction object will have to
and from
attributes containing the addresses of the transaction initiator and recipient respectively.
Balances
For kicks and giggles, let's check the balances of the to and from addresses from this transaction. To achieve this, we'll use eth.get_balance()
:
address_to = transaction["to"]
address_from = transaction["from"]
balance_to = web3.eth.get_balance(address_to)
balance_from = web3.eth.get_balance(address_from)
print(f"Balance of {address_to} ('to' address) is {balance_to}")
print(f"Balance of {address_from} ('from' address) is {balance_from}")
If you run first_contact.py again, you may notice something weird about the balances:
...
Balance of 0x929832b1f1515cf02c9548A0fF454f1B0E216B18 ('to' address) is 0
Balance of 0x8A01fa5A77311BBCf29e293D8EcB48707cfDB700 ('from' address) is 8924442679551502040
get_balance()
returns the account's balance in wei. If this is what you're looking for, great. Otherwise, thankfully, the library provides a nice little helper function to convert this to a more familiar representation: web3.fromWei()
. Let's rewrite those two lines that fetch the balances:
balance_to = web3.fromWei(web3.eth.get_balance(address_to), "ether")
balance_from = web3.fromWei(web3.eth.get_balance(address_from), "ether")
That's better:
...
Balance of 0x929832b1f1515cf02c9548A0fF454f1B0E216B18 ('to' address) is 0
Balance of 0x8A01fa5A77311BBCf29e293D8EcB48707cfDB700 ('from' address) is 8.92444267955150204
Putting Everything Together
Now, our first_contact.py should be roughly identical to the one in the GitHub repo. Let's run the entire script once more:
ChainID: 0x1
AttributeDict({...})
Latest block number: 16152249
Transaction count: 90
AttributeDict({...})
Balance of 0x929832b1f1515cf02c9548A0fF454f1B0E216B18 ('to' address) is 0
Balance of 0x8A01fa5A77311BBCf29e293D8EcB48707cfDB700 ('from' address) is 8.297836181652525434
That's a Wrap!
Today, we accomplished a number of things. For those new to this, we spoke to the blockchain for the first time, and in fact, had a decent chat with it (4 calls in total: 1 to chainId
, 1 to get_transaction()
, and 2 to get_balance()
).
The complete script for this section may be found on the series Github repo.
In the next section, we'll explore how to create and manage transactions and talk to smart contracts. Thanks for reading and hope to see you then!
This article was originally published on my Publish0x Applied Web3 blog.