solana_trader_client_rust/common/
signing.rs

1use anyhow::Result;
2use base64::{engine::general_purpose::STANDARD, Engine};
3use bincode::{deserialize, serialize};
4use serde::Serialize;
5use solana_hash::Hash;
6use solana_sdk::{
7    instruction::Instruction,
8    message::VersionedMessage,
9    pubkey::Pubkey,
10    signature::{Keypair, Signature},
11    signer::Signer,
12    transaction::{Transaction, VersionedTransaction},
13};
14use solana_trader_proto::api;
15
16use crate::provider::utils::IntoTransactionMessage;
17
18#[derive(Debug, Clone, Serialize)]
19pub struct SubmitParams {
20    pub skip_pre_flight: bool,
21    pub front_running_protection: bool,
22    pub use_staked_rpcs: bool,
23    pub fast_best_effort: bool,
24    pub submit_strategy: api::SubmitStrategy,
25    pub allow_back_run: Option<bool>,
26    pub revenue_address: Option<String>,
27    pub allow_revert: Option<bool>,
28}
29
30impl Default for SubmitParams {
31    fn default() -> Self {
32        Self {
33            skip_pre_flight: true,
34            front_running_protection: false,
35            use_staked_rpcs: false,
36            fast_best_effort: false,
37            submit_strategy: api::SubmitStrategy::PSubmitAll,
38            allow_back_run: None,
39            revenue_address: None,
40            allow_revert: None,
41        }
42    }
43}
44
45#[derive(Debug, Serialize)]
46pub struct SignedTransaction {
47    pub content: String,
48    pub is_cleanup: bool,
49}
50
51pub async fn sign_transaction<T>(
52    tx: &T,
53    keypair: &Keypair,
54    blockhash: String,
55) -> Result<SignedTransaction>
56where
57    T: IntoTransactionMessage + Clone,
58{
59    let tx_message = tx.clone().into_transaction_message();
60    let rawbytes = STANDARD.decode(&tx_message.content)?;
61    let parsed_hash = blockhash.parse()?;
62
63    let signed_data = match deserialize(&rawbytes) {
64        Ok(versioned_tx) => sign_versioned_transaction(versioned_tx, keypair, parsed_hash)?,
65        Err(_) => sign_legacy_transaction(&rawbytes, keypair, parsed_hash)?,
66    };
67
68    Ok(SignedTransaction {
69        content: STANDARD.encode(signed_data),
70        is_cleanup: tx_message.is_cleanup,
71    })
72}
73
74fn sign_versioned_transaction(
75    mut tx: VersionedTransaction,
76    keypair: &Keypair,
77    blockhash: solana_sdk::hash::Hash,
78) -> Result<Vec<u8>> {
79    match &mut tx.message {
80        VersionedMessage::Legacy(message) => {
81            message.recent_blockhash = blockhash;
82        }
83        VersionedMessage::V0(message) => {
84            message.recent_blockhash = blockhash;
85        }
86    }
87
88    tx.signatures = vec![Signature::default()];
89    let message_data = tx.message.serialize();
90    tx.signatures[0] = keypair.sign_message(&message_data);
91
92    Ok(serialize(&tx)?)
93}
94
95fn sign_legacy_transaction(
96    rawbytes: &[u8],
97    keypair: &Keypair,
98    blockhash: solana_sdk::hash::Hash,
99) -> Result<Vec<u8>> {
100    let mut tx: Transaction = deserialize(rawbytes)?;
101    tx.try_partial_sign(&[keypair], blockhash)?;
102    Ok(serialize(&tx)?)
103}
104
105pub fn create_signed_transaction(
106    instruction: Vec<Instruction>,
107    payer: &Pubkey,
108    keypair: &Keypair,
109    block_hash: Hash,
110) -> anyhow::Result<Transaction> {
111    let mut transaction =
112        Transaction::new_signed_with_payer(&instruction, Some(payer), &[keypair], block_hash);
113
114    let message_data = transaction.message.serialize();
115    transaction.signatures = vec![Signature::default()];
116    transaction.signatures[0] = keypair.sign_message(&message_data);
117
118    Ok(transaction)
119}