1use chrono::Local;
2use dotenv::dotenv;
3use env_logger::Builder;
4use log::LevelFilter;
5use serde::Deserialize;
6use solana_account_decoder::parse_account_data::ParsedAccount;
7use solana_account_decoder::UiAccountData;
8use solana_client::nonblocking::rpc_client::RpcClient;
9use solana_client::rpc_response::RpcKeyedAccount;
10use solana_sdk::commitment_config::CommitmentConfig;
11use solana_sdk::compute_budget::ComputeBudgetInstruction;
12use solana_sdk::instruction::Instruction;
13use solana_sdk::pubkey::Pubkey;
14use solana_sdk::signature::Keypair;
15use std::error::Error;
16use std::io::Write;
17use std::str::FromStr;
18
19pub fn env(var: &str) -> String {
20 std::env::var(var).unwrap_or_else(|_| panic!("{} env var not set", var))
21}
22
23pub fn pubkey_to_string<S>(
25 pubkey: &Pubkey,
26 serializer: S,
27) -> Result<S::Ok, S::Error>
28where
29 S: serde::Serializer,
30{
31 serializer.serialize_str(&pubkey.to_string())
32}
33
34pub fn string_to_pubkey<'de, D>(deserializer: D) -> Result<Pubkey, D::Error>
36where
37 D: serde::Deserializer<'de>,
38{
39 let s = String::deserialize(deserializer)?;
40 Pubkey::from_str(&s).map_err(serde::de::Error::custom)
41}
42
43pub fn string_to_u64<'de, D>(deserializer: D) -> Result<u64, D::Error>
44where
45 D: serde::Deserializer<'de>,
46{
47 let s: String = Deserialize::deserialize(deserializer)?;
48 s.parse().map_err(serde::de::Error::custom)
49}
50
51pub fn make_compute_budget_ixs(
52 price: u64,
53 max_units: u32,
54) -> Vec<Instruction> {
55 vec![
56 ComputeBudgetInstruction::set_compute_unit_price(price),
57 ComputeBudgetInstruction::set_compute_unit_limit(max_units),
58 ]
59}
60
61#[derive(Debug, Default, Clone)]
62pub struct Holding {
63 pub mint: Pubkey,
64 pub ata: Pubkey,
65 pub amount: u64,
66}
67
68pub fn parse_holding(ata: RpcKeyedAccount) -> Result<Holding, Box<dyn Error>> {
69 if let UiAccountData::Json(ParsedAccount {
70 program: _,
71 parsed,
72 space: _,
73 }) = ata.account.data
74 {
75 let amount = parsed["info"]["tokenAmount"]["amount"]
76 .as_str()
77 .expect("amount")
78 .parse::<u64>()?;
79 let mint =
80 Pubkey::from_str(parsed["info"]["mint"].as_str().expect("mint"))?;
81 let ata = Pubkey::from_str(&ata.pubkey)?;
82 Ok(Holding { mint, ata, amount })
83 } else {
84 Err("failed to parse holding".into())
85 }
86}
87
88pub fn init_logger() -> Result<(), Box<dyn Error>> {
89 let logs_level = match std::env::var("RUST_LOG") {
90 Ok(level) => {
91 LevelFilter::from_str(&level).unwrap_or(LevelFilter::Info)
92 }
93 Err(_) => LevelFilter::Info,
94 };
95
96 Builder::from_default_env()
98 .format(|buf, record| {
99 writeln!(
100 buf,
101 "{} [{}] {}",
102 Local::now().timestamp_millis(),
103 record.level(),
104 record.args()
105 )
106 })
107 .filter(None, logs_level)
108 .try_init()?;
109
110 Ok(())
111}
112
113pub fn apply_fee(amount: u64) -> u64 {
114 amount * 101 / 100
115}
116
117pub fn load_keypair_for_tests() -> Keypair {
118 dotenv().ok();
119 Keypair::from_base58_string(&env("PRIVATE_KEY"))
120}
121
122pub fn make_rpc_client() -> RpcClient {
123 dotenv().ok();
124 let rpc_url = env("RPC_URL");
125 RpcClient::new(rpc_url)
126}
127
128pub async fn verify_transaction(
129 signature: &str,
130 rpc_client: &RpcClient,
131) -> bool {
132 let confirmation = rpc_client
134 .confirm_transaction_with_commitment(
135 &signature.parse().unwrap(),
136 CommitmentConfig::confirmed(),
137 )
138 .await;
139
140 println!("signature: {}, confirmation: {:?}", signature, confirmation);
141
142 match confirmation {
143 Ok(resp) => resp.value,
144 Err(_) => false,
145 }
146}
147
148pub fn parse_pubkey(s: &str) -> Result<Pubkey, Box<dyn Error>> {
149 match Pubkey::from_str(s) {
150 Ok(pubkey) => Ok(pubkey),
151 Err(e) => return Err(Box::new(e)),
152 }
153}