0.7.25
This commit is contained in:
@@ -12,11 +12,11 @@ pub struct KbDetectionPersistenceService {
|
||||
impl KbDetectionPersistenceService {
|
||||
/// Creates a new detection persistence service.
|
||||
pub fn new(database: std::sync::Arc<crate::KbDatabase>) -> Self {
|
||||
Self { database }
|
||||
return Self { database };
|
||||
}
|
||||
/// Returns the shared database handle.
|
||||
pub fn database(&self) -> &std::sync::Arc<crate::KbDatabase> {
|
||||
&self.database
|
||||
return &self.database;
|
||||
}
|
||||
|
||||
/// Persists one on-chain observation.
|
||||
@@ -32,7 +32,7 @@ impl KbDetectionPersistenceService {
|
||||
input.slot,
|
||||
input.payload.clone(),
|
||||
);
|
||||
crate::insert_onchain_observation(self.database.as_ref(), &dto).await
|
||||
return crate::insert_onchain_observation(self.database.as_ref(), &dto).await;
|
||||
}
|
||||
|
||||
/// Persists one analysis signal.
|
||||
@@ -48,7 +48,7 @@ impl KbDetectionPersistenceService {
|
||||
input.score,
|
||||
input.payload.clone(),
|
||||
);
|
||||
crate::insert_analysis_signal(self.database.as_ref(), &dto).await
|
||||
return crate::insert_analysis_signal(self.database.as_ref(), &dto).await;
|
||||
}
|
||||
|
||||
/// Registers one token candidate from a technical source.
|
||||
@@ -104,11 +104,7 @@ impl KbDetectionPersistenceService {
|
||||
Ok(signal_id) => signal_id,
|
||||
Err(error) => return Err(error),
|
||||
};
|
||||
Ok(crate::KbDetectionTokenCandidateResult {
|
||||
token_id,
|
||||
observation_id,
|
||||
signal_id,
|
||||
})
|
||||
return Ok(crate::KbDetectionTokenCandidateResult { token_id, observation_id, signal_id });
|
||||
}
|
||||
|
||||
/// Registers one pool candidate from a technical source.
|
||||
@@ -154,7 +150,7 @@ impl KbDetectionPersistenceService {
|
||||
"cannot register pool candidate: no known dex matches program id '{}'",
|
||||
input.dex_program_id
|
||||
)));
|
||||
}
|
||||
},
|
||||
};
|
||||
let dex_id = match matched_dex.id {
|
||||
Some(dex_id) => dex_id,
|
||||
@@ -162,7 +158,7 @@ impl KbDetectionPersistenceService {
|
||||
return Err(crate::KbError::Db(
|
||||
"cannot register pool candidate: matched dex has no id".to_string(),
|
||||
));
|
||||
}
|
||||
},
|
||||
};
|
||||
let pool_dto = crate::KbPoolDto::new(
|
||||
dex_id,
|
||||
@@ -221,13 +217,13 @@ impl KbDetectionPersistenceService {
|
||||
Ok(signal_id) => signal_id,
|
||||
Err(error) => return Err(error),
|
||||
};
|
||||
Ok(crate::KbDetectionPoolCandidateResult {
|
||||
return Ok(crate::KbDetectionPoolCandidateResult {
|
||||
dex_id,
|
||||
pool_id,
|
||||
pool_listing_id,
|
||||
observation_id,
|
||||
signal_id,
|
||||
})
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -248,9 +244,9 @@ mod tests {
|
||||
use_wal: true,
|
||||
},
|
||||
};
|
||||
crate::KbDatabase::connect_and_initialize(&config)
|
||||
return crate::KbDatabase::connect_and_initialize(&config)
|
||||
.await
|
||||
.expect("database init must succeed")
|
||||
.expect("database init must succeed");
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
@@ -293,14 +289,8 @@ mod tests {
|
||||
.expect("list signals must succeed");
|
||||
assert_eq!(observations.len(), 1);
|
||||
assert_eq!(signals.len(), 1);
|
||||
assert_eq!(
|
||||
observations[0].object_key,
|
||||
"So11111111111111111111111111111111111111112"
|
||||
);
|
||||
assert_eq!(
|
||||
signals[0].object_key,
|
||||
"So11111111111111111111111111111111111111112"
|
||||
);
|
||||
assert_eq!(observations[0].object_key, "So11111111111111111111111111111111111111112");
|
||||
assert_eq!(signals[0].object_key, "So11111111111111111111111111111111111111112");
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
@@ -340,10 +330,7 @@ mod tests {
|
||||
.await
|
||||
.expect("get token must succeed");
|
||||
assert!(token.is_some());
|
||||
assert_eq!(
|
||||
token.expect("token must exist").symbol.as_deref(),
|
||||
Some("TEST")
|
||||
);
|
||||
assert_eq!(token.expect("token must exist").symbol.as_deref(), Some("TEST"));
|
||||
let observations = crate::list_recent_onchain_observations(service.database().as_ref(), 10)
|
||||
.await
|
||||
.expect("list observations must succeed");
|
||||
@@ -352,18 +339,9 @@ mod tests {
|
||||
.expect("list signals must succeed");
|
||||
assert_eq!(observations.len(), 1);
|
||||
assert_eq!(signals.len(), 1);
|
||||
assert_eq!(
|
||||
observations[0].object_key,
|
||||
"Mint111111111111111111111111111111111111111"
|
||||
);
|
||||
assert_eq!(
|
||||
signals[0].object_key,
|
||||
"Mint111111111111111111111111111111111111111"
|
||||
);
|
||||
assert_eq!(
|
||||
signals[0].related_observation_id,
|
||||
Some(result.observation_id)
|
||||
);
|
||||
assert_eq!(observations[0].object_key, "Mint111111111111111111111111111111111111111");
|
||||
assert_eq!(signals[0].object_key, "Mint111111111111111111111111111111111111111");
|
||||
assert_eq!(signals[0].related_observation_id, Some(result.observation_id));
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
|
||||
@@ -37,12 +37,12 @@ pub struct KbSolanaWsDetectionService {
|
||||
impl KbSolanaWsDetectionService {
|
||||
/// Creates a new Solana WebSocket detection service.
|
||||
pub fn new(persistence: crate::KbDetectionPersistenceService) -> Self {
|
||||
Self { persistence }
|
||||
return Self { persistence };
|
||||
}
|
||||
|
||||
/// Returns the shared persistence façade.
|
||||
pub fn persistence(&self) -> &crate::KbDetectionPersistenceService {
|
||||
&self.persistence
|
||||
return &self.persistence;
|
||||
}
|
||||
|
||||
/// Processes one Solana WebSocket JSON-RPC notification.
|
||||
@@ -57,24 +57,22 @@ impl KbSolanaWsDetectionService {
|
||||
Some(observation_kind) => observation_kind,
|
||||
None => return Ok(crate::KbSolanaWsDetectionOutcome::Ignored),
|
||||
};
|
||||
let token_candidate_result = self
|
||||
.try_register_token_candidate(endpoint_name.clone(), notification)
|
||||
.await;
|
||||
let token_candidate_result =
|
||||
self.try_register_token_candidate(endpoint_name.clone(), notification).await;
|
||||
match token_candidate_result {
|
||||
Ok(Some(result)) => {
|
||||
return Ok(crate::KbSolanaWsDetectionOutcome::TokenCandidateRegistered { result });
|
||||
}
|
||||
Ok(None) => {}
|
||||
},
|
||||
Ok(None) => {},
|
||||
Err(error) => return Err(error),
|
||||
}
|
||||
let pool_candidate_result = self
|
||||
.try_register_pool_candidate(endpoint_name.clone(), notification)
|
||||
.await;
|
||||
let pool_candidate_result =
|
||||
self.try_register_pool_candidate(endpoint_name.clone(), notification).await;
|
||||
match pool_candidate_result {
|
||||
Ok(Some(result)) => {
|
||||
return Ok(crate::KbSolanaWsDetectionOutcome::PoolCandidateRegistered { result });
|
||||
}
|
||||
Ok(None) => {}
|
||||
},
|
||||
Ok(None) => {},
|
||||
Err(error) => return Err(error),
|
||||
}
|
||||
let payload = build_notification_payload(notification);
|
||||
@@ -93,10 +91,7 @@ impl KbSolanaWsDetectionService {
|
||||
slot,
|
||||
payload.clone(),
|
||||
);
|
||||
let observation_id_result = self
|
||||
.persistence
|
||||
.record_observation(&observation_input)
|
||||
.await;
|
||||
let observation_id_result = self.persistence.record_observation(&observation_input).await;
|
||||
let observation_id = match observation_id_result {
|
||||
Ok(observation_id) => observation_id,
|
||||
Err(error) => return Err(error),
|
||||
@@ -127,7 +122,7 @@ impl KbSolanaWsDetectionService {
|
||||
return Err(error);
|
||||
}
|
||||
}
|
||||
Ok(crate::KbSolanaWsDetectionOutcome::ObservationRecorded { observation_id })
|
||||
return Ok(crate::KbSolanaWsDetectionOutcome::ObservationRecorded { observation_id });
|
||||
}
|
||||
|
||||
/// Tries to register a token candidate from one notification.
|
||||
@@ -210,8 +205,8 @@ impl KbSolanaWsDetectionService {
|
||||
);
|
||||
let result = self.persistence.register_token_candidate(&input).await;
|
||||
match result {
|
||||
Ok(result) => Ok(Some(result)),
|
||||
Err(error) => Err(error),
|
||||
Ok(result) => return Ok(Some(result)),
|
||||
Err(error) => return Err(error),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -288,8 +283,8 @@ impl KbSolanaWsDetectionService {
|
||||
);
|
||||
let result = self.persistence.register_pool_candidate(&input).await;
|
||||
match result {
|
||||
Ok(result) => Ok(Some(result)),
|
||||
Err(error) => Err(error),
|
||||
Ok(result) => return Ok(Some(result)),
|
||||
Err(error) => return Err(error),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -299,27 +294,27 @@ fn map_notification_method_to_observation_kind(
|
||||
method: &str,
|
||||
) -> std::option::Option<std::string::String> {
|
||||
match method {
|
||||
"accountNotification" => Some("ws.account_notification".to_string()),
|
||||
"blockNotification" => Some("ws.block_notification".to_string()),
|
||||
"logsNotification" => Some("ws.logs_notification".to_string()),
|
||||
"programNotification" => Some("ws.program_notification".to_string()),
|
||||
"rootNotification" => Some("ws.root_notification".to_string()),
|
||||
"signatureNotification" => Some("ws.signature_notification".to_string()),
|
||||
"slotNotification" => Some("ws.slot_notification".to_string()),
|
||||
"slotsUpdatesNotification" => Some("ws.slots_updates_notification".to_string()),
|
||||
"voteNotification" => Some("ws.vote_notification".to_string()),
|
||||
_ => None,
|
||||
"accountNotification" => return Some("ws.account_notification".to_string()),
|
||||
"blockNotification" => return Some("ws.block_notification".to_string()),
|
||||
"logsNotification" => return Some("ws.logs_notification".to_string()),
|
||||
"programNotification" => return Some("ws.program_notification".to_string()),
|
||||
"rootNotification" => return Some("ws.root_notification".to_string()),
|
||||
"signatureNotification" => return Some("ws.signature_notification".to_string()),
|
||||
"slotNotification" => return Some("ws.slot_notification".to_string()),
|
||||
"slotsUpdatesNotification" => return Some("ws.slots_updates_notification".to_string()),
|
||||
"voteNotification" => return Some("ws.vote_notification".to_string()),
|
||||
_ => return None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Wraps one raw notification into a normalized JSON payload.
|
||||
fn build_notification_payload(notification: &crate::KbJsonRpcWsNotification) -> serde_json::Value {
|
||||
serde_json::json!({
|
||||
return serde_json::json!({
|
||||
"jsonrpc": notification.jsonrpc,
|
||||
"method": notification.method,
|
||||
"subscription": notification.params.subscription,
|
||||
"result": notification.params.result,
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
/// Builds one logical object key from the notification result.
|
||||
@@ -340,8 +335,7 @@ fn build_object_key(
|
||||
if let Some(slot) = slot_option {
|
||||
return format!("slot:{slot}");
|
||||
}
|
||||
|
||||
format!("subscription:{subscription}")
|
||||
return format!("subscription:{subscription}");
|
||||
}
|
||||
|
||||
/// Extracts a slot number from one notification result.
|
||||
@@ -364,7 +358,7 @@ fn extract_slot_from_result(method: &str, result: &serde_json::Value) -> std::op
|
||||
return Some(slot);
|
||||
}
|
||||
}
|
||||
None
|
||||
return None;
|
||||
}
|
||||
|
||||
/// Extracts a pubkey from one notification result.
|
||||
@@ -379,7 +373,7 @@ fn extract_pubkey_from_result(
|
||||
return Some(pubkey.to_string());
|
||||
}
|
||||
}
|
||||
None
|
||||
return None;
|
||||
}
|
||||
|
||||
/// Extracts a signature from one notification result.
|
||||
@@ -394,13 +388,13 @@ fn extract_signature_from_result(
|
||||
return Some(signature.to_string());
|
||||
}
|
||||
}
|
||||
None
|
||||
return None;
|
||||
}
|
||||
|
||||
/// Extracts one account-like JSON object from one notification result.
|
||||
fn extract_account_value_from_result<'a>(
|
||||
result: &'a serde_json::Value,
|
||||
) -> std::option::Option<&'a serde_json::Value> {
|
||||
fn extract_account_value_from_result(
|
||||
result: &serde_json::Value,
|
||||
) -> std::option::Option<&serde_json::Value> {
|
||||
if let Some(account) = result.get("account") {
|
||||
return Some(account);
|
||||
}
|
||||
@@ -412,7 +406,7 @@ fn extract_account_value_from_result<'a>(
|
||||
return Some(value);
|
||||
}
|
||||
}
|
||||
None
|
||||
return None;
|
||||
}
|
||||
|
||||
/// Extracts the parsed account type from one account-like JSON object.
|
||||
@@ -431,8 +425,8 @@ fn extract_parsed_account_type(
|
||||
};
|
||||
let type_option = parsed.get("type").and_then(serde_json::Value::as_str);
|
||||
match type_option {
|
||||
Some(parsed_type) => Some(parsed_type.to_string()),
|
||||
None => None,
|
||||
Some(parsed_type) => return Some(parsed_type.to_string()),
|
||||
None => return None,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -440,12 +434,10 @@ fn extract_parsed_account_type(
|
||||
fn extract_account_owner(
|
||||
account_value: &serde_json::Value,
|
||||
) -> std::option::Option<std::string::String> {
|
||||
let owner_option = account_value
|
||||
.get("owner")
|
||||
.and_then(serde_json::Value::as_str);
|
||||
let owner_option = account_value.get("owner").and_then(serde_json::Value::as_str);
|
||||
match owner_option {
|
||||
Some(owner) => Some(owner.to_string()),
|
||||
None => None,
|
||||
Some(owner) => return Some(owner.to_string()),
|
||||
None => return None,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -475,8 +467,8 @@ fn extract_decimals_from_account_value(
|
||||
};
|
||||
let converted = u8::try_from(decimals);
|
||||
match converted {
|
||||
Ok(decimals) => Some(decimals),
|
||||
Err(_) => None,
|
||||
Ok(decimals) => return Some(decimals),
|
||||
Err(_) => return None,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -489,7 +481,7 @@ fn extract_program_notification_owner(
|
||||
Some(account_value) => account_value,
|
||||
None => return None,
|
||||
};
|
||||
extract_account_owner(account_value)
|
||||
return extract_account_owner(account_value);
|
||||
}
|
||||
|
||||
/// Extracts the parsed token amount decimals from one parsed token account notification.
|
||||
@@ -516,17 +508,15 @@ fn extract_token_account_decimals_from_account_value(
|
||||
Some(token_amount) => token_amount,
|
||||
None => return None,
|
||||
};
|
||||
let decimals_option = token_amount
|
||||
.get("decimals")
|
||||
.and_then(serde_json::Value::as_u64);
|
||||
let decimals_option = token_amount.get("decimals").and_then(serde_json::Value::as_u64);
|
||||
let decimals = match decimals_option {
|
||||
Some(decimals) => decimals,
|
||||
None => return None,
|
||||
};
|
||||
let convert_result = u8::try_from(decimals);
|
||||
match convert_result {
|
||||
Ok(decimals) => Some(decimals),
|
||||
Err(_) => None,
|
||||
Ok(decimals) => return Some(decimals),
|
||||
Err(_) => return None,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -551,8 +541,8 @@ fn extract_parsed_account_mint(
|
||||
};
|
||||
let mint_option = info.get("mint").and_then(serde_json::Value::as_str);
|
||||
match mint_option {
|
||||
Some(mint) => Some(mint.to_string()),
|
||||
None => None,
|
||||
Some(mint) => return Some(mint.to_string()),
|
||||
None => return None,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -580,7 +570,7 @@ fn extract_logs_lines(result: &serde_json::Value) -> std::vec::Vec<std::string::
|
||||
lines.push(line.to_string());
|
||||
}
|
||||
}
|
||||
lines
|
||||
return lines;
|
||||
}
|
||||
|
||||
/// Extracts the error field from a signature notification result.
|
||||
@@ -593,8 +583,8 @@ fn extract_signature_notification_err(
|
||||
None => return None,
|
||||
};
|
||||
match value.get("err") {
|
||||
Some(err) => Some(err.clone()),
|
||||
None => None,
|
||||
Some(err) => return Some(err.clone()),
|
||||
None => return None,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -621,8 +611,8 @@ fn build_signal_kind_for_notification(
|
||||
}
|
||||
}
|
||||
}
|
||||
"signal.account_notification.generic".to_string()
|
||||
}
|
||||
return "signal.account_notification.generic".to_string();
|
||||
},
|
||||
"logsNotification" => {
|
||||
let lines = extract_logs_lines(result);
|
||||
for line in &lines {
|
||||
@@ -639,21 +629,21 @@ fn build_signal_kind_for_notification(
|
||||
return "signal.logs_notification.initialize_account".to_string();
|
||||
}
|
||||
}
|
||||
"signal.logs_notification.generic".to_string()
|
||||
}
|
||||
return "signal.logs_notification.generic".to_string();
|
||||
},
|
||||
"signatureNotification" => {
|
||||
let err_option = extract_signature_notification_err(result);
|
||||
match err_option {
|
||||
Some(err) => {
|
||||
if err.is_null() {
|
||||
"signal.signature_notification.confirmed".to_string()
|
||||
return "signal.signature_notification.confirmed".to_string();
|
||||
} else {
|
||||
"signal.signature_notification.failed".to_string()
|
||||
return "signal.signature_notification.failed".to_string();
|
||||
}
|
||||
}
|
||||
None => "signal.signature_notification.generic".to_string(),
|
||||
},
|
||||
None => return "signal.signature_notification.generic".to_string(),
|
||||
}
|
||||
}
|
||||
},
|
||||
"programNotification" => {
|
||||
let owner_option = extract_program_notification_owner(result);
|
||||
let owner = match owner_option {
|
||||
@@ -666,12 +656,9 @@ fn build_signal_kind_for_notification(
|
||||
if owner == crate::SPL_TOKEN_2022_PROGRAM_ID.to_string() {
|
||||
return "signal.program_notification.spl_token_2022".to_string();
|
||||
}
|
||||
"signal.program_notification.generic".to_string()
|
||||
}
|
||||
_ => format!(
|
||||
"signal.{}",
|
||||
method.replace("Notification", "").to_lowercase()
|
||||
),
|
||||
return "signal.program_notification.generic".to_string();
|
||||
},
|
||||
_ => return format!("signal.{}", method.replace("Notification", "").to_lowercase()),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -681,8 +668,8 @@ fn build_signal_severity_for_notification(
|
||||
result: &serde_json::Value,
|
||||
) -> crate::KbAnalysisSignalSeverity {
|
||||
match method {
|
||||
"programNotification" => crate::KbAnalysisSignalSeverity::Medium,
|
||||
"accountNotification" => crate::KbAnalysisSignalSeverity::Low,
|
||||
"programNotification" => return crate::KbAnalysisSignalSeverity::Medium,
|
||||
"accountNotification" => return crate::KbAnalysisSignalSeverity::Low,
|
||||
"logsNotification" => {
|
||||
let lines = extract_logs_lines(result);
|
||||
for line in &lines {
|
||||
@@ -690,22 +677,22 @@ fn build_signal_severity_for_notification(
|
||||
return crate::KbAnalysisSignalSeverity::Medium;
|
||||
}
|
||||
}
|
||||
crate::KbAnalysisSignalSeverity::Low
|
||||
}
|
||||
return crate::KbAnalysisSignalSeverity::Low;
|
||||
},
|
||||
"signatureNotification" => {
|
||||
let err_option = extract_signature_notification_err(result);
|
||||
match err_option {
|
||||
Some(err) => {
|
||||
if err.is_null() {
|
||||
crate::KbAnalysisSignalSeverity::Low
|
||||
return crate::KbAnalysisSignalSeverity::Low;
|
||||
} else {
|
||||
crate::KbAnalysisSignalSeverity::Medium
|
||||
return crate::KbAnalysisSignalSeverity::Medium;
|
||||
}
|
||||
}
|
||||
None => crate::KbAnalysisSignalSeverity::Low,
|
||||
},
|
||||
None => return crate::KbAnalysisSignalSeverity::Low,
|
||||
}
|
||||
}
|
||||
_ => crate::KbAnalysisSignalSeverity::Low,
|
||||
},
|
||||
_ => return crate::KbAnalysisSignalSeverity::Low,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -726,13 +713,13 @@ mod tests {
|
||||
use_wal: true,
|
||||
},
|
||||
};
|
||||
crate::KbDatabase::connect_and_initialize(&config)
|
||||
return crate::KbDatabase::connect_and_initialize(&config)
|
||||
.await
|
||||
.expect("database init must succeed")
|
||||
.expect("database init must succeed");
|
||||
}
|
||||
|
||||
fn build_slot_notification() -> crate::KbJsonRpcWsNotification {
|
||||
crate::KbJsonRpcWsNotification {
|
||||
return crate::KbJsonRpcWsNotification {
|
||||
jsonrpc: "2.0".to_string(),
|
||||
method: "slotNotification".to_string(),
|
||||
params: crate::KbJsonRpcWsNotificationParams {
|
||||
@@ -743,11 +730,11 @@ mod tests {
|
||||
}),
|
||||
subscription: 1008_u64,
|
||||
},
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
fn build_program_mint_notification() -> crate::KbJsonRpcWsNotification {
|
||||
crate::KbJsonRpcWsNotification {
|
||||
return crate::KbJsonRpcWsNotification {
|
||||
jsonrpc: "2.0".to_string(),
|
||||
method: "programNotification".to_string(),
|
||||
params: crate::KbJsonRpcWsNotificationParams {
|
||||
@@ -773,11 +760,11 @@ mod tests {
|
||||
}),
|
||||
subscription: 2048_u64,
|
||||
},
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
fn build_program_pool_candidate_notification() -> crate::KbJsonRpcWsNotification {
|
||||
crate::KbJsonRpcWsNotification {
|
||||
return crate::KbJsonRpcWsNotification {
|
||||
jsonrpc: "2.0".to_string(),
|
||||
method: "programNotification".to_string(),
|
||||
params: crate::KbJsonRpcWsNotificationParams {
|
||||
@@ -798,11 +785,11 @@ mod tests {
|
||||
}),
|
||||
subscription: 5555_u64,
|
||||
},
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
fn build_logs_notification() -> crate::KbJsonRpcWsNotification {
|
||||
crate::KbJsonRpcWsNotification {
|
||||
return crate::KbJsonRpcWsNotification {
|
||||
jsonrpc: "2.0".to_string(),
|
||||
method: "logsNotification".to_string(),
|
||||
params: crate::KbJsonRpcWsNotificationParams {
|
||||
@@ -820,11 +807,11 @@ mod tests {
|
||||
}),
|
||||
subscription: 3001_u64,
|
||||
},
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
fn build_signature_notification() -> crate::KbJsonRpcWsNotification {
|
||||
crate::KbJsonRpcWsNotification {
|
||||
return crate::KbJsonRpcWsNotification {
|
||||
jsonrpc: "2.0".to_string(),
|
||||
method: "signatureNotification".to_string(),
|
||||
params: crate::KbJsonRpcWsNotificationParams {
|
||||
@@ -838,7 +825,7 @@ mod tests {
|
||||
}),
|
||||
subscription: 4001_u64,
|
||||
},
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
@@ -859,7 +846,7 @@ mod tests {
|
||||
match outcome {
|
||||
crate::KbSolanaWsDetectionOutcome::ObservationRecorded { observation_id } => {
|
||||
assert!(observation_id > 0);
|
||||
}
|
||||
},
|
||||
_ => panic!("unexpected detection outcome"),
|
||||
}
|
||||
let observations_result =
|
||||
@@ -894,7 +881,7 @@ mod tests {
|
||||
assert!(result.token_id > 0);
|
||||
assert!(result.observation_id > 0);
|
||||
assert!(result.signal_id > 0);
|
||||
}
|
||||
},
|
||||
_ => panic!("unexpected detection outcome"),
|
||||
}
|
||||
let token_result = crate::get_token_by_mint(
|
||||
@@ -923,14 +910,8 @@ mod tests {
|
||||
};
|
||||
assert_eq!(observations.len(), 1);
|
||||
assert_eq!(signals.len(), 1);
|
||||
assert_eq!(
|
||||
observations[0].object_key,
|
||||
"Mint111111111111111111111111111111111111111"
|
||||
);
|
||||
assert_eq!(
|
||||
signals[0].object_key,
|
||||
"Mint111111111111111111111111111111111111111"
|
||||
);
|
||||
assert_eq!(observations[0].object_key, "Mint111111111111111111111111111111111111111");
|
||||
assert_eq!(signals[0].object_key, "Mint111111111111111111111111111111111111111");
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
@@ -951,7 +932,7 @@ mod tests {
|
||||
match outcome {
|
||||
crate::KbSolanaWsDetectionOutcome::ObservationRecorded { observation_id } => {
|
||||
assert!(observation_id > 0);
|
||||
}
|
||||
},
|
||||
_ => panic!("unexpected detection outcome"),
|
||||
}
|
||||
let observations_result =
|
||||
@@ -970,10 +951,7 @@ mod tests {
|
||||
};
|
||||
assert_eq!(observations.len(), 1);
|
||||
assert_eq!(signals.len(), 1);
|
||||
assert_eq!(
|
||||
signals[0].signal_kind,
|
||||
"signal.logs_notification.initialize_mint"
|
||||
);
|
||||
assert_eq!(signals[0].signal_kind, "signal.logs_notification.initialize_mint");
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
@@ -994,7 +972,7 @@ mod tests {
|
||||
match outcome {
|
||||
crate::KbSolanaWsDetectionOutcome::ObservationRecorded { observation_id } => {
|
||||
assert!(observation_id > 0);
|
||||
}
|
||||
},
|
||||
_ => panic!("unexpected detection outcome"),
|
||||
}
|
||||
let observations_result =
|
||||
@@ -1013,10 +991,7 @@ mod tests {
|
||||
};
|
||||
assert_eq!(observations.len(), 1);
|
||||
assert_eq!(signals.len(), 1);
|
||||
assert_eq!(
|
||||
signals[0].signal_kind,
|
||||
"signal.signature_notification.confirmed"
|
||||
);
|
||||
assert_eq!(signals[0].signal_kind, "signal.signature_notification.confirmed");
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
@@ -1053,7 +1028,7 @@ mod tests {
|
||||
assert!(result.pool_id > 0);
|
||||
assert!(result.pool_listing_id > 0);
|
||||
result.pool_id
|
||||
}
|
||||
},
|
||||
_ => panic!("unexpected detection outcome"),
|
||||
};
|
||||
let pool_result = crate::get_pool_by_address(
|
||||
|
||||
@@ -29,14 +29,14 @@ impl KbDetectionObservationInput {
|
||||
slot: std::option::Option<u64>,
|
||||
payload: serde_json::Value,
|
||||
) -> Self {
|
||||
Self {
|
||||
return Self {
|
||||
observation_kind,
|
||||
source_kind,
|
||||
endpoint_name,
|
||||
object_key,
|
||||
slot,
|
||||
payload,
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -67,14 +67,14 @@ impl KbDetectionSignalInput {
|
||||
score: std::option::Option<f64>,
|
||||
payload: serde_json::Value,
|
||||
) -> Self {
|
||||
Self {
|
||||
return Self {
|
||||
signal_kind,
|
||||
severity,
|
||||
object_key,
|
||||
related_observation_id,
|
||||
score,
|
||||
payload,
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -133,7 +133,7 @@ impl KbDetectionTokenCandidateInput {
|
||||
signal_score: std::option::Option<f64>,
|
||||
signal_payload: std::option::Option<serde_json::Value>,
|
||||
) -> Self {
|
||||
Self {
|
||||
return Self {
|
||||
mint,
|
||||
symbol,
|
||||
name,
|
||||
@@ -149,7 +149,7 @@ impl KbDetectionTokenCandidateInput {
|
||||
signal_severity,
|
||||
signal_score,
|
||||
signal_payload,
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -207,7 +207,7 @@ impl KbDetectionPoolCandidateInput {
|
||||
signal_score: std::option::Option<f64>,
|
||||
signal_payload: std::option::Option<serde_json::Value>,
|
||||
) -> Self {
|
||||
Self {
|
||||
return Self {
|
||||
pool_address,
|
||||
dex_program_id,
|
||||
source_kind,
|
||||
@@ -219,7 +219,7 @@ impl KbDetectionPoolCandidateInput {
|
||||
signal_severity,
|
||||
signal_score,
|
||||
signal_payload,
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -20,10 +20,7 @@ impl KbWsDetectionNotificationEnvelope {
|
||||
endpoint_name: std::option::Option<std::string::String>,
|
||||
notification: crate::KbJsonRpcWsNotification,
|
||||
) -> Self {
|
||||
Self {
|
||||
endpoint_name,
|
||||
notification,
|
||||
}
|
||||
return Self { endpoint_name, notification };
|
||||
}
|
||||
}
|
||||
|
||||
@@ -54,7 +51,7 @@ pub struct KbWsDetectionRelay {
|
||||
impl KbWsDetectionRelay {
|
||||
/// Creates a new relay.
|
||||
pub fn new(detector: crate::KbSolanaWsDetectionService) -> Self {
|
||||
Self { detector }
|
||||
return Self { detector };
|
||||
}
|
||||
|
||||
/// Creates a bounded relay channel.
|
||||
@@ -64,7 +61,7 @@ impl KbWsDetectionRelay {
|
||||
tokio::sync::mpsc::Sender<crate::KbWsDetectionNotificationEnvelope>,
|
||||
tokio::sync::mpsc::Receiver<crate::KbWsDetectionNotificationEnvelope>,
|
||||
) {
|
||||
tokio::sync::mpsc::channel(capacity)
|
||||
return tokio::sync::mpsc::channel(capacity);
|
||||
}
|
||||
|
||||
/// Processes one forwarded notification.
|
||||
@@ -72,9 +69,10 @@ impl KbWsDetectionRelay {
|
||||
&self,
|
||||
envelope: &crate::KbWsDetectionNotificationEnvelope,
|
||||
) -> Result<crate::KbSolanaWsDetectionOutcome, crate::KbError> {
|
||||
self.detector
|
||||
return self
|
||||
.detector
|
||||
.process_notification(envelope.endpoint_name.clone(), &envelope.notification)
|
||||
.await
|
||||
.await;
|
||||
}
|
||||
|
||||
/// Spawns one background relay worker.
|
||||
@@ -82,7 +80,7 @@ impl KbWsDetectionRelay {
|
||||
self,
|
||||
mut receiver: tokio::sync::mpsc::Receiver<crate::KbWsDetectionNotificationEnvelope>,
|
||||
) -> tokio::task::JoinHandle<crate::KbWsDetectionRelayStats> {
|
||||
tokio::spawn(async move {
|
||||
return tokio::spawn(async move {
|
||||
let mut stats = crate::KbWsDetectionRelayStats::default();
|
||||
loop {
|
||||
let recv_result = receiver.recv().await;
|
||||
@@ -103,25 +101,25 @@ impl KbWsDetectionRelay {
|
||||
error
|
||||
);
|
||||
continue;
|
||||
}
|
||||
},
|
||||
};
|
||||
match outcome {
|
||||
crate::KbSolanaWsDetectionOutcome::Ignored => {
|
||||
stats.ignored_count += 1;
|
||||
}
|
||||
},
|
||||
crate::KbSolanaWsDetectionOutcome::ObservationRecorded { .. } => {
|
||||
stats.observation_count += 1;
|
||||
}
|
||||
},
|
||||
crate::KbSolanaWsDetectionOutcome::TokenCandidateRegistered { .. } => {
|
||||
stats.token_candidate_count += 1;
|
||||
}
|
||||
},
|
||||
crate::KbSolanaWsDetectionOutcome::PoolCandidateRegistered { .. } => {
|
||||
stats.pool_candidate_count += 1;
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
stats
|
||||
})
|
||||
return stats;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -142,13 +140,13 @@ mod tests {
|
||||
use_wal: true,
|
||||
},
|
||||
};
|
||||
crate::KbDatabase::connect_and_initialize(&config)
|
||||
return crate::KbDatabase::connect_and_initialize(&config)
|
||||
.await
|
||||
.expect("database init must succeed")
|
||||
.expect("database init must succeed");
|
||||
}
|
||||
|
||||
fn build_slot_notification() -> crate::KbJsonRpcWsNotification {
|
||||
crate::KbJsonRpcWsNotification {
|
||||
return crate::KbJsonRpcWsNotification {
|
||||
jsonrpc: "2.0".to_string(),
|
||||
method: "slotNotification".to_string(),
|
||||
params: crate::KbJsonRpcWsNotificationParams {
|
||||
@@ -159,7 +157,7 @@ mod tests {
|
||||
}),
|
||||
subscription: 1008_u64,
|
||||
},
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
@@ -180,7 +178,7 @@ mod tests {
|
||||
match outcome {
|
||||
crate::KbSolanaWsDetectionOutcome::ObservationRecorded { observation_id } => {
|
||||
assert!(observation_id > 0);
|
||||
}
|
||||
},
|
||||
_ => panic!("unexpected relay outcome"),
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user