DEV Community

Byaigo
Byaigo

Posted on

Building a Multi-Chain Crypto Portfolio Tracker with Python and Web3.py

Portfolio Tracker

Managing crypto across multiple chains is a hassle — different explorers, different UIs, scattered balances. In this tutorial, we will build a command-line multi-chain portfolio tracker that aggregates your ETH, ERC-20 token, and native coin balances across Ethereum, Polygon, and Arbitrum in one unified view.


🧰 What You Will Learn

  • Querying native balances and ERC-20 token balances with web3.py
  • Working with multiple RPC providers (Infura, Alchemy, public endpoints)
  • Aggregating data from multiple chains into a single CLI dashboard
  • Handling rate limits and fallback providers gracefully

📦 Prerequisites

pip install web3 rich tabulate python-dotenv
Enter fullscreen mode Exit fullscreen mode
  • web3 — Ethereum interaction
  • rich — Beautiful CLI tables
  • python-dotenv — Manage RPC URLs securely

🔧 Step 1: Configure RPC Endpoints

Create a .env file:

ETH_RPC=https://mainnet.infura.io/v3/YOUR_KEY
POLYGON_RPC=https://polygon-rpc.com
ARBITRUM_RPC=https://arb1.arbitrum.io/rpc
Enter fullscreen mode Exit fullscreen mode

🐍 Step 2: The Core Tracker Script

from web3 import Web3
from rich.console import Console
from rich.table import Table
import json, os
from dotenv import load_dotenv

load_dotenv()
console = Console()

CHAINS = {
    "Ethereum": Web3(Web3.HTTPProvider(os.getenv("ETH_RPC"))),
    "Polygon": Web3(Web3.HTTPProvider(os.getenv("POLYGON_RPC"))),
    "Arbitrum": Web3(Web3.HTTPProvider(os.getenv("ARBITRUM_RPC"))),
}

ERC20_ABI = json.loads('[{"constant":true,"inputs":[],"name":"symbol","outputs":[{"name":"","type":"string"}],"type":"function"},{"constant":true,"inputs":[{"name":"_owner","type":"address"}],"name":"balanceOf","outputs":[{"name":"","type":"uint256"}],"type":"function"},{"constant":true,"inputs":[],"name":"decimals","outputs":[{"name":"","type":"uint8"}],"type":"function"}]')

TOKENS = {
    "USDC": {
        "ethereum": "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
        "polygon": "0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174",
        "arbitrum": "0xaf88d065e77c8cC2239327C5EDb3A432268e5831"
    },
    "USDT": {
        "ethereum": "0xdAC17F958D2ee523a2206206994597C13D831ec7",
        "polygon": "0xc2132D05D31c914a87C6611C10748AEb04B58e8F",
    }
}

def get_native_balance(w3, address):
    balance_wei = w3.eth.get_balance(address)
    return w3.from_wei(balance_wei, "ether")

def get_token_balance(w3, token_address, address):
    contract = w3.eth.contract(address=token_address, abi=ERC20_ABI)
    symbol = contract.functions.symbol().call()
    decimals = contract.functions.decimals().call()
    balance = contract.functions.balanceOf(address).call()
    return symbol, balance / (10 ** decimals)

def track_portfolio(addresses):
    table = Table(title="Multi-Chain Portfolio")
    table.add_column("Chain", style="cyan")
    table.add_column("Address", style="magenta")
    table.add_column("Asset", style="green")
    table.add_column("Balance", justify="right")

    for chain_name, w3 in CHAINS.items():
        for addr in addresses:
            try:
                native = get_native_balance(w3, addr)
                table.add_row(chain_name, addr[:10] + "...", "Native", f"{native:.4f}")

                for token_name, chain_tokens in TOKENS.items():
                    if chain_name.lower() in chain_tokens:
                        symbol, bal = get_token_balance(w3, chain_tokens[chain_name.lower()], addr)
                        table.add_row("", "", symbol, f"{bal:.2f}")
            except Exception as e:
                table.add_row(chain_name, addr[:10] + "...", "ERROR", str(e)[:30])

    console.print(table)

if __name__ == "__main__":
    WALLETS = [
        "0x18da907cb9d981bc798acb87ac27b03a2dc3cbb7",
    ]
    track_portfolio(WALLETS)
Enter fullscreen mode Exit fullscreen mode

🚀 Step 3: Run It

python portfolio_tracker.py
Enter fullscreen mode Exit fullscreen mode

You will see a formatted table showing native coin and token balances across all configured chains.


🔐 Security Notes

  • Never commit your .env file
  • Use read-only RPC keys
  • For production, consider using The Graph or Dune Analytics for heavy queries

🧩 Extending the Tracker

  • Add NFT detection using OpenSea API
  • Integrate price feeds from CoinGecko for USD values
  • Export to CSV for spreadsheet analysis
  • Add Telegram bot alerts for balance changes

📂 Source Code

Full project on GitHub: github.com/Byaigo


Like this article? Drop a ❤️ and follow for more Python + Web3 content!


ETH Tip Jar: 0x18da907cb9d981bc798acb87ac27b03a2dc3cbb7

Top comments (0)