From 48ba5469073f3a53661f2330ea2196bfb081ac97 Mon Sep 17 00:00:00 2001 From: Michael Huang Date: Sat, 12 Oct 2024 12:38:15 -0400 Subject: [PATCH] Add back send_legacy_transaction rpc methods --- src/solana/rpc/api.py | 65 +++++++++++++++++++++++++++++++++++-- src/solana/rpc/async_api.py | 59 +++++++++++++++++++++++++++++++-- 2 files changed, 118 insertions(+), 6 deletions(-) diff --git a/src/solana/rpc/api.py b/src/solana/rpc/api.py index 76794066..83479a15 100644 --- a/src/solana/rpc/api.py +++ b/src/solana/rpc/api.py @@ -4,8 +4,11 @@ from time import sleep, time from typing import Dict, List, Optional, Sequence, Union +from warnings import warn +from solders.hash import Hash as Blockhash from solders.message import VersionedMessage +from solders.keypair import Keypair from solders.pubkey import Pubkey from solders.rpc.responses import ( GetAccountInfoMaybeJsonParsedResp, @@ -60,12 +63,12 @@ ValidatorExitResp, ) from solders.signature import Signature -from solders.transaction import VersionedTransaction +from solders.transaction import Transaction, VersionedTransaction from solana.rpc import types -from solders.transaction import Transaction +from solana.transaction import Transaction as LegacyTransaction -from .commitment import Commitment +from .commitment import Commitment, Finalized from .core import ( _COMMITMENT_TO_SOLDERS, RPCException, @@ -995,6 +998,62 @@ def send_raw_transaction(self, txn: bytes, opts: Optional[types.TxOpts] = None) post_send_args = self._send_raw_transaction_post_send_args(resp, opts_to_use) return self.__post_send_with_confirm(*post_send_args) + def send_legacy_transaction( + self, + txn: LegacyTransaction, + *signers: Keypair, + opts: Optional[types.TxOpts] = None, + recent_blockhash: Optional[Blockhash] = None, + ) -> SendTransactionResp: + """Send a legacy transaction. + + Args: + txn: transaction object. + signers: Signers to sign the transaction. Only supported for legacy Transaction. + opts: (optional) Transaction options. + recent_blockhash: (optional) Pass a valid recent blockhash here if you want to + skip fetching the recent blockhash or relying on the cache. + Only supported for legacy Transaction. + + Example: + >>> from solders.keypair import Keypair + >>> from solders.pubkey import Pubkey + >>> from solana.rpc.api import Client + >>> from solders.system_program import TransferParams, transfer + >>> from solana.transaction import Transaction + >>> leading_zeros = [0] * 31 + >>> sender, receiver = Keypair.from_seed(leading_zeros + [1]), Keypair.from_seed(leading_zeros + [2]) + >>> txn = Transaction().add(transfer(TransferParams( + ... from_pubkey=sender.pubkey(), to_pubkey=receiver.pubkey(), lamports=1000))) + >>> solana_client = Client("http://localhost:8899") + >>> solana_client.send_transaction(txn, sender).value # doctest: +SKIP + Signature( + 1111111111111111111111111111111111111111111111111111111111111111, + ) + """ + warn("send_transaction_legacy is deprecated. Use send_transaction instead.", DeprecationWarning) + + last_valid_block_height = None + if recent_blockhash is None: + blockhash_resp = self.get_latest_blockhash(Finalized) + recent_blockhash = self.parse_recent_blockhash(blockhash_resp) + last_valid_block_height = blockhash_resp.value.last_valid_block_height + + txn.recent_blockhash = recent_blockhash + + txn.sign(*signers) + opts_to_use = ( + types.TxOpts( + preflight_commitment=self._commitment, + last_valid_block_height=last_valid_block_height, + ) + if opts is None + else opts + ) + + txn_resp = self.send_raw_transaction(txn.serialize(), opts=opts_to_use) + return txn_resp + def send_transaction( self, txn: Union[VersionedTransaction, Transaction], diff --git a/src/solana/rpc/async_api.py b/src/solana/rpc/async_api.py index 5334a639..8489ca71 100644 --- a/src/solana/rpc/async_api.py +++ b/src/solana/rpc/async_api.py @@ -4,7 +4,9 @@ from time import time from typing import Dict, List, Optional, Sequence, Union +from solders.hash import Hash as Blockhash from solders.message import VersionedMessage +from solders.keypair import Keypair from solders.pubkey import Pubkey from solders.rpc.responses import ( GetAccountInfoMaybeJsonParsedResp, @@ -58,12 +60,12 @@ ValidatorExitResp, ) from solders.signature import Signature -from solders.transaction import VersionedTransaction +from solders.transaction import Transaction, VersionedTransaction from solana.rpc import types -from solders.transaction import Transaction +from solana.transaction import Transaction as LegacyTransaction -from .commitment import Commitment +from .commitment import Commitment, Finalized from .core import ( _COMMITMENT_TO_SOLDERS, TransactionExpiredBlockheightExceededError, @@ -1008,6 +1010,57 @@ async def send_raw_transaction(self, txn: bytes, opts: Optional[types.TxOpts] = post_send_args = self._send_raw_transaction_post_send_args(resp, opts_to_use) return await self.__post_send_with_confirm(*post_send_args) + async def send_legacy_transaction( + self, + txn: LegacyTransaction, + *signers: Keypair, + opts: Optional[types.TxOpts] = None, + recent_blockhash: Optional[Blockhash] = None, + ) -> SendTransactionResp: + """Send a legacy transaction. + + Args: + txn: transaction object. + signers: Signers to sign the transaction. Only supported for legacy Transaction. + opts: (optional) Transaction options. + recent_blockhash: (optional) Pass a valid recent blockhash here if you want to + skip fetching the recent blockhash or relying on the cache. + Only supported for legacy Transaction. + + Example: + >>> from solders.keypair import Keypair + >>> from solders.system_program import TransferParams, transfer + >>> from solana.transaction import Transaction + >>> leading_zeros = [0] * 31 + >>> sender, receiver = Keypair.from_seed(leading_zeros + [1]), Keypair.from_seed(leading_zeros + [2]) + >>> txn = Transaction().add(transfer(TransferParams( + ... from_pubkey=sender.pubkey(), to_pubkey=receiver.pubkey(), lamports=1000))) + >>> solana_client = AsyncClient("http://localhost:8899") + >>> (await solana_client.send_transaction(txn, sender)).value # doctest: +SKIP + Signature( + 1111111111111111111111111111111111111111111111111111111111111111, + ) + """ + last_valid_block_height = None + if recent_blockhash is None: + blockhash_resp = await self.get_latest_blockhash(Finalized) + recent_blockhash = self.parse_recent_blockhash(blockhash_resp) + last_valid_block_height = blockhash_resp.value.last_valid_block_height + + txn.recent_blockhash = recent_blockhash + + txn.sign(*signers) + opts_to_use = ( + types.TxOpts( + preflight_commitment=self._commitment, + last_valid_block_height=last_valid_block_height, + ) + if opts is None + else opts + ) + txn_resp = await self.send_raw_transaction(txn.serialize(), opts=opts_to_use) + return txn_resp + async def send_transaction( self, txn: Union[VersionedTransaction, Transaction],