Appendix E: Bitcoin and Ethereum RPC API Reference

This appendix provides a quick reference for the blockchain APIs and libraries used in the textbook's code exercises and data analysis chapters. It covers the raw RPC interfaces for Bitcoin and Ethereum, the two most popular Ethereum client libraries (web3.py and ethers.js), the Etherscan data API, and The Graph's GraphQL subgraph queries.


E.1 Bitcoin Core RPC

Bitcoin Core exposes a JSON-RPC interface on port 8332 (mainnet) or 18332 (testnet). Authentication uses the rpcuser and rpcpassword configured in bitcoin.conf.

Connection

import requests
import json

def bitcoin_rpc(method, params=None):
    """Call Bitcoin Core RPC."""
    url = "http://127.0.0.1:8332"
    headers = {"Content-Type": "application/json"}
    payload = {
        "jsonrpc": "2.0",
        "id": 1,
        "method": method,
        "params": params or [],
    }
    response = requests.post(
        url,
        data=json.dumps(payload),
        headers=headers,
        auth=("rpcuser", "rpcpassword"),
    )
    result = response.json()
    if result.get("error"):
        raise Exception(result["error"])
    return result["result"]

Common Methods

Method Parameters Returns Description
getblockchaininfo Object Chain state: blocks, difficulty, chain name, verification progress
getblockcount Integer Current block height
getblockhash height String Block hash at given height
getblock blockhash, verbosity Object Block data. Verbosity: 0=hex, 1=JSON, 2=JSON with full tx
getrawtransaction txid, verbose, blockhash Object/String Transaction data. verbose=true for decoded JSON
decoderawtransaction hex_string Object Decode a raw transaction hex without broadcasting
getmempoolinfo Object Mempool size, bytes, fee statistics
getrawmempool verbose Array/Object List of unconfirmed transaction IDs
getnetworkinfo Object P2P network: version, connections, relay fee
getpeerinfo Array Connected peers with latency, services, and version
estimatesmartfee conf_target Object Fee estimate (BTC/kvB) for confirmation within n blocks
gettxout txid, n, include_mempool Object UTXO data for a specific output
getbalance *args Number Wallet balance (requires wallet loaded)
sendrawtransaction hex_string String Broadcast a signed raw transaction, returns txid
validateaddress address Object Check if an address is valid

Example Calls

# Get the latest block height
height = bitcoin_rpc("getblockcount")
# 831542

# Get a block by height
block_hash = bitcoin_rpc("getblockhash", [831542])
block = bitcoin_rpc("getblock", [block_hash, 2])  # verbosity=2 includes full transactions
print(f"Block {block['height']}: {len(block['tx'])} transactions")

# Get a transaction
tx = bitcoin_rpc("getrawtransaction", ["txid_here", True])
print(f"Inputs: {len(tx['vin'])}, Outputs: {len(tx['vout'])}")
print(f"Size: {tx['vsize']} vbytes")

# Estimate fee for 6-block confirmation
fee = bitcoin_rpc("estimatesmartfee", [6])
print(f"Fee rate: {fee['feerate']} BTC/kvB")

# Get mempool statistics
mempool = bitcoin_rpc("getmempoolinfo")
print(f"Mempool: {mempool['size']} txs, {mempool['bytes']} bytes")

E.2 Ethereum JSON-RPC

Ethereum nodes (Geth, Nethermind, Besu, Erigon) expose a JSON-RPC interface over HTTP (port 8545), WebSocket (port 8546), or IPC. Hosted providers (Infura, Alchemy, QuickNode) expose the same interface over HTTPS.

Connection

import requests
import json

def eth_rpc(method, params=None, provider_url="http://127.0.0.1:8545"):
    """Call Ethereum JSON-RPC."""
    payload = {
        "jsonrpc": "2.0",
        "id": 1,
        "method": method,
        "params": params or [],
    }
    response = requests.post(provider_url, json=payload)
    result = response.json()
    if "error" in result:
        raise Exception(result["error"])
    return result["result"]

Common Methods

Method Parameters Returns Description
eth_blockNumber Hex string Latest block number
eth_getBlockByNumber block_number, full_txs Object Block data; full_txs=true includes transaction objects
eth_getBlockByHash block_hash, full_txs Object Block data by hash
eth_getBalance address, block Hex string Account balance in wei
eth_getTransactionByHash tx_hash Object Transaction data
eth_getTransactionReceipt tx_hash Object Receipt with status, gas used, logs
eth_getTransactionCount address, block Hex string Nonce (transaction count) for address
eth_call {to, data}, block Hex string Execute a read-only call (no state change)
eth_sendRawTransaction signed_tx_hex String Broadcast a signed transaction, returns tx hash
eth_estimateGas {from, to, data, value} Hex string Estimate gas for a transaction
eth_gasPrice Hex string Current gas price in wei
eth_getLogs {fromBlock, toBlock, address, topics} Array Event logs matching filter criteria
eth_getCode address, block Hex string Contract bytecode at address (0x for EOAs)
eth_getStorageAt address, slot, block Hex string Raw storage value at slot
eth_chainId Hex string Network chain ID
net_version String Network ID
web3_clientVersion String Node client and version

