From 9f1fb25a04e3d87094160be92492854b7d569e4a Mon Sep 17 00:00:00 2001 From: Evan <0xIchigo@protonmail.com> Date: Wed, 19 Jun 2024 17:24:23 -0400 Subject: [PATCH] Better Transaction Confirmation --- src/jito.rs | 12 ++++++------ src/optimized_transaction.rs | 24 +++++++++++++++++------- 2 files changed, 23 insertions(+), 13 deletions(-) diff --git a/src/jito.rs b/src/jito.rs index d57c5834..1534f0c6 100644 --- a/src/jito.rs +++ b/src/jito.rs @@ -89,12 +89,12 @@ impl Helius { /// * `tip_amount` - The amount of lamports to tip. Defaults to `1000` /// /// # Returns - /// A `Result` containing the serialized transaction as a base58-encoded string + /// A `Result` containing the serialized transaction as a base58-encoded string and the last valid block height pub async fn create_smart_transaction_with_tip( &self, mut config: CreateSmartTransactionConfig<'_>, tip_amount: Option, - ) -> Result { + ) -> Result<(String, u64)> { if config.signers.is_empty() { return Err(HeliusError::InvalidInput( "The transaction must have at least one signer".to_string(), @@ -109,7 +109,7 @@ impl Helius { self.add_tip_instruction(&mut config.instructions, payer_key, random_tip_account, tip_amount); - let smart_transaction: SmartTransaction = self.create_smart_transaction(&config).await?; + let (smart_transaction, last_valid_block_height) = self.create_smart_transaction(&config).await?; let serialized_transaction: Vec = match smart_transaction { SmartTransaction::Legacy(tx) => { serialize(&tx).map_err(|e: Box| HeliusError::InvalidInput(e.to_string()))? @@ -120,7 +120,7 @@ impl Helius { }; let transaction_base58: String = encode(&serialized_transaction).into_string(); - Ok(transaction_base58) + Ok((transaction_base58, last_valid_block_height)) } /// Sends a bundle of transactions to the Jito Block Engine @@ -231,7 +231,7 @@ impl Helius { let jito_api_url: &str = jito_api_url_string.as_str(); // Create the smart transaction with tip - let serialized_transaction: String = self + let (serialized_transaction, last_valid_block_height) = self .create_smart_transaction_with_tip(config.create_config, Some(tip)) .await?; @@ -245,7 +245,7 @@ impl Helius { let interval: Duration = Duration::from_secs(5); let start: tokio::time::Instant = tokio::time::Instant::now(); - while start.elapsed() < timeout { + while start.elapsed() < timeout || self.connection().get_block_height()? <= last_valid_block_height { let bundle_statuses: Value = self.get_bundle_statuses(vec![bundle_id.clone()], jito_api_url).await?; if let Some(values) = bundle_statuses["result"]["value"].as_array() { diff --git a/src/optimized_transaction.rs b/src/optimized_transaction.rs index ea66e4df..53f3fd1f 100644 --- a/src/optimized_transaction.rs +++ b/src/optimized_transaction.rs @@ -117,11 +117,11 @@ impl Helius { /// whether it's a legacy or versioned smart transaction. The transaction's send configuration can also be changed, if provided /// /// # Returns - /// An optimized `Transaction` or `VersionedTransaction` + /// An optimized `SmartTransaction` (i.e., `Transaction` or `VersionedTransaction`) and the `last_valid_block_height` pub async fn create_smart_transaction( &self, config: &CreateSmartTransactionConfig<'_>, - ) -> Result { + ) -> Result<(SmartTransaction, u64)> { if config.signers.is_empty() { return Err(HeliusError::InvalidInput( "The fee payer must sign the transaction".to_string(), @@ -131,7 +131,9 @@ impl Helius { let payer_pubkey: Pubkey = config .fee_payer .map_or(config.signers[0].pubkey(), |signer| signer.pubkey()); - let recent_blockhash: Hash = self.connection().get_latest_blockhash()?; + let (recent_blockhash, last_valid_block_hash) = self + .connection() + .get_latest_blockhash_with_commitment(CommitmentConfig::confirmed())?; let mut final_instructions: Vec = vec![]; // Check if any of the instructions provided set the compute unit price and/or limit, and throw an error if `true` @@ -288,7 +290,10 @@ impl Helius { message: versioned_message, }); - Ok(SmartTransaction::Versioned(versioned_transaction.unwrap())) + Ok(( + SmartTransaction::Versioned(versioned_transaction.unwrap()), + last_valid_block_hash, + )) } else { let mut tx: Transaction = Transaction::new_with_payer(&final_instructions, Some(&payer_pubkey)); tx.try_partial_sign(&config.signers, recent_blockhash)?; @@ -299,7 +304,10 @@ impl Helius { legacy_transaction = Some(tx); - Ok(SmartTransaction::Legacy(legacy_transaction.unwrap())) + Ok(( + SmartTransaction::Legacy(legacy_transaction.unwrap()), + last_valid_block_hash, + )) } } @@ -312,7 +320,7 @@ impl Helius { /// # Returns /// The transaction signature, if successful pub async fn send_smart_transaction(&self, config: SmartTransactionConfig<'_>) -> Result { - let transaction: SmartTransaction = self.create_smart_transaction(&config.create_config).await?; + let (transaction, last_valid_block_height) = self.create_smart_transaction(&config.create_config).await?; // Common logic for sending transactions let send_transaction_config: RpcSendTransactionConfig = RpcSendTransactionConfig { @@ -336,7 +344,9 @@ impl Helius { let timeout: Duration = Duration::from_secs(60); let start_time: Instant = Instant::now(); - while Instant::now().duration_since(start_time) < timeout { + while Instant::now().duration_since(start_time) < timeout + || self.connection().get_block_height()? <= last_valid_block_height + { let result = match &transaction { SmartTransaction::Legacy(tx) => send_result(tx), SmartTransaction::Versioned(tx) => send_versioned_result(tx),