Python bindings for Solana Rust tools providing high-performance blockchain development primitives, RPC functionality, and testing infrastructure.
—
Transaction and message building with support for both legacy and versioned formats. This includes instruction compilation, message construction, address lookup table integration, and transaction signing for efficient Solana blockchain interactions.
Core transaction structures supporting both legacy and modern versioned formats with address lookup table compression.
class Transaction:
"""
Legacy transaction format containing instructions and signatures.
"""
def __init__(self, instructions: List[Instruction], payer: Optional[Pubkey]):
"""
Create transaction with instructions and payer.
Parameters:
- instructions: List[Instruction], instructions to execute
- payer: Optional[Pubkey], fee payer account
"""
@staticmethod
def new_with_payer(instructions: List[Instruction], payer: Optional[Pubkey]) -> 'Transaction':
"""
Create new transaction with specified payer.
Parameters:
- instructions: List[Instruction], instructions to include
- payer: Optional[Pubkey], account that pays transaction fees
Returns:
Transaction object
"""
@staticmethod
def new_unsigned(instructions: List[Instruction]) -> 'Transaction':
"""
Create unsigned transaction without payer.
Parameters:
- instructions: List[Instruction], instructions to include
Returns:
Unsigned Transaction object
"""
@staticmethod
def new_with_compiled_instructions(
num_required_signatures: int,
num_readonly_signed_accounts: int,
num_readonly_unsigned_accounts: int,
account_keys: List[Pubkey],
recent_blockhash: Hash,
instructions: List[CompiledInstruction]
) -> 'Transaction':
"""
Create transaction from pre-compiled instructions.
Parameters:
- num_required_signatures: int, number of signatures required
- num_readonly_signed_accounts: int, readonly accounts that must sign
- num_readonly_unsigned_accounts: int, readonly accounts (no signing)
- account_keys: List[Pubkey], all accounts referenced in transaction
- recent_blockhash: Hash, recent network blockhash
- instructions: List[CompiledInstruction], compiled instructions
Returns:
Transaction object
"""
def sign(self, signers: List[Union[Keypair, Presigner]], recent_blockhash: Hash) -> None:
"""
Sign transaction with provided signers.
Parameters:
- signers: List of keypairs or presigners
- recent_blockhash: Hash, recent network blockhash for replay protection
"""
def partial_sign(self, signers: List[Union[Keypair, Presigner]], recent_blockhash: Hash) -> None:
"""
Partially sign transaction (allows additional signatures later).
Parameters:
- signers: List of available signers (subset of required)
- recent_blockhash: Hash, recent network blockhash
"""
def verify(self) -> bool:
"""
Verify all signatures in the transaction.
Returns:
bool, True if all signatures are valid
"""
def serialize(self) -> bytes:
"""
Serialize transaction to bytes for network transmission.
Returns:
bytes, serialized transaction
"""
@classmethod
def deserialize(cls, data: bytes) -> 'Transaction':
"""
Deserialize transaction from bytes.
Parameters:
- data: bytes, serialized transaction data
Returns:
Transaction object
"""
@property
def message(self) -> Message:
"""Get the transaction message."""
@property
def signatures(self) -> List[Signature]:
"""Get transaction signatures."""class VersionedTransaction:
"""
Versioned transaction supporting address lookup tables for compression.
"""
def __init__(self, message: VersionedMessage, signatures: List[Signature]):
"""
Create versioned transaction with message and signatures.
Parameters:
- message: VersionedMessage, transaction message (legacy or v0)
- signatures: List[Signature], transaction signatures
"""
@staticmethod
def new_unsigned(message: VersionedMessage) -> 'VersionedTransaction':
"""
Create unsigned versioned transaction.
Parameters:
- message: VersionedMessage, transaction message
Returns:
Unsigned VersionedTransaction
"""
def sign(self, signers: List[Union[Keypair, Presigner]]) -> None:
"""
Sign versioned transaction.
Parameters:
- signers: List of keypairs or presigners
"""
def serialize(self) -> bytes:
"""
Serialize versioned transaction to bytes.
Returns:
bytes, serialized transaction
"""
@classmethod
def deserialize(cls, data: bytes) -> 'VersionedTransaction':
"""
Deserialize versioned transaction from bytes.
Parameters:
- data: bytes, serialized transaction data
Returns:
VersionedTransaction object
"""
@property
def message(self) -> VersionedMessage:
"""Get the transaction message."""
@property
def signatures(self) -> List[Signature]:
"""Get transaction signatures."""Message structures that contain instructions and account metadata for transaction execution.
class Message:
"""
Legacy message format containing instructions and account metadata.
"""
def __init__(
self,
header: MessageHeader,
account_keys: List[Pubkey],
recent_blockhash: Hash,
instructions: List[CompiledInstruction]
):
"""
Create message with header, accounts, blockhash, and instructions.
Parameters:
- header: MessageHeader, message metadata
- account_keys: List[Pubkey], accounts referenced in message
- recent_blockhash: Hash, recent network blockhash
- instructions: List[CompiledInstruction], compiled instructions
"""
@staticmethod
def new_with_compiled_instructions(
num_required_signatures: int,
num_readonly_signed_accounts: int,
num_readonly_unsigned_accounts: int,
account_keys: List[Pubkey],
recent_blockhash: Hash,
instructions: List[CompiledInstruction]
) -> 'Message':
"""
Create message from compiled instructions and metadata.
Returns:
Message object
"""
@staticmethod
def new_with_blockhash(instructions: List[Instruction], payer: Optional[Pubkey], blockhash: Hash) -> 'Message':
"""
Create message from instructions with specified blockhash.
Parameters:
- instructions: List[Instruction], instructions to compile
- payer: Optional[Pubkey], fee payer
- blockhash: Hash, recent blockhash
Returns:
Message object
"""
def serialize(self) -> bytes:
"""Serialize message to bytes."""
@classmethod
def deserialize(cls, data: bytes) -> 'Message':
"""Deserialize message from bytes."""
def is_key_passed_to_program(self, key_index: int, program_id_index: int) -> bool:
"""
Check if account key is passed to specific program.
Parameters:
- key_index: int, index of account key
- program_id_index: int, index of program ID
Returns:
bool, True if key is passed to program
"""
def is_key_called_as_program(self, key_index: int) -> bool:
"""
Check if account key is called as program.
Parameters:
- key_index: int, index of account key
Returns:
bool, True if key is invoked as program
"""
@property
def header(self) -> MessageHeader:
"""Get message header."""
@property
def account_keys(self) -> List[Pubkey]:
"""Get account keys."""
@property
def recent_blockhash(self) -> Hash:
"""Get recent blockhash."""
@property
def instructions(self) -> List[CompiledInstruction]:
"""Get compiled instructions."""class MessageV0:
"""
Version 0 message with address lookup table support for compression.
"""
def __init__(
self,
header: MessageHeader,
account_keys: List[Pubkey],
recent_blockhash: Hash,
instructions: List[CompiledInstruction],
address_table_lookups: List[MessageAddressTableLookup]
):
"""
Create v0 message with address lookup tables.
Parameters:
- header: MessageHeader, message metadata
- account_keys: List[Pubkey], static account keys
- recent_blockhash: Hash, recent network blockhash
- instructions: List[CompiledInstruction], compiled instructions
- address_table_lookups: List[MessageAddressTableLookup], lookup table references
"""
@staticmethod
def try_compile(
payer: Pubkey,
instructions: List[Instruction],
address_lookup_table_accounts: List[AddressLookupTableAccount],
recent_blockhash: Hash
) -> 'MessageV0':
"""
Compile instructions into v0 message using lookup tables.
Parameters:
- payer: Pubkey, fee payer account
- instructions: List[Instruction], instructions to compile
- address_lookup_table_accounts: List[AddressLookupTableAccount], available lookup tables
- recent_blockhash: Hash, recent blockhash
Returns:
MessageV0 object
Raises:
- CompileError: if compilation fails
"""
def serialize(self) -> bytes:
"""Serialize v0 message to bytes."""
@classmethod
def deserialize(cls, data: bytes) -> 'MessageV0':
"""Deserialize v0 message from bytes."""
@property
def address_table_lookups(self) -> List[MessageAddressTableLookup]:
"""Get address table lookups."""Supporting structures for message construction and metadata.
class MessageHeader:
"""
Message header containing account signature and readonly counts.
"""
def __init__(
self,
num_required_signatures: int,
num_readonly_signed_accounts: int,
num_readonly_unsigned_accounts: int
):
"""
Create message header with account counts.
Parameters:
- num_required_signatures: int, total signatures required
- num_readonly_signed_accounts: int, readonly accounts that must sign
- num_readonly_unsigned_accounts: int, readonly accounts (no signing)
"""
@property
def num_required_signatures(self) -> int:
"""Number of signatures required."""
@property
def num_readonly_signed_accounts(self) -> int:
"""Number of readonly accounts that must sign."""
@property
def num_readonly_unsigned_accounts(self) -> int:
"""Number of readonly unsigned accounts."""class MessageAddressTableLookup:
"""
Reference to addresses from an address lookup table.
"""
def __init__(
self,
account_key: Pubkey,
writable_indexes: List[int],
readonly_indexes: List[int]
):
"""
Create address table lookup.
Parameters:
- account_key: Pubkey, address of lookup table account
- writable_indexes: List[int], indexes of writable addresses
- readonly_indexes: List[int], indexes of readonly addresses
"""
@property
def account_key(self) -> Pubkey:
"""Lookup table account address."""
@property
def writable_indexes(self) -> List[int]:
"""Writable address indexes."""
@property
def readonly_indexes(self) -> List[int]:
"""Readonly address indexes."""Individual instruction construction with program invocation and account specification.
class Instruction:
"""
Transaction instruction containing program ID, accounts, and data.
"""
def __init__(self, program_id: Pubkey, accounts: List[AccountMeta], data: bytes):
"""
Create instruction with program, accounts, and data.
Parameters:
- program_id: Pubkey, program to invoke
- accounts: List[AccountMeta], accounts required by instruction
- data: bytes, instruction-specific data
"""
@property
def program_id(self) -> Pubkey:
"""Program to invoke."""
@property
def accounts(self) -> List[AccountMeta]:
"""Accounts required by instruction."""
@property
def data(self) -> bytes:
"""Instruction-specific data."""class AccountMeta:
"""
Account metadata specifying permissions and signing requirements.
"""
def __init__(self, pubkey: Pubkey, is_signer: bool, is_writable: bool):
"""
Create account metadata.
Parameters:
- pubkey: Pubkey, account address
- is_signer: bool, whether account must sign transaction
- is_writable: bool, whether account data may be modified
"""
@staticmethod
def new(pubkey: Pubkey, is_signer: bool) -> 'AccountMeta':
"""
Create writable account metadata.
Parameters:
- pubkey: Pubkey, account address
- is_signer: bool, whether account must sign
Returns:
Writable AccountMeta
"""
@staticmethod
def new_readonly(pubkey: Pubkey, is_signer: bool) -> 'AccountMeta':
"""
Create readonly account metadata.
Parameters:
- pubkey: Pubkey, account address
- is_signer: bool, whether account must sign
Returns:
Readonly AccountMeta
"""
@property
def pubkey(self) -> Pubkey:
"""Account address."""
@property
def is_signer(self) -> bool:
"""Whether account must sign."""
@property
def is_writable(self) -> bool:
"""Whether account may be modified."""class CompiledInstruction:
"""
Compiled instruction with account indexes instead of pubkeys.
"""
def __init__(self, program_id_index: int, accounts: List[int], data: bytes):
"""
Create compiled instruction.
Parameters:
- program_id_index: int, index of program ID in account keys
- accounts: List[int], indexes of accounts in account keys
- data: bytes, instruction data
"""
@property
def program_id_index(self) -> int:
"""Program ID index in account keys."""
@property
def accounts(self) -> List[int]:
"""Account indexes."""
@property
def data(self) -> bytes:
"""Instruction data."""Transaction version handling and type definitions.
class Legacy:
"""Transaction version marker for legacy format."""
# Type aliases for version handling
TransactionVersion = Union[Legacy, int]
VersionedMessage = Union[Message, MessageV0]from solders.keypair import Keypair
from solders.pubkey import Pubkey
from solders.transaction import Transaction
from solders.instruction import Instruction, AccountMeta
from solders.hash import Hash
# Create keypairs
payer = Keypair()
recipient = Keypair()
program_id = Pubkey.from_string("11111111111111111111111111111112")
# Create instruction
accounts = [
AccountMeta(payer.pubkey(), is_signer=True, is_writable=True),
AccountMeta(recipient.pubkey(), is_signer=False, is_writable=True)
]
instruction = Instruction(program_id, accounts, b"instruction_data")
# Create and sign transaction
transaction = Transaction.new_with_payer([instruction], payer.pubkey())
recent_blockhash = Hash.from_string("11111111111111111111111111111112")
transaction.sign([payer], recent_blockhash)
# Serialize for transmission
serialized = transaction.serialize()from solders.transaction import VersionedTransaction
from solders.message import MessageV0
from solders.address_lookup_table_account import AddressLookupTableAccount
# Assume we have lookup table accounts loaded
lookup_tables = [] # List of AddressLookupTableAccount objects
# Compile v0 message
message_v0 = MessageV0.try_compile(
payer=payer.pubkey(),
instructions=[instruction],
address_lookup_table_accounts=lookup_tables,
recent_blockhash=recent_blockhash
)
# Create versioned transaction
versioned_tx = VersionedTransaction.new_unsigned(message_v0)
versioned_tx.sign([payer])from solders.system_program import transfer, TransferParams
from solders.compute_budget import set_compute_unit_limit
# Create multiple instructions
transfer_ix = transfer(TransferParams(
from_pubkey=payer.pubkey(),
to_pubkey=recipient.pubkey(),
lamports=1000000
))
compute_budget_ix = set_compute_unit_limit(200000)
# Combine in transaction
multi_instruction_tx = Transaction.new_with_payer([
compute_budget_ix, # Always include compute budget first
transfer_ix
], payer.pubkey())
multi_instruction_tx.sign([payer], recent_blockhash)# Create transaction requiring multiple signers
multi_signer_accounts = [
AccountMeta(payer.pubkey(), is_signer=True, is_writable=True),
AccountMeta(recipient.pubkey(), is_signer=True, is_writable=True), # Also must sign
AccountMeta(Pubkey.from_string("11111111111111111111111111111112"), is_signer=False, is_writable=False)
]
multi_signer_ix = Instruction(program_id, multi_signer_accounts, b"multi_signer_data")
multi_signer_tx = Transaction.new_with_payer([multi_signer_ix], payer.pubkey())
# Partial sign with available signers
multi_signer_tx.partial_sign([payer], recent_blockhash)
# Later, add remaining signatures
multi_signer_tx.partial_sign([recipient], recent_blockhash)
# Verify all signatures are present
assert multi_signer_tx.verify()from solders.message import Message, to_bytes_versioned, from_bytes_versioned
# Create message directly
message = Message.new_with_blockhash([instruction], payer.pubkey(), recent_blockhash)
# Serialize legacy message
message_bytes = message.serialize()
# Serialize versioned message
versioned_bytes = to_bytes_versioned(message)
# Deserialize versioned message
restored_message = from_bytes_versioned(versioned_bytes)# Union type for all signer implementations
Signer = Union[Keypair, Presigner, NullSigner]Different signer types provide flexibility for various signing scenarios:
class SanitizeError(Exception):
"""Transaction sanitization errors during construction or validation."""
class TransactionError(Exception):
"""General transaction processing errors."""
class CompileError(Exception):
"""Message compilation errors, especially with address lookup tables."""Common error scenarios:
from solders.transaction import Transaction, SanitizeError
from solders.message import MessageV0, CompileError
try:
# Invalid transaction construction
invalid_tx = Transaction.new_with_payer([], None) # No payer
except SanitizeError as e:
print(f"Transaction sanitization failed: {e}")
try:
# Message compilation failure
message_v0 = MessageV0.try_compile(
payer=payer.pubkey(),
instructions=[], # Empty instructions
address_lookup_table_accounts=[],
recent_blockhash=recent_blockhash
)
except CompileError as e:
print(f"Message compilation failed: {e}")