Interacting with Ethereum Using Python and Web3.py

·

The Ethereum ecosystem offers developers a range of tools to interact with the blockchain. Among the most popular Python libraries for this purpose are web3.py and pyethereum. Understanding their differences is crucial for selecting the right tool for your project.

web3.py acts as an external client for accessing Ethereum. It does not become a blockchain node itself nor does it synchronize the blockchain. Instead, it connects to an existing node, treating the blockchain like an external database. This makes it a lightweight and efficient choice for many applications.

In contrast, pyethereum is designed to turn your machine into a full Ethereum node. It performs blockchain synchronization and can even function as a miner. This requires significant storage space and resources.

This guide focuses on web3.py for developers seeking a lightweight client to interact with Ethereum without maintaining a full node.

Setting Up the Web3.py Environment

Getting started with web3.py is straightforward. You can install it using the Python package manager, pip.

pip install web3

A note for Windows users: You must have Visual C++ Build Tools installed beforehand. Without it, the installation may fail during the final compilation stage.

Connecting to an Ethereum Node with Web3.py

Since web3.py is not a node itself, it requires a connection to an existing Ethereum node to access blockchain data. The most secure method is to run your own node using software like Geth or Parity.

However, for development and testing, third-party services can provide convenient access. One popular option is Infura, which offers managed HTTP-based node services.

To connect to the Ropsten testnet via Infura, you use a URL formatted like this: https://ropsten.infura.io/v3/YOUR_API_KEY. You must register for an account to obtain a unique API key.

The following Python code demonstrates how to connect to the Ropsten testnet using an API key stored in an environment variable.

import os
from web3 import HTTPProvider, Web3

INFURA_ROPSTEN_BASE_URL = 'https://ropsten.infura.io/v3'

def load_infura_url():
    key = os.environ.get('INFURA_API_KEY', '')
    return f"{INFURA_ROPSTEN_BASE_URL}/{key}"

w3 = Web3(HTTPProvider(load_infura_url()))

Accessing ERC20 Contracts on the Blockchain

Interacting with smart contracts requires an Application Binary Interface (ABI). Since contracts exist as compiled bytecode on the blockchain, the ABI is a JSON file that defines how to encode and decode data for the contract's functions. It tells web3.py how to interact with the contract.

The following code shows how to load an ABI and create a contract instance to call its functions.

import json

# Assume the contract we're going to invoke is a standard ERC20 contract.
with open("erc20.abi.json") as f:
    erc20_abi = json.load(f)

# Web3 accept only checksum address. So we should ensure the given address is a
# checksum address before accessing the corresponding contract.
contract_addr = w3.toChecksumAddress('0x4e470dc7321e84ca96fcaedd0c8abcebbaeb68c6')
erc20_contract = w3.eth.contract(address=contract_addr, abi=erc20_abi)

# Print all available functions in the contract
for func in erc20_contract.all_functions():
    print(func)

# Call the `name()` function to get the token's name
token_name = erc20_contract.functions.name().call()
print(f"Name of the token: {token_name}")

This example uses a random ERC20 contract on the Ropsten testnet. The standard ERC20 ABI allows us to interact with it. The output will list all available functions and return the token's name, which in this case is "KyberNetwork".

👉 Explore more strategies for smart contract interaction

Signing and Sending Transactions

The previous example involved calling a view or pure function, which only reads data from the blockchain and does not require a transaction or gas. To modify the state of the blockchain—such as transferring tokens—you must create, sign, and broadcast a transaction.

This process involves building a transaction object, estimating the required gas, signing it with a private key, and sending it to the network.

The following example demonstrates how to execute the transferFrom() function of an ERC20 contract, which moves tokens from one account to another.

# Set the account which makes the transaction.
account = w3.toChecksumAddress(os.environ.get('ETHEREUM_ACCOUNT', ''))
w3.eth.defaultAccount = account

contract_address = w3.toChecksumAddress('0x4e470dc7321e84ca96fcaedd0c8abcebbaeb68c6')
contract = w3.eth.contract(address=contract_address, abi=contract_abi)

# Estimate the gas required for the transaction
estimate_gas = contract.functions.transferFrom(account, account, w3.toWei('1', 'eth')).estimateGas()
nonce = w3.eth.getTransactionCount(account)

# Build the transaction dictionary
txn = contract.functions.transferFrom(account, account, w3.toWei('1', 'eth')).buildTransaction({
    'chainId': 3,  # Chain ID for Ropsten testnet
    'gas': estimate_gas,
    'gasPrice': w3.toWei('1', 'gwei'),
    'nonce': nonce
})

# Sign the transaction with the sender's private key
private_key = bytes.fromhex(os.environ.get('ETHEREUM_ACCOUNT_PKEY', ''))
signed_txn = w3.eth.account.signTransaction(txn, private_key=private_key)

# Send the raw transaction
tx_hash = w3.eth.sendRawTransaction(signed_txn.rawTransaction)
print(f'Txhash: 0x{tx_hash.hex()}')

This code first sets the account that will send the transaction and pay for the gas. It then estimates the gas required and gets the current nonce for the account. The nonce is a crucial number that prevents transaction replay. The transaction is built with these parameters, signed using the account's private key, and finally broadcast to the network. The returned transaction hash can be used to track the transaction on a block explorer like Etherscan.

Frequently Asked Questions

What is the main difference between web3.py and pyethereum?
web3.py is a library for interacting with the Ethereum blockchain as an external client. It connects to a remote node and does not store the blockchain locally. pyethereum is a project for implementing a full Ethereum node, including mining and blockchain synchronization, which requires substantial storage and resources.

Why do I need an ABI to interact with a smart contract?
An ABI (Application Binary Interface) defines the methods and structures used to encode and decode data for a specific smart contract. Since contracts are deployed as bytecode, the ABI is essential for your application to understand how to call functions and interpret the results correctly.

How can I securely manage my private keys when using web3.py?
Never hardcode private keys into your source code. Instead, use environment variables or secure secret management services. The private key is used to sign transactions, and compromising it will give attackers full control over your Ethereum account and its assets.

What is gas, and why do I need to estimate it?
Gas is the unit of computational effort required to execute operations on the Ethereum network. You pay for gas in ETH. Estimating gas before sending a transaction helps ensure you provide enough to complete the transaction without wasting ETH by setting a limit that is too high.

Can I use web3.py with Ethereum testnets?
Yes, web3.py works seamlessly with Ethereum testnets like Ropsten, Goerli, and Sepolia. You simply need to connect to a node that supports the desired testnet, such as one provided by Infura, and use the correct chain ID when creating transactions.

What is a checksum address, and why does web3.py require it?
A checksum address is an Ethereum address that includes capitalization to verify the integrity of the address and prevent errors from mistyping. web3.py requires checksum addresses to enhance security and prevent accidental loss of funds sent to invalid addresses. You can convert any address to a checksum address using Web3.toChecksumAddress().