listen_kit/
util.rs

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
23/// Helper function for pubkey serialize
24pub 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
34/// Helper function for pubkey deserialize
35pub 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    // in logs, use unix timestamp in ms
97    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    // Wait for transaction confirmation
133    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}