listen_kit/
jito.rs

1use log::info;
2use rand::rngs::ThreadRng;
3use rand::thread_rng;
4use rand::Rng;
5use serde::Deserialize;
6use serde_json::json;
7use solana_sdk::pubkey::Pubkey;
8use solana_sdk::transaction::Transaction;
9use solana_transaction_status::{
10    Encodable, EncodedTransaction, UiTransactionEncoding,
11};
12use std::cell::RefCell;
13use std::str::FromStr;
14
15#[derive(Debug, Deserialize)]
16pub struct JitoResponse {
17    pub jsonrpc: String,
18    pub result: String,
19    pub id: i64,
20}
21
22// TODO support versioned transactions (jup 3+ multi-hops)
23#[timed::timed(duration(printer = "info!"))]
24pub async fn send_jito_tx(
25    tx: Transaction,
26) -> Result<String, Box<dyn std::error::Error>> {
27    let client = reqwest::Client::new();
28
29    let encoded_tx = match tx.encode(UiTransactionEncoding::Binary) {
30        EncodedTransaction::LegacyBinary(b) => b,
31        _ => return Err("Failed to encode transaction".into()),
32    };
33
34    let res = client
35        .post("https://mainnet.block-engine.jito.wtf/api/v1/transactions")
36        .header("content-type", "application/json")
37        .json(&json!({
38            "jsonrpc": "2.0",
39            "id": 1,
40            "method": "sendTransaction",
41            "params": [encoded_tx]
42        }))
43        .send()
44        .await
45        .expect("send tx");
46
47    let jito_response = res.json::<JitoResponse>().await?;
48
49    Ok(jito_response.result)
50}
51
52thread_local! {
53    static RNG: RefCell<ThreadRng> = RefCell::new(thread_rng());
54}
55
56#[inline(always)]
57pub fn fast_random_0_to_7() -> u8 {
58    RNG.with(|rng| rng.borrow_mut().gen_range(0..8))
59}
60
61pub fn get_jito_tip_pubkey() -> Pubkey {
62    const PUBKEYS: [&str; 8] = [
63        "96gYZGLnJYVFmbjzopPSU6QiEV5fGqZNyN9nmNhvrZU5",
64        "HFqU5x63VTqvQss8hp11i4wVV8bD44PvwucfZ2bU7gRe",
65        "Cw8CFyM9FkoMi7K7Crf6HNQqf4uEMzpKw6QNghXLvLkY",
66        "ADaUMid9yfUytqMBgopwjb2DTLSokTSzL1zt6iGPaS49",
67        "DfXygSm4jCyNCybVYYK6DwvWqjKee8pbDmJGcLWNDXjh",
68        "ADuUkR4vqLUMWXxW9gh6D6L8pMSawimctcNZ5pGwDcEt",
69        "DttWaMuVvTiduZRnguLF7jNxTgiMBZ1hyAumKUiL2KRL",
70        "3AVi9Tg9Uo68tJfuvoKvqKNWKkC5wPdSSdeBnizKZ6jT",
71    ];
72    let index = fast_random_0_to_7();
73    Pubkey::from_str(PUBKEYS[index as usize]).expect("parse tip pubkey")
74}
75
76#[cfg(test)]
77mod tests {
78    #[test]
79    fn bench_get_jito_tip_pubkey() {
80        for _ in 0..100 {
81            let start = std::time::Instant::now();
82            let _ = super::get_jito_tip_pubkey();
83            let elapsed = start.elapsed();
84            println!("elapsed: {:?}", elapsed);
85        }
86    }
87}