Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor: simplify Keypair read from file #7

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ To run the basic transaction example:

```rust
// Load the sender's keypair
let sender = load_keypair("/path/to/wallet.json")?;
let sender = Keypair::read_from_file(("/path/to/wallet.json").expect("Failed to read wallet file");

// Set up receiver pubkey
let receiver = Pubkey::from_str("YOUR_RECEIVER_PUBKEY")?;
Expand All @@ -79,7 +79,7 @@ To run the basic bundle example:

```rust
// Load the sender's keypair
let sender = load_keypair("/path/to/wallet.json")?;
let sender = Keypair::read_from_file(("/path/to/wallet.json").expect("Failed to read wallet file");

// Set up receiver pubkey
let receiver = Pubkey::from_str("YOUR_RECEIVER_PUBKEY")?;
Expand Down
220 changes: 116 additions & 104 deletions examples/basic_bundle.rs
Original file line number Diff line number Diff line change
@@ -1,18 +1,16 @@
use anyhow::{Result, anyhow};
use anyhow::{anyhow, Result};
use jito_sdk_rust::JitoJsonRpcSDK;
use serde_json::json;
use solana_client::rpc_client::RpcClient;
use solana_sdk::{
instruction::{AccountMeta, Instruction},
pubkey::Pubkey,
signature::{Keypair, Signer},
signer::EncodableKey,
system_instruction,
transaction::Transaction,
instruction::{Instruction, AccountMeta},
};
use std::str::FromStr;
use std::fs::File;
use std::io::BufReader;
use serde_json::json;
use bs58;
use tokio::time::{sleep, Duration};

#[derive(Debug)]
Expand All @@ -22,13 +20,6 @@ struct BundleStatus {
transactions: Option<Vec<String>>,
}

fn load_keypair(path: &str) -> Result<Keypair> {
let file = File::open(path)?;
let reader = BufReader::new(file);
let wallet: Vec<u8> = serde_json::from_reader(reader)?;
Ok(Keypair::from_bytes(&wallet)?)
}

#[tokio::main]
async fn main() -> Result<()> {
// Set up Solana RPC client (for getting recent blockhash and confirming transaction)
Expand All @@ -41,7 +32,8 @@ async fn main() -> Result<()> {
//let jito_sdk = JitoJsonRpcSDK::new("https://mainnet.block-engine.jito.wtf/api/v1", "UUID-API-KEY");

// Load the sender's keypair
let sender = load_keypair("/path/to/wallet.json" )?;
let sender =
Keypair::read_from_file("/path/to/wallet.json").expect("Failed to read wallet file");
println!("Sender pubkey: {}", sender.pubkey());

// Set up receiver and Jito tip account
Expand All @@ -54,16 +46,10 @@ async fn main() -> Result<()> {
let jito_tip_amount = 1_000; // 0.000001 SOL

// Create instructions
let main_transfer_ix = system_instruction::transfer(
&sender.pubkey(),
&receiver,
main_transfer_amount,
);
let jito_tip_ix = system_instruction::transfer(
&sender.pubkey(),
&jito_tip_account,
jito_tip_amount,
);
let main_transfer_ix =
system_instruction::transfer(&sender.pubkey(), &receiver, main_transfer_amount);
let jito_tip_ix =
system_instruction::transfer(&sender.pubkey(), &jito_tip_account, jito_tip_amount);

// Create memo instruction
let memo_program_id = Pubkey::from_str("MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr")?;
Expand All @@ -85,103 +71,118 @@ async fn main() -> Result<()> {

// Serialize the transaction
let serialized_tx = bs58::encode(bincode::serialize(&transaction)?).into_string();

// Prepare bundle for submission (array of transactions)
let bundle = json!([serialized_tx]);

// UUID for the bundle
let uuid = None;

// Send bundle using Jito SDK
println!("Sending bundle with 1 transaction...");
let response = jito_sdk.send_bundle(Some(bundle), uuid).await?;

// Extract bundle UUID from response
let bundle_uuid = response["result"]
.as_str()
.ok_or_else(|| anyhow!("Failed to get bundle UUID from response"))?;
println!("Bundle sent with UUID: {}", bundle_uuid);

// Confirm bundle status
let max_retries = 10;
let retry_delay = Duration::from_secs(2);

for attempt in 1..=max_retries {
println!("Checking bundle status (attempt {}/{})", attempt, max_retries);

let status_response = jito_sdk.get_in_flight_bundle_statuses(vec![bundle_uuid.to_string()]).await?;

if let Some(result) = status_response.get("result") {
if let Some(value) = result.get("value") {
if let Some(statuses) = value.as_array() {
if let Some(bundle_status) = statuses.get(0) {
if let Some(status) = bundle_status.get("status") {
match status.as_str() {
Some("Landed") => {
println!("Bundle landed on-chain. Checking final status...");
return check_final_bundle_status(&jito_sdk, bundle_uuid).await;
},
Some("Pending") => {
println!("Bundle is pending. Waiting...");
},
Some(status) => {
println!("Unexpected bundle status: {}. Waiting...", status);
},
None => {
println!("Unable to parse bundle status. Waiting...");
}
}
} else {
println!("Status field not found in bundle status. Waiting...");
}
} else {
println!("Bundle status not found. Waiting...");
}
} else {
println!("Unexpected value format. Waiting...");
}
} else {
println!("Value field not found in result. Waiting...");

}
} else if let Some(error) = status_response.get("error") {
println!("Error checking bundle status: {:?}", error);
} else {
println!("Unexpected response format. Waiting...");
}

if attempt < max_retries {
sleep(retry_delay).await;
}
}

Err(anyhow!("Failed to confirm bundle status after {} attempts", max_retries))
}

async fn check_final_bundle_status(jito_sdk: &JitoJsonRpcSDK, bundle_uuid: &str) -> Result<()> {
println!("Sending bundle with 1 transaction...");
let response = jito_sdk.send_bundle(Some(bundle), uuid).await?;

// Extract bundle UUID from response
let bundle_uuid = response["result"]
.as_str()
.ok_or_else(|| anyhow!("Failed to get bundle UUID from response"))?;
println!("Bundle sent with UUID: {}", bundle_uuid);

// Confirm bundle status
let max_retries = 10;
let retry_delay = Duration::from_secs(2);

for attempt in 1..=max_retries {
println!("Checking final bundle status (attempt {}/{})", attempt, max_retries);
println!(
"Checking bundle status (attempt {}/{})",
attempt, max_retries
);

let status_response = jito_sdk
.get_in_flight_bundle_statuses(vec![bundle_uuid.to_string()])
.await?;

if let Some(result) = status_response.get("result") {
if let Some(value) = result.get("value") {
if let Some(statuses) = value.as_array() {
if let Some(bundle_status) = statuses.first() {
if let Some(status) = bundle_status.get("status") {
match status.as_str() {
Some("Landed") => {
println!("Bundle landed on-chain. Checking final status...");
return check_final_bundle_status(&jito_sdk, bundle_uuid).await;
}
Some("Pending") => {
println!("Bundle is pending. Waiting...");
}
Some(status) => {
println!("Unexpected bundle status: {}. Waiting...", status);
}
None => {
println!("Unable to parse bundle status. Waiting...");
}
}
} else {
println!("Status field not found in bundle status. Waiting...");
}
} else {
println!("Bundle status not found. Waiting...");
}
} else {
println!("Unexpected value format. Waiting...");
}
} else {
println!("Value field not found in result. Waiting...");
}
} else if let Some(error) = status_response.get("error") {
println!("Error checking bundle status: {:?}", error);
} else {
println!("Unexpected response format. Waiting...");
}

let status_response = jito_sdk.get_bundle_statuses(vec![bundle_uuid.to_string()]).await?;
if attempt < max_retries {
sleep(retry_delay).await;
}
}

Err(anyhow!(
"Failed to confirm bundle status after {} attempts",
max_retries
))
}

async fn check_final_bundle_status(jito_sdk: &JitoJsonRpcSDK, bundle_uuid: &str) -> Result<()> {
let max_retries = 10;
let retry_delay = Duration::from_secs(2);

for attempt in 1..=max_retries {
println!(
"Checking final bundle status (attempt {}/{})",
attempt, max_retries
);

let status_response = jito_sdk
.get_bundle_statuses(vec![bundle_uuid.to_string()])
.await?;
let bundle_status = get_bundle_status(&status_response)?;

match bundle_status.confirmation_status.as_deref() {
Some("confirmed") => {
println!("Bundle confirmed on-chain. Waiting for finalization...");
check_transaction_error(&bundle_status)?;
},
}
Some("finalized") => {
println!("Bundle finalized on-chain successfully!");
check_transaction_error(&bundle_status)?;
print_transaction_url(&bundle_status);
return Ok(());
},
}
Some(status) => {
println!("Unexpected final bundle status: {}. Continuing to poll...", status);
},
println!(
"Unexpected final bundle status: {}. Continuing to poll...",
status
);
}
None => {
println!("Unable to parse final bundle status. Continuing to poll...");
}
Expand All @@ -192,22 +193,33 @@ async fn main() -> Result<()> {
}
}

Err(anyhow!("Failed to get finalized status after {} attempts", max_retries))
Err(anyhow!(
"Failed to get finalized status after {} attempts",
max_retries
))
}

fn get_bundle_status(status_response: &serde_json::Value) -> Result<BundleStatus> {
status_response
.get("result")
.and_then(|result| result.get("value"))
.and_then(|value| value.as_array())
.and_then(|statuses| statuses.get(0))
.and_then(|statuses| statuses.first())
.ok_or_else(|| anyhow!("Failed to parse bundle status"))
.map(|bundle_status| BundleStatus {
confirmation_status: bundle_status.get("confirmation_status").and_then(|s| s.as_str()).map(String::from),
confirmation_status: bundle_status
.get("confirmation_status")
.and_then(|s| s.as_str())
.map(String::from),
err: bundle_status.get("err").cloned(),
transactions: bundle_status.get("transactions").and_then(|t| t.as_array()).map(|arr| {
arr.iter().filter_map(|v| v.as_str().map(String::from)).collect()
}),
transactions: bundle_status
.get("transactions")
.and_then(|t| t.as_array())
.map(|arr| {
arr.iter()
.filter_map(|v| v.as_str().map(String::from))
.collect()
}),
})
}

Expand Down Expand Up @@ -235,4 +247,4 @@ fn print_transaction_url(bundle_status: &BundleStatus) {
} else {
println!("No transactions found in the bundle status.");
}
}
}
Loading