Solana Smart Contracts with Python: A Beginner's Complete Guide
Solana is one of the fastest blockchains on the planet, processing thousands of transactions per second at a fraction of the cost of Ethereum — and you don't need to learn Rust to start building on it. While Rust remains the primary language for on-chain Solana programs, Python developers can interact with, deploy, and test smart contracts using powerful tools that bridge the gap. This guide walks you through everything you need to get started today.
What You Actually Need to Understand First
Before writing a single line of code, let's clear up a common misconception: Solana smart contracts are called "programs," and they live on-chain written in Rust or C. However, Python is a first-class citizen for:
- Client-side interaction — sending transactions, reading account data
- Testing and scripting — automating deployments and integration tests
- Anchor framework clients — calling on-chain programs from Python scripts
The key library you'll use is solders (a Rust-powered Python binding) combined with solana-py, the official Python SDK.
Install your dependencies:
pip install solana solders anchorpy
You'll also need the Solana CLI:
sh -c "$(curl -sSfL https://release.solana.com/v1.18.0/install)"
Set your cluster to devnet for free testing:
solana config set --url devnet
solana-keygen new --outfile ~/.config/solana/id.json
solana airdrop 2
Setting Up Your Python Client to Talk to Solana
The solana-py SDK uses an async client to communicate with the blockchain. Here's a minimal setup to connect, check your balance, and confirm the connection works:
import asyncio
from solana.rpc.async_api import AsyncClient
from solders.pubkey import Pubkey
async def check_balance():
async with AsyncClient("https://api.devnet.solana.com") as client:
# Check if the client is connected
result = await client.is_connected()
print(f"Connected: {result}")
# Replace with your wallet address
pubkey = Pubkey.from_string("YourWalletAddressHere")
balance = await client.get_balance(pubkey)
# Balance is returned in lamports (1 SOL = 1,000,000,000 lamports)
sol_balance = balance.value / 1_000_000_000
print(f"Balance: {sol_balance} SOL")
asyncio.run(check_balance())
This async pattern is important — almost every solana-py operation is non-blocking, which mirrors how high-throughput blockchain clients should behave.
Writing and Deploying Your First Solana Program
For the on-chain program itself, you'll use the Anchor framework, which dramatically simplifies Solana development. Install the Anchor CLI:
cargo install --git https://github.com/coral-xyz/anchor avm --locked
avm install latest && avm use latest
Initialize a new project:
anchor init hello_solana
cd hello_solana
Open programs/hello_solana/src/lib.rs and replace its contents with this simple counter program:
use anchor_lang::prelude::*;
declare_id!("YOUR_PROGRAM_ID_HERE");
#[program]
pub mod hello_solana {
use super::*;
pub fn initialize(ctx: Context<Initialize>) -> Result<()> {
let counter = &mut ctx.accounts.counter;
counter.count = 0;
msg!("Counter initialized!");
Ok(())
}
pub fn increment(ctx: Context<Increment>) -> Result<()> {
let counter = &mut ctx.accounts.counter;
counter.count += 1;
msg!("Count is now: {}", counter.count);
Ok(())
}
}
#[derive(Accounts)]
pub struct Initialize<'info> {
#[account(init, payer = user, space = 8 + 8)]
pub counter: Account<'info, Counter>,
#[account(mut)]
pub user: Signer<'info>,
pub system_program: Program<'info, System>,
}
#[derive(Accounts)]
pub struct Increment<'info> {
#[account(mut)]
pub counter: Account<'info, Counter>,
pub user: Signer<'info>,
}
#[account]
pub struct Counter {
pub count: u64,
}
Build and deploy to devnet:
anchor build
anchor deploy --provider.cluster devnet
After deployment, you'll see your Program ID in the terminal. Copy it — you'll need it for the Python client.
Calling Your Smart Contract from Python with AnchorPy
This is where it all comes together. AnchorPy reads your program's IDL (Interface Definition Language) file — automatically generated by Anchor — and lets you call on-chain functions from Python with almost no boilerplate.
Your IDL file is at target/idl/hello_solana.json after building. Here's a complete Python script to initialize and increment the counter:
import asyncio
from pathlib import Path
from anchorpy import Program, Provider, Wallet, Idl
from anchorpy.provider import DEFAULT_OPTIONS
from solana.rpc.async_api import AsyncClient
from solders.keypair import Keypair
from solders.system_program import ID as SYS_PROGRAM_ID
async def main():
# Load your wallet keypair
wallet = Wallet.local() # reads from ~/.config/solana/id.json
# Connect to devnet
client = AsyncClient("https://api.devnet.solana.com", commitment="confirmed")
provider = Provider(client, wallet, DEFAULT_OPTIONS)
# Load the IDL from your Anchor build output
idl_path = Path("target/idl/hello_solana.json")
with idl_path.open() as f:
raw_idl = f.read()
idl = Idl.from_json(raw_idl)
# Replace with your deployed program ID
program_id = "YOUR_PROGRAM_ID_HERE"
program = Program(idl, program_id, provider)
# Generate a new keypair for the counter account
counter_keypair = Keypair()
print("Initializing counter account...")
tx_init = await program.rpc["initialize"](
ctx=program.type["Initialize"](
accounts={
"counter": counter_keypair.pubkey(),
"user": wallet.public_key,
"system_program": SYS_PROGRAM_ID,
},
signers=[counter_keypair],
)
)
print(f"Init transaction: {tx_init}")
# Increment the counter
print("Incrementing counter...")
tx_inc = await program.rpc["increment"](
ctx=program.type["Increment"](
accounts={
"counter": counter_keypair.pubkey(),
"user": wallet.public_key,
}
)
)
print(f"Increment transaction: {tx_inc}")
# Fetch and display the counter state
counter_account = await program.account["Counter"].fetch(counter_keypair.pubkey())
print(f"Current count: {counter_account.count}")
await client.close()
asyncio.run(main())
Run it with python main.py and watch your Python script interact live with the Solana blockchain.
Reading Account Data and Handling Errors Like a Pro
Production scripts need robust error handling. Solana transactions can fail for several reasons — insufficient funds, account not found, or network congestion. Here's a pattern to handle these gracefully:
from solana.rpc.core import RPCException
from solders.pubkey import Pubkey
import asyncio
from solana.rpc.async_api import AsyncClient
async def safe_fetch_account(pubkey_str: str):
async with AsyncClient("https://api.devnet.solana.com") as client:
try:
pubkey = Pubkey.from_string(pubkey_str)
response = await client.get_account_info(pubkey)
if response.value is None:
print(f"Account {pubkey_str} does not exist on-chain.")
return None
account_data = response.value
print(f"Account owner: {account_data.owner}")
print(f"Lamports: {account_data.lamports}")
print(f"Data length: {len(account_data.data)} bytes")
return account_data
except RPCException as e:
print(f"RPC Error: {e.args[0].message}")
except ValueError as e:
print(f"Invalid public key format: {e}")
except Exception as e:
print(f"Unexpected error: {e}")
asyncio.run(safe_fetch_account("11111111111111111111111111111111"))
Pro tip: Always validate public key strings before making network calls, and implement exponential backoff for retries during high network load. For production scripts, consider using a dedicated RPC provider like Helius or QuickNode instead of the public devnet endpoint.
Conclusion: Your Solana Python Journey Starts Here
You now have a working foundation for building Solana applications with Python. You've connected to the blockchain, deployed a real on-chain program using Anchor, called it from Python via AnchorPy, and learned how to read account data safely.
The path forward is clear:
- Experiment on devnet — it's free, and mistakes don't cost real money
- Explore the Anchor documentation at anchor-lang.com for more complex program patterns like PDAs and cross-program invocations
- Join the Solana developer Discord — the community answers questions fast
- Build something real — a token faucet, a simple voting app, or a leaderboard
The tooling around Solana and Python is maturing rapidly. Developers who invest time now are positioning themselves ahead of the curve. Clone this code, get it running, and make your first on-chain transaction today — the next step is always just one asyncio.run() away.
Top comments (0)