Block Parameter Values

Many methods accept a block parameter:

Value Meaning
"latest" Most recent mined block
"pending" Pending block (includes mempool transactions)
"earliest" Genesis block
"finalized" Most recent finalized block (PoS)
"safe" Most recent safe block (PoS)
"0x..." Specific block number in hex

Example Calls

provider = "https://eth-mainnet.g.alchemy.com/v2/YOUR_KEY"

# Get latest block number
block_num = eth_rpc("eth_blockNumber", provider_url=provider)
print(f"Block: {int(block_num, 16)}")

# Get ETH balance
balance_wei = eth_rpc(
    "eth_getBalance",
    ["0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045", "latest"],
    provider_url=provider,
)
balance_eth = int(balance_wei, 16) / 1e18
print(f"Balance: {balance_eth:.4f} ETH")

# Get event logs (ERC-20 Transfer events from a token)
transfer_topic = "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef"
logs = eth_rpc("eth_getLogs", [{
    "fromBlock": hex(19000000),
    "toBlock": hex(19000100),
    "address": "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",  # USDC
    "topics": [transfer_topic],
}], provider_url=provider)
print(f"Found {len(logs)} Transfer events")

E.3 Web3.py Common Operations

Web3.py is the primary Python library for Ethereum interaction (used in Chapters 11, 13, 14, 19, 20, 22, 33, 35).

Setup

from web3 import Web3

# Connect to a provider
w3 = Web3(Web3.HTTPProvider("https://eth-mainnet.g.alchemy.com/v2/YOUR_KEY"))

# For WebSocket (real-time subscriptions)
# w3 = Web3(Web3.WebSocketProvider("wss://eth-mainnet.g.alchemy.com/v2/YOUR_KEY"))

# For local node
# w3 = Web3(Web3.HTTPProvider("http://127.0.0.1:8545"))

assert w3.is_connected()

Account and Balance Operations

# Get balance
balance = w3.eth.get_balance("0xAddress")
print(w3.from_wei(balance, "ether"))

# Get transaction count (nonce)
nonce = w3.eth.get_transaction_count("0xAddress")

# Get current gas price
gas_price = w3.eth.gas_price

# Get chain ID
chain_id = w3.eth.chain_id

Transaction Operations

from eth_account import Account

# Build and sign a transaction
acct = Account.from_key("0xPrivateKey")

tx = {
    "nonce": w3.eth.get_transaction_count(acct.address),
    "to": "0xRecipient",
    "value": w3.to_wei(0.01, "ether"),
    "gas": 21000,
    "maxFeePerGas": w3.to_wei(30, "gwei"),
    "maxPriorityFeePerGas": w3.to_wei(2, "gwei"),
    "chainId": w3.eth.chain_id,
}

signed = acct.sign_transaction(tx)
tx_hash = w3.eth.send_raw_transaction(signed.raw_transaction)
receipt = w3.eth.wait_for_transaction_receipt(tx_hash)
print(f"Status: {'success' if receipt.status == 1 else 'failed'}")

Contract Interactions

# Load a contract
contract = w3.eth.contract(address="0xContractAddress", abi=contract_abi)

# Read (call) - no gas, no signing
balance = contract.functions.balanceOf("0xAddress").call()
name = contract.functions.name().call()
total_supply = contract.functions.totalSupply().call()

# Write (transact) - requires signing
tx = contract.functions.transfer(
    "0xRecipient",
    w3.to_wei(100, "ether")
).build_transaction({
    "from": acct.address,
    "nonce": w3.eth.get_transaction_count(acct.address),
    "maxFeePerGas": w3.to_wei(30, "gwei"),
    "maxPriorityFeePerGas": w3.to_wei(2, "gwei"),
})

signed = acct.sign_transaction(tx)
tx_hash = w3.eth.send_raw_transaction(signed.raw_transaction)
receipt = w3.eth.wait_for_transaction_receipt(tx_hash)

Event Log Queries

# Get past events
events = contract.events.Transfer.get_logs(
    from_block=19000000,
    to_block=19001000,
    argument_filters={"from": "0xSenderAddress"},
)

for event in events:
    print(f"{event.args['from']} -> {event.args['to']}: {event.args['value']}")

# Create a filter for new events
event_filter = contract.events.Transfer.create_filter(from_block="latest")
# Poll for new events
new_events = event_filter.get_new_entries()

Utility Functions

