Python bindings for QuasarSVM with solders integration
Project description
QuasarSVM Python Bindings
Python bindings for QuasarSVM with solders integration.
Installation
pip install quasar-svm
Requirements
- Python >= 3.10
- solders >= 0.27.0
Quick Start
from quasar_svm import (
QuasarSvm,
create_keyed_mint_account,
create_keyed_token_account,
SPL_TOKEN_PROGRAM_ID,
)
from solders.pubkey import Pubkey
from solders.instruction import Instruction, AccountMeta
# Initialize with SPL programs loaded
with QuasarSvm() as svm:
# Create accounts
authority = Pubkey.new_unique()
mint = create_keyed_mint_account(
Pubkey.new_unique(),
mint_authority=authority,
decimals=6,
supply=10_000,
)
token_account = create_keyed_token_account(
Pubkey.new_unique(),
mint=mint.address,
owner=authority,
amount=1_000,
)
# Execute instruction
result = svm.process_instruction(transfer_ix, [mint, token_account])
result.assert_success()
print(f"Compute units: {result.compute_units}")
Core API
QuasarSvm
Main class for SVM execution.
from quasar_svm import QuasarSvm
# Auto-load all SPL programs
svm = QuasarSvm()
# Selective program loading
svm = QuasarSvm(
load_token=True,
load_token_2022=False,
load_associated_token=True,
)
# Context manager for automatic cleanup
with QuasarSvm() as svm:
result = svm.process_instruction(ix, accounts)
Methods
add_program(program_id: Pubkey, elf: bytes, loader_version: int = 3)
Load a custom BPF program.
from quasar_svm import LOADER_V2, LOADER_V3
svm.add_program(program_id, elf_data, LOADER_V3)
process_instruction(instruction: Instruction, accounts: List[KeyedAccount]) -> ExecutionResult
Execute a single instruction.
result = svm.process_instruction(ix, [account1, account2])
process_instruction_chain(instructions: List[Instruction], accounts: List[KeyedAccount]) -> ExecutionResult
Execute multiple instructions atomically.
result = svm.process_instruction_chain([ix1, ix2], accounts)
Sysvar Configuration
# Warp to future slot
svm.warp_to_slot(100)
# Set clock
svm.set_clock(
slot=100,
epoch_start_timestamp=0,
epoch=0,
leader_schedule_epoch=0,
unix_timestamp=1000000,
)
# Set rent
svm.set_rent(lamports_per_byte_year=6960)
# Set epoch schedule
svm.set_epoch_schedule(
slots_per_epoch=432000,
leader_schedule_slot_offset=0,
warmup=False,
first_normal_epoch=0,
first_normal_slot=0,
)
# Set compute budget
svm.set_compute_budget(max_units=200_000)
ExecutionResult
Rich result object with metadata and helper methods.
Properties
result.status # ExecutionStatus (ok or error)
result.compute_units # int - compute units consumed
result.execution_time_us # int - execution time in microseconds
result.return_data # bytes - program return data
result.accounts # List[KeyedAccount] - resulting accounts
result.logs # List[str] - program logs
# RPC metadata
result.pre_balances # List[int]
result.post_balances # List[int]
result.pre_token_balances # List[TokenBalance]
result.post_token_balances # List[TokenBalance]
result.execution_trace # ExecutionTrace
Methods
is_success() -> bool
Check if execution succeeded.
if result.is_success():
print("Success!")
is_error() -> bool
Check if execution failed.
assert_success()
Raise RuntimeError if execution failed.
result.assert_success()
assert_error(expected: ProgramError)
Raise RuntimeError if didn't fail with expected error.
from quasar_svm.types import ProgramErrorInsufficientFunds
result.assert_error(ProgramErrorInsufficientFunds())
assert_custom_error(code: int)
Convenience for asserting custom error code.
result.assert_custom_error(6001)
account(address: Pubkey, decoder=None) -> KeyedAccount | Any | None
Get account by address, optionally decode data.
# Get raw account
acct = result.account(address)
print(acct.lamports)
# Decode account data
def decode_token(data: bytes) -> dict:
# Your decoder logic
return {...}
token = result.account(address, decode_token)
print(token['amount'])
print_logs()
Print all logs to stdout.
result.print_logs()
Account Factories
Convenient functions for creating pre-initialized accounts.
System Account
from quasar_svm import create_keyed_system_account, LAMPORTS_PER_SOL
account = create_keyed_system_account(
Pubkey.new_unique(),
lamports=LAMPORTS_PER_SOL, # 1 SOL
)
Mint Account
from quasar_svm import create_keyed_mint_account, SPL_TOKEN_PROGRAM_ID
mint = create_keyed_mint_account(
Pubkey.new_unique(),
mint_authority=authority_pubkey,
decimals=6,
supply=10_000,
freeze_authority=None, # optional
token_program_id=SPL_TOKEN_PROGRAM_ID, # default
)
Token Account
from quasar_svm import create_keyed_token_account
token_account = create_keyed_token_account(
Pubkey.new_unique(),
mint=mint_pubkey,
owner=owner_pubkey,
amount=1_000,
token_program_id=SPL_TOKEN_PROGRAM_ID, # default
)
Associated Token Account
from quasar_svm import create_keyed_associated_token_account
# Automatically derives ATA address
ata = create_keyed_associated_token_account(
owner=owner_pubkey,
mint=mint_pubkey,
amount=1_000,
token_program_id=SPL_TOKEN_PROGRAM_ID, # default
)
print(ata.address) # Derived ATA address
Program Constants
from quasar_svm import (
SPL_TOKEN_PROGRAM_ID,
SPL_TOKEN_2022_PROGRAM_ID,
SPL_ASSOCIATED_TOKEN_PROGRAM_ID,
SYSTEM_PROGRAM_ID,
LOADER_V2,
LOADER_V3,
LAMPORTS_PER_SOL,
)
Complete Example
from quasar_svm import (
QuasarSvm,
create_keyed_mint_account,
create_keyed_associated_token_account,
SPL_TOKEN_PROGRAM_ID,
)
from solders.pubkey import Pubkey
from solders.instruction import Instruction, AccountMeta
# Create authority and accounts
authority = Pubkey.new_unique()
recipient = Pubkey.new_unique()
# Initialize VM with SPL programs
with QuasarSvm() as svm:
# Create mint
mint = create_keyed_mint_account(
Pubkey.new_unique(),
mint_authority=authority,
decimals=6,
supply=10_000,
)
# Create token accounts (ATAs derived automatically)
alice = create_keyed_associated_token_account(
owner=authority,
mint=mint.address,
amount=5_000,
)
bob = create_keyed_associated_token_account(
owner=recipient,
mint=mint.address,
amount=0,
)
# Build transfer instruction
# Use spl-token instruction builders or build manually
transfer_ix = Instruction(
program_id=SPL_TOKEN_PROGRAM_ID,
data=bytes([3]) + (1_000).to_bytes(8, 'little'), # Transfer instruction
accounts=[
AccountMeta(alice.address, is_signer=False, is_writable=True),
AccountMeta(bob.address, is_signer=False, is_writable=True),
AccountMeta(authority, is_signer=True, is_writable=False),
],
)
# Execute
result = svm.process_instruction(transfer_ix, [mint, alice, bob])
# Verify
result.assert_success()
print(f"Compute units: {result.compute_units}")
print(f"Execution time: {result.execution_time_us}μs")
# Check updated account
bob_account = result.account(bob.address)
print(f"Bob's lamports: {bob_account.lamports}")
Type System
KeyedAccount
@dataclass
class KeyedAccount:
address: Pubkey # Account address
owner: Pubkey # Owner program
lamports: int # Account balance
data: bytes # Account data
executable: bool # Executable flag
ProgramError
Discriminated union of error types:
ProgramErrorCustom(code=6001)
ProgramErrorInvalidArgument()
ProgramErrorInvalidInstructionData()
ProgramErrorInsufficientFunds()
# ... and more
ExecutionStatus
ExecutionStatusOk() # Successful execution
ExecutionStatusError(error=ProgramErrorCustom(code=6001)) # Failed execution
Development
# Install dev dependencies
pip install -e ".[dev]"
# Run tests
pytest
# Format code
black quasar_svm/
ruff check quasar_svm/
# Type checking
mypy quasar_svm/
Comparison with TypeScript Kit Layer
The Python API closely mirrors the TypeScript kit layer:
| Feature | Python | TypeScript (kit) |
|---|---|---|
| Address Type | solders.Pubkey |
Address (branded string) |
| Instruction Type | solders.Instruction |
Instruction from @solana/instructions |
| Account Type | KeyedAccount |
Account<Uint8Array> from @solana/accounts |
| ATA Derivation | Pubkey.find_program_address() |
getProgramDerivedAddress() (async) |
| Memory Management | Context manager (with) |
using or explicit free() |
Both provide the same core functionality with type-safe, ergonomic APIs.
License
MIT
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distributions
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file quasar_svm-0.1.0-py3-none-any.whl.
File metadata
- Download URL: quasar_svm-0.1.0-py3-none-any.whl
- Upload date:
- Size: 9.8 MB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.14.0
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
cc47660f934bb53dcc3e6787575735f9852599a97d1e454f73577fa89ead3952
|
|
| MD5 |
bf496d365ada9b53dc864c9db4f76fab
|
|
| BLAKE2b-256 |
93426fb949ee826b9cbd2085e9c3c08b13a29270b0ab744bdc32b24a10b0b2a0
|