solders & solana-py
solders supplies fast, typed Solana primitives in Python; solana-py (import solana) wraps JSON-RPC, transaction helpers, and websocket subscriptions. Together they are the default off-chain Python stack against Agave 4.1.1 RPC nodes.
Recipe
Quick-reference recipe card - copy-paste ready.
pip install solders solana
from solders.pubkey import Pubkey
from solders.keypair import Keypair
from solana.rpc.api import Client
client = Client("https://api.devnet.solana.com")
payer = Keypair()
balance = client.get_balance(payer.pubkey()).valueWhen to reach for this:
- Building bots or backends that read chain state and send transactions.
- Needing Rust-grade performance for serialization and key handling in Python.
- Avoiding heavy JS tooling when your team already runs Python services.
- Prototyping ops scripts that mirror Solana CLI 3.0.10 workflows.
Working Example
import asyncio
from solders.keypair import Keypair
from solders.pubkey import Pubkey
from solders.system_program import TransferParams, transfer
from solders.hash import Hash
from solders.message import Message
from solders.transaction import VersionedTransaction
from solana.rpc.async_api import AsyncClient
from solana.rpc.types import TxOpts
RPC = "https://api.devnet.solana.com"
RECIPIENT = Pubkey.from_string("11111111111111111111111111111111")
async def main():
payer = Keypair()
async with AsyncClient(RPC) as client:
bh_resp = await client.get_latest_blockhash()
blockhash = Hash.from_string(str(bh_resp.value.blockhash))
ix = transfer(
TransferParams(
from_pubkey=payer.pubkey(),
to_pubkey=RECIPIENT,
lamports=1_000,
)
)
msg = Message.new_with_blockhash([ix], payer.pubkey(), blockhash)
tx = VersionedTransaction(msg, [payer])
sim = await client.simulate_transaction(tx)
if sim.value.err:
raise RuntimeError(sim.value.err)
sig = await client.send_transaction(
tx,
opts=TxOpts(skip_preflight=True, preflight_commitment="confirmed"),
)
print("signature:", sig.value)
asyncio.run(main())What this demonstrates:
soldersconstructs instructions, messages, and versioned transactions.AsyncClientfetches blockhashes and submits transactions.- Simulation catches failures before fee spend.
VersionedTransactionis the modern default on Agave 4.x clusters.
Deep Dive
How It Works
- solders binds to Rust implementations of Solana types (
Pubkey,Keypair,Instruction, sysvars). - solana-py implements JSON-RPC 2.0 over HTTP/WebSocket with solders-compatible types.
- Serialization crosses the Python/Rust boundary for hot paths like signing and message packing.
- Neither package compiles on-chain programs - pair with Anchor 0.32.1 for program work.
Package Responsibilities
| Package | Role | Typical imports |
|---|---|---|
solders | Primitives, program helpers, sysvars | Pubkey, Keypair, Instruction, system_program |
solana (solana-py) | RPC client, subscriptions, tx opts | Client, AsyncClient, TxOpts |
anchorpy | Anchor IDL clients (optional) | Program, Provider, Wallet |
Python Notes
# solders types are immutable - rebuild rather than mutate
from solders.instruction import Instruction, AccountMeta
# Prefer explicit encodings in RPC calls
client.get_account_info(pubkey, encoding="base64")
# Async for concurrent reads in bots
async with AsyncClient(RPC) as client:
await asyncio.gather(
client.get_balance(pk1),
client.get_balance(pk2),
)Gotchas
- Mixing web3.js examples with solders - field names and transaction shapes differ. Fix: follow solders message APIs end-to-end.
- Float lamport math -
0.1 * 1e9introduces rounding errors. Fix: integer lamports only. - Stale blockhashes in long loops - transactions expire after ~60-90s. Fix: refresh blockhash each attempt.
- Unbounded
get_program_accounts- can OOM your process. Fix: memcmp filters and external indexers. - Default commitment
finalized- too slow for bots. Fix: useconfirmedorprocessedexplicitly per call.
Alternatives
| Alternative | Use When | Don't Use When |
|---|---|---|
@solana/kit 7.0.0 (TypeScript) | Full-stack dApp with Next.js | Team is Python-only |
| Solana CLI 3.0.10 scripts | One-off ops and deploys | Need exchange-grade automation |
httpx + raw JSON-RPC | Minimal dependencies | You will reimplement types poorly |
gill TS client | Kit-native app layer | Python service tier |
FAQs
Is solders required if I use solana-py?
Yes for modern solana-py releases - it depends on solders for core types.
Sync or async client?
Use AsyncClient for bots and services with concurrent I/O; Client for notebooks and scripts.
Does this work on mainnet-beta?
Yes - point RPC URL at your provider; behavior matches Agave 4.1.1 JSON-RPC.
How do I add priority fees?
Append Compute Budget instructions built with solders before send.
Versioned vs legacy transactions?
Prefer versioned transactions; legacy paths are deprecated on many tooling guides.
Where do IDLs fit?
Use AnchorPy on top of this stack for Anchor program calls.
Local testing?
Point RPC at http://127.0.0.1:8899 with solana-test-validator or Surfpool 0.12.0.
Key storage?
Load from env or HSM - never embed secrets in source control.
RPC rate limits?
Backoff with jitter; shard reads across multiple endpoints if needed.
Logging simulation failures?
Print sim.value.logs - they mirror on-chain program logs.
Related
- Python for Solana Basics - section intro
- Building Transactions in Python - signing and sending
- AnchorPy - Anchor IDL clients
- RPC Basics - JSON-RPC concepts
Stack versions: This page was written for Agave 4.1.1, Solana CLI 3.0.10, Anchor 0.32.1, anchor-lang 0.32.1, Rust 1.91.1, @solana/kit 7.0.0, Surfpool 0.12.0, and LiteSVM 0.6.x.