# Unit conversion
w3.to_wei(1.5, "ether")          # 1500000000000000000
w3.from_wei(1500000000000000000, "ether")  # Decimal('1.5')

# Address utilities
w3.is_address("0xABC...")                    # True/False
w3.to_checksum_address("0xabc...")           # Mixed-case checksummed

# Hashing
w3.keccak(text="Hello").hex()                # Keccak-256
w3.solidity_keccak(["address", "uint256"], [addr, amount]).hex()

# ENS resolution (if connected to mainnet)
address = w3.ens.address("vitalik.eth")

E.4 Ethers.js v6 Common Operations

Ethers.js is the dominant JavaScript library for Ethereum. Version 6 is the current major release. Used in textbook JavaScript/frontend examples.

Setup

import { ethers } from "ethers";

// Connect to provider
const provider = new ethers.JsonRpcProvider(
  "https://eth-mainnet.g.alchemy.com/v2/YOUR_KEY"
);

// For MetaMask (browser)
// const provider = new ethers.BrowserProvider(window.ethereum);

// Create a signer (for sending transactions)
const signer = new ethers.Wallet("0xPrivateKey", provider);

Account and Balance

// Get balance
const balance = await provider.getBalance("0xAddress");
console.log(ethers.formatEther(balance)); // "1.5"

// Get nonce
const nonce = await provider.getTransactionCount("0xAddress");

// Get gas price
const feeData = await provider.getFeeData();
console.log(ethers.formatUnits(feeData.gasPrice, "gwei"));

// Get block
const block = await provider.getBlock("latest");
console.log(block.number, block.timestamp);

Contract Interactions

// Read-only contract (no signer needed)
const contract = new ethers.Contract(contractAddress, abi, provider);
const name = await contract.name();
const balance = await contract.balanceOf("0xAddress");

// Read-write contract (signer required)
const contractWithSigner = new ethers.Contract(contractAddress, abi, signer);
const tx = await contractWithSigner.transfer("0xRecipient", ethers.parseEther("100"));
const receipt = await tx.wait();
console.log(`Tx confirmed in block ${receipt.blockNumber}`);

Event Queries

// Query past events
const filter = contract.filters.Transfer("0xFromAddress", null);
const events = await contract.queryFilter(filter, fromBlock, toBlock);

events.forEach((event) => {
  console.log(`${event.args.from} -> ${event.args.to}: ${event.args.value}`);
});

// Listen for new events (real-time)
contract.on("Transfer", (from, to, value, event) => {
  console.log(`Transfer: ${from} -> ${to}: ${ethers.formatEther(value)}`);
});

Utilities

// Unit conversion
ethers.parseEther("1.5");              // 1500000000000000000n (BigInt)
ethers.formatEther(1500000000000000000n); // "1.5"
ethers.parseUnits("30", "gwei");       // 30000000000n
ethers.formatUnits(30000000000n, "gwei"); // "30.0"

// Hashing
ethers.keccak256(ethers.toUtf8Bytes("Hello"));
ethers.solidityPackedKeccak256(["address", "uint256"], [addr, amount]);

// ABI encoding
const iface = new ethers.Interface(abi);
const calldata = iface.encodeFunctionData("transfer", [recipient, amount]);
const decoded = iface.decodeFunctionResult("balanceOf", returnData);

// Address validation
ethers.isAddress("0xABC...");
ethers.getAddress("0xabc...");  // Checksummed

E.5 Etherscan API Endpoints

The Etherscan API provides indexed blockchain data without running a full node. Rate limit: 5 calls/second (free tier). API key required.

Base URLs

Network URL
Ethereum Mainnet https://api.etherscan.io/api
Sepolia Testnet https://api-sepolia.etherscan.io/api
Polygon https://api.polygonscan.com/api
Arbitrum https://api.arbiscan.io/api
Optimism https://api-optimistic.etherscan.io/api
Base https://api.basescan.org/api

Common Endpoints

import requests

BASE = "https://api.etherscan.io/api"
API_KEY = "YOUR_KEY"

def etherscan(module, action, **params):
    params.update({"module": module, "action": action, "apikey": API_KEY})
    response = requests.get(BASE, params=params)
    return response.json()["result"]
Module Action Parameters Returns
account balance address, tag Balance in wei
account balancemulti address (comma-sep), tag Multiple balances
account txlist address, startblock, endblock, sort Normal transactions
account txlistinternal address, startblock, endblock Internal transactions
account tokentx address, contractaddress ERC-20 transfers
account tokennfttx address, contractaddress ERC-721 transfers
contract getabi address Contract ABI JSON
contract getsourcecode address Verified source code
transaction getstatus txhash Transaction execution status
transaction gettxreceiptstatus txhash Receipt status (1=success, 0=fail)
block getblockreward blockno Block and uncle rewards
stats ethprice Current ETH price (USD, BTC)
stats ethsupply Total ETH supply
gastracker gasoracle Gas price estimates (low, medium, high)
logs getLogs fromBlock, toBlock, address, topic0... Event logs

