This commit is contained in:
2026-04-18 23:35:30 +02:00
parent f08dfbd611
commit 54d4d8b18a
4 changed files with 402 additions and 44 deletions

View File

@@ -8,7 +8,7 @@ members = [
]
[workspace.package]
version = "0.5.5"
version = "0.5.6"
edition = "2024"
license = "MIT"
repository = "https://git.sasedev.com/Sasedev/khadhroony-bobot"

View File

@@ -26,6 +26,7 @@ mod ids;
mod heuristics;
mod account_enrichment;
mod enriched_classifier;
mod signal_correlation;
/// Runs the listener application bootstrap workflow.
pub use crate::app::run_listener_app;
@@ -127,3 +128,11 @@ pub use crate::enriched_classifier::KhbbConfirmedTokenAccountActivityEvent;
pub use crate::enriched_classifier::KhbbConfirmedMintAccountActivityEvent;
/// Unknown token-program-owned account activity.
pub use crate::enriched_classifier::KhbbUnknownTokenProgramAccountActivityEvent;
/// Correlated signal produced from enriched and heuristic activity.
pub use crate::signal_correlation::KhbbCorrelatedSignal;
/// Correlated signal indicating a confirmed token account update.
pub use crate::signal_correlation::KhbbConfirmedTokenAccountUpdateSignal;
/// Correlated signal indicating a potential new token mint.
pub use crate::signal_correlation::KhbbPotentialNewTokenMintSignal;
/// Correlated signal indicating a potential token bootstrap flow.
pub use crate::signal_correlation::KhbbPotentialTokenBootstrapFlowSignal;

View File

