Files
khadhroony-bobobot/kb_app/src/demo_pipeline.rs
2026-05-01 12:01:13 +02:00

1603 lines
63 KiB
Rust

// file: kb_app/src/demo_pipeline.rs
//! Tauri commands for the pipeline inspection demo window.
use tauri::Manager;
/// Request payload for one pipeline inspection by signature.
#[derive(Clone, Debug, serde::Deserialize, ts_rs::TS)]
#[ts(export, export_to = "../frontend/ts/bindings/KbDemoPipelineInspectRequest.ts")]
#[serde(rename_all = "camelCase")]
pub(crate) struct KbDemoPipelineInspectRequest {
/// Transaction signature to inspect.
pub signature: std::string::String,
/// Optional custom timeframe in seconds for on-demand candle rebuild.
pub custom_timeframe_seconds: std::option::Option<i64>,
}
/// Response payload for one pipeline inspection.
#[derive(Clone, Debug, serde::Serialize, ts_rs::TS)]
#[ts(export, export_to = "../frontend/ts/bindings/KbDemoPipelineInspectPayload.ts")]
#[serde(rename_all = "camelCase")]
pub(crate) struct KbDemoPipelineInspectPayload {
/// Inspected signature.
pub signature: std::string::String,
/// Summary JSON block.
pub summary_json: std::string::String,
/// Resolved transaction JSON block.
pub transaction_json: std::string::String,
/// Decoded events JSON block.
pub decoded_events_json: std::string::String,
/// Pools JSON block.
pub pools_json: std::string::String,
/// Pairs JSON block.
pub pairs_json: std::string::String,
/// Launch attributions JSON block.
pub launch_attributions_json: std::string::String,
/// Pool origins JSON block.
pub pool_origins_json: std::string::String,
/// Wallet inspection JSON block.
pub wallets_json: std::string::String,
/// Trade events JSON block.
pub trade_events_json: std::string::String,
/// Pair metrics JSON block.
pub pair_metrics_json: std::string::String,
/// Pair candles JSON block.
pub pair_candles_json: std::string::String,
/// Pair analytic signals JSON block.
pub pair_analytic_signals_json: std::string::String,
}
/// Request payload for one pipeline inspection by token mint.
#[derive(Clone, Debug, serde::Deserialize, ts_rs::TS)]
#[ts(export, export_to = "../frontend/ts/bindings/KbDemoPipelineInspectTokenRequest.ts")]
#[serde(rename_all = "camelCase")]
pub(crate) struct KbDemoPipelineInspectTokenRequest {
/// Token mint to inspect.
pub token_mint: std::string::String,
/// Optional custom timeframe in seconds for on-demand candle rebuild.
pub custom_timeframe_seconds: std::option::Option<i64>,
}
/// Request payload for one pipeline inspection by pair id.
#[derive(Clone, Debug, serde::Deserialize, ts_rs::TS)]
#[ts(export, export_to = "../frontend/ts/bindings/KbDemoPipelineInspectPairRequest.ts")]
#[serde(rename_all = "camelCase")]
pub(crate) struct KbDemoPipelineInspectPairRequest {
/// Pair id to inspect.
pub pair_id: i64,
/// Optional custom timeframe in seconds for on-demand candle rebuild.
pub custom_timeframe_seconds: std::option::Option<i64>,
}
/// Request payload for one pipeline inspection by pool address.
#[derive(Clone, Debug, serde::Deserialize, ts_rs::TS)]
#[ts(export, export_to = "../frontend/ts/bindings/KbDemoPipelineInspectPoolRequest.ts")]
#[serde(rename_all = "camelCase")]
pub(crate) struct KbDemoPipelineInspectPoolRequest {
/// Pool address to inspect.
pub pool_address: std::string::String,
/// Optional custom timeframe in seconds for on-demand candle rebuild.
pub custom_timeframe_seconds: std::option::Option<i64>,
}
/// Request payload for one token backfill launched from `kb_app`.
#[derive(Clone, Debug, serde::Deserialize, ts_rs::TS)]
#[ts(export, export_to = "../frontend/ts/bindings/KbDemoPipelineBackfillTokenRequest.ts")]
#[serde(rename_all = "camelCase")]
pub(crate) struct KbDemoPipelineBackfillTokenRequest {
/// Token mint to backfill.
pub token_mint: std::string::String,
/// HTTP role used to select one endpoint in the pool.
pub http_role: std::option::Option<std::string::String>,
/// Maximum number of signatures fetched directly from the mint.
pub mint_signature_limit: usize,
/// Maximum number of signatures fetched from each discovered pool.
pub pool_signature_limit: usize,
}
/// Response payload for one token backfill launched from `kb_app`.
#[derive(Clone, Debug, serde::Serialize, ts_rs::TS)]
#[ts(export, export_to = "../frontend/ts/bindings/KbDemoPipelineBackfillTokenPayload.ts")]
#[serde(rename_all = "camelCase")]
pub(crate) struct KbDemoPipelineBackfillTokenPayload {
/// Backfilled token mint.
pub token_mint: std::string::String,
/// HTTP role used during backfill.
pub http_role: std::string::String,
/// Pretty JSON summary returned by `KbTokenBackfillService`.
pub backfill_json: std::string::String,
/// Whether the token exists in persisted token objects after backfill.
pub token_persisted_after_backfill: bool,
}
/// Launches one token backfill through the persisted `kb_lib` services.
#[tauri::command]
pub(crate) async fn demo_pipeline_backfill_token_mint(
state: tauri::State<'_, crate::KbAppState>,
request: KbDemoPipelineBackfillTokenRequest,
) -> Result<KbDemoPipelineBackfillTokenPayload, std::string::String> {
let token_mint = request.token_mint.trim().to_string();
if token_mint.is_empty() {
return Err("demo pipeline backfill token mint must not be empty".to_string());
}
let http_role = match request.http_role.clone() {
Some(http_role) => {
let trimmed = http_role.trim().to_string();
if trimmed.is_empty() {
"history_backfill".to_string()
} else {
trimmed
}
}
None => "history_backfill".to_string(),
};
if request.mint_signature_limit == 0 {
return Err("demo pipeline mintSignatureLimit must be > 0".to_string());
}
if request.pool_signature_limit == 0 {
return Err("demo pipeline poolSignatureLimit must be > 0".to_string());
}
let database = state.database.clone();
let http_pool = std::sync::Arc::new(state.http_pool.clone());
let service =
kb_lib::KbTokenBackfillService::new(http_pool, database.clone(), http_role.clone());
let backfill_result = service
.backfill_token_by_mint(
token_mint.as_str(),
request.mint_signature_limit,
request.pool_signature_limit,
)
.await;
let backfill = match backfill_result {
Ok(backfill) => backfill,
Err(error) => {
return Err(format!(
"cannot backfill token mint '{}' with role '{}': {}",
token_mint, http_role, error
));
}
};
let backfill_json_result = serde_json::to_string_pretty(&backfill);
let backfill_json = match backfill_json_result {
Ok(backfill_json) => backfill_json,
Err(error) => {
return Err(format!(
"cannot serialize token backfill result for '{}': {}",
token_mint, error
));
}
};
let token_result = kb_lib::get_token_by_mint(database.as_ref(), token_mint.as_str()).await;
let token_option = match token_result {
Ok(token_option) => token_option,
Err(error) => {
return Err(format!(
"cannot verify persisted token mint '{}' after backfill with role '{}': {}",
token_mint, http_role, error
));
}
};
let token_persisted_after_backfill = token_option.is_some();
Ok(KbDemoPipelineBackfillTokenPayload {
token_mint,
http_role,
backfill_json,
token_persisted_after_backfill,
})
}
/// Inspects one pair id through the persisted `kb_lib` pipeline state.
#[tauri::command]
pub(crate) async fn demo_pipeline_inspect_pair_id(
state: tauri::State<'_, crate::KbAppState>,
request: KbDemoPipelineInspectPairRequest,
) -> Result<KbDemoPipelineInspectPayload, std::string::String> {
if request.pair_id <= 0 {
return Err("demo pipeline pair id must be > 0".to_string());
}
let database = state.database.clone();
let pairs_result = kb_lib::list_pairs(database.as_ref()).await;
let all_pairs = match pairs_result {
Ok(all_pairs) => all_pairs,
Err(error) => {
return Err(format!("cannot list pairs from database: {}", error));
}
};
let mut inspected_pair_option = std::option::Option::<kb_lib::KbPairDto>::None;
for pair in all_pairs {
let pair_id_option = pair.id;
let pair_id = match pair_id_option {
Some(pair_id) => pair_id,
None => continue,
};
if pair_id == request.pair_id {
inspected_pair_option = Some(pair);
break;
}
}
let inspected_pair = match inspected_pair_option {
Some(inspected_pair) => inspected_pair,
None => {
return Err(format!(
"unknown pair id '{}' in local pipeline database '{}'",
request.pair_id,
state.database.database_url()
));
}
};
kb_demo_pipeline_build_pair_payload(
state.database.clone(),
state.database.database_url().to_string(),
inspected_pair,
request.custom_timeframe_seconds,
format!("pair:{}", request.pair_id),
)
.await
}
/// Inspects one pool address through the persisted `kb_lib` pipeline state.
#[tauri::command]
pub(crate) async fn demo_pipeline_inspect_pool_address(
state: tauri::State<'_, crate::KbAppState>,
request: KbDemoPipelineInspectPoolRequest,
) -> Result<KbDemoPipelineInspectPayload, std::string::String> {
let pool_address = request.pool_address.trim().to_string();
if pool_address.is_empty() {
return Err("demo pipeline pool address must not be empty".to_string());
}
let database = state.database.clone();
let pool_result = kb_lib::get_pool_by_address(database.as_ref(), pool_address.as_str()).await;
let pool_option = match pool_result {
Ok(pool_option) => pool_option,
Err(error) => {
return Err(format!(
"cannot read pool address '{}' from database: {}",
pool_address, error
));
}
};
let pool = match pool_option {
Some(pool) => pool,
None => {
return Err(format!(
"unknown pool address '{}' in local pipeline database '{}'",
pool_address,
state.database.database_url()
));
}
};
let pool_id = match pool.id {
Some(pool_id) => pool_id,
None => {
return Err(format!("pool '{}' has no internal id", pool.address));
}
};
let pair_result = kb_lib::get_pair_by_pool_id(database.as_ref(), pool_id).await;
let pair_option = match pair_result {
Ok(pair_option) => pair_option,
Err(error) => {
return Err(format!(
"cannot fetch pair by pool_id '{}' for pool '{}': {}",
pool_id, pool.address, error
));
}
};
let pair = match pair_option {
Some(pair) => pair,
None => {
return Err(format!(
"pool '{}' has no associated pair in local pipeline database",
pool.address
));
}
};
kb_demo_pipeline_build_pair_payload(
state.database.clone(),
state.database.database_url().to_string(),
pair,
request.custom_timeframe_seconds,
format!("pool:{}", pool_address),
)
.await
}
/// Inspects one token mint through the persisted `kb_lib` pipeline state.
#[tauri::command]
pub(crate) async fn demo_pipeline_inspect_token_mint(
state: tauri::State<'_, crate::KbAppState>,
request: KbDemoPipelineInspectTokenRequest,
) -> Result<KbDemoPipelineInspectPayload, std::string::String> {
let token_mint = request.token_mint.trim().to_string();
if token_mint.is_empty() {
return Err("demo pipeline token mint must not be empty".to_string());
}
let database = state.database.clone();
let token_result = kb_lib::get_token_by_mint(database.as_ref(), token_mint.as_str()).await;
let token_option = match token_result {
Ok(token_option) => token_option,
Err(error) => {
return Err(format!(
"cannot read token mint '{}' from database: {}",
token_mint, error
));
}
};
let token = match token_option {
Some(token) => token,
None => {
return Err(format!(
"unknown token mint '{}' in local pipeline database '{}'",
token_mint,
state.database.database_url()
));
}
};
let token_id = match token.id {
Some(token_id) => token_id,
None => {
return Err(format!("token mint '{}' has no internal id", token.mint));
}
};
let pools_result = kb_lib::list_pools(database.as_ref()).await;
let all_pools = match pools_result {
Ok(all_pools) => all_pools,
Err(error) => {
return Err(format!("cannot list pools from database: {}", error));
}
};
let pairs_result = kb_lib::list_pairs(database.as_ref()).await;
let all_pairs = match pairs_result {
Ok(all_pairs) => all_pairs,
Err(error) => {
return Err(format!("cannot list pairs from database: {}", error));
}
};
let pool_listings_result = kb_lib::list_pool_listings(database.as_ref()).await;
let all_pool_listings = match pool_listings_result {
Ok(all_pool_listings) => all_pool_listings,
Err(error) => {
return Err(format!(
"cannot list pool listings from database: {}",
error
));
}
};
let mut pools = std::vec::Vec::<kb_lib::KbPoolDto>::new();
let mut pool_ids = std::collections::BTreeSet::<i64>::new();
for pool in all_pools {
let pool_id = match pool.id {
Some(pool_id) => pool_id,
None => continue,
};
let pool_tokens_result =
kb_lib::list_pool_tokens_by_pool_id(database.as_ref(), pool_id).await;
let pool_tokens = match pool_tokens_result {
Ok(pool_tokens) => pool_tokens,
Err(error) => {
return Err(format!(
"cannot list pool tokens for pool_id '{}': {}",
pool_id, error
));
}
};
let mut contains_token = false;
for pool_token in pool_tokens {
if pool_token.token_id == token_id {
contains_token = true;
break;
}
}
if contains_token {
pool_ids.insert(pool_id);
pools.push(pool);
}
}
let mut pairs = std::vec::Vec::<kb_lib::KbPairDto>::new();
let mut pair_ids = std::collections::BTreeSet::<i64>::new();
for pair in all_pairs {
let pair_id = match pair.id {
Some(pair_id) => pair_id,
None => continue,
};
if pair.base_token_id == token_id || pair.quote_token_id == token_id {
pair_ids.insert(pair_id);
pairs.push(pair);
}
}
let mut pool_listings = std::vec::Vec::<kb_lib::KbPoolListingDto>::new();
for listing in all_pool_listings {
if pool_ids.contains(&listing.pool_id) {
pool_listings.push(listing);
}
}
let mut launch_attributions = std::vec::Vec::<kb_lib::KbLaunchAttributionDto>::new();
for pool_id in &pool_ids {
let attributions_result =
kb_lib::list_launch_attributions_by_pool_id(database.as_ref(), *pool_id).await;
let attributions = match attributions_result {
Ok(attributions) => attributions,
Err(error) => {
return Err(format!(
"cannot list launch attributions for pool_id '{}': {}",
pool_id, error
));
}
};
for attribution in attributions {
launch_attributions.push(attribution);
}
}
let mut pool_origins = std::vec::Vec::<kb_lib::KbPoolOriginDto>::new();
for pool_id in &pool_ids {
let pool_origin_result =
kb_lib::get_pool_origin_by_pool_id(database.as_ref(), *pool_id).await;
let pool_origin_option = match pool_origin_result {
Ok(pool_origin_option) => pool_origin_option,
Err(error) => {
return Err(format!(
"cannot fetch pool origin for pool_id '{}': {}",
pool_id, error
));
}
};
if let Some(pool_origin) = pool_origin_option {
pool_origins.push(pool_origin);
}
}
let mut wallet_holding_groups = std::vec::Vec::<serde_json::Value>::new();
let wallets_result = kb_lib::list_wallets(database.as_ref()).await;
let wallets = match wallets_result {
Ok(wallets) => wallets,
Err(error) => {
return Err(format!("cannot list wallets from database: {}", error));
}
};
for wallet in wallets {
let wallet_id = match wallet.id {
Some(wallet_id) => wallet_id,
None => continue,
};
let holdings_result =
kb_lib::list_wallet_holdings_by_wallet_id(database.as_ref(), wallet_id).await;
let holdings = match holdings_result {
Ok(holdings) => holdings,
Err(error) => {
return Err(format!(
"cannot list wallet holdings for wallet_id '{}': {}",
wallet_id, error
));
}
};
let mut filtered_holdings = std::vec::Vec::new();
for holding in holdings {
if holding.token_id == token_id {
filtered_holdings.push(holding);
}
}
if !filtered_holdings.is_empty() {
let wallet_value_result = serde_json::to_value(&wallet);
let wallet_value = match wallet_value_result {
Ok(wallet_value) => wallet_value,
Err(error) => {
return Err(format!(
"cannot serialize wallet '{}' to JSON value: {}",
wallet.address, error
));
}
};
let holdings_value_result = serde_json::to_value(&filtered_holdings);
let holdings_value = match holdings_value_result {
Ok(holdings_value) => holdings_value,
Err(error) => {
return Err(format!(
"cannot serialize holdings for wallet '{}' to JSON value: {}",
wallet.address, error
));
}
};
wallet_holding_groups.push(serde_json::json!({
"walletAddress": wallet.address,
"wallet": wallet_value,
"holdings": holdings_value
}));
}
}
let mut pair_metrics = std::vec::Vec::<kb_lib::KbPairMetricDto>::new();
let mut pair_candle_groups = std::vec::Vec::<serde_json::Value>::new();
let mut pair_analytic_signal_groups = std::vec::Vec::<serde_json::Value>::new();
let query_service = kb_lib::KbPairCandleQueryService::new(database.clone());
let mut timeframes = vec![60_i64, 300_i64, 900_i64, 3600_i64];
if let Some(custom_timeframe_seconds) = request.custom_timeframe_seconds {
if custom_timeframe_seconds > 0 && !timeframes.contains(&custom_timeframe_seconds) {
timeframes.push(custom_timeframe_seconds);
}
}
for pair_id in &pair_ids {
let pair_metric_result =
kb_lib::get_pair_metric_by_pair_id(database.as_ref(), *pair_id).await;
let pair_metric_option = match pair_metric_result {
Ok(pair_metric_option) => pair_metric_option,
Err(error) => {
return Err(format!(
"cannot fetch pair metric for pair_id '{}': {}",
pair_id, error
));
}
};
if let Some(pair_metric) = pair_metric_option {
pair_metrics.push(pair_metric);
}
let pair_signals_result =
kb_lib::list_pair_analytic_signals_by_pair_id(database.as_ref(), *pair_id).await;
let pair_signals = match pair_signals_result {
Ok(pair_signals) => pair_signals,
Err(error) => {
return Err(format!(
"cannot list pair analytic signals for pair_id '{}': {}",
pair_id, error
));
}
};
let pair_signals_value_result = serde_json::to_value(&pair_signals);
let pair_signals_value = match pair_signals_value_result {
Ok(pair_signals_value) => pair_signals_value,
Err(error) => {
return Err(format!(
"cannot serialize pair analytic signals for pair_id '{}': {}",
pair_id, error
));
}
};
pair_analytic_signal_groups.push(serde_json::json!({
"pairId": pair_id,
"signals": pair_signals_value
}));
for timeframe_seconds in &timeframes {
let candles_result = if *timeframe_seconds == 60
|| *timeframe_seconds == 300
|| *timeframe_seconds == 900
|| *timeframe_seconds == 3600
{
kb_lib::list_pair_candles_by_pair_and_timeframe(
database.as_ref(),
*pair_id,
*timeframe_seconds,
)
.await
} else {
query_service
.list_pair_candles(*pair_id, *timeframe_seconds, None, None, false)
.await
};
let candles = match candles_result {
Ok(candles) => candles,
Err(error) => {
return Err(format!(
"cannot list/rebuild pair candles for pair_id '{}' timeframe '{}': {}",
pair_id, timeframe_seconds, error
));
}
};
let candles_value_result = serde_json::to_value(&candles);
let candles_value = match candles_value_result {
Ok(candles_value) => candles_value,
Err(error) => {
return Err(format!(
"cannot serialize pair candles for pair_id '{}' timeframe '{}': {}",
pair_id, timeframe_seconds, error
));
}
};
pair_candle_groups.push(serde_json::json!({
"pairId": pair_id,
"timeframeSeconds": timeframe_seconds,
"candles": candles_value
}));
}
}
let summary_value = serde_json::json!({
"mode": "tokenMint",
"databaseUrl": state.database.database_url(),
"tokenMint": token.mint,
"tokenId": token_id,
"customTimeframeSeconds": request.custom_timeframe_seconds,
"poolCount": pools.len(),
"pairCount": pairs.len(),
"poolListingCount": pool_listings.len(),
"launchAttributionCount": launch_attributions.len(),
"poolOriginCount": pool_origins.len(),
"walletHoldingGroupCount": wallet_holding_groups.len(),
"pairMetricCount": pair_metrics.len(),
"pairCandleGroupCount": pair_candle_groups.len(),
"pairAnalyticSignalGroupCount": pair_analytic_signal_groups.len()
});
let summary_json = kb_to_pretty_json(&summary_value, "summary")?;
let transaction_json = kb_to_pretty_json(&token, "token")?;
let decoded_events_json = kb_to_pretty_json(&pool_listings, "pool listings")?;
let pools_json = kb_to_pretty_json(&pools, "pools")?;
let pairs_json = kb_to_pretty_json(&pairs, "pairs")?;
let launch_attributions_json = kb_to_pretty_json(&launch_attributions, "launch attributions")?;
let pool_origins_json = kb_to_pretty_json(&pool_origins, "pool origins")?;
let wallets_json = kb_to_pretty_json(&wallet_holding_groups, "wallet holdings")?;
let trade_events_json =
kb_to_pretty_json(&std::vec::Vec::<serde_json::Value>::new(), "trade events")?;
let pair_metrics_json = kb_to_pretty_json(&pair_metrics, "pair metrics")?;
let pair_candles_json = kb_to_pretty_json(&pair_candle_groups, "pair candles")?;
let pair_analytic_signals_json =
kb_to_pretty_json(&pair_analytic_signal_groups, "pair analytic signals")?;
Ok(KbDemoPipelineInspectPayload {
signature: token_mint,
summary_json,
transaction_json,
decoded_events_json,
pools_json,
pairs_json,
launch_attributions_json,
pool_origins_json,
wallets_json,
trade_events_json,
pair_metrics_json,
pair_candles_json,
pair_analytic_signals_json,
})
}
/// Opens the dedicated pipeline inspection window.
#[tauri::command]
pub(crate) fn open_demo_pipeline_window(
app_handle: tauri::AppHandle,
) -> Result<(), std::string::String> {
let existing_window_option = app_handle.get_webview_window("demo_pipeline");
let demo_window = match existing_window_option {
Some(demo_window) => demo_window,
None => {
let builder = tauri::WebviewWindowBuilder::new(
&app_handle,
"demo_pipeline",
tauri::WebviewUrl::App("demo_pipeline.html".into()),
)
.title("Demo Pipeline")
.inner_size(1480.0, 920.0)
.min_inner_size(1000.0, 700.0)
.center()
.visible(true)
.transparent(false)
.decorations(true);
let build_result = builder.build();
match build_result {
Ok(window) => window,
Err(error) => {
return Err(format!("cannot create demo_pipeline window: {error:?}"));
}
}
}
};
let show_result = demo_window.show();
if let Err(error) = show_result {
return Err(format!("cannot show demo_pipeline window: {error:?}"));
}
let focus_result = demo_window.set_focus();
if let Err(error) = focus_result {
return Err(format!("cannot focus demo_pipeline window: {error:?}"));
}
Ok(())
}
/// Inspects one transaction signature through the persisted `kb_lib` pipeline state.
#[tauri::command]
pub(crate) async fn demo_pipeline_inspect_signature(
state: tauri::State<'_, crate::KbAppState>,
request: KbDemoPipelineInspectRequest,
) -> Result<KbDemoPipelineInspectPayload, std::string::String> {
let signature = request.signature.trim().to_string();
if signature.is_empty() {
return Err("demo pipeline signature must not be empty".to_string());
}
let database = state.database.clone();
let transaction_result =
kb_lib::get_chain_transaction_by_signature(database.as_ref(), signature.as_str()).await;
let transaction_option = match transaction_result {
Ok(transaction_option) => transaction_option,
Err(error) => {
return Err(format!(
"cannot read chain transaction '{}' from database: {}",
signature, error
));
}
};
let transaction = match transaction_option {
Some(transaction) => transaction,
None => {
return Err(format!(
"unknown transaction signature '{}' in local pipeline database",
signature
));
}
};
let transaction_id = match transaction.id {
Some(transaction_id) => transaction_id,
None => {
return Err(format!("transaction '{}' has no internal id", signature));
}
};
let decoded_events_result =
kb_lib::list_dex_decoded_events_by_transaction_id(database.as_ref(), transaction_id).await;
let decoded_events = match decoded_events_result {
Ok(decoded_events) => decoded_events,
Err(error) => {
return Err(format!(
"cannot list decoded events for transaction '{}': {}",
signature, error
));
}
};
let mut decoded_event_ids = std::collections::BTreeSet::<i64>::new();
let mut pool_ids = std::collections::BTreeSet::<i64>::new();
let mut pair_ids = std::collections::BTreeSet::<i64>::new();
let mut wallet_addresses = std::collections::BTreeSet::<std::string::String>::new();
let mut pools = std::vec::Vec::<kb_lib::KbPoolDto>::new();
let mut pairs = std::vec::Vec::<kb_lib::KbPairDto>::new();
let mut launch_attributions = std::vec::Vec::<kb_lib::KbLaunchAttributionDto>::new();
let mut pool_origins = std::vec::Vec::<kb_lib::KbPoolOriginDto>::new();
for decoded_event in &decoded_events {
if let Some(decoded_event_id) = decoded_event.id {
decoded_event_ids.insert(decoded_event_id);
let launch_attribution_result = kb_lib::get_launch_attribution_by_decoded_event_id(
database.as_ref(),
decoded_event_id,
)
.await;
let launch_attribution_option = match launch_attribution_result {
Ok(launch_attribution_option) => launch_attribution_option,
Err(error) => {
return Err(format!(
"cannot fetch launch attribution for decoded_event_id '{}': {}",
decoded_event_id, error
));
}
};
if let Some(launch_attribution) = launch_attribution_option {
launch_attributions.push(launch_attribution);
}
}
let payload_parse_result =
serde_json::from_str::<serde_json::Value>(decoded_event.payload_json.as_str());
let payload_value = match payload_parse_result {
Ok(payload_value) => payload_value,
Err(error) => {
return Err(format!(
"cannot parse decoded_event payload_json for '{}': {}",
signature, error
));
}
};
let extracted_wallets = kb_extract_wallet_addresses_from_value(&payload_value);
for wallet_address in extracted_wallets {
wallet_addresses.insert(wallet_address);
}
if let Some(token_a_mint) = decoded_event.token_a_mint.clone() {
let _ = token_a_mint;
}
if let Some(token_b_mint) = decoded_event.token_b_mint.clone() {
let _ = token_b_mint;
}
if let Some(pool_address) = decoded_event.pool_account.clone() {
let pool_result =
kb_lib::get_pool_by_address(database.as_ref(), pool_address.as_str()).await;
let pool_option = match pool_result {
Ok(pool_option) => pool_option,
Err(error) => {
return Err(format!(
"cannot fetch pool by address '{}' for '{}': {}",
pool_address, signature, error
));
}
};
if let Some(pool) = pool_option {
let pool_id = match pool.id {
Some(pool_id) => pool_id,
None => {
return Err(format!("pool '{}' has no internal id", pool.address));
}
};
if !pool_ids.contains(&pool_id) {
pool_ids.insert(pool_id);
pools.push(pool.clone());
let pool_origin_result =
kb_lib::get_pool_origin_by_pool_id(database.as_ref(), pool_id).await;
let pool_origin_option = match pool_origin_result {
Ok(pool_origin_option) => pool_origin_option,
Err(error) => {
return Err(format!(
"cannot fetch pool origin for pool_id '{}': {}",
pool_id, error
));
}
};
if let Some(pool_origin) = pool_origin_option {
pool_origins.push(pool_origin);
}
let pair_result = kb_lib::get_pair_by_pool_id(database.as_ref(), pool_id).await;
let pair_option = match pair_result {
Ok(pair_option) => pair_option,
Err(error) => {
return Err(format!(
"cannot fetch pair by pool_id '{}': {}",
pool_id, error
));
}
};
if let Some(pair) = pair_option {
let pair_id = match pair.id {
Some(pair_id) => pair_id,
None => {
return Err(format!(
"pair for pool '{}' has no internal id",
pool_id
));
}
};
if !pair_ids.contains(&pair_id) {
pair_ids.insert(pair_id);
pairs.push(pair);
}
}
}
}
}
}
let mut wallet_contexts = std::vec::Vec::<serde_json::Value>::new();
let mut wallet_participation_count = 0_usize;
let mut wallet_holding_count = 0_usize;
for wallet_address in wallet_addresses {
let wallet_result =
kb_lib::get_wallet_by_address(database.as_ref(), wallet_address.as_str()).await;
let wallet_option = match wallet_result {
Ok(wallet_option) => wallet_option,
Err(error) => {
return Err(format!(
"cannot fetch wallet by address '{}': {}",
wallet_address, error
));
}
};
let wallet = match wallet_option {
Some(wallet) => wallet,
None => continue,
};
let wallet_id = match wallet.id {
Some(wallet_id) => wallet_id,
None => {
return Err(format!("wallet '{}' has no internal id", wallet.address));
}
};
let participations_result =
kb_lib::list_wallet_participations_by_wallet_id(database.as_ref(), wallet_id).await;
let participations = match participations_result {
Ok(participations) => participations,
Err(error) => {
return Err(format!(
"cannot list wallet participations for wallet_id '{}': {}",
wallet_id, error
));
}
};
let mut filtered_participations = std::vec::Vec::new();
for participation in participations {
let mut include_participation = false;
if participation.transaction_id == transaction_id {
include_participation = true;
}
if !include_participation {
if let Some(decoded_event_id) = participation.decoded_event_id {
if decoded_event_ids.contains(&decoded_event_id) {
include_participation = true;
}
}
}
if include_participation {
filtered_participations.push(participation);
}
}
let holdings_result =
kb_lib::list_wallet_holdings_by_wallet_id(database.as_ref(), wallet_id).await;
let holdings = match holdings_result {
Ok(holdings) => holdings,
Err(error) => {
return Err(format!(
"cannot list wallet holdings for wallet_id '{}': {}",
wallet_id, error
));
}
};
let mut filtered_holdings = std::vec::Vec::new();
for holding in holdings {
if holding.last_transaction_id == transaction_id {
filtered_holdings.push(holding);
}
}
wallet_participation_count += filtered_participations.len();
wallet_holding_count += filtered_holdings.len();
let wallet_value_result = serde_json::to_value(&wallet);
let wallet_value = match wallet_value_result {
Ok(wallet_value) => wallet_value,
Err(error) => {
return Err(format!(
"cannot serialize wallet '{}' to JSON value: {}",
wallet.address, error
));
}
};
let participations_value_result = serde_json::to_value(&filtered_participations);
let participations_value = match participations_value_result {
Ok(participations_value) => participations_value,
Err(error) => {
return Err(format!(
"cannot serialize wallet participations for '{}' to JSON value: {}",
wallet.address, error
));
}
};
let holdings_value_result = serde_json::to_value(&filtered_holdings);
let holdings_value = match holdings_value_result {
Ok(holdings_value) => holdings_value,
Err(error) => {
return Err(format!(
"cannot serialize wallet holdings for '{}' to JSON value: {}",
wallet.address, error
));
}
};
wallet_contexts.push(serde_json::json!({
"walletAddress": wallet.address,
"wallet": wallet_value,
"participations": participations_value,
"holdings": holdings_value
}));
}
let trade_events_result =
kb_lib::list_trade_events_by_transaction_id(database.as_ref(), transaction_id).await;
let transaction_trade_events = match trade_events_result {
Ok(transaction_trade_events) => transaction_trade_events,
Err(error) => {
return Err(format!(
"cannot list trade events for transaction_id '{}': {}",
transaction_id, error
));
}
};
let mut pair_metrics = std::vec::Vec::<kb_lib::KbPairMetricDto>::new();
let mut pair_candle_groups = std::vec::Vec::<serde_json::Value>::new();
let mut pair_candle_count = 0_usize;
let mut pair_analytic_signal_groups = std::vec::Vec::<serde_json::Value>::new();
let mut pair_analytic_signal_count = 0_usize;
let query_service = kb_lib::KbPairCandleQueryService::new(database.clone());
let mut timeframes = vec![60_i64, 300_i64, 900_i64, 3600_i64];
if let Some(custom_timeframe_seconds) = request.custom_timeframe_seconds {
if custom_timeframe_seconds > 0 && !timeframes.contains(&custom_timeframe_seconds) {
timeframes.push(custom_timeframe_seconds);
}
}
for pair_id in &pair_ids {
let pair_metric_result =
kb_lib::get_pair_metric_by_pair_id(database.as_ref(), *pair_id).await;
let pair_metric_option = match pair_metric_result {
Ok(pair_metric_option) => pair_metric_option,
Err(error) => {
return Err(format!(
"cannot fetch pair metric for pair_id '{}': {}",
pair_id, error
));
}
};
if let Some(pair_metric) = pair_metric_option {
pair_metrics.push(pair_metric);
}
let pair_signals_result =
kb_lib::list_pair_analytic_signals_by_pair_id(database.as_ref(), *pair_id).await;
let pair_signals = match pair_signals_result {
Ok(pair_signals) => pair_signals,
Err(error) => {
return Err(format!(
"cannot list pair analytic signals for pair_id '{}': {}",
pair_id, error
));
}
};
pair_analytic_signal_count += pair_signals.len();
let pair_signals_value_result = serde_json::to_value(&pair_signals);
let pair_signals_value = match pair_signals_value_result {
Ok(pair_signals_value) => pair_signals_value,
Err(error) => {
return Err(format!(
"cannot serialize pair analytic signals for pair_id '{}': {}",
pair_id, error
));
}
};
pair_analytic_signal_groups.push(serde_json::json!({
"pairId": pair_id,
"signals": pair_signals_value
}));
for timeframe_seconds in &timeframes {
let candles_result = if *timeframe_seconds == 60
|| *timeframe_seconds == 300
|| *timeframe_seconds == 900
|| *timeframe_seconds == 3600
{
kb_lib::list_pair_candles_by_pair_and_timeframe(
database.as_ref(),
*pair_id,
*timeframe_seconds,
)
.await
} else {
query_service
.list_pair_candles(*pair_id, *timeframe_seconds, None, None, false)
.await
};
let candles = match candles_result {
Ok(candles) => candles,
Err(error) => {
return Err(format!(
"cannot list/rebuild pair candles for pair_id '{}' timeframe '{}': {}",
pair_id, timeframe_seconds, error
));
}
};
pair_candle_count += candles.len();
let candles_value_result = serde_json::to_value(&candles);
let candles_value = match candles_value_result {
Ok(candles_value) => candles_value,
Err(error) => {
return Err(format!(
"cannot serialize pair candles for pair_id '{}' timeframe '{}': {}",
pair_id, timeframe_seconds, error
));
}
};
pair_candle_groups.push(serde_json::json!({
"pairId": pair_id,
"timeframeSeconds": timeframe_seconds,
"candles": candles_value
}));
}
}
let summary_value = serde_json::json!({
"signature": signature,
"customTimeframeSeconds": request.custom_timeframe_seconds,
"decodedEventCount": decoded_events.len(),
"poolCount": pools.len(),
"pairCount": pairs.len(),
"launchAttributionCount": launch_attributions.len(),
"poolOriginCount": pool_origins.len(),
"walletCount": wallet_contexts.len(),
"walletParticipationCount": wallet_participation_count,
"walletHoldingCount": wallet_holding_count,
"transactionTradeEventCount": transaction_trade_events.len(),
"pairMetricCount": pair_metrics.len(),
"pairCandleGroupCount": pair_candle_groups.len(),
"pairCandleCount": pair_candle_count,
"pairAnalyticSignalGroupCount": pair_analytic_signal_groups.len(),
"pairAnalyticSignalCount": pair_analytic_signal_count
});
let summary_json_result = kb_to_pretty_json(&summary_value, "summary");
let summary_json = match summary_json_result {
Ok(summary_json) => summary_json,
Err(error) => return Err(error),
};
let transaction_json_result = kb_to_pretty_json(&transaction, "transaction");
let transaction_json = match transaction_json_result {
Ok(transaction_json) => transaction_json,
Err(error) => return Err(error),
};
let decoded_events_json_result = kb_to_pretty_json(&decoded_events, "decoded events");
let decoded_events_json = match decoded_events_json_result {
Ok(decoded_events_json) => decoded_events_json,
Err(error) => return Err(error),
};
let pools_json_result = kb_to_pretty_json(&pools, "pools");
let pools_json = match pools_json_result {
Ok(pools_json) => pools_json,
Err(error) => return Err(error),
};
let pairs_json_result = kb_to_pretty_json(&pairs, "pairs");
let pairs_json = match pairs_json_result {
Ok(pairs_json) => pairs_json,
Err(error) => return Err(error),
};
let launch_attributions_json_result =
kb_to_pretty_json(&launch_attributions, "launch attributions");
let launch_attributions_json = match launch_attributions_json_result {
Ok(launch_attributions_json) => launch_attributions_json,
Err(error) => return Err(error),
};
let pool_origins_json_result = kb_to_pretty_json(&pool_origins, "pool origins");
let pool_origins_json = match pool_origins_json_result {
Ok(pool_origins_json) => pool_origins_json,
Err(error) => return Err(error),
};
let wallets_json_result = kb_to_pretty_json(&wallet_contexts, "wallet contexts");
let wallets_json = match wallets_json_result {
Ok(wallets_json) => wallets_json,
Err(error) => return Err(error),
};
let trade_events_json_result = kb_to_pretty_json(&transaction_trade_events, "trade events");
let trade_events_json = match trade_events_json_result {
Ok(trade_events_json) => trade_events_json,
Err(error) => return Err(error),
};
let pair_metrics_json_result = kb_to_pretty_json(&pair_metrics, "pair metrics");
let pair_metrics_json = match pair_metrics_json_result {
Ok(pair_metrics_json) => pair_metrics_json,
Err(error) => return Err(error),
};
let pair_candles_json_result = kb_to_pretty_json(&pair_candle_groups, "pair candles");
let pair_candles_json = match pair_candles_json_result {
Ok(pair_candles_json) => pair_candles_json,
Err(error) => return Err(error),
};
let pair_analytic_signals_json_result =
kb_to_pretty_json(&pair_analytic_signal_groups, "pair analytic signals");
let pair_analytic_signals_json = match pair_analytic_signals_json_result {
Ok(pair_analytic_signals_json) => pair_analytic_signals_json,
Err(error) => return Err(error),
};
Ok(KbDemoPipelineInspectPayload {
signature,
summary_json,
transaction_json,
decoded_events_json,
pools_json,
pairs_json,
launch_attributions_json,
pool_origins_json,
wallets_json,
trade_events_json,
pair_metrics_json,
pair_candles_json,
pair_analytic_signals_json,
})
}
async fn kb_demo_pipeline_build_pair_payload(
database: std::sync::Arc<kb_lib::KbDatabase>,
database_url: std::string::String,
inspected_pair: kb_lib::KbPairDto,
custom_timeframe_seconds: std::option::Option<i64>,
object_key: std::string::String,
) -> Result<KbDemoPipelineInspectPayload, std::string::String> {
let pair_id = match inspected_pair.id {
Some(pair_id) => pair_id,
None => {
return Err("inspected pair has no internal id".to_string());
}
};
let pools_result = kb_lib::list_pools(database.as_ref()).await;
let all_pools = match pools_result {
Ok(all_pools) => all_pools,
Err(error) => {
return Err(format!("cannot list pools from database: {}", error));
}
};
let mut inspected_pool_option = std::option::Option::<kb_lib::KbPoolDto>::None;
for pool in all_pools {
let pool_id_option = pool.id;
let pool_id = match pool_id_option {
Some(pool_id) => pool_id,
None => continue,
};
if pool_id == inspected_pair.pool_id {
inspected_pool_option = Some(pool);
break;
}
}
let inspected_pool = match inspected_pool_option {
Some(inspected_pool) => inspected_pool,
None => {
return Err(format!(
"pair '{}' references unknown pool_id '{}'",
pair_id, inspected_pair.pool_id
));
}
};
let pool_listings_result = kb_lib::list_pool_listings(database.as_ref()).await;
let all_pool_listings = match pool_listings_result {
Ok(all_pool_listings) => all_pool_listings,
Err(error) => {
return Err(format!(
"cannot list pool listings from database: {}",
error
));
}
};
let mut pool_listings = std::vec::Vec::<kb_lib::KbPoolListingDto>::new();
for listing in all_pool_listings {
if listing.pool_id == inspected_pair.pool_id {
pool_listings.push(listing);
}
}
let launch_attributions_result =
kb_lib::list_launch_attributions_by_pool_id(database.as_ref(), inspected_pair.pool_id)
.await;
let launch_attributions = match launch_attributions_result {
Ok(launch_attributions) => launch_attributions,
Err(error) => {
return Err(format!(
"cannot list launch attributions for pool_id '{}': {}",
inspected_pair.pool_id, error
));
}
};
let pool_origin_result =
kb_lib::get_pool_origin_by_pool_id(database.as_ref(), inspected_pair.pool_id).await;
let pool_origin_option = match pool_origin_result {
Ok(pool_origin_option) => pool_origin_option,
Err(error) => {
return Err(format!(
"cannot fetch pool origin for pool_id '{}': {}",
inspected_pair.pool_id, error
));
}
};
let trade_events_result =
kb_lib::list_trade_events_by_pair_id(database.as_ref(), pair_id).await;
let trade_events = match trade_events_result {
Ok(trade_events) => trade_events,
Err(error) => {
return Err(format!(
"cannot list trade events for pair_id '{}': {}",
pair_id, error
));
}
};
let pair_metric_result = kb_lib::get_pair_metric_by_pair_id(database.as_ref(), pair_id).await;
let pair_metric_option = match pair_metric_result {
Ok(pair_metric_option) => pair_metric_option,
Err(error) => {
return Err(format!(
"cannot fetch pair metric for pair_id '{}': {}",
pair_id, error
));
}
};
let pair_signals_result =
kb_lib::list_pair_analytic_signals_by_pair_id(database.as_ref(), pair_id).await;
let pair_signals = match pair_signals_result {
Ok(pair_signals) => pair_signals,
Err(error) => {
return Err(format!(
"cannot list pair analytic signals for pair_id '{}': {}",
pair_id, error
));
}
};
let query_service = kb_lib::KbPairCandleQueryService::new(database.clone());
let mut timeframes = vec![60_i64, 300_i64, 900_i64, 3600_i64];
if let Some(custom_timeframe_seconds_value) = custom_timeframe_seconds {
if custom_timeframe_seconds_value > 0
&& !timeframes.contains(&custom_timeframe_seconds_value)
{
timeframes.push(custom_timeframe_seconds_value);
}
}
let mut pair_candle_groups = std::vec::Vec::<serde_json::Value>::new();
for timeframe_seconds in &timeframes {
let candles_result = if *timeframe_seconds == 60
|| *timeframe_seconds == 300
|| *timeframe_seconds == 900
|| *timeframe_seconds == 3600
{
kb_lib::list_pair_candles_by_pair_and_timeframe(
database.as_ref(),
pair_id,
*timeframe_seconds,
)
.await
} else {
query_service
.list_pair_candles(pair_id, *timeframe_seconds, None, None, false)
.await
};
let candles = match candles_result {
Ok(candles) => candles,
Err(error) => {
return Err(format!(
"cannot list/rebuild pair candles for pair_id '{}' timeframe '{}': {}",
pair_id, timeframe_seconds, error
));
}
};
let candles_value_result = serde_json::to_value(&candles);
let candles_value = match candles_value_result {
Ok(candles_value) => candles_value,
Err(error) => {
return Err(format!(
"cannot serialize pair candles for pair_id '{}' timeframe '{}': {}",
pair_id, timeframe_seconds, error
));
}
};
pair_candle_groups.push(serde_json::json!({
"pairId": pair_id,
"timeframeSeconds": timeframe_seconds,
"candles": candles_value
}));
}
let wallets_result = kb_lib::list_wallets(database.as_ref()).await;
let wallets = match wallets_result {
Ok(wallets) => wallets,
Err(error) => {
return Err(format!("cannot list wallets from database: {}", error));
}
};
let mut wallet_contexts = std::vec::Vec::<serde_json::Value>::new();
for wallet in wallets {
let wallet_id = match wallet.id {
Some(wallet_id) => wallet_id,
None => continue,
};
let participations_result =
kb_lib::list_wallet_participations_by_wallet_id(database.as_ref(), wallet_id).await;
let participations = match participations_result {
Ok(participations) => participations,
Err(error) => {
return Err(format!(
"cannot list wallet participations for wallet_id '{}': {}",
wallet_id, error
));
}
};
let mut filtered_participations = std::vec::Vec::new();
for participation in participations {
let mut include_participation = false;
if let Some(participation_pair_id) = participation.pair_id {
if participation_pair_id == pair_id {
include_participation = true;
}
}
if !include_participation {
if let Some(participation_pool_id) = participation.pool_id {
if participation_pool_id == inspected_pair.pool_id {
include_participation = true;
}
}
}
if include_participation {
filtered_participations.push(participation);
}
}
let holdings_result =
kb_lib::list_wallet_holdings_by_wallet_id(database.as_ref(), wallet_id).await;
let holdings = match holdings_result {
Ok(holdings) => holdings,
Err(error) => {
return Err(format!(
"cannot list wallet holdings for wallet_id '{}': {}",
wallet_id, error
));
}
};
let mut filtered_holdings = std::vec::Vec::new();
for holding in holdings {
let mut include_holding = false;
if let Some(last_pair_id) = holding.last_pair_id {
if last_pair_id == pair_id {
include_holding = true;
}
}
if !include_holding {
if let Some(last_pool_id) = holding.last_pool_id {
if last_pool_id == inspected_pair.pool_id {
include_holding = true;
}
}
}
if include_holding {
filtered_holdings.push(holding);
}
}
if !filtered_participations.is_empty() || !filtered_holdings.is_empty() {
let wallet_value_result = serde_json::to_value(&wallet);
let wallet_value = match wallet_value_result {
Ok(wallet_value) => wallet_value,
Err(error) => {
return Err(format!(
"cannot serialize wallet '{}' to JSON value: {}",
wallet.address, error
));
}
};
let participations_value_result = serde_json::to_value(&filtered_participations);
let participations_value = match participations_value_result {
Ok(participations_value) => participations_value,
Err(error) => {
return Err(format!(
"cannot serialize wallet participations for '{}' to JSON value: {}",
wallet.address, error
));
}
};
let holdings_value_result = serde_json::to_value(&filtered_holdings);
let holdings_value = match holdings_value_result {
Ok(holdings_value) => holdings_value,
Err(error) => {
return Err(format!(
"cannot serialize wallet holdings for '{}' to JSON value: {}",
wallet.address, error
));
}
};
wallet_contexts.push(serde_json::json!({
"walletAddress": wallet.address,
"wallet": wallet_value,
"participations": participations_value,
"holdings": holdings_value
}));
}
}
let mut pair_metrics = std::vec::Vec::<kb_lib::KbPairMetricDto>::new();
if let Some(pair_metric) = pair_metric_option {
pair_metrics.push(pair_metric);
}
let pair_signals_value_result = serde_json::to_value(&pair_signals);
let pair_signals_value = match pair_signals_value_result {
Ok(pair_signals_value) => pair_signals_value,
Err(error) => {
return Err(format!(
"cannot serialize pair analytic signals for pair_id '{}': {}",
pair_id, error
));
}
};
let pair_signal_groups = vec![serde_json::json!({
"pairId": pair_id,
"signals": pair_signals_value
})];
let summary_value = serde_json::json!({
"mode": "pairOrPool",
"databaseUrl": database_url,
"objectKey": object_key,
"pairId": pair_id,
"poolId": inspected_pair.pool_id,
"poolAddress": inspected_pool.address,
"customTimeframeSeconds": custom_timeframe_seconds,
"poolListingCount": pool_listings.len(),
"launchAttributionCount": launch_attributions.len(),
"hasPoolOrigin": pool_origin_option.is_some(),
"walletContextCount": wallet_contexts.len(),
"tradeEventCount": trade_events.len(),
"pairMetricCount": pair_metrics.len(),
"pairCandleGroupCount": pair_candle_groups.len(),
"pairAnalyticSignalCount": pair_signals.len()
});
let summary_json_result = kb_to_pretty_json(&summary_value, "summary");
let summary_json = match summary_json_result {
Ok(summary_json) => summary_json,
Err(error) => return Err(error),
};
let transaction_json_result = kb_to_pretty_json(&inspected_pair, "pair");
let transaction_json = match transaction_json_result {
Ok(transaction_json) => transaction_json,
Err(error) => return Err(error),
};
let decoded_events_json_result = kb_to_pretty_json(&pool_listings, "pool listings");
let decoded_events_json = match decoded_events_json_result {
Ok(decoded_events_json) => decoded_events_json,
Err(error) => return Err(error),
};
let pools_json_result = kb_to_pretty_json(&vec![inspected_pool], "pool");
let pools_json = match pools_json_result {
Ok(pools_json) => pools_json,
Err(error) => return Err(error),
};
let pairs_json_result = kb_to_pretty_json(&vec![inspected_pair], "pair");
let pairs_json = match pairs_json_result {
Ok(pairs_json) => pairs_json,
Err(error) => return Err(error),
};
let launch_attributions_json_result =
kb_to_pretty_json(&launch_attributions, "launch attributions");
let launch_attributions_json = match launch_attributions_json_result {
Ok(launch_attributions_json) => launch_attributions_json,
Err(error) => return Err(error),
};
let pool_origins_json_result = kb_to_pretty_json(&pool_origin_option, "pool origin");
let pool_origins_json = match pool_origins_json_result {
Ok(pool_origins_json) => pool_origins_json,
Err(error) => return Err(error),
};
let wallets_json_result = kb_to_pretty_json(&wallet_contexts, "wallet contexts");
let wallets_json = match wallets_json_result {
Ok(wallets_json) => wallets_json,
Err(error) => return Err(error),
};
let trade_events_json_result = kb_to_pretty_json(&trade_events, "trade events");
let trade_events_json = match trade_events_json_result {
Ok(trade_events_json) => trade_events_json,
Err(error) => return Err(error),
};
let pair_metrics_json_result = kb_to_pretty_json(&pair_metrics, "pair metrics");
let pair_metrics_json = match pair_metrics_json_result {
Ok(pair_metrics_json) => pair_metrics_json,
Err(error) => return Err(error),
};
let pair_candles_json_result = kb_to_pretty_json(&pair_candle_groups, "pair candles");
let pair_candles_json = match pair_candles_json_result {
Ok(pair_candles_json) => pair_candles_json,
Err(error) => return Err(error),
};
let pair_analytic_signals_json_result =
kb_to_pretty_json(&pair_signal_groups, "pair analytic signals");
let pair_analytic_signals_json = match pair_analytic_signals_json_result {
Ok(pair_analytic_signals_json) => pair_analytic_signals_json,
Err(error) => return Err(error),
};
Ok(KbDemoPipelineInspectPayload {
signature: object_key,
summary_json,
transaction_json,
decoded_events_json,
pools_json,
pairs_json,
launch_attributions_json,
pool_origins_json,
wallets_json,
trade_events_json,
pair_metrics_json,
pair_candles_json,
pair_analytic_signals_json,
})
}
fn kb_to_pretty_json<T: serde::Serialize>(
value: &T,
label: &str,
) -> Result<std::string::String, std::string::String> {
let json_result = serde_json::to_string_pretty(value);
match json_result {
Ok(json) => Ok(json),
Err(error) => Err(format!("cannot serialize {} as JSON: {}", label, error)),
}
}
fn kb_extract_wallet_addresses_from_value(
value: &serde_json::Value,
) -> std::collections::BTreeSet<std::string::String> {
let mut addresses = std::collections::BTreeSet::<std::string::String>::new();
let candidate_keys = ["creator", "poolCreator", "payer", "funder", "owner", "user"];
kb_extract_wallet_addresses_from_value_inner(value, &candidate_keys, &mut addresses);
addresses
}
fn kb_extract_wallet_addresses_from_value_inner(
value: &serde_json::Value,
candidate_keys: &[&str],
addresses: &mut std::collections::BTreeSet<std::string::String>,
) {
if let Some(object) = value.as_object() {
for candidate_key in candidate_keys {
let direct_option = object.get(*candidate_key);
if let Some(direct) = direct_option {
let text_option = direct.as_str();
if let Some(text) = text_option {
if !text.is_empty() {
addresses.insert(text.to_string());
}
}
}
}
for nested_value in object.values() {
kb_extract_wallet_addresses_from_value_inner(nested_value, candidate_keys, addresses);
}
return;
}
if let Some(array) = value.as_array() {
for nested_value in array {
kb_extract_wallet_addresses_from_value_inner(nested_value, candidate_keys, addresses);
}
}
}