diff --git a/wasm/solana-client/src/client.rs b/wasm/solana-client/src/client.rs index 6b8409a7..c7d7009a 100644 --- a/wasm/solana-client/src/client.rs +++ b/wasm/solana-client/src/client.rs @@ -20,7 +20,7 @@ use solana_sdk::{ message::Message, pubkey::Pubkey, signature::Signature, - transaction::Transaction, + transaction::{Transaction, VersionedTransaction}, }; use crate::{ @@ -244,6 +244,31 @@ impl WasmClient { Ok(response.into()) } + pub async fn send_versioned_transaction_with_config( + &self, + transaction: &VersionedTransaction, + config: RpcSendTransactionConfig, + ) -> ClientResult { + let request = + SendVersionedTransactionRequest::new_with_config(transaction.to_owned(), config).into(); + let response = SendVersionedTransactionRequest::from(self.send(request).await?); + + let signature: Signature = response.into(); + + // A mismatching RPC response signature indicates an issue with the RPC node, and + // should not be passed along to confirmation methods. The transaction may or may + // not have been submitted to the cluster, so callers should verify the success of + // the correct transaction signature independently. + if signature != transaction.signatures[0] { + Err(ClientError::new(&format!( + "RPC node returned mismatched signature {:?}, expected {:?}", + signature, transaction.signatures[0] + ))) + } else { + Ok(transaction.signatures[0]) + } + } + pub async fn send_transaction_with_config( &self, transaction: &Transaction, @@ -281,6 +306,21 @@ impl WasmClient { .await } + pub async fn send_versioned_transaction( + &self, + transaction: &VersionedTransaction, + ) -> ClientResult { + self.send_versioned_transaction_with_config( + transaction, + RpcSendTransactionConfig { + preflight_commitment: Some(self.commitment()), + encoding: Some(UiTransactionEncoding::Base64), + ..Default::default() + }, + ) + .await + } + pub async fn confirm_transaction_with_commitment( &self, signature: &Signature, diff --git a/wasm/solana-client/src/methods/mod.rs b/wasm/solana-client/src/methods/mod.rs index 63a7c9a9..7e01300c 100644 --- a/wasm/solana-client/src/methods/mod.rs +++ b/wasm/solana-client/src/methods/mod.rs @@ -33,6 +33,7 @@ mod program_accounts; mod recent_performance_samples; mod request_airdrop; mod send_transaction; +mod send_versioned_transaction; mod serde_utils; mod signature_statuses; mod signatures_for_address; @@ -92,6 +93,9 @@ pub use { }, request_airdrop::{RequestAirdropRequest, RequestAirdropResponse}, send_transaction::{SendTransactionRequest, SendTransactionResponse}, + send_versioned_transaction::{ + SendVersionedTransactionRequest, SendVersionedTransactionResponse, + }, signature_statuses::{ GetSignatureStatusesRequest, GetSignatureStatusesResponse, SignatureStatusesValue, }, diff --git a/wasm/solana-client/src/methods/send_versioned_transaction.rs b/wasm/solana-client/src/methods/send_versioned_transaction.rs new file mode 100644 index 00000000..d3a922b9 --- /dev/null +++ b/wasm/solana-client/src/methods/send_versioned_transaction.rs @@ -0,0 +1,76 @@ +use std::str::FromStr; + +use solana_extra_wasm::transaction_status::UiTransactionEncoding; +use solana_sdk::{signature::Signature, transaction::VersionedTransaction}; + +use crate::utils::rpc_config::{serialize_and_encode, RpcSendTransactionConfig}; +use crate::{ClientRequest, ClientResponse}; + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct SendVersionedTransactionRequest { + transaction: VersionedTransaction, + #[serde(skip_serializing_if = "Option::is_none")] + config: Option, +} + +impl SendVersionedTransactionRequest { + pub fn new(transaction: VersionedTransaction) -> Self { + Self { + transaction, + config: None, + } + } + pub fn new_with_config( + transaction: VersionedTransaction, + config: RpcSendTransactionConfig, + ) -> Self { + Self { + transaction, + config: Some(config), + } + } +} + +impl From for serde_json::Value { + fn from(value: SendVersionedTransactionRequest) -> Self { + let encoding = match value.config { + Some(ref c) => c.encoding.unwrap_or(UiTransactionEncoding::Base64), + None => UiTransactionEncoding::Base64, + }; + + let serialized_encoded = + serialize_and_encode::(&value.transaction, encoding).unwrap(); + + match value.config { + Some(config) => serde_json::json!([serialized_encoded, config]), + None => serde_json::json!([serialized_encoded]), + } + } +} + +impl From for ClientRequest { + fn from(val: SendVersionedTransactionRequest) -> Self { + let mut request = ClientRequest::new("sendTransaction"); + let params = val.into(); + + request.params(params).clone() + } +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct SendVersionedTransactionResponse(Signature); + +impl From for Signature { + fn from(val: SendVersionedTransactionResponse) -> Self { + val.0 + } +} + +impl From for SendVersionedTransactionResponse { + fn from(response: ClientResponse) -> Self { + let signature = response.result.as_str().expect("invalid response"); + let signature = Signature::from_str(signature).expect("invalid signature"); + + SendVersionedTransactionResponse(signature) + } +}