@@ -560,9 +560,9 @@ pub async fn run_listener_runtime(
classified.clone(),
),
);
match heuristic_result {
let heuristic_signals = match heuristic_result {
Ok(signals) => {
for signal in signals {
for signal in &signals {
match signal {
crate::KhbbHeuristicSignal::PotentialTokenAccountActivity(inner) => {
tracing::trace!(
@@ -597,6 +597,7 @@ pub async fn run_listener_runtime(
crate::KhbbHeuristicSignal::PotentialTokenBootstrapActivity(_) => {}
}
}
signals
}
Err(error) => {
tracing::error!(
@@ -604,8 +605,9 @@ pub async fn run_listener_runtime(
error = %error,
"failed to derive heuristic signals from spl-token program activity"
);
vec![]
}
}
};
let account_info_result = http_client
.get_account_info(
&classified.pubkey,
@@ -671,6 +673,51 @@ pub async fn run_listener_runtime(
lamports = ?inner.lamports,
"confirmed token account activity event"
);
let correlated_result =
crate::signal_correlation::correlate_signals(
&crate::KhbbEnrichedConfirmedEvent::ConfirmedTokenAccountActivity(
inner.clone(),
),
&heuristic_signals,
);
match correlated_result {
Ok(signals) => {
for signal in signals {
match signal {
crate::KhbbCorrelatedSignal::ConfirmedTokenAccountUpdate(correlated) => {
tracing::trace!(
listener_session_id = session.id,
pubkey = %correlated.pubkey,
context_slot = correlated.context_slot,
owner = ?correlated.owner,
lamports = ?correlated.lamports,
"correlated confirmed token account update signal"
);
}
crate::KhbbCorrelatedSignal::PotentialTokenBootstrapFlow(correlated) => {
tracing::trace!(
listener_session_id = session.id,
pubkey = ?correlated.pubkey,
context_slot = correlated.context_slot,
saw_token_program = correlated.saw_token_program,
saw_associated_token_account = correlated.saw_associated_token_account,
saw_bootstrap_logs = correlated.saw_bootstrap_logs,
"correlated potential token bootstrap flow signal"
);
}
crate::KhbbCorrelatedSignal::PotentialNewTokenMint(_) => {}
}
}
}
Err(error) => {
tracing::error!(
listener_session_id = session.id,
error = %error,
pubkey = %classified.pubkey,
"failed to correlate confirmed token account activity"
);
}
}
}
Ok(Some(crate::KhbbEnrichedConfirmedEvent::ConfirmedMintAccountActivity(inner))) => {
tracing::trace!(
@@ -681,6 +728,41 @@ pub async fn run_listener_runtime(
lamports = ?inner.lamports,
"confirmed mint account activity event"
);
let correlated_result =
crate::signal_correlation::correlate_signals(
&crate::KhbbEnrichedConfirmedEvent::ConfirmedMintAccountActivity(
inner.clone(),
),
&heuristic_signals,
);
match correlated_result {
Ok(signals) => {
for signal in signals {
match signal {
crate::KhbbCorrelatedSignal::PotentialNewTokenMint(correlated) => {
tracing::trace!(
listener_session_id = session.id,
pubkey = %correlated.pubkey,
context_slot = correlated.context_slot,
owner = ?correlated.owner,
lamports = ?correlated.lamports,
"correlated potential new token mint signal"
);
}
crate::KhbbCorrelatedSignal::ConfirmedTokenAccountUpdate(_) => {}
crate::KhbbCorrelatedSignal::PotentialTokenBootstrapFlow(_) => {}
}
}
}
Err(error) => {
tracing::error!(
listener_session_id = session.id,
error = %error,
pubkey = %classified.pubkey,
"failed to correlate confirmed mint account activity"
);
}
}
}
Ok(Some(crate::KhbbEnrichedConfirmedEvent::UnknownTokenProgramAccountActivity(inner))) => {
tracing::trace!(
@@ -691,6 +773,42 @@ pub async fn run_listener_runtime(
lamports = ?inner.lamports,
"unknown token-program-owned account activity event"
);
let correlated_result =
crate::signal_correlation::correlate_signals(
&crate::KhbbEnrichedConfirmedEvent::UnknownTokenProgramAccountActivity(
inner.clone(),
),
&heuristic_signals,
);
match correlated_result {
Ok(signals) => {
for signal in signals {
match signal {
crate::KhbbCorrelatedSignal::PotentialTokenBootstrapFlow(correlated) => {
tracing::trace!(
listener_session_id = session.id,
pubkey = ?correlated.pubkey,
context_slot = correlated.context_slot,
saw_token_program = correlated.saw_token_program,
saw_associated_token_account = correlated.saw_associated_token_account,
saw_bootstrap_logs = correlated.saw_bootstrap_logs,
"correlated potential token bootstrap flow signal"
);
}
crate::KhbbCorrelatedSignal::ConfirmedTokenAccountUpdate(_) => {}
crate::KhbbCorrelatedSignal::PotentialNewTokenMint(_) => {}
}
}
}
Err(error) => {
tracing::error!(
listener_session_id = session.id,
error = %error,
pubkey = %classified.pubkey,
"failed to correlate unknown token-program-owned account activity"
);
}
}
}
Ok(None) => {}
Err(error) => {

View File

@@ -0,0 +1,231 @@
// file: khbb_lib/src/signal_correlation.rs
//! Local correlation of enriched and heuristic signals into stronger trading-oriented signals.
/// Correlated signal produced from enriched and heuristic activity.
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub enum KhbbCorrelatedSignal {
/// Confirmed token account update.
ConfirmedTokenAccountUpdate(KhbbConfirmedTokenAccountUpdateSignal),
/// Potential new token mint.
PotentialNewTokenMint(KhbbPotentialNewTokenMintSignal),
/// Potential token bootstrap flow.
PotentialTokenBootstrapFlow(KhbbPotentialTokenBootstrapFlowSignal),
}
/// Correlated signal indicating a confirmed token account update.
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct KhbbConfirmedTokenAccountUpdateSignal {
/// Account pubkey.
pub pubkey: std::string::String,
/// Context slot.
pub context_slot: u64,
/// Owner program if available.
pub owner: std::option::Option<std::string::String>,
/// Lamports if available.
pub lamports: std::option::Option<u64>,
}
/// Correlated signal indicating a potential new token mint.
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct KhbbPotentialNewTokenMintSignal {
/// Account pubkey.
pub pubkey: std::string::String,
/// Context slot.
pub context_slot: u64,
/// Owner program if available.
pub owner: std::option::Option<std::string::String>,
/// Lamports if available.
pub lamports: std::option::Option<u64>,
}
/// Correlated signal indicating a potential token bootstrap flow.
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct KhbbPotentialTokenBootstrapFlowSignal {
/// Account pubkey if one is available from the enriched side.
pub pubkey: std::option::Option<std::string::String>,
/// Context slot.
pub context_slot: u64,
/// Whether token program activity was seen.
pub saw_token_program: bool,
/// Whether associated token account activity was seen.
pub saw_associated_token_account: bool,
/// Whether bootstrap-style logs were seen.
pub saw_bootstrap_logs: bool,
}
/// Correlates an enriched confirmed event with heuristic signals.
pub(crate) fn correlate_signals(
enriched_event: &crate::KhbbEnrichedConfirmedEvent,
heuristic_signals: &[crate::KhbbHeuristicSignal],
) -> core::result::Result<std::vec::Vec<KhbbCorrelatedSignal>, crate::KhbbError> {
let mut results = std::vec::Vec::<KhbbCorrelatedSignal>::new();
match enriched_event {
crate::KhbbEnrichedConfirmedEvent::ConfirmedTokenAccountActivity(event) => {
results.push(KhbbCorrelatedSignal::ConfirmedTokenAccountUpdate(
KhbbConfirmedTokenAccountUpdateSignal {
pubkey: event.pubkey.clone(),
context_slot: event.context_slot,
owner: event.owner.clone(),
lamports: event.lamports,
},
));
let mut saw_token_program = false;
let mut saw_associated_token_account = false;
let mut saw_bootstrap_logs = false;
for signal in heuristic_signals {
match signal {
crate::KhbbHeuristicSignal::PotentialTokenAccountActivity(inner) => {
if inner.pubkey == event.pubkey {
saw_token_program = true;
}
},
crate::KhbbHeuristicSignal::PotentialAssociatedTokenAccountActivity(inner) => {
if inner.pubkey == event.pubkey {
saw_associated_token_account = true;
}
},
crate::KhbbHeuristicSignal::PotentialTokenBootstrapActivity(_) => {
saw_bootstrap_logs = true;
},
_ => {},
}
}
if saw_token_program && (saw_associated_token_account || saw_bootstrap_logs) {
results.push(KhbbCorrelatedSignal::PotentialTokenBootstrapFlow(
KhbbPotentialTokenBootstrapFlowSignal {
pubkey: Some(event.pubkey.clone()),
context_slot: event.context_slot,
saw_token_program,
saw_associated_token_account,
saw_bootstrap_logs,
},
));
}
},
crate::KhbbEnrichedConfirmedEvent::ConfirmedMintAccountActivity(event) => {
results.push(KhbbCorrelatedSignal::PotentialNewTokenMint(
KhbbPotentialNewTokenMintSignal {
pubkey: event.pubkey.clone(),
context_slot: event.context_slot,
owner: event.owner.clone(),
lamports: event.lamports,
},
));
},
crate::KhbbEnrichedConfirmedEvent::UnknownTokenProgramAccountActivity(event) => {
let mut saw_bootstrap_logs = false;
for signal in heuristic_signals {
if let crate::KhbbHeuristicSignal::PotentialTokenBootstrapActivity(_) = signal {
saw_bootstrap_logs = true;
}
}
if saw_bootstrap_logs {
results.push(KhbbCorrelatedSignal::PotentialTokenBootstrapFlow(
KhbbPotentialTokenBootstrapFlowSignal {
pubkey: Some(event.pubkey.clone()),
context_slot: event.context_slot,
saw_token_program: true,
saw_associated_token_account: false,
saw_bootstrap_logs,
},
));
}
},
}
Ok(results)
}
#[cfg(test)]
mod tests {
#[test]
fn correlate_confirmed_token_account_returns_confirmed_update() {
let enriched = crate::KhbbEnrichedConfirmedEvent::ConfirmedTokenAccountActivity(
crate::KhbbConfirmedTokenAccountActivityEvent {
pubkey: std::string::String::from("SomePubkey"),
context_slot: 100,
owner: Some(crate::ids::SPL_TOKEN_PROGRAM_ID.to_string()),
lamports: Some(123),
},
);
let heuristics = vec![crate::KhbbHeuristicSignal::PotentialTokenAccountActivity(
crate::KhbbPotentialTokenAccountActivitySignal {
pubkey: std::string::String::from("SomePubkey"),
context_slot: 100,
subscription_id: 1,
token_program_family: std::string::String::from("spl-token"),
},
)];
let result = super::correlate_signals(&enriched, &heuristics);
assert!(result.is_ok());
let signals = result.expect("correlate token account");
assert_eq!(signals.len(), 1);
match &signals[0] {
super::KhbbCorrelatedSignal::ConfirmedTokenAccountUpdate(inner) => {
assert_eq!(inner.pubkey, "SomePubkey");
},
_ => {
panic!("expected confirmed token account update");
},
}
}
#[test]
fn correlate_confirmed_mint_returns_potential_new_token_mint() {
let enriched = crate::KhbbEnrichedConfirmedEvent::ConfirmedMintAccountActivity(
crate::KhbbConfirmedMintAccountActivityEvent {
pubkey: std::string::String::from("MintPubkey"),
context_slot: 200,
owner: Some(crate::ids::SPL_TOKEN_PROGRAM_ID.to_string()),
lamports: Some(456),
},
);
let heuristics = vec![];
let result = super::correlate_signals(&enriched, &heuristics);
assert!(result.is_ok());
let signals = result.expect("correlate mint");
assert_eq!(signals.len(), 1);
match &signals[0] {
super::KhbbCorrelatedSignal::PotentialNewTokenMint(inner) => {
assert_eq!(inner.pubkey, "MintPubkey");
},
_ => {
panic!("expected potential new token mint");
},
}
}
#[test]
fn correlate_confirmed_token_account_returns_bootstrap_flow_when_context_matches() {
let enriched = crate::KhbbEnrichedConfirmedEvent::ConfirmedTokenAccountActivity(
crate::KhbbConfirmedTokenAccountActivityEvent {
pubkey: std::string::String::from("AtaLikePubkey"),
context_slot: 300,
owner: Some(crate::ids::SPL_TOKEN_PROGRAM_ID.to_string()),
lamports: Some(789),
},
);
let heuristics = vec![
crate::KhbbHeuristicSignal::PotentialTokenAccountActivity(
crate::KhbbPotentialTokenAccountActivitySignal {
pubkey: std::string::String::from("AtaLikePubkey"),
context_slot: 300,
subscription_id: 2,
token_program_family: std::string::String::from("spl-token"),
},
),
crate::KhbbHeuristicSignal::PotentialAssociatedTokenAccountActivity(
crate::KhbbPotentialAssociatedTokenAccountActivitySignal {
pubkey: std::string::String::from("AtaLikePubkey"),
context_slot: 300,
subscription_id: 2,
token_program_family: std::string::String::from("spl-token"),
},
),
];
let result = super::correlate_signals(&enriched, &heuristics);
assert!(result.is_ok());
let signals = result.expect("correlate bootstrap");
assert_eq!(signals.len(), 2);
}
}