solana_trader_client_rust/common/
signing.rs1use anyhow::Result;
2use base64::{engine::general_purpose::STANDARD, Engine};
3use serde::Serialize;
4use anyhow::{anyhow};
5use solana_hash::Hash;
6use solana_sdk::{
7 instruction::Instruction,
8 pubkey::Pubkey,
9 signature::{Keypair, Signature},
10 signer::Signer,
11 transaction::{Transaction, VersionedTransaction},
12};
13use solana_trader_proto::api;
14
15use crate::provider::utils::IntoTransactionMessage;
16
17#[derive(Debug, Clone, Serialize)]
18pub struct SubmitParams {
19 pub skip_pre_flight: bool,
20 pub front_running_protection: bool,
21 pub use_staked_rpcs: bool,
22 pub fast_best_effort: bool,
23 pub submit_strategy: api::SubmitStrategy,
24 pub allow_back_run: Option<bool>,
25 pub revenue_address: Option<String>,
26 pub allow_revert: Option<bool>,
27}
28
29impl Default for SubmitParams {
30 fn default() -> Self {
31 Self {
32 skip_pre_flight: true,
33 front_running_protection: false,
34 use_staked_rpcs: false,
35 fast_best_effort: false,
36 submit_strategy: api::SubmitStrategy::PSubmitAll,
37 allow_back_run: None,
38 revenue_address: None,
39 allow_revert: None,
40 }
41 }
42}
43
44#[derive(Debug, Serialize)]
45pub struct SignedTransaction {
46 pub content: String,
47 pub is_cleanup: bool,
48}
49
50pub async fn sign_transaction<T>(
51 tx: &T,
52 keypair: &Keypair,
53) -> Result<SignedTransaction>
54where
55 T: IntoTransactionMessage + Clone,
56{
57 let tx_message = tx.clone().into_transaction_message();
58
59 let signed_b64 = sign_existing_transaction(&tx_message.content, keypair)?;
60 Ok(SignedTransaction {
61 content: signed_b64,
62 is_cleanup: tx_message.is_cleanup,
63 })
64}
65
66pub fn create_signed_transaction(
67 instruction: Vec<Instruction>,
68 payer: &Pubkey,
69 keypair: &Keypair,
70 block_hash: Hash,
71) -> anyhow::Result<Transaction> {
72 let mut transaction =
73 Transaction::new_signed_with_payer(&instruction, Some(payer), &[keypair], block_hash);
74
75 let message_data = transaction.message.serialize();
76 transaction.signatures = vec![Signature::default()];
77 transaction.signatures[0] = keypair.sign_message(&message_data);
78
79 Ok(transaction)
80}
81
82fn sign_existing_transaction(base64_tx: &str, keypair: &Keypair) -> Result<String> {
83 let tx_bytes = STANDARD.decode(base64_tx)?;
84
85 if let Ok(mut tx) = bincode::deserialize::<VersionedTransaction>(&tx_bytes) {
87 let sig_index = tx.signatures.iter().position(|sig| *sig == Signature::default())
88 .ok_or_else(|| anyhow!("No empty signature slot found"))?;
89
90 let msg_bytes = tx.message.serialize();
91 tx.signatures[sig_index] = keypair.sign_message(&msg_bytes);
92
93 let signed_bytes = bincode::serialize(&tx)?;
94 return Ok(STANDARD.encode(signed_bytes));
95 }
96
97 let mut legacy_tx: Transaction = bincode::deserialize(&tx_bytes)?;
99 legacy_tx.try_partial_sign(&[keypair], legacy_tx.message.recent_blockhash)?;
100 let signed_bytes = bincode::serialize(&legacy_tx)?;
101 Ok(STANDARD.encode(signed_bytes))
102}
103
104