Python bindings for Solana Rust tools providing high-performance blockchain development primitives, RPC functionality, and testing infrastructure.
—
LiteSVM lightweight runtime for local testing and transaction simulation without requiring a full Solana network. This provides a complete local Solana environment for development, testing, and integration scenarios with fast execution and deterministic results.
Lightweight Solana Virtual Machine for local testing and development with full program execution capabilities.
class LiteSVM:
"""
Lightweight Solana runtime for testing and transaction simulation.
Provides a complete local Solana environment without requiring network connectivity
or external validator nodes. Supports all core Solana features including:
- Transaction execution and simulation
- Account state management
- Built-in program execution (System, Token, etc.)
- Custom program deployment and execution
- Deterministic block production
"""
def __init__(self):
"""
Create new LiteSVM instance with default configuration.
Initializes a fresh Solana runtime with:
- Genesis accounts and configuration
- Built-in programs (System, Token, etc.)
- Default rent and fee configurations
- Empty validator set (single node)
"""
@classmethod
def new_with_config(cls, config: 'LiteSvmConfig') -> 'LiteSVM':
"""
Create LiteSVM with custom configuration.
Parameters:
- config: LiteSvmConfig, runtime configuration options
Returns:
LiteSVM instance with specified configuration
"""
def send_transaction(self, transaction: Union[Transaction, VersionedTransaction]) -> 'TransactionResult':
"""
Execute transaction and return detailed results.
Parameters:
- transaction: Union transaction to execute (must be signed)
Returns:
TransactionResult with execution metadata or error details
Raises:
- ValueError: if transaction is not properly signed
- RuntimeError: if transaction execution fails critically
"""
def simulate_transaction(
self,
transaction: Union[Transaction, VersionedTransaction],
config: Optional['SimulationConfig'] = None
) -> 'SimulateResult':
"""
Simulate transaction execution without committing state changes.
Parameters:
- transaction: Union transaction to simulate
- config: Optional[SimulationConfig], simulation parameters
Returns:
SimulateResult with execution results and logs
"""
def get_account(self, pubkey: Pubkey) -> Optional[Account]:
"""
Retrieve account data and metadata.
Parameters:
- pubkey: Pubkey, account address to query
Returns:
Optional[Account], account data or None if account doesn't exist
"""
def get_balance(self, pubkey: Pubkey) -> int:
"""
Get account balance in lamports.
Parameters:
- pubkey: Pubkey, account address
Returns:
int, account balance in lamports (0 if account doesn't exist)
"""
def airdrop(self, pubkey: Pubkey, lamports: int) -> None:
"""
Add lamports to account (test utility).
Parameters:
- pubkey: Pubkey, recipient account
- lamports: int, amount to add
Note:
Creates account if it doesn't exist
"""
def create_account(
self,
pubkey: Pubkey,
lamports: int,
data: bytes,
owner: Pubkey
) -> None:
"""
Create account with specified data and owner.
Parameters:
- pubkey: Pubkey, new account address
- lamports: int, initial balance
- data: bytes, account data
- owner: Pubkey, program that owns the account
Raises:
- ValueError: if account already exists
"""
def set_account(
self,
pubkey: Pubkey,
account: Account
) -> None:
"""
Set complete account state (test utility).
Parameters:
- pubkey: Pubkey, account address
- account: Account, complete account data to set
"""
def get_clock(self) -> Clock:
"""
Get current runtime clock information.
Returns:
Clock with current slot, epoch, and timestamp
"""
def get_rent(self) -> Rent:
"""
Get current rent configuration.
Returns:
Rent configuration for exemption calculations
"""
def advance_slot(self) -> None:
"""
Advance to next slot manually.
Increments slot counter and updates clock sysvar.
Useful for testing time-dependent behavior.
"""
def advance_slots(self, count: int) -> None:
"""
Advance multiple slots.
Parameters:
- count: int, number of slots to advance
"""
def set_slot(self, slot: int) -> None:
"""
Set current slot number.
Parameters:
- slot: int, slot number to set
Note:
Updates all time-dependent sysvars
"""
def get_latest_blockhash(self) -> Hash:
"""
Get current blockhash for transactions.
Returns:
Hash representing current block
"""
def get_program_accounts(
self,
program_id: Pubkey,
filters: Optional[List['AccountFilter']] = None
) -> List[tuple[Pubkey, Account]]:
"""
Get all accounts owned by program.
Parameters:
- program_id: Pubkey, program to search for
- filters: Optional[List[AccountFilter]], account filtering criteria
Returns:
List[tuple[Pubkey, Account]], matching accounts with addresses
"""
def load_program(self, program_id: Pubkey, elf_bytes: bytes) -> None:
"""
Load custom program into runtime.
Parameters:
- program_id: Pubkey, address for program
- elf_bytes: bytes, compiled program binary (ELF format)
Raises:
- ValueError: if program binary is invalid
"""
def get_transaction_count(self) -> int:
"""
Get total number of transactions processed.
Returns:
int, transaction count since runtime creation
"""
def get_signature_status(self, signature: Signature) -> Optional['TransactionStatus']:
"""
Get status of previously executed transaction.
Parameters:
- signature: Signature, transaction signature to query
Returns:
Optional[TransactionStatus], transaction status or None if not found
"""Detailed execution results and metadata from transaction processing.
class TransactionMetadata:
"""
Successful transaction execution metadata.
"""
def __init__(
self,
compute_units_consumed: int,
fee: int,
inner_instructions: List['InnerInstruction'],
log_messages: List[str],
return_data: Optional[bytes],
status: 'TransactionStatus'
):
"""
Create transaction metadata for successful execution.
Parameters:
- compute_units_consumed: int, compute units used
- fee: int, transaction fee paid
- inner_instructions: List[InnerInstruction], cross-program invocations
- log_messages: List[str], program log messages
- return_data: Optional[bytes], program return data
- status: TransactionStatus, final transaction status
"""
@property
def compute_units_consumed(self) -> int:
"""Compute units used in execution."""
@property
def fee(self) -> int:
"""Transaction fee paid in lamports."""
@property
def inner_instructions(self) -> List['InnerInstruction']:
"""Cross-program invocations executed."""
@property
def log_messages(self) -> List[str]:
"""Program log messages."""
@property
def return_data(self) -> Optional[bytes]:
"""Program return data."""
@property
def status(self) -> 'TransactionStatus':
"""Final transaction status."""
def get_logs_for_program(self, program_id: Pubkey) -> List[str]:
"""
Filter log messages for specific program.
Parameters:
- program_id: Pubkey, program to filter logs for
Returns:
List[str], log messages from specified program
"""
class FailedTransactionMetadata:
"""
Failed transaction execution metadata.
"""
def __init__(
self,
compute_units_consumed: int,
fee: int,
inner_instructions: List['InnerInstruction'],
log_messages: List[str],
error: 'TransactionErrorType'
):
"""
Create failed transaction metadata.
Parameters:
- compute_units_consumed: int, compute units used before failure
- fee: int, transaction fee paid
- inner_instructions: List[InnerInstruction], instructions executed before failure
- log_messages: List[str], program logs before failure
- error: TransactionErrorType, error that caused failure
"""
@property
def compute_units_consumed(self) -> int:
"""Compute units used before failure."""
@property
def fee(self) -> int:
"""Transaction fee paid."""
@property
def inner_instructions(self) -> List['InnerInstruction']:
"""Instructions executed before failure."""
@property
def log_messages(self) -> List[str]:
"""Program logs before failure."""
@property
def error(self) -> 'TransactionErrorType':
"""Error that caused transaction failure."""
class SimulatedTransactionInfo:
"""
Transaction simulation results.
"""
def __init__(
self,
compute_units_consumed: int,
inner_instructions: List['InnerInstruction'],
log_messages: List[str],
return_data: Optional[bytes],
accounts: Optional[List[Account]]
):
"""
Create simulation results.
Parameters:
- compute_units_consumed: int, compute units that would be used
- inner_instructions: List[InnerInstruction], CPIs that would execute
- log_messages: List[str], program logs from simulation
- return_data: Optional[bytes], program return data from simulation
- accounts: Optional[List[Account]], final account states (if requested)
"""
@property
def compute_units_consumed(self) -> int:
"""Compute units that would be consumed."""
@property
def inner_instructions(self) -> List['InnerInstruction']:
"""Cross-program invocations that would execute."""
@property
def log_messages(self) -> List[str]:
"""Program log messages from simulation."""
@property
def return_data(self) -> Optional[bytes]:
"""Program return data from simulation."""
@property
def accounts(self) -> Optional[List[Account]]:
"""Final account states after simulation."""
class InnerInstruction:
"""
Inner instruction from cross-program invocation.
"""
def __init__(self, instruction_index: int, instructions: List[Instruction]):
"""
Create inner instruction container.
Parameters:
- instruction_index: int, parent instruction index
- instructions: List[Instruction], inner instructions executed
"""
@property
def instruction_index(self) -> int:
"""Parent instruction index that generated these inner instructions."""
@property
def instructions(self) -> List[Instruction]:
"""Inner instructions that were executed."""
# Type aliases for transaction results
TransactionResult = Union[TransactionMetadata, FailedTransactionMetadata]
SimulateResult = Union[SimulatedTransactionInfo, FailedTransactionMetadata]Runtime configuration options and account filtering capabilities.
class LiteSvmConfig:
"""
Configuration options for LiteSVM runtime.
"""
def __init__(
self,
rent: Optional[Rent] = None,
fee_rate_governor: Optional['FeeRateGovernor'] = None,
genesis_config: Optional['GenesisConfig'] = None
):
"""
Create LiteSVM configuration.
Parameters:
- rent: Optional[Rent], rent configuration
- fee_rate_governor: Optional[FeeRateGovernor], fee calculation parameters
- genesis_config: Optional[GenesisConfig], genesis block configuration
"""
@classmethod
def default() -> 'LiteSvmConfig':
"""Create default configuration suitable for testing."""
class SimulationConfig:
"""
Configuration for transaction simulation.
"""
def __init__(
self,
sig_verify: bool = True,
replace_recent_blockhash: bool = False,
commitment: CommitmentLevel = CommitmentLevel.Processed,
accounts_to_return: Optional[List[Pubkey]] = None
):
"""
Create simulation configuration.
Parameters:
- sig_verify: bool, whether to verify signatures during simulation
- replace_recent_blockhash: bool, use current blockhash instead of transaction's
- commitment: CommitmentLevel, commitment level for simulation context
- accounts_to_return: Optional[List[Pubkey]], accounts to include in response
"""
class AccountFilter:
"""
Filter criteria for account queries.
"""
def __init__(self, offset: int, bytes: bytes):
"""
Create account filter by data content.
Parameters:
- offset: int, byte offset in account data
- bytes: bytes, data that must match at offset
"""
@classmethod
def memcmp(cls, offset: int, bytes: bytes) -> 'AccountFilter':
"""
Create memcmp filter.
Parameters:
- offset: int, byte offset to compare
- bytes: bytes, expected data at offset
Returns:
AccountFilter for memory comparison
"""
@classmethod
def data_size(cls, size: int) -> 'AccountFilter':
"""
Create data size filter.
Parameters:
- size: int, required account data size
Returns:
AccountFilter for data size matching
"""from solders.litesvm import LiteSVM
from solders.keypair import Keypair
from solders.system_program import transfer, TransferParams
# Create test environment
svm = LiteSVM()
# Create test accounts
payer = Keypair()
recipient = Keypair()
# Fund payer account
svm.airdrop(payer.pubkey(), 10_000_000_000) # 10 SOL
print(f"Payer balance: {svm.get_balance(payer.pubkey())} lamports")
print(f"Recipient balance: {svm.get_balance(recipient.pubkey())} lamports")
# Create transfer transaction
transfer_ix = transfer(TransferParams(
from_pubkey=payer.pubkey(),
to_pubkey=recipient.pubkey(),
lamports=1_000_000_000 # 1 SOL
))
# Build and sign transaction
from solders.transaction import Transaction
tx = Transaction.new_with_payer([transfer_ix], payer.pubkey())
tx.recent_blockhash = svm.get_latest_blockhash()
tx.sign([payer], tx.recent_blockhash)
# Execute transaction
result = svm.send_transaction(tx)
if isinstance(result, TransactionMetadata):
print("Transaction successful!")
print(f"Fee paid: {result.fee} lamports")
print(f"Compute units used: {result.compute_units_consumed}")
# Check updated balances
print(f"Payer balance after: {svm.get_balance(payer.pubkey())} lamports")
print(f"Recipient balance after: {svm.get_balance(recipient.pubkey())} lamports")
else:
print(f"Transaction failed: {result.error}")from solders.token.associated import get_associated_token_address
from solders.instruction import Instruction, AccountMeta
from solders.token import ID as TOKEN_PROGRAM_ID
# Create token mint and accounts for testing
def setup_token_test_environment(svm: LiteSVM):
"""Set up token mint and associated accounts for testing."""
# Create mint authority
mint_authority = Keypair()
svm.airdrop(mint_authority.pubkey(), 5_000_000_000) # 5 SOL for fees
# Create mint account
mint = Keypair()
# Create mint account with proper size and owner
mint_account = Account(
lamports=svm.get_rent().minimum_balance(82), # Mint account size
data=bytes(82), # Empty mint data (will be initialized)
owner=TOKEN_PROGRAM_ID,
executable=False,
rent_epoch=0
)
svm.set_account(mint.pubkey(), mint_account)
# Initialize mint (this would normally be done via instruction)
# For testing, we'll create a properly initialized mint
from solders.token.state import Mint
mint_data = Mint(
mint_authority=mint_authority.pubkey(),
supply=0,
decimals=9,
is_initialized=True,
freeze_authority=None
)
mint_account_initialized = Account(
lamports=mint_account.lamports,
data=mint_data.serialize(),
owner=TOKEN_PROGRAM_ID,
executable=False,
rent_epoch=0
)
svm.set_account(mint.pubkey(), mint_account_initialized)
return mint_authority, mint
def test_token_operations(svm: LiteSVM):
"""Test token minting and transfers."""
mint_authority, mint = setup_token_test_environment(svm)
# Create user accounts
user1 = Keypair()
user2 = Keypair()
svm.airdrop(user1.pubkey(), 2_000_000_000) # Fund for fees
svm.airdrop(user2.pubkey(), 2_000_000_000)
# Calculate associated token accounts
user1_ata = get_associated_token_address(user1.pubkey(), mint.pubkey())
user2_ata = get_associated_token_address(user2.pubkey(), mint.pubkey())
print(f"User1 ATA: {user1_ata}")
print(f"User2 ATA: {user2_ata}")
# Test ATA creation and token minting would go here
# This requires implementing the full SPL token instruction set
return mint_authority, mint, user1, user2def test_transaction_simulation(svm: LiteSVM):
"""Test transaction simulation before execution."""
# Setup accounts
payer = Keypair()
recipient = Keypair()
svm.airdrop(payer.pubkey(), 5_000_000_000)
# Create transaction
transfer_ix = transfer(TransferParams(
from_pubkey=payer.pubkey(),
to_pubkey=recipient.pubkey(),
lamports=2_000_000_000 # 2 SOL
))
tx = Transaction.new_with_payer([transfer_ix], payer.pubkey())
tx.recent_blockhash = svm.get_latest_blockhash()
tx.sign([payer], tx.recent_blockhash)
# Simulate before executing
simulation_config = SimulationConfig(
sig_verify=True,
replace_recent_blockhash=False,
accounts_to_return=[payer.pubkey(), recipient.pubkey()]
)
sim_result = svm.simulate_transaction(tx, simulation_config)
if isinstance(sim_result, SimulatedTransactionInfo):
print("Simulation successful!")
print(f"Estimated compute units: {sim_result.compute_units_consumed}")
print(f"Log messages: {len(sim_result.log_messages)}")
# Show final account states
if sim_result.accounts:
for i, account in enumerate(sim_result.accounts):
print(f"Account {i} final balance: {account.lamports}")
# Now execute the actual transaction
result = svm.send_transaction(tx)
print(f"Actual execution result: {type(result).__name__}")
else:
print(f"Simulation failed: {sim_result.error}")
print("Transaction would fail, not executing")def test_custom_program(svm: LiteSVM):
"""Test custom program deployment and execution."""
# Load custom program (would need actual program binary)
program_id = Keypair().pubkey() # Random program ID for example
# Note: In real usage, you'd load actual program bytecode:
# program_elf = open("my_program.so", "rb").read()
# svm.load_program(program_id, program_elf)
# For this example, we'll test with system program functionality
payer = Keypair()
svm.airdrop(payer.pubkey(), 10_000_000_000)
# Create account for program data
data_account = Keypair()
# Create account creation instruction
from solders.system_program import create_account, CreateAccountParams
create_ix = create_account(CreateAccountParams(
from_pubkey=payer.pubkey(),
to_pubkey=data_account.pubkey(),
lamports=svm.get_rent().minimum_balance(100),
space=100,
owner=program_id # Our "custom" program owns this account
))
# Execute account creation
tx = Transaction.new_with_payer([create_ix], payer.pubkey())
tx.recent_blockhash = svm.get_latest_blockhash()
tx.sign([payer, data_account], tx.recent_blockhash)
result = svm.send_transaction(tx)
if isinstance(result, TransactionMetadata):
print("Account created successfully!")
# Verify account was created
created_account = svm.get_account(data_account.pubkey())
if created_account:
print(f"Account owner: {created_account.owner}")
print(f"Account size: {len(created_account.data)} bytes")
# Test program account query
program_accounts = svm.get_program_accounts(program_id)
print(f"Found {len(program_accounts)} accounts owned by program")
else:
print(f"Account creation failed: {result.error}")def test_multi_instruction_transaction(svm: LiteSVM):
"""Test transaction with multiple instructions and cross-program invocations."""
# Setup
payer = Keypair()
recipient1 = Keypair()
recipient2 = Keypair()
svm.airdrop(payer.pubkey(), 10_000_000_000)
# Create multiple instructions
from solders.compute_budget import set_compute_unit_limit, set_compute_unit_price
instructions = [
set_compute_unit_limit(300_000), # Set compute budget
set_compute_unit_price(1000), # Set priority fee
transfer(TransferParams( # First transfer
from_pubkey=payer.pubkey(),
to_pubkey=recipient1.pubkey(),
lamports=1_000_000_000
)),
transfer(TransferParams( # Second transfer
from_pubkey=payer.pubkey(),
to_pubkey=recipient2.pubkey(),
lamports=500_000_000
))
]
# Execute transaction
tx = Transaction.new_with_payer(instructions, payer.pubkey())
tx.recent_blockhash = svm.get_latest_blockhash()
tx.sign([payer], tx.recent_blockhash)
result = svm.send_transaction(tx)
if isinstance(result, TransactionMetadata):
print("Multi-instruction transaction successful!")
print(f"Total compute units: {result.compute_units_consumed}")
print(f"Transaction fee: {result.fee}")
print(f"Log messages: {len(result.log_messages)}")
# Verify balances
balances = {
"payer": svm.get_balance(payer.pubkey()),
"recipient1": svm.get_balance(recipient1.pubkey()),
"recipient2": svm.get_balance(recipient2.pubkey())
}
for name, balance in balances.items():
print(f"{name} balance: {balance:,} lamports")
else:
print(f"Transaction failed: {result.error}")
def test_time_dependent_behavior(svm: LiteSVM):
"""Test behavior that depends on slots/time."""
initial_clock = svm.get_clock()
print(f"Initial slot: {initial_clock.slot}")
print(f"Initial epoch: {initial_clock.epoch}")
# Advance time
svm.advance_slots(100)
updated_clock = svm.get_clock()
print(f"After advancing slot: {updated_clock.slot}")
print(f"Slot difference: {updated_clock.slot - initial_clock.slot}")
# Test blockhash changes
initial_blockhash = svm.get_latest_blockhash()
svm.advance_slots(1)
new_blockhash = svm.get_latest_blockhash()
print(f"Blockhash changed: {initial_blockhash != new_blockhash}")
def benchmark_transaction_performance(svm: LiteSVM):
"""Benchmark transaction execution performance."""
import time
# Setup
payers = [Keypair() for _ in range(100)]
recipients = [Keypair() for _ in range(100)]
for payer in payers:
svm.airdrop(payer.pubkey(), 2_000_000_000)
# Prepare transactions
transactions = []
for payer, recipient in zip(payers, recipients):
transfer_ix = transfer(TransferParams(
from_pubkey=payer.pubkey(),
to_pubkey=recipient.pubkey(),
lamports=1_000_000
))
tx = Transaction.new_with_payer([transfer_ix], payer.pubkey())
tx.recent_blockhash = svm.get_latest_blockhash()
tx.sign([payer], tx.recent_blockhash)
transactions.append(tx)
# Execute and time
start_time = time.time()
results = []
for tx in transactions:
result = svm.send_transaction(tx)
results.append(result)
end_time = time.time()
# Analyze results
successful = sum(1 for r in results if isinstance(r, TransactionMetadata))
total_time = end_time - start_time
print(f"Executed {len(transactions)} transactions in {total_time:.3f}s")
print(f"Success rate: {successful}/{len(transactions)} ({successful/len(transactions)*100:.1f}%)")
print(f"Throughput: {len(transactions)/total_time:.1f} TPS")
if successful > 0:
successful_results = [r for r in results if isinstance(r, TransactionMetadata)]
avg_compute = sum(r.compute_units_consumed for r in successful_results) / len(successful_results)
avg_fee = sum(r.fee for r in successful_results) / len(successful_results)
print(f"Average compute units: {avg_compute:.0f}")
print(f"Average fee: {avg_fee:.0f} lamports")class TestEnvironment:
"""Helper class for managing test environments."""
def __init__(self):
self.svm = LiteSVM()
self.funded_accounts = {}
def create_funded_keypair(self, lamports: int = 10_000_000_000) -> Keypair:
"""Create and fund a new keypair."""
keypair = Keypair()
self.svm.airdrop(keypair.pubkey(), lamports)
self.funded_accounts[keypair.pubkey()] = keypair
return keypair
def execute_and_assert_success(self, transaction: Transaction) -> TransactionMetadata:
"""Execute transaction and assert it succeeds."""
result = self.svm.send_transaction(transaction)
assert isinstance(result, TransactionMetadata), f"Transaction failed: {result.error if hasattr(result, 'error') else 'Unknown error'}"
return result
def simulate_and_assert_success(self, transaction: Transaction) -> SimulatedTransactionInfo:
"""Simulate transaction and assert it succeeds."""
result = self.svm.simulate_transaction(transaction)
assert isinstance(result, SimulatedTransactionInfo), f"Simulation failed: {result.error if hasattr(result, 'error') else 'Unknown error'}"
return result
def assert_balance_change(self, pubkey: Pubkey, expected_change: int, tolerance: int = 0):
"""Assert account balance changed by expected amount."""
current_balance = self.svm.get_balance(pubkey)
if pubkey not in self.last_balances:
self.last_balances[pubkey] = current_balance
return
actual_change = current_balance - self.last_balances[pubkey]
assert abs(actual_change - expected_change) <= tolerance, \
f"Expected balance change {expected_change}, got {actual_change}"
self.last_balances[pubkey] = current_balance
def snapshot_balances(self):
"""Take snapshot of all tracked account balances."""
self.last_balances = {}
for pubkey in self.funded_accounts.keys():
self.last_balances[pubkey] = self.svm.get_balance(pubkey)
# Usage example
def test_with_environment():
"""Example using the test environment helper."""
env = TestEnvironment()
# Create test accounts
payer = env.create_funded_keypair(10_000_000_000) # 10 SOL
recipient = env.create_funded_keypair(0) # Empty account
# Snapshot initial state
env.snapshot_balances()
# Create and execute transaction
transfer_amount = 1_000_000_000 # 1 SOL
transfer_ix = transfer(TransferParams(
from_pubkey=payer.pubkey(),
to_pubkey=recipient.pubkey(),
lamports=transfer_amount
))
tx = Transaction.new_with_payer([transfer_ix], payer.pubkey())
tx.recent_blockhash = env.svm.get_latest_blockhash()
tx.sign([payer], tx.recent_blockhash)
result = env.execute_and_assert_success(tx)
# Verify expected balance changes
env.assert_balance_change(recipient.pubkey(), transfer_amount)
env.assert_balance_change(payer.pubkey(), -(transfer_amount + result.fee))
print("Test completed successfully!")def measure_litesvm_performance():
"""Measure LiteSVM performance characteristics."""
svm = LiteSVM()
# Measure account creation throughput
start = time.time()
for i in range(1000):
keypair = Keypair()
svm.airdrop(keypair.pubkey(), 1000000)
account_creation_time = time.time() - start
print(f"Account creation: {1000/account_creation_time:.0f} accounts/second")
# Measure transaction throughput
payers = [Keypair() for _ in range(100)]
recipients = [Keypair() for _ in range(100)]
for payer in payers:
svm.airdrop(payer.pubkey(), 100_000_000)
transactions = []
for payer, recipient in zip(payers, recipients):
transfer_ix = transfer(TransferParams(
from_pubkey=payer.pubkey(),
to_pubkey=recipient.pubkey(),
lamports=1_000_000
))
tx = Transaction.new_with_payer([transfer_ix], payer.pubkey())
tx.recent_blockhash = svm.get_latest_blockhash()
tx.sign([payer], tx.recent_blockhash)
transactions.append(tx)
start = time.time()
for tx in transactions:
svm.send_transaction(tx)
transaction_time = time.time() - start
print(f"Transaction throughput: {len(transactions)/transaction_time:.0f} TPS")# LiteSVM limitations to be aware of:
# 1. No network communication - purely local execution
# 2. Single-threaded execution model
# 3. Simplified validator behavior
# 4. No stake/vote program functionality
# 5. Limited to loaded programs only
# 6. Deterministic but not identical to mainnet timing
# Best practices:
def litesvm_best_practices():
"""Best practices for using LiteSVM effectively."""
# 1. Use for unit tests and integration tests
# 2. Test edge cases and error conditions
# 3. Benchmark performance locally
# 4. Validate with devnet/testnet before mainnet
# 5. Keep test environments isolated
# 6. Use proper assertion helpers
# 7. Clean up resources between tests
pass