Substrate Interface
The `substrate-interface` library provides a Pythonic way to interact with Substrate-based blockchain nodes, including Polkadot and Kusama. It supports both traditional RPC connections and lightweight client functionality via Smoldot integration introduced in v1.8.0. The library enables querying chain state, submitting extrinsics, and managing subscriptions. It is actively maintained with frequent updates, currently at version 1.8.1.
Common errors
-
ModuleNotFoundError: No module named 'smoldot_module_py'
cause The `smoldot` light client dependency is optional and not installed by default with the base `pip install substrate-interface` command.fixInstall `substrate-interface` with the `lightclient` extra: `pip install "substrate-interface[lightclient]"`. -
websocket._exceptions.WebSocketConnectionClosedException: Connection is already closed.
cause The WebSocket connection to the Substrate node was unexpectedly terminated or could not be established. This often indicates the node is offline, inaccessible, or the URL is incorrect.fixVerify the Substrate node is running and accessible at the specified URL (e.g., `ws://127.0.0.1:9944` or `wss://rpc.polkadot.io`). Check firewall rules or ensure the node is properly configured to accept connections. -
ConnectionRefusedError: [Errno 111] Connection refused
cause The attempt to connect to the Substrate node at the specified address and port was actively refused. This typically means there is no service listening on that port, or a firewall is blocking the connection.fixEnsure the Substrate node is running and listening on the expected address and port. Verify the `url` parameter in `SubstrateInterface()` is correct. Check local and network firewall configurations. -
KeyError: 'some_expected_key_here'
cause When querying chain state or decoding extrinsic results/events, an expected dictionary key (representing a module, function, or data field) was not found. This often points to a metadata mismatch or an incorrect understanding of the runtime's data structure.fixEnsure `substrate-interface` is updated to the latest version to match the connected node's runtime metadata. Review the specific Substrate chain's documentation or use `substrate.get_metadata_dict()` to inspect the exact structure of modules, calls, and storage maps.
Warnings
- gotcha The `eth-utils` dependency frequently receives updates and has tight version pinning, which can lead to conflicts if other libraries in your environment require a different, incompatible version range.
- breaking Substrate runtimes (and thus their metadata) are constantly evolving. Older versions of `substrate-interface` may not correctly parse or interact with newer runtime metadata (e.g., MetadataV15, Ink! V5 contracts) and vice-versa.
- deprecated Certain older RPC calls or methods for interacting with node metadata may be deprecated or removed in newer versions of `substrate-interface` or on newer Substrate runtimes, leading to `ModuleNotFound` or `AttributeError`.
Install
-
pip install substrate-interface -
pip install "substrate-interface[lightclient]" -
pip install "substrate-interface[sr25519]" -
pip install "substrate-interface[ed25519]"
Imports
- SubstrateInterface
from substrateinterface import SubstrateInterface
Quickstart
import os
from substrateinterface import SubstrateInterface
# Connect to a public Polkadot node by default
# Override with environment variable SUBSTRATE_NODE_URL if needed
node_url = os.environ.get('SUBSTRATE_NODE_URL', 'wss://rpc.polkadot.io')
try:
substrate = SubstrateInterface(
url=node_url
)
print(f"Connected to chain: {substrate.chain}")
print(f"Node name: {substrate.node_name}")
print(f"Node version: {substrate.node_version}")
print(f"Current block number: {substrate.chain_head}")
# Example: Query account balance for a well-known test address (Alice)
# Override with environment variable POLKADOT_ALICE_ADDRESS if needed
alice_address = os.environ.get('POLKADOT_ALICE_ADDRESS', '5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY')
# The 'System' module and 'Account' storage map are standard across Substrate chains.
# The exact structure of the account info might vary slightly by chain/runtime version.
result = substrate.query(
module='System',
function='Account',
params=[alice_address]
)
# Accessing the free balance, typically found under 'data' and 'free'.
# Assuming 10 decimal places for Polkadot for display purposes.
# The '.value' attribute extracts the raw integer.
free_balance = result['data']['free'].value
print(f"Balance for {alice_address}: {free_balance / 10**10} DOT")
except ConnectionRefusedError:
print(f"Error: Connection refused. Is the node running or accessible at {node_url}?")
except Exception as e:
print(f"An unexpected error occurred: {e}")
finally:
if 'substrate' in locals() and substrate.is_connected:
substrate.close()