148 lines
4.0 KiB
Rust
148 lines
4.0 KiB
Rust
use anyhow::{Context, Result};
|
|
use tokio::sync::mpsc;
|
|
use yellowstone_grpc_proto::geyser::SubscribeUpdateTransaction;
|
|
use yellowstone_grpc_proto::prost::Message;
|
|
use bs58;
|
|
use dotenvy::dotenv;
|
|
use std::fs;
|
|
|
|
use sniper_bot::listener;
|
|
use sniper_bot::listener::YellowstoneSource;
|
|
|
|
use sniper_bot::utils::{
|
|
save_tx_update,
|
|
load_keypair_and_pubkey_from_json,
|
|
init_tracing,
|
|
};
|
|
|
|
use sniper_bot::engine::prepare_and_build;
|
|
use sniper_bot::protocols::default_adapters;
|
|
use sniper_bot::rpc::{HttpRpc, Rpc, sign_versioned_tx};
|
|
|
|
use solana_sdk::{hash::Hash, message::VersionedMessage};
|
|
|
|
|
|
#[tokio::main]
|
|
async fn main() -> Result<()> {
|
|
init_tracing();
|
|
|
|
// Load environment variables from .env file
|
|
dotenv().ok();
|
|
let bundler = std::env::var("BUNDLER")?;
|
|
|
|
|
|
|
|
// Testing conditions
|
|
//
|
|
let simulate_first = std::env::var("SIMULATE_FIRST").unwrap_or_else(|_| "true".to_string()) == "true";
|
|
let bin_path = std::env::var("BIN_PATH").context("Falta BIN_PATH en .env (o variable de entorno)")?;
|
|
//
|
|
// Testing conditions
|
|
|
|
// Load wallet
|
|
let (kp, kp_pub) = load_keypair_and_pubkey_from_json()?;
|
|
eprintln!("Keypair pubkey: {}", kp_pub);
|
|
|
|
|
|
tracing::info!("[INFO] Bot initialited");
|
|
|
|
// RPC client
|
|
let rpc = HttpRpc::new()?;
|
|
|
|
// Testing conditions
|
|
//
|
|
// Cargar y decodificar el BIN a txu (protobuf)
|
|
let bytes = fs::read(&bin_path).with_context(|| format!("Leyendo binario: {bin_path}"))?;
|
|
let txu = SubscribeUpdateTransaction::decode(bytes.as_slice()).context("Decodificando prost: SubscribeUpdateTransaction")?;
|
|
//
|
|
// Testing conditions
|
|
|
|
let adapters = default_adapters();
|
|
|
|
// === A partir de aquí: orquestar compra ===
|
|
// 1) Plan + mensaje preparado (por ahora: replay 1:1)
|
|
let (_plan, prepared) = prepare_and_build(txu, &adapters, &kp_pub)?;
|
|
|
|
// 2) Blockhash fresco + slot
|
|
let (new_bh, ctx_slot) = rpc.get_latest_blockhash().await?;
|
|
println!("Latest slot solana: {ctx_slot}");
|
|
|
|
// 3) Actualiza blockhash del mensaje
|
|
let vm = set_recent_blockhash(prepared.message, new_bh);
|
|
|
|
// 4) Firma con tu keypair
|
|
let vtx = sign_versioned_tx(vm, &[&kp])?;
|
|
|
|
// 5) Simula (opcional)
|
|
if simulate_first {
|
|
rpc.simulate(&vtx, Some(ctx_slot)).await?;
|
|
println!("🧪 simulateTransaction OK");
|
|
}
|
|
|
|
|
|
// 6) Enviar
|
|
let sig = rpc.send_transaction(&vtx, Some(ctx_slot)).await?;
|
|
println!("🚀 Enviada: {}", sig);
|
|
|
|
|
|
// Testing conditions
|
|
//
|
|
return Ok(());
|
|
//
|
|
// Testing conditions
|
|
|
|
|
|
// Create a channel for transaction updates
|
|
let (tx, mut rx) = mpsc::channel::<SubscribeUpdateTransaction>(1024);
|
|
|
|
// Connect to Yellowstone source
|
|
let yellowstone_source = YellowstoneSource::connect(
|
|
bundler
|
|
).await?;
|
|
|
|
// Launch listener
|
|
let _run = tokio::spawn({
|
|
let tx = tx.clone();
|
|
async move {
|
|
if let Err(e) = listener::yellowstone_forward_source_to_channel(yellowstone_source, tx).await {
|
|
tracing::error!("[ERR] listener error: {}", e);
|
|
}
|
|
}
|
|
});
|
|
|
|
|
|
while let Some(txu) = rx.recv().await {
|
|
// Guarda en disco
|
|
if let Err(e) = save_tx_update(&txu).await {
|
|
tracing::warn!("No se pudo guardar el frame: {e:?}");
|
|
}
|
|
|
|
//tracing::info!("[INFO] received transaction update: \n{:#?}", txu);
|
|
let sig = txu
|
|
.transaction
|
|
.as_ref()
|
|
.map(|t| bs58::encode(&t.signature).into_string())
|
|
.unwrap_or("<no-signature>".to_string());
|
|
|
|
|
|
|
|
|
|
tracing::info!("OK: slot={} sig={}", txu.slot, sig);
|
|
}
|
|
|
|
|
|
Ok(())
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn set_recent_blockhash(mut vm: VersionedMessage, new_bh: Hash) -> VersionedMessage {
|
|
match &mut vm {
|
|
VersionedMessage::V0(m) => { m.recent_blockhash = new_bh; }
|
|
VersionedMessage::Legacy(m) => { m.recent_blockhash = new_bh; }
|
|
}
|
|
vm
|
|
} |