diff --git a/src/solana/rpc/api.py b/src/solana/rpc/api.py index cbfed693..5ecc56d0 100644 --- a/src/solana/rpc/api.py +++ b/src/solana/rpc/api.py @@ -8,6 +8,7 @@ from solders.keypair import Keypair from solders.message import VersionedMessage from solders.pubkey import Pubkey +from solders.rpc.config import RpcSimulateTransactionAccountsConfig from solders.rpc.responses import ( GetAccountInfoMaybeJsonParsedResp, GetAccountInfoResp, @@ -60,6 +61,7 @@ ValidatorExitResp, ) from solders.signature import Signature +from solders.transaction import Transaction as SoldersTransaction from solders.transaction import VersionedTransaction from solana.blockhash import BlockhashCache @@ -1072,17 +1074,25 @@ def send_transaction( def simulate_transaction( self, - txn: Union[Transaction, VersionedTransaction], + txn: Union[SoldersTransaction, VersionedTransaction], sig_verify: bool = False, + replace_recent_blockhash: bool = False, commitment: Optional[Commitment] = None, + accounts: Optional[RpcSimulateTransactionAccountsConfig] = None, + min_context_slot: Optional[int] = None, ) -> SimulateTransactionResp: """Simulate sending a transaction. Args: - txn: A transaction object. + txn: A Transaction object, a transaction in wire format, or a transaction as base-64 encoded string The transaction must have a valid blockhash, but is not required to be signed. sig_verify: If true the transaction signatures will be verified (default: false). + replace_recent_blockhash: if `true` the transaction recent blockhash will be replaced with the + most recent blockhash (default: false, conflicts with `sigVerify`). commitment: Bank state to query. It can be either "finalized", "confirmed" or "processed". + accounts: List of accounts to be returned. + RpcSimulateTransactionAccountsConfig object containing `addresses` and `encoding` fields. + min_context_slot: the minimum slot that the request can be evaluated at Example: >>> solana_client = Client("http://localhost:8899") @@ -1097,7 +1107,14 @@ def simulate_transaction( >>> solana_client.simulate_transaction(tx).value.logs # doctest: +SKIP ['BPF program 83astBRguLMdt2h5U1Tpdq5tjFoJ6noeGwaY3mDLVcri success'] """ - body = self._simulate_transaction_body(txn, sig_verify, commitment) + body = self._simulate_transaction_body( + txn, + sig_verify, + replace_recent_blockhash, + commitment, + accounts, + min_context_slot, + ) return self._provider.make_request(body, SimulateTransactionResp) def validator_exit(self) -> ValidatorExitResp: diff --git a/src/solana/rpc/async_api.py b/src/solana/rpc/async_api.py index 53bc01d8..420775cb 100644 --- a/src/solana/rpc/async_api.py +++ b/src/solana/rpc/async_api.py @@ -7,6 +7,7 @@ from solders.keypair import Keypair from solders.message import VersionedMessage from solders.pubkey import Pubkey +from solders.rpc.config import RpcSimulateTransactionAccountsConfig from solders.rpc.responses import ( GetAccountInfoMaybeJsonParsedResp, GetAccountInfoResp, @@ -58,6 +59,7 @@ ValidatorExitResp, ) from solders.signature import Signature +from solders.transaction import Transaction as SoldersTransaction from solders.transaction import VersionedTransaction from solana.blockhash import BlockhashCache @@ -620,7 +622,7 @@ async def get_multiple_accounts_json_parsed( encoding="jsonParsed", data_slice=None, ) - return await self._provider.make_request(body, GetMultipleAccountsResp) + return await self._provider.make_request(body, GetMultipleAccountsMaybeJsonParsedResp) async def get_program_accounts( # pylint: disable=too-many-arguments self, @@ -1081,9 +1083,12 @@ async def send_transaction( async def simulate_transaction( self, - txn: Union[Transaction, VersionedTransaction], + txn: Union[SoldersTransaction, VersionedTransaction], sig_verify: bool = False, + replace_recent_blockhash: bool = False, commitment: Optional[Commitment] = None, + accounts: Optional[RpcSimulateTransactionAccountsConfig] = None, + min_context_slot: Optional[int] = None, ) -> SimulateTransactionResp: """Simulate sending a transaction. @@ -1091,7 +1096,12 @@ async def simulate_transaction( txn: A Transaction object, a transaction in wire format, or a transaction as base-64 encoded string The transaction must have a valid blockhash, but is not required to be signed. sig_verify: If true the transaction signatures will be verified (default: false). + replace_recent_blockhash: if `true` the transaction recent blockhash will be replaced with the + most recent blockhash (default: false, conflicts with `sigVerify`). commitment: Bank state to query. It can be either "finalized", "confirmed" or "processed". + accounts: List of accounts to be returned. + RpcSimulateTransactionAccountsConfig object containing `addresses` and `encoding` fields. + min_context_slot: the minimum slot that the request can be evaluated at Example: >>> solana_client = AsyncClient("http://localhost:8899") @@ -1106,7 +1116,14 @@ async def simulate_transaction( >>> (await solana_client.simulate_transaction(tx)).value.logs # doctest: +SKIP ['BPF program 83astBRguLMdt2h5U1Tpdq5tjFoJ6noeGwaY3mDLVcri success'] """ - body = self._simulate_transaction_body(txn, sig_verify, commitment) + body = self._simulate_transaction_body( + txn, + sig_verify, + replace_recent_blockhash, + commitment, + accounts, + min_context_slot, + ) return await self._provider.make_request(body, SimulateTransactionResp) async def validator_exit(self) -> ValidatorExitResp: diff --git a/src/solana/rpc/core.py b/src/solana/rpc/core.py index 8033dc96..0dfbb13a 100644 --- a/src/solana/rpc/core.py +++ b/src/solana/rpc/core.py @@ -25,6 +25,7 @@ RpcSendTransactionConfig, RpcSignaturesForAddressConfig, RpcSignatureStatusConfig, + RpcSimulateTransactionAccountsConfig, RpcSimulateTransactionConfig, RpcSupplyConfig, RpcTokenAccountsFilterMint, @@ -81,12 +82,12 @@ ) from solders.rpc.responses import GetLatestBlockhashResp, SendTransactionResp from solders.signature import Signature +from solders.transaction import Transaction as SoldersTransaction from solders.transaction import VersionedTransaction from solders.transaction_status import UiTransactionEncoding from solana.blockhash import BlockhashCache from solana.rpc import types -from solana.transaction import Transaction from .commitment import Commitment, Confirmed, Finalized, Processed @@ -485,26 +486,51 @@ def _send_raw_transaction_post_send_args( @overload def _simulate_transaction_body( - self, txn: Transaction, sig_verify: bool, commitment: Optional[Commitment] + self, + txn: SoldersTransaction, + sig_verify: bool, + replace_recent_blockhash: bool, + commitment: Optional[Commitment], + accounts: Optional[RpcSimulateTransactionAccountsConfig], + min_context_slot: Optional[int], ) -> SimulateLegacyTransaction: ... @overload def _simulate_transaction_body( - self, txn: VersionedTransaction, sig_verify: bool, commitment: Optional[Commitment] + self, + txn: VersionedTransaction, + sig_verify: bool, + replace_recent_blockhash: bool, + commitment: Optional[Commitment], + accounts: Optional[RpcSimulateTransactionAccountsConfig], + min_context_slot: Optional[int], ) -> SimulateVersionedTransaction: ... def _simulate_transaction_body( - self, txn: Union[Transaction, VersionedTransaction], sig_verify: bool, commitment: Optional[Commitment] + self, + txn: Union[SoldersTransaction, VersionedTransaction], + sig_verify: bool = False, + replace_recent_blockhash: bool = False, + commitment: Optional[Commitment] = None, + accounts: Optional[RpcSimulateTransactionAccountsConfig] = None, + min_context_slot: Optional[int] = None, ) -> Union[SimulateLegacyTransaction, SimulateVersionedTransaction]: commitment_to_use = _COMMITMENT_TO_SOLDERS[commitment or self._commitment] - config = RpcSimulateTransactionConfig(sig_verify=sig_verify, commitment=commitment_to_use) - if isinstance(txn, Transaction): - if txn.recent_blockhash is None: + config = RpcSimulateTransactionConfig( + sig_verify=sig_verify, + replace_recent_blockhash=replace_recent_blockhash, + commitment=commitment_to_use, + accounts=accounts, + min_context_slot=min_context_slot, + ) + if isinstance(txn, SoldersTransaction): + if txn.message.recent_blockhash is None: raise ValueError("transaction must have a valid blockhash") - return SimulateLegacyTransaction(txn.to_solders(), config) - return SimulateVersionedTransaction(txn, config) + return SimulateLegacyTransaction(txn, config) + else: + return SimulateVersionedTransaction(txn, config) @staticmethod def _post_send(resp: SendTransactionResp) -> SendTransactionResp: