Skip to content

Instantly share code, notes, and snippets.

@adilcpm
Created April 6, 2024 02:29
Show Gist options
  • Save adilcpm/4b142929ad17d4348d4b62f7cf90e45d to your computer and use it in GitHub Desktop.
Save adilcpm/4b142929ad17d4348d4b62f7cf90e45d to your computer and use it in GitHub Desktop.
send modified with jito
use reqwest::Client;
use serde_json::json;
use solana_client::{
client_error::{ClientError, ClientErrorKind, Result as ClientResult},
nonblocking::rpc_client::RpcClient,
rpc_config::RpcSimulateTransactionConfig,
};
use solana_program::instruction::Instruction;
use solana_sdk::{
commitment_config::CommitmentConfig,
compute_budget::ComputeBudgetInstruction,
signature::{Signature, Signer},
transaction::Transaction,
};
use solana_transaction_status::{TransactionConfirmationStatus, UiTransactionEncoding};
use std::{
io::{stdout, Write},
str::FromStr,
time::Duration,
};
use crate::Miner;
use base64; // Add this line to import the base64 crate
const RPC_RETRIES: usize = 0;
const GATEWAY_RETRIES: usize = 4;
const CONFIRM_RETRIES: usize = 4;
impl Miner {
pub async fn send_and_confirm(
&self,
ixs: &[Instruction],
dynamic_cus: bool,
skip_confirm: bool,
) -> ClientResult<Signature> {
let mut stdout = stdout();
let signer = self.signer();
let client =
RpcClient::new_with_commitment(self.cluster.clone(), CommitmentConfig::confirmed());
let jito_rpc_url = "https://mainnet.block-engine.jito.wtf/api/v1/transactions";
// Return error if balance is zero
let balance = client
.get_balance_with_commitment(&signer.pubkey(), CommitmentConfig::confirmed())
.await
.unwrap();
if balance.value <= 0 {
return Err(ClientError {
request: None,
kind: ClientErrorKind::Custom("Insufficient SOL balance".into()),
});
}
// Build tx
let (mut hash, mut slot) = client
.get_latest_blockhash_with_commitment(CommitmentConfig::confirmed())
.await
.unwrap();
// let mut send_cfg = RpcSendTransactionConfig {
// skip_preflight: true,
// preflight_commitment: Some(CommitmentLevel::Confirmed),
// encoding: Some(UiTransactionEncoding::Base64),
// max_retries: Some(RPC_RETRIES),
// min_context_slot: Some(slot),
// };
let mut tx = Transaction::new_with_payer(ixs, Some(&signer.pubkey()));
// Simulate if necessary
if dynamic_cus {
let sim_res = client
.simulate_transaction_with_config(
&tx,
RpcSimulateTransactionConfig {
sig_verify: false,
replace_recent_blockhash: false,
commitment: Some(CommitmentConfig::confirmed()),
encoding: Some(UiTransactionEncoding::Base64),
accounts: None,
min_context_slot: Some(slot),
inner_instructions: false,
},
)
.await;
if let Ok(sim_res) = sim_res {
if let Some(units_consumed) = sim_res.value.units_consumed {
let cu_budget_ix = ComputeBudgetInstruction::set_compute_unit_limit(
units_consumed as u32 + 1000,
);
let cu_price_ix =
ComputeBudgetInstruction::set_compute_unit_price(self.priority_fee);
let mut final_ixs = vec![];
final_ixs.extend_from_slice(&[cu_budget_ix, cu_price_ix]);
final_ixs.extend_from_slice(ixs);
tx = Transaction::new_with_payer(&final_ixs, Some(&signer.pubkey()));
}
}
}
// Submit tx
tx.sign(&[&signer], hash);
let mut sigs = vec![];
let mut attempts = 0;
loop {
println!("Attempt: {:?}", attempts);
let tx_base64 = base64::encode(bincode::serialize(&tx).unwrap());
// Create a reqwest client
let http_client = Client::new();
// Send the transaction
let response = http_client
.post(jito_rpc_url)
.header("Content-Type", "application/json")
.json(&json!({
"jsonrpc": "2.0",
"id": 4141,
"method": "sendTransaction",
"params": [tx_base64, {
"encoding": "base64",
"commitment": "confirmed",
"skipPreflight": true,
"preflightCommitment": "confirmed",
"maxRetries": RPC_RETRIES,
"minContextSlot": slot,
}],
"bundleOnly": true,
}))
.send()
.await?;
match response.status().is_success() {
true => {
let response_json: serde_json::Value = response.json().await?;
let sig =
Signature::from_str(response_json["result"].as_str().unwrap()).unwrap();
sigs.push(sig);
println!("{:?}", sig);
// Confirm tx
if skip_confirm {
return Ok(sig);
}
for _ in 0..CONFIRM_RETRIES {
std::thread::sleep(Duration::from_millis(2000));
match client.get_signature_statuses(&sigs).await {
Ok(signature_statuses) => {
println!("Confirms: {:?}", signature_statuses.value);
for signature_status in signature_statuses.value {
if let Some(signature_status) = signature_status.as_ref() {
if signature_status.confirmation_status.is_some() {
let current_commitment = signature_status
.confirmation_status
.as_ref()
.unwrap();
match current_commitment {
TransactionConfirmationStatus::Processed => {}
TransactionConfirmationStatus::Confirmed
| TransactionConfirmationStatus::Finalized => {
println!("Transaction landed!");
return Ok(sig);
}
}
} else {
println!("No status");
}
}
}
}
// Handle confirmation errors
Err(err) => {
println!("Error: {:?}", err);
}
}
}
println!("Transaction did not land");
}
// Handle submit errors
false => {
println!("Error {:?}", response.status());
}
}
stdout.flush().ok();
// Retry
std::thread::sleep(Duration::from_millis(2000));
(hash, slot) = client
.get_latest_blockhash_with_commitment(CommitmentConfig::confirmed())
.await
.unwrap();
tx.sign(&[&signer], hash);
attempts += 1;
if attempts > GATEWAY_RETRIES {
return Err(ClientError {
request: None,
kind: ClientErrorKind::Custom("Max retries".into()),
});
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment