Files
lapa_v3/src/engine.rs

92 lines
3.0 KiB
Rust

// src/engine.rs
use anyhow::{anyhow, Result};
use yellowstone_grpc_proto::geyser::SubscribeUpdateTransaction;
use solana_sdk::{
instruction::CompiledInstruction,
message::{v0::LoadedAddresses, VersionedMessage},
pubkey::Pubkey,
};
#[derive(Clone, Debug)]
pub struct TxuSnapshot {
pub slot: u64,
pub sig_b58_short: String,
pub txu: SubscribeUpdateTransaction,
}
/// Plan neutral que debe devolver extract_plan
#[derive(Clone)]
pub struct SwapPlan {
/// El VersionedMessage original **(opcional)** si lo tienes; útil para rebuild.
pub original_message: Option<VersionedMessage>,
/// account_keys completos (en el mismo orden que el message)
pub account_keys: Vec<Pubkey>,
/// instrucciones tal cual (program_id_index, accounts indices, data)
pub instructions: Vec<CompiledInstruction>,
/// si aplica: loaded address lookups (ALT)
pub loaded_addresses: Option<Vec<LoadedAddresses>>,
/// cuentas que identificamos como 'pool PDAs' (no sustituir)
pub pool_accounts: Vec<Pubkey>,
/// cuentas identificadas como 'trader accounts' (ATAs/payer) que deberemos sustituir
pub trader_accounts: Vec<Pubkey>,
/// (Opcional) amounts / min_out extraídos por decodificar data
pub amounts: Option<(u64, u64)>, // ejemplo: (amount_in, min_out)
}
/// Resultado: mensaje preparado (VersionedMessage) listo para firmar.
/// Si tu pipeline necesita devolver VersionedTransaction, puedes adaptarlo.
pub struct PreparedMessage {
pub message: VersionedMessage,
}
pub trait SwapAdapter {
fn name(&self) -> &'static str;
fn probe(&self, snap: &TxuSnapshot) -> bool;
/// Extrae el SwapPlan desde el txu (analiza message/ixs/data)
fn extract_plan(&self, snap: &TxuSnapshot) -> Result<SwapPlan>;
/// Con un plan y tu owner (pubkey), construye el VersionedMessage listo para firmar.
fn build_message(&self, plan: SwapPlan, my_owner: &Pubkey) -> Result<PreparedMessage>;
}
/// Helper: crea snapshot simple desde txu
pub fn snapshot_from_txu(txu: SubscribeUpdateTransaction) -> TxuSnapshot {
let slot = txu.slot;
let sig_b58_short = txu
.transaction
.as_ref()
.and_then(|t| (!t.signature.is_empty()).then(|| bs58::encode(&t.signature).into_string()))
.map(|s| s.chars().take(12).collect::<String>())
.unwrap_or_else(|| "nosig".to_string());
TxuSnapshot { slot, sig_b58_short, txu }
}
/// Crea snapshot, selecciona adapter y devuelve el resultado.
pub fn prepare_and_build(
txu: SubscribeUpdateTransaction,
adapters: &[Box<dyn SwapAdapter>],
my_owner: &Pubkey,
) -> Result<(SwapPlan, PreparedMessage)> {
let snap = snapshot_from_txu(txu);
let adapter = adapters
.iter()
.find(|a| a.probe(&snap))
.ok_or_else(|| anyhow!("Ningún adapter reconoce este txu"))?;
let plan = adapter.extract_plan(&snap)?;
let prepared = adapter.build_message(plan.clone(), my_owner)?;
Ok((plan, prepared))
}