solana_trader_client_rust/provider/
utils.rs1use std::{collections::HashMap, str::FromStr};
2
3use anyhow::Result;
4use base64::{engine::general_purpose, Engine};
5use serde_json::{json, Value};
6use solana_sdk::{
7 address_lookup_table::AddressLookupTableAccount,
8 instruction::{AccountMeta, Instruction},
9 pubkey::Pubkey,
10 transaction::Transaction,
11};
12use solana_trader_proto::api::{self, Project, TransactionMessage, TransactionMessageV2};
13use std::time::{SystemTime, UNIX_EPOCH};
14use chrono::{Utc, DateTime};
15use prost_wkt_types::Timestamp;
16pub trait IntoTransactionMessage {
17 fn into_transaction_message(self) -> TransactionMessage;
18}
19
20impl IntoTransactionMessage for TransactionMessage {
21 fn into_transaction_message(self) -> TransactionMessage {
22 self
23 }
24}
25
26impl IntoTransactionMessage for TransactionMessageV2 {
27 fn into_transaction_message(self) -> TransactionMessage {
28 TransactionMessage {
29 content: self.content,
30 is_cleanup: false,
31 }
32 }
33}
34
35pub fn convert_address_lookup_table(
36 address_lookup_table: &HashMap<String, api::PublicKeys>,
37) -> Result<Vec<AddressLookupTableAccount>> {
38 let mut result = Vec::new();
39
40 for (key, accounts) in address_lookup_table {
41 let lookup_table_address = Pubkey::from_str(key)?;
42 let addresses: Vec<Pubkey> = accounts
43 .pks
44 .iter()
45 .map(|pk| Pubkey::from_str(pk))
46 .collect::<Result<_, _>>()?;
47
48 let lookup_table = AddressLookupTableAccount {
49 key: lookup_table_address,
50 addresses,
51 };
52
53 result.push(lookup_table);
54 }
55
56 Ok(result)
57}
58
59pub fn convert_jupiter_instructions(
60 instructions: &[api::InstructionJupiter],
61) -> Result<Vec<Instruction>> {
62 let mut solana_instructions = Vec::new();
63
64 for inst in instructions {
65 let program_id = Pubkey::from_str(&inst.program_id)?;
66
67 let accounts: Vec<AccountMeta> = inst
68 .accounts
69 .iter()
70 .map(|acc| {
71 let pubkey = Pubkey::from_str(&acc.program_id)?;
72 Ok(AccountMeta {
73 pubkey,
74 is_signer: acc.is_signer,
75 is_writable: acc.is_writable,
76 })
77 })
78 .collect::<Result<Vec<_>>>()?;
79
80 solana_instructions.push(Instruction {
81 program_id,
82 accounts,
83 data: inst.data.clone(),
84 });
85 }
86
87 Ok(solana_instructions)
88}
89
90pub fn convert_raydium_instructions(
91 instructions: &[api::InstructionRaydium],
92) -> Result<Vec<Instruction>> {
93 let mut solana_instructions = Vec::new();
94
95 for inst in instructions {
96 let program_id = Pubkey::from_str(&inst.program_id)?;
97
98 let accounts: Vec<AccountMeta> = inst
99 .accounts
100 .iter()
101 .map(|acc| {
102 let pubkey = Pubkey::from_str(&acc.program_id)?;
103 Ok(AccountMeta {
104 pubkey,
105 is_signer: acc.is_signer,
106 is_writable: acc.is_writable,
107 })
108 })
109 .collect::<Result<Vec<_>>>()?;
110
111 solana_instructions.push(Instruction {
112 program_id,
113 accounts,
114 data: inst.data.clone(),
115 });
116 }
117
118 Ok(solana_instructions)
119}
120
121pub fn create_transaction_message(
122 instructions: Vec<Instruction>,
123 recent_blockhash: &str,
124) -> Result<api::TransactionMessage> {
125 let mut transaction = Transaction::new_with_payer(&instructions, None);
126 transaction.message.recent_blockhash = recent_blockhash.parse()?;
127
128 let serialized = bincode::serialize(&transaction)?;
129 let content = general_purpose::STANDARD.encode(serialized);
130
131 Ok(api::TransactionMessage {
132 content,
133 is_cleanup: false,
134 })
135}
136
137pub fn convert_string_enums(value: &mut Value) {
138 match value {
139 Value::Object(map) => {
140 for (key, val) in map {
141 match (key.as_str(), &val) {
142 ("project", Value::String(s)) => {
143 if let Some(project_enum) = Project::from_str_name(s) {
144 *val = json!(project_enum as i32);
145 }
146 }
147
148 ("infinity", Value::String(s)) => {
149 let mapped = match s.as_str() {
150 "INF_NOT" => 0,
151 "INF" => 1,
152 "INF_NEG" => 2,
153 _ => return,
154 };
155 *val = json!(mapped);
156 }
157
158 _ => convert_string_enums(val),
159 }
160 }
161 }
162 Value::Array(arr) => arr.iter_mut().for_each(convert_string_enums),
163 _ => {}
164 }
165}
166
167#[cfg(test)]
168mod tests {
169 use super::*;
170
171 #[test]
172 fn test_conversions() {
173 let mut value = json!({
174 "project": "P_JUPITER",
175 "tradeFeeRate": "1000",
176 "nested": {
177 "project": "P_RAYDIUM",
178 "priceImpactPercent": {
179 "infinity": "INF_NOT"
180 }
181 },
182 "array": [
183 {"project": "P_OPENBOOK"}
184 ]
185 });
186
187 convert_string_enums(&mut value);
188
189 assert_eq!(value["project"], 2);
190 assert_eq!(value["nested"]["project"], 3);
191 assert_eq!(value["nested"]["priceImpactPercent"]["infinity"], 0);
192 assert_eq!(value["array"][0]["project"], 5);
193 }
194}
195
196pub fn timestamp() -> Option<Timestamp> {
199 SystemTime::now().duration_since(UNIX_EPOCH).ok().map(|duration| {
200 Timestamp {
201 seconds: duration.as_secs() as i64,
202 nanos: duration.subsec_nanos() as i32,
203 }
204 })
205}
206
207pub fn timestamp_rfc3339() -> String {
210 let now: DateTime<Utc> = Utc::now();
211 format!("{}.{}Z",
212 now.format("%Y-%m-%dT%H:%M:%S"),
213 format!("{:06}", now.timestamp_subsec_micros()).chars().take(3).collect::<String>() + "000"
214 )
215}