92 lines
3.0 KiB
Rust
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))
|
|
} |