Welcome back to our web3.py series! In Part 1, we got our development environment set up and learned how to connect to an Ethereum node to read basic blockchain data like block numbers and gas prices.
Now, it’s time to interact with smart contracts. These are the self-executing programs stored on the blockchain that power decentralised applications (dApps). Just like you interact with a web API to get data from a traditional server, you’ll use web3.py to „talk“ to smart contracts on the blockchain.
Smart Contracts: The Programmable Building Blocks of Web3
A smart contract is essentially a piece of code that lives on the blockchain. Once deployed, it runs exactly as programmed, without any possibility of censorship, downtime, or third-party interference.
To interact with a smart contract from web3.py, you primarily need two pieces of information:
-
The Contract’s Address: This is its unique location on the blockchain (e.g., 0xDE893…).
-
The Contract’s ABI (Application Binary Interface): This is a JSON description of the contract’s public functions and events. Think of the ABI as the „instruction manual“ for your web3.py client.
-
It tells web3.py what functions the contract has, what arguments they take, and what values they return. Without the ABI, web3.py wouldn’t know how to correctly encode your function calls or decode the contract’s responses.
Your First Smart Contract: The Simple Counter
To make this hands-on, we’re going to use a super simple Counter smart contract. This contract will store a single number (count) and allow us to read its current value.
Here’s the Solidity code for our Counter contract:
// Counter.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
contract Counter {
uint public count; // A public state variable, automatically creates a getter function
constructor() {
count = 0; // Initialize count to 0 when the contract is deployed
}
// A 'view' function to explicitly get the count
// 'view' means it only reads state and doesn't modify it (no gas cost for calling)
function getCount() public view returns (uint) {
return count;
}
}
This contract has:
-
A
count
variable, initialised to 0. Thepublic
keyword automatically creates acount()
getter function for us. -
A
getCount()
function, which is explicitly declared aspublic view
. Bothcount()
andgetCount()
are view functions, meaning they only read the contract’s state and do not modify it.
Hands-On: Deploying Your Contract to Ganache (Skip the Faucet Frustration!)
When I first started learning blockchain development, I spent countless hours hunting for free testnet tokens from faucets. Every time I ran out, it was another delay, another break in my learning flow. It consumed so much of my valuable learning time.
This is exactly why local development with Ganache is crucial. You get instant transactions, unlimited „test Ether,“ and complete control over your environment. No more waiting for faucets, no more empty wallets interrupting your flow, just pure, uninterrupted learning.
In Part 1, we learned about connecting web3.py to your local Ganache blockchain. Now, we’ll deploy our Counter.sol contract directly to that Ganache blockchain using Remix IDE. This ensures your Python script can interact with the contract you’ve deployed!
For this project, we will be using Remix IDE for easy development and deployment. While we’ll start with Remix’s built-in deployment features, we may explore deploying through code later.
Step-by-step guide to deploy your Counter contract to Ganache with Remix:
1. Open Ganache Desktop: First, ensure your Ganache Desktop application is open and running. You should see its „Quickstart“ workspace active, showing accounts and current blocks.
2. Open Remix IDE: Open your web browser and navigate to https://remix.ethereum.org/.
3. Create a New File:
-
On the left sidebar, click on the „File Explorer“ icon (looks like a folder).
-
Under „contracts“, click the „Create new file“ icon (a blank paper with a plus sign).
-
Name the file
Counter.sol
and press Enter.
4. Paste the Contract Code: Copy the Counter.sol code from above and paste it into the Counter.sol file in Remix.
5. Compile the Contract and Get the ABI:
-
Click on the „Solidity Compiler“ icon on the left sidebar (looks like a Solidity logo).
-
Ensure the „Compiler“ version matches our
pragma solidity ^0.8.0;
(e.g., 0.8.20). -
Click on „Advanced configurations“ (it might be a dropdown or expandable section)
-
From the „EVM version“ dropdown, select „Paris“.
- Click on the light blue „Compile Counter.sol“ button.
-
You should see a green checkmark on the compiler icon if compilation is successful (as in the image above).
-
Now, scroll down within the Solidity Compiler tab until you see the „ABI“ section (it might be a button or a copy icon next to the ABI output). Click the copy ABI button.
6. Connect Remix to Ganache (Environment Setup):
-
Click on the „Deploy & Run Transactions“ icon on the left sidebar (just below that „Solidity Compiler“ icon).
-
In the „Environment“ dropdown, select „Customize this list…“
- Scroll down in the „Environment Customization“ list until you see „DEV — GANACHE PROVIDER“. Check its checkbox.
- Click the „Environment“ dropdown again. Now you’ll see „DEV — GANACHE PROVIDER“ on the list — click it.
- Remix will likely pop up a small window asking for the Ganache RPC endpoint. Copy your Ganache RPC URL from your Ganache application’s „Accounts“ section (it’s typically
http://127.0.0.1:7545
by default) and paste it into the Remix prompt. Then click „OK“.
7. Deploy the Contract to Ganache:
-
In Remix, ensure „Counter“ is selected in the „Contract“ dropdown.
-
Click the orange „Deploy“ button.
- You should see a new transaction appear in your Ganache Desktop application under the „Transactions“ tab, confirming the deployment.
8. Get Contract Address:
-
After successful deployment, expand the „Deployed Contracts“ section below the Deploy button in Remix. You’ll see your Counter contract listed with its address (e.g., COUNTER AT 0x…).
-
Click the copy icon next to the address to save it. This is your
CONTRACT_ADDRESS
.
In case you’re wondering why we changed the Remix EVM to ‚Paris‘ for Ganache…
The ‚EVM version‘ in Remix and the ‚Hardfork‘ setting in Ganache both refer to specific versions of the Ethereum protocol. These different versions incorporate various updates, bug fixes, and new features.
For successful interaction and deployment, it’s crucial that your chosen EVM version in Remix (which determines how your contract’s bytecode is compiled) is compatible with the hardfork setting of your local Ganache blockchain. This ensures that the environment where your contract is compiled and the environment where it’s deployed understand and execute the same set of rules. Setting Remix to ‚Paris‘ generally provides good compatibility with recent Ganache versions.
Reading Your Contract’s Data with web3.py
Now that you have your CONTRACT_ADDRESS
and CONTRACT_ABI
from your Ganache deployment, let’s update our app.py
script to interact with it.
Open your app.py
from Part 1. We’ll add a new section.
import json # Import json for parsing the ABI string
# --- Smart Contract Interaction ---
print("n--- Smart Contract Interaction ---")
# IMPORTANT: Replace with the actual address you copied from Remix (deployed to Ganache)
CONTRACT_ADDRESS = "0xD369F9Eefd646054F3E46D97D45e2E88030F9A80" # e.g., "0x5FbDB2315678afecb367f032d93F642f64180aa3"
# IMPORTANT: Paste your ENTIRE ABI here. It's a long JSON string.
# The ABI below is what you should get from our simple Counter contract.
# Make sure to replace this with the exact ABI you copied from Remix!
CONTRACT_ABI = json.loads('''
[
{
"inputs": [],
"stateMutability": "nonpayable",
"type": "constructor"
},
{
"inputs": [],
"name": "count",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "getCount",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
}
]
''')
try:
# Create a contract object: This tells web3.py how to interact with your deployed contract
contract = w3.eth.contract(address=CONTRACT_ADDRESS, abi=CONTRACT_ABI)
print(f"✅ Contract loaded successfully at address: {CONTRACT_ADDRESS}")
# Call the 'getCount()' view function
# View functions don't change state, so they are free and instant.
# We use .call() to execute them.
current_count_from_get_function = contract.functions.getCount().call()
print(f"n🔢 Current Counter Value (via getCount() function): {current_count_from_get_function}")
# Access the 'count' public state variable directly
# Public state variables automatically get a getter function generated by Solidity.
# This also uses .call() and is free.
current_count_from_public_var = contract.functions.count().call()
print(f"🔢 Current Counter Value (via 'count' public variable): {current_count_from_public_var}")
# Note: Both methods above do the same thing - they read the current value
# without modifying the blockchain state, which is why they're free to call
except Exception as e:
print(f"❌ Error interacting with contract: {e}")
print("Please ensure your CONTRACT_ADDRESS and CONTRACT_ABI are correct and match your deployed contract.")
print("Also, ensure your Ganache is running and Remix was successfully connected to 'DEV - GANACHE PROVIDER' and deployed to Ganache.")
print("n--- End of Smart Contract Interaction ---")
Important: Make sure you replace CONTRACT_ADDRESS
and the content of CONTRACT_ABI
with the exact values you copied from Remix after deploying to Ganache!
Now, run your app.py
script again:
python app.py
You should see output similar to this, showing the initial count of 0:
Troubleshooting Common Issues
If you encounter errors, here are the most common causes and solutions:
„Error interacting with contract“:
-
Double-check that your
CONTRACT_ADDRESS
is correct and matches what you copied from Remix -
Ensure your
CONTRACT_ABI
is valid JSON (no trailing commas, proper quotes) -
Verify that Ganache is still running on the same port
„Connection refused“:
-
Make sure Ganache Desktop is running
-
Check that the RPC URL in your
app.py
matches Ganache’s RPC server URL -
Go back to Part 1 to double-check your RPC setup if you’re still having connection issues
„Invalid address“:
-
Contract addresses should start with
0x
and be 42 characters total -
Make sure you copied the entire address from Remix
What You’ve Achieved
Congratulations! You’ve successfully:
-
Deployed your very own smart contract to your local Ganache blockchain
-
Used web3.py to read data directly from a smart contract
-
Set up a complete local development environment for blockchain applications
This is a fundamental step in building any dApp that needs to display information from the blockchain. You now understand how to bridge the gap between your Python application and smart contracts running on Ethereum.
Coming Up in Part 3
In the next post, things get even more exciting! We’ll learn how to:
-
Change the state of our smart contract (increment that counter!)
-
Send transactions that modify blockchain data
Get ready to truly interact with the blockchain by writing data, not just reading it!