Transaction
solana.transaction
Library to package an atomic sequence of instructions to a transaction.
PACKET_DATA_SIZE
Constant for maximum over-the-wire size of a Transaction.
NonceInformation
NonceInformation to be used to build a Transaction.
Source code in solana/transaction.py
class NonceInformation(NamedTuple):
"""NonceInformation to be used to build a Transaction."""
nonce: Blockhash
"""The current Nonce blockhash."""
nonce_instruction: Instruction
"""AdvanceNonceAccount Instruction."""
nonce: Hash
The current Nonce blockhash.
nonce_instruction: Instruction
AdvanceNonceAccount Instruction.
Transaction
Transaction class to represent an atomic transaction.
Parameters:
Name | Type | Description | Default |
---|---|---|---|
recent_blockhash |
Optional[Blockhash] |
A recent transaction id. |
None |
nonce_info |
Optional[NonceInformation] |
Nonce information.
If populated, transaction will use a durable Nonce hash instead of a |
None |
fee_payer |
Optional[Pubkey] |
The transaction fee payer. |
None |
instructions |
Optional[Sequence[Instruction]] |
The instructions to be executed in this transaction. |
None |
Source code in solana/transaction.py
class Transaction:
"""Transaction class to represent an atomic transaction.
Args:
recent_blockhash: A recent transaction id.
nonce_info: Nonce information.
If populated, transaction will use a durable Nonce hash instead of a `recent_blockhash`.
fee_payer: The transaction fee payer.
instructions: The instructions to be executed in this transaction.
"""
def __init__(
self,
recent_blockhash: Optional[Blockhash] = None,
nonce_info: Optional[NonceInformation] = None,
fee_payer: Optional[Pubkey] = None,
instructions: Optional[Sequence[Instruction]] = None,
) -> None:
"""Init transaction object."""
warn(
"""Transaction is deprecated and will be removed in a later release.
Please use the Transaction module from solders.transaction instead.""",
DeprecationWarning,
)
self._solders = _build_solders_tx(
recent_blockhash=recent_blockhash,
nonce_info=nonce_info,
fee_payer=fee_payer,
instructions=instructions,
)
@classmethod
def from_solders(cls, txn: SoldersTx) -> Transaction:
"""Convert from a `solders` transaction.
Args:
txn: The `solders` transaction.
Returns:
The `solana-py` transaction.
"""
new_tx = cls()
new_tx._solders = txn
return new_tx
def to_solders(self) -> SoldersTx:
"""Convert to a `solders` transaction.
Returns:
The `solders` transaction.
"""
return self._solders
def __eq__(self, other: Any) -> bool:
"""Equality defintion for Transactions."""
if not isinstance(other, Transaction):
return False
return self.to_solders() == other.to_solders()
@property
def recent_blockhash(self) -> Optional[Blockhash]:
"""Optional[Blockhash]: The blockhash assigned to this transaction."""
return self._solders.message.recent_blockhash
@recent_blockhash.setter
def recent_blockhash(self, blockhash: Optional[Blockhash]) -> None: # noqa: D102
self._solders = _build_solders_tx(
recent_blockhash=blockhash,
nonce_info=None,
fee_payer=self.fee_payer,
instructions=self.instructions,
)
@property
def fee_payer(self) -> Optional[Pubkey]:
"""Optional[Pubkey]: The transaction fee payer."""
account_keys = self._solders.message.account_keys
return account_keys[0] if account_keys else None
@fee_payer.setter
def fee_payer(self, payer: Optional[Pubkey]) -> None: # noqa: D102
self._solders = _build_solders_tx(
recent_blockhash=self.recent_blockhash,
nonce_info=None,
fee_payer=payer,
instructions=self.instructions,
)
@property
def instructions(self) -> Tuple[Instruction, ...]:
"""Tuple[Instruction]: The instructions contained in this transaction."""
msg = self._solders.message
return tuple(_decompile_instructions(msg))
@instructions.setter
def instructions(self, ixns: Sequence[Instruction]) -> None: # noqa: D102
self._solders = _build_solders_tx(
recent_blockhash=self.recent_blockhash,
nonce_info=None,
fee_payer=self.fee_payer,
instructions=ixns,
)
@property
def signatures(self) -> Tuple[Signature, ...]:
"""Tuple[Signature]: Signatures for the transaction."""
return tuple(self._solders.signatures)
def signature(self) -> Signature:
"""The first (payer) Transaction signature.
Returns:
The payer signature.
"""
return self._solders.signatures[0]
def add(self, *args: Union[Transaction, Instruction]) -> Transaction:
"""Add one or more instructions to this Transaction.
Args:
*args: The instructions to add to this Transaction.
If a `Transaction` is passsed, the instructions will be extracted from it.
Returns:
The transaction with the added instructions.
"""
for arg in args:
if isinstance(arg, Transaction):
self.instructions = self.instructions + arg.instructions
elif isinstance(arg, Instruction):
self.instructions = (*self.instructions, arg)
else:
raise ValueError("invalid instruction:", arg)
return self
def compile_message(self) -> Message: # pylint: disable=too-many-locals
"""Compile transaction data.
Returns:
The compiled message.
"""
return self._solders.message
def serialize_message(self) -> bytes:
"""Get raw transaction data that need to be covered by signatures.
Returns:
The serialized message.
"""
return bytes(self.compile_message())
def sign_partial(self, *partial_signers: Keypair) -> None:
"""Partially sign a Transaction with the specified keypairs.
All the caveats from the `sign` method apply to `sign_partial`
"""
self._solders.partial_sign(partial_signers, self._solders.message.recent_blockhash)
def sign(self, *signers: Keypair) -> None:
"""Sign the Transaction with the specified accounts.
Multiple signatures may be applied to a Transaction. The first signature
is considered "primary" and is used when testing for Transaction confirmation.
Transaction fields should not be modified after the first call to `sign`,
as doing so may invalidate the signature and cause the Transaction to be
rejected.
The Transaction must be assigned a valid `recent_blockhash` before invoking this method.
"""
self._solders.sign(signers, self._solders.message.recent_blockhash)
def add_signature(self, pubkey: Pubkey, signature: Signature) -> None:
"""Add an externally created signature to a transaction.
Args:
pubkey: The public key that created the signature.
signature: The signature to add.
"""
presigner = Presigner(pubkey, signature)
self._solders.partial_sign([presigner], self._solders.message.recent_blockhash)
def verify_signatures(self) -> bool:
"""Verify signatures of a complete, signed Transaction.
Returns:
a bool indicating if the signatures are correct or not.
"""
try:
self._solders.verify()
except TransactionError:
return False
return True
def serialize(self, verify_signatures: bool = True) -> bytes:
"""Serialize the Transaction in the wire format.
The Transaction must have a valid `signature` before invoking this method.
verify_signatures can be added if the signature does not require to be verified.
Args:
verify_signatures: a bool indicating to verify the signature or not. Defaults to True
Example:
>>> from solders.keypair import Keypair
>>> from solders.pubkey import Pubkey
>>> from solders.hash import Hash
>>> from solders.system_program import transfer, TransferParams
>>> leading_zeros = [0] * 31
>>> seed = bytes(leading_zeros + [1])
>>> sender, receiver = Keypair.from_seed(seed), Pubkey(leading_zeros + [2])
>>> transfer_tx = Transaction().add(transfer(TransferParams(from_pubkey=sender.pubkey(), to_pubkey=receiver, lamports=1000)))
>>> transfer_tx.recent_blockhash = Hash(leading_zeros + [3])
>>> transfer_tx.sign(sender)
>>> transfer_tx.serialize().hex()
'019d53be8af3a7c30f86c1092d2c3ea61d270c0cfa275a23ba504674c8fbbb724827b23b42dc8e08019e23120f1b6f40f9799355ce54185b4415be37ca2cee6e0e010001034cb5abf6ad79fbf5abbccafcc269d85cd2651ed4b885b5869f241aedf0a5ba2900000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000301020200010c02000000e803000000000000'
Returns:
The serialized transaction.
""" # noqa: E501 pylint: disable=line-too-long
if self.signatures == [Signature.default() for sig in self.signatures]:
raise AttributeError("transaction has not been signed")
if verify_signatures and not self.verify_signatures():
raise AttributeError("transaction has not been signed correctly")
return bytes(self._solders)
@classmethod
def deserialize(cls, raw_transaction: bytes) -> Transaction:
"""Parse a wire transaction into a Transaction object.
Example:
>>> raw_transaction = bytes.fromhex(
... '019d53be8af3a7c30f86c1092d2c3ea61d270c0cfa2'
... '75a23ba504674c8fbbb724827b23b42dc8e08019e23'
... '120f1b6f40f9799355ce54185b4415be37ca2cee6e0'
... 'e010001034cb5abf6ad79fbf5abbccafcc269d85cd2'
... '651ed4b885b5869f241aedf0a5ba290000000000000'
... '0000000000000000000000000000000000000000000'
... '0000000200000000000000000000000000000000000'
... '0000000000000000000000000000000000000000000'
... '0000000000000000000000000000000000000000000'
... '000000301020200010c02000000e803000000000000'
... )
>>> type(Transaction.deserialize(raw_transaction))
<class 'solana.transaction.Transaction'>
Returns:
The deserialized transaction.
"""
return cls.from_solders(SoldersTx.from_bytes(raw_transaction))
@classmethod
def populate(cls, message: Message, signatures: List[Signature]) -> Transaction:
"""Populate Transaction object from message and signatures.
Example:
>>> raw_message = bytes.fromhex(
... '0200030500000000000000000000000000000000000000000000'
... '0000000000000000000100000000000000000000000000000000'
... '0000000000000000000000000000000200000000000000000000'
... '0000000000000000000000000000000000000000000300000000'
... '0000000000000000000000000000000000000000000000000000'
... '0004000000000000000000000000000000000000000000000000'
... '0000000000000005c49ae77603782054f17a9decea43b444eba0'
... 'edb12c6f1d31c6e0e4a84bf052eb010403010203050909090909'
... )
>>> from solders.message import Message
>>> from solders.signature import Signature
>>> msg = Message.from_bytes(raw_message)
>>> signatures = [Signature(bytes([1] * Signature.LENGTH)), Signature(bytes([2] * Signature.LENGTH))]
>>> type(Transaction.populate(msg, signatures))
<class 'solana.transaction.Transaction'>
Returns:
The populated transaction.
"""
return cls.from_solders(SoldersTx.populate(message, signatures))
fee_payer: Optional[Pubkey]
property
writable
Optional[Pubkey]: The transaction fee payer.
instructions: Tuple[Instruction, ...]
property
writable
Tuple[Instruction]: The instructions contained in this transaction.
recent_blockhash: Optional[Blockhash]
property
writable
Optional[Blockhash]: The blockhash assigned to this transaction.
signatures: Tuple[Signature, ...]
property
readonly
Tuple[Signature]: Signatures for the transaction.
__init__(self, recent_blockhash=None, nonce_info=None, fee_payer=None, instructions=None)
special
Init transaction object.
Source code in solana/transaction.py
def __init__(
self,
recent_blockhash: Optional[Blockhash] = None,
nonce_info: Optional[NonceInformation] = None,
fee_payer: Optional[Pubkey] = None,
instructions: Optional[Sequence[Instruction]] = None,
) -> None:
"""Init transaction object."""
warn(
"""Transaction is deprecated and will be removed in a later release.
Please use the Transaction module from solders.transaction instead.""",
DeprecationWarning,
)
self._solders = _build_solders_tx(
recent_blockhash=recent_blockhash,
nonce_info=nonce_info,
fee_payer=fee_payer,
instructions=instructions,
)
add(self, *args)
Add one or more instructions to this Transaction.
Parameters:
Name | Type | Description | Default |
---|---|---|---|
*args |
Union[Transaction, Instruction] |
The instructions to add to this Transaction.
If a |
() |
Returns:
Type | Description |
---|---|
Transaction |
The transaction with the added instructions. |
Source code in solana/transaction.py
def add(self, *args: Union[Transaction, Instruction]) -> Transaction:
"""Add one or more instructions to this Transaction.
Args:
*args: The instructions to add to this Transaction.
If a `Transaction` is passsed, the instructions will be extracted from it.
Returns:
The transaction with the added instructions.
"""
for arg in args:
if isinstance(arg, Transaction):
self.instructions = self.instructions + arg.instructions
elif isinstance(arg, Instruction):
self.instructions = (*self.instructions, arg)
else:
raise ValueError("invalid instruction:", arg)
return self
add_signature(self, pubkey, signature)
Add an externally created signature to a transaction.
Parameters:
Name | Type | Description | Default |
---|---|---|---|
pubkey |
Pubkey |
The public key that created the signature. |
required |
signature |
Signature |
The signature to add. |
required |
Source code in solana/transaction.py
def add_signature(self, pubkey: Pubkey, signature: Signature) -> None:
"""Add an externally created signature to a transaction.
Args:
pubkey: The public key that created the signature.
signature: The signature to add.
"""
presigner = Presigner(pubkey, signature)
self._solders.partial_sign([presigner], self._solders.message.recent_blockhash)
compile_message(self)
Compile transaction data.
Returns:
Type | Description |
---|---|
Message |
The compiled message. |
Source code in solana/transaction.py
def compile_message(self) -> Message: # pylint: disable=too-many-locals
"""Compile transaction data.
Returns:
The compiled message.
"""
return self._solders.message
deserialize(raw_transaction)
classmethod
Parse a wire transaction into a Transaction object.
Examples:
>>> raw_transaction = bytes.fromhex(
... '019d53be8af3a7c30f86c1092d2c3ea61d270c0cfa2'
... '75a23ba504674c8fbbb724827b23b42dc8e08019e23'
... '120f1b6f40f9799355ce54185b4415be37ca2cee6e0'
... 'e010001034cb5abf6ad79fbf5abbccafcc269d85cd2'
... '651ed4b885b5869f241aedf0a5ba290000000000000'
... '0000000000000000000000000000000000000000000'
... '0000000200000000000000000000000000000000000'
... '0000000000000000000000000000000000000000000'
... '0000000000000000000000000000000000000000000'
... '000000301020200010c02000000e803000000000000'
... )
>>> type(Transaction.deserialize(raw_transaction))
<class 'solana.transaction.Transaction'>
Returns:
Type | Description |
---|---|
Transaction |
The deserialized transaction. |
Source code in solana/transaction.py
@classmethod
def deserialize(cls, raw_transaction: bytes) -> Transaction:
"""Parse a wire transaction into a Transaction object.
Example:
>>> raw_transaction = bytes.fromhex(
... '019d53be8af3a7c30f86c1092d2c3ea61d270c0cfa2'
... '75a23ba504674c8fbbb724827b23b42dc8e08019e23'
... '120f1b6f40f9799355ce54185b4415be37ca2cee6e0'
... 'e010001034cb5abf6ad79fbf5abbccafcc269d85cd2'
... '651ed4b885b5869f241aedf0a5ba290000000000000'
... '0000000000000000000000000000000000000000000'
... '0000000200000000000000000000000000000000000'
... '0000000000000000000000000000000000000000000'
... '0000000000000000000000000000000000000000000'
... '000000301020200010c02000000e803000000000000'
... )
>>> type(Transaction.deserialize(raw_transaction))
<class 'solana.transaction.Transaction'>
Returns:
The deserialized transaction.
"""
return cls.from_solders(SoldersTx.from_bytes(raw_transaction))
from_solders(txn)
classmethod
Convert from a solders
transaction.
Parameters:
Name | Type | Description | Default |
---|---|---|---|
txn |
SoldersTx |
The |
required |
Returns:
Type | Description |
---|---|
Transaction |
The |
Source code in solana/transaction.py
@classmethod
def from_solders(cls, txn: SoldersTx) -> Transaction:
"""Convert from a `solders` transaction.
Args:
txn: The `solders` transaction.
Returns:
The `solana-py` transaction.
"""
new_tx = cls()
new_tx._solders = txn
return new_tx
populate(message, signatures)
classmethod
Populate Transaction object from message and signatures.
Examples:
>>> raw_message = bytes.fromhex(
... '0200030500000000000000000000000000000000000000000000'
... '0000000000000000000100000000000000000000000000000000'
... '0000000000000000000000000000000200000000000000000000'
... '0000000000000000000000000000000000000000000300000000'
... '0000000000000000000000000000000000000000000000000000'
... '0004000000000000000000000000000000000000000000000000'
... '0000000000000005c49ae77603782054f17a9decea43b444eba0'
... 'edb12c6f1d31c6e0e4a84bf052eb010403010203050909090909'
... )
>>> from solders.message import Message
>>> from solders.signature import Signature
>>> msg = Message.from_bytes(raw_message)
>>> signatures = [Signature(bytes([1] * Signature.LENGTH)), Signature(bytes([2] * Signature.LENGTH))]
>>> type(Transaction.populate(msg, signatures))
<class 'solana.transaction.Transaction'>
Returns:
Type | Description |
---|---|
Transaction |
The populated transaction. |
Source code in solana/transaction.py
@classmethod
def populate(cls, message: Message, signatures: List[Signature]) -> Transaction:
"""Populate Transaction object from message and signatures.
Example:
>>> raw_message = bytes.fromhex(
... '0200030500000000000000000000000000000000000000000000'
... '0000000000000000000100000000000000000000000000000000'
... '0000000000000000000000000000000200000000000000000000'
... '0000000000000000000000000000000000000000000300000000'
... '0000000000000000000000000000000000000000000000000000'
... '0004000000000000000000000000000000000000000000000000'
... '0000000000000005c49ae77603782054f17a9decea43b444eba0'
... 'edb12c6f1d31c6e0e4a84bf052eb010403010203050909090909'
... )
>>> from solders.message import Message
>>> from solders.signature import Signature
>>> msg = Message.from_bytes(raw_message)
>>> signatures = [Signature(bytes([1] * Signature.LENGTH)), Signature(bytes([2] * Signature.LENGTH))]
>>> type(Transaction.populate(msg, signatures))
<class 'solana.transaction.Transaction'>
Returns:
The populated transaction.
"""
return cls.from_solders(SoldersTx.populate(message, signatures))
serialize(self, verify_signatures=True)
Serialize the Transaction in the wire format.
The Transaction must have a valid signature
before invoking this method.
verify_signatures can be added if the signature does not require to be verified.
Parameters:
Name | Type | Description | Default |
---|---|---|---|
verify_signatures |
bool |
a bool indicating to verify the signature or not. Defaults to True |
True |
Examples:
>>> from solders.keypair import Keypair
>>> from solders.pubkey import Pubkey
>>> from solders.hash import Hash
>>> from solders.system_program import transfer, TransferParams
>>> leading_zeros = [0] * 31
>>> seed = bytes(leading_zeros + [1])
>>> sender, receiver = Keypair.from_seed(seed), Pubkey(leading_zeros + [2])
>>> transfer_tx = Transaction().add(transfer(TransferParams(from_pubkey=sender.pubkey(), to_pubkey=receiver, lamports=1000)))
>>> transfer_tx.recent_blockhash = Hash(leading_zeros + [3])
>>> transfer_tx.sign(sender)
>>> transfer_tx.serialize().hex()
'019d53be8af3a7c30f86c1092d2c3ea61d270c0cfa275a23ba504674c8fbbb724827b23b42dc8e08019e23120f1b6f40f9799355ce54185b4415be37ca2cee6e0e010001034cb5abf6ad79fbf5abbccafcc269d85cd2651ed4b885b5869f241aedf0a5ba2900000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000301020200010c02000000e803000000000000'
Returns:
Type | Description |
---|---|
bytes |
The serialized transaction. |
Source code in solana/transaction.py
def serialize(self, verify_signatures: bool = True) -> bytes:
"""Serialize the Transaction in the wire format.
The Transaction must have a valid `signature` before invoking this method.
verify_signatures can be added if the signature does not require to be verified.
Args:
verify_signatures: a bool indicating to verify the signature or not. Defaults to True
Example:
>>> from solders.keypair import Keypair
>>> from solders.pubkey import Pubkey
>>> from solders.hash import Hash
>>> from solders.system_program import transfer, TransferParams
>>> leading_zeros = [0] * 31
>>> seed = bytes(leading_zeros + [1])
>>> sender, receiver = Keypair.from_seed(seed), Pubkey(leading_zeros + [2])
>>> transfer_tx = Transaction().add(transfer(TransferParams(from_pubkey=sender.pubkey(), to_pubkey=receiver, lamports=1000)))
>>> transfer_tx.recent_blockhash = Hash(leading_zeros + [3])
>>> transfer_tx.sign(sender)
>>> transfer_tx.serialize().hex()
'019d53be8af3a7c30f86c1092d2c3ea61d270c0cfa275a23ba504674c8fbbb724827b23b42dc8e08019e23120f1b6f40f9799355ce54185b4415be37ca2cee6e0e010001034cb5abf6ad79fbf5abbccafcc269d85cd2651ed4b885b5869f241aedf0a5ba2900000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000301020200010c02000000e803000000000000'
Returns:
The serialized transaction.
""" # noqa: E501 pylint: disable=line-too-long
if self.signatures == [Signature.default() for sig in self.signatures]:
raise AttributeError("transaction has not been signed")
if verify_signatures and not self.verify_signatures():
raise AttributeError("transaction has not been signed correctly")
return bytes(self._solders)
serialize_message(self)
Get raw transaction data that need to be covered by signatures.
Returns:
Type | Description |
---|---|
bytes |
The serialized message. |
Source code in solana/transaction.py
def serialize_message(self) -> bytes:
"""Get raw transaction data that need to be covered by signatures.
Returns:
The serialized message.
"""
return bytes(self.compile_message())
sign(self, *signers)
Sign the Transaction with the specified accounts.
Multiple signatures may be applied to a Transaction. The first signature is considered "primary" and is used when testing for Transaction confirmation.
Transaction fields should not be modified after the first call to sign
,
as doing so may invalidate the signature and cause the Transaction to be
rejected.
The Transaction must be assigned a valid recent_blockhash
before invoking this method.
Source code in solana/transaction.py
def sign(self, *signers: Keypair) -> None:
"""Sign the Transaction with the specified accounts.
Multiple signatures may be applied to a Transaction. The first signature
is considered "primary" and is used when testing for Transaction confirmation.
Transaction fields should not be modified after the first call to `sign`,
as doing so may invalidate the signature and cause the Transaction to be
rejected.
The Transaction must be assigned a valid `recent_blockhash` before invoking this method.
"""
self._solders.sign(signers, self._solders.message.recent_blockhash)
sign_partial(self, *partial_signers)
Partially sign a Transaction with the specified keypairs.
All the caveats from the sign
method apply to sign_partial
Source code in solana/transaction.py
def sign_partial(self, *partial_signers: Keypair) -> None:
"""Partially sign a Transaction with the specified keypairs.
All the caveats from the `sign` method apply to `sign_partial`
"""
self._solders.partial_sign(partial_signers, self._solders.message.recent_blockhash)
signature(self)
The first (payer) Transaction signature.
Returns:
Type | Description |
---|---|
Signature |
The payer signature. |
Source code in solana/transaction.py
def signature(self) -> Signature:
"""The first (payer) Transaction signature.
Returns:
The payer signature.
"""
return self._solders.signatures[0]
to_solders(self)
Convert to a solders
transaction.
Returns:
Type | Description |
---|---|
SoldersTx |
The |
Source code in solana/transaction.py
def to_solders(self) -> SoldersTx:
"""Convert to a `solders` transaction.
Returns:
The `solders` transaction.
"""
return self._solders
verify_signatures(self)
Verify signatures of a complete, signed Transaction.
Returns:
Type | Description |
---|---|
bool |
a bool indicating if the signatures are correct or not. |
Source code in solana/transaction.py
def verify_signatures(self) -> bool:
"""Verify signatures of a complete, signed Transaction.
Returns:
a bool indicating if the signatures are correct or not.
"""
try:
self._solders.verify()
except TransactionError:
return False
return True