Blockchain 8 min read

How to Programmatically Retrieve Uniswap V3 ETH/USDC Pool Balances with Python

This guide explains why accessing on‑chain data directly is the most trustless method, outlines the steps to query a Uniswap V3 ETH/USDC pool using Web3.py, provides full Python code, and offers practical tips for extending the approach to other pools and historical data.

Ops Development & AI Practice
Ops Development & AI Practice
Ops Development & AI Practice
How to Programmatically Retrieve Uniswap V3 ETH/USDC Pool Balances with Python

Why Access the Blockchain Directly?

In DeFi, data equals money. Querying pool information directly from the chain provides reliable, decentralized, 24/7 automation and avoids latency, outages, or errors that centralized APIs may introduce.

Core Idea: Talk to the Smart Contract

Liquidity pools are smart contracts that hold user‑deposited tokens. To obtain a pool’s asset status, query the contract’s token balances (ETH/WETH and USDC) via the ERC‑20 balanceOf function. For Uniswap V3 this is the most straightforward and reliable method.

Step‑by‑Step Process

Connect to an Ethereum node Use a provider such as Infura, Alchemy, or a self‑hosted node.

Locate the target contracts Identify the Uniswap V3 ETH/USDC pool address and the token contract addresses for WETH and USDC.

Use a minimal ERC‑20 ABI Include only the balanceOf , decimals , and symbol functions.

Execute the query and parse results Call balanceOf(pool_address).call() for each token, then adjust for token decimals.

Visual overview of the workflow:

Workflow diagram
Workflow diagram

Hands‑On Demo: Query with Python and Web3.py

Install the library: pip install web3 You also need an Ethereum node URL (Infura, Alchemy, or a local node).

Key Addresses on Mainnet

Uniswap V3 Pool (ETH/USDC 0.3%) 0x88e6a0c2ddd26feeb64f039a2c41296fcb3f5640 WETH Token 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2 USDC Token

0xA0b86991c6218b36c1d19d4a2e9eb0ce3606eb48

Core Python Code

from web3 import Web3

# 1. Preparation
infura_url = "https://mainnet.infura.io/v3/72a0c21cf2854a9d810d8986b88b9e8d"
web3 = Web3(Web3.HTTPProvider(infura_url))
if not web3.is_connected():
    raise ConnectionError("Unable to connect to Ethereum node")
print("Connected to Ethereum node!")

# 2. Define addresses and a minimal ERC‑20 ABI
pool_address = web3.to_checksum_address("0x88e6a0c2ddd26feeb64f039a2c41296fcb3f5640")
weth_address = web3.to_checksum_address("0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2")
usdc_address = web3.to_checksum_address("0xA0b86991c6218b36c1d19d4a2e9eb0ce3606eb48")
erc20_abi = [
    {"constant": True, "inputs": [{"name": "_owner", "type": "address"}], "name": "balanceOf", "outputs": [{"name": "balance", "type": "uint256"}], "type": "function"},
    {"constant": True, "inputs": [], "name": "decimals", "outputs": [{"name": "", "type": "uint8"}], "type": "function"},
    {"constant": True, "inputs": [], "name": "symbol", "outputs": [{"name": "", "type": "string"}], "type": "function"}
]

# 3. Create contract instances
weth_contract = web3.eth.contract(address=weth_address, abi=erc20_abi)
usdc_contract = web3.eth.contract(address=usdc_address, abi=erc20_abi)

# 4. Query raw balances
weth_balance_raw = weth_contract.functions.balanceOf(pool_address).call()
usdc_balance_raw = usdc_contract.functions.balanceOf(pool_address).call()

# 5. Adjust for decimals
weth_decimals = weth_contract.functions.decimals().call()
usdc_decimals = usdc_contract.functions.decimals().call()
weth_balance = weth_balance_raw / (10 ** weth_decimals)
usdc_balance = usdc_balance_raw / (10 ** usdc_decimals)

# 6. Get token symbols
weth_symbol = weth_contract.functions.symbol().call()
usdc_symbol = usdc_contract.functions.symbol().call()

# 7. Print results
print(f"  {weth_symbol} balance: {weth_balance:.4f}")
print(f"  {usdc_symbol} balance: {usdc_balance:.2f}")

Code Walkthrough

web3.to_checksum_address ensures addresses are correctly formatted, avoiding case‑sensitivity issues.

erc20_abi includes only the balanceOf, decimals, and symbol functions, which are sufficient for balance queries.

.call() performs a read‑only operation that does not consume gas.

Handling Decimals – On‑chain balances are integers; dividing by 10**decimals converts them to human‑readable values (WETH = 18 decimals, USDC = 6 decimals).

Conclusion & Practical Recommendations

The method equips you with the core skill of automatically monitoring DEX pool assets directly from the blockchain, eliminating reliance on centralized services and providing a foundation for more advanced on‑chain tools.

Practical tips:

Extend to other pools – Locate the desired pool and token addresses (via Etherscan or DEX documentation) and reuse the same script.

Query historical data – Pass a block_identifier to .call(), e.g., balanceOf(pool_address).call(block_identifier=18000000), to retrieve balances at a specific block.

Select reliable node providers – For production, use paid, high‑availability services with fallback nodes.

Stay updated – DeFi evolves rapidly; monitor upcoming upgrades such as Uniswap V4.

pythonblockchainWeb3EthereumSmart ContractDeFiUniswap
Ops Development & AI Practice
Written by

Ops Development & AI Practice

DevSecOps engineer sharing experiences and insights on AI, Web3, and Claude code development. Aims to help solve technical challenges, improve development efficiency, and grow through community interaction. Feel free to comment and discuss.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.