Example Queries

# Get transaction history for an address
txs = etherscan("account", "txlist",
    address="0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045",
    startblock=0,
    endblock=99999999,
    sort="desc"
)
print(f"Found {len(txs)} transactions")

# Get ERC-20 token transfers
transfers = etherscan("account", "tokentx",
    address="0xAddress",
    contractaddress="0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",  # USDC
    sort="desc"
)

# Get verified contract ABI (for use with web3.py)
import json
abi_json = etherscan("contract", "getabi", address="0xContractAddress")
abi = json.loads(abi_json)

# Get current gas prices
gas = etherscan("gastracker", "gasoracle")
print(f"Low: {gas['SafeGasPrice']} | Medium: {gas['ProposeGasPrice']} | High: {gas['FastGasPrice']} Gwei")

E.6 The Graph — GraphQL Queries

The Graph is a decentralized indexing protocol that indexes blockchain events into queryable subgraphs. Many DeFi protocols publish subgraphs providing rich, structured data.

Query Endpoint

Subgraphs are queried via HTTP POST to a GraphQL endpoint:

https://gateway.thegraph.com/api/YOUR_API_KEY/subgraphs/id/SUBGRAPH_ID

Or for the hosted service (being sunset):

https://api.thegraph.com/subgraphs/name/ORGANIZATION/SUBGRAPH_NAME

Python Client

import requests

def query_subgraph(subgraph_url, query, variables=None):
    """Execute a GraphQL query against a subgraph."""
    response = requests.post(
        subgraph_url,
        json={"query": query, "variables": variables or {}},
    )
    data = response.json()
    if "errors" in data:
        raise Exception(data["errors"])
    return data["data"]

Example: Uniswap V3 Queries

UNISWAP_V3 = "https://gateway.thegraph.com/api/KEY/subgraphs/id/5zvR82..."

# Top pools by TVL
result = query_subgraph(UNISWAP_V3, """
{
  pools(first: 10, orderBy: totalValueLockedUSD, orderDirection: desc) {
    id
    token0 { symbol }
    token1 { symbol }
    feeTier
    totalValueLockedUSD
    volumeUSD
  }
}
""")

for pool in result["pools"]:
    print(f"{pool['token0']['symbol']}/{pool['token1']['symbol']}: "
          f"TVL ${float(pool['totalValueLockedUSD']):,.0f}")

Example: Aave V3 Queries

AAVE_V3 = "https://gateway.thegraph.com/api/KEY/subgraphs/id/..."

# Get market data
result = query_subgraph(AAVE_V3, """
{
  markets(first: 10, orderBy: totalValueLockedUSD, orderDirection: desc) {
    name
    inputToken { symbol }
    totalValueLockedUSD
    totalBorrowBalanceUSD
    rates {
      rate
      side
      type
    }
  }
}
""")

Example: ENS Queries

ENS_SUBGRAPH = "https://gateway.thegraph.com/api/KEY/subgraphs/id/..."

# Look up an ENS name
result = query_subgraph(ENS_SUBGRAPH, """
{
  domains(where: { name: "vitalik.eth" }) {
    name
    resolvedAddress { id }
    owner { id }
    expiryDate
    registrant { id }
    createdAt
  }
}
""")
Protocol Data Available
Uniswap V2/V3 Pools, swaps, positions, token prices, volume
Aave V2/V3 Markets, borrows, liquidations, flash loans
Compound V2/V3 Markets, accounts, proposals, governance
Lido Staking deposits, withdrawals, oracle reports
ENS Domains, registrations, transfers, resolvers
OpenSea (Seaport) Sales, orders, collections
Chainlink Price feeds, oracle updates, VRF requests
Maker/DAI Vaults, liquidations, stability fees

Pagination

The Graph limits results to 1,000 per query. Use cursor-based pagination for larger datasets:

all_swaps = []
last_id = ""

while True:
    result = query_subgraph(UNISWAP_V3, """
    query ($lastId: String!) {
      swaps(first: 1000, where: { id_gt: $lastId }, orderBy: id) {
        id
        timestamp
        amountUSD
        pool { id }
      }
    }
    """, variables={"lastId": last_id})

    swaps = result["swaps"]
    if not swaps:
        break

    all_swaps.extend(swaps)
    last_id = swaps[-1]["id"]

print(f"Fetched {len(all_swaps)} total swaps")

This reference covers the primary APIs for interacting with Bitcoin and Ethereum programmatically. For hands-on practice, work through the exercises in Chapters 6, 11, 13, 33, and 35, which use these APIs extensively.