Web3 Development - First Contact (Python)

in web3 •  2 years ago  (edited)

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:
    • An IDE such as PyCharm
    • Python3 and pip installed along with a text editor (could also be an IDE) and a shell/command prompt/terminal from which Python and pip may be accessed. In fact, if you're comfortable enough, you might even try to run the lines provided herein from the Python shell itself.
  • 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.

Authors get paid when people like you upvote their post.
If you enjoyed what you read here, create your account today and start earning FREE STEEM!