0.7.40
This commit is contained in:
735
kb_demo_app/src/demo3.rs
Normal file
735
kb_demo_app/src/demo3.rs
Normal file
@@ -0,0 +1,735 @@
|
||||
// file: kb_demo_app/src/demo3.rs
|
||||
|
||||
//! Tauri commands for Demo3 local DEX corpus search.
|
||||
//!
|
||||
//! Demo3 is intentionally a thin UI-facing wrapper around `kb_lib` local
|
||||
//! corpus search services. It does not contain DEX decoding or protocol logic.
|
||||
|
||||
use tauri::Manager;
|
||||
use ts_rs::TS;
|
||||
|
||||
/// Request payload for a local DEX corpus search.
|
||||
#[derive(Clone, Debug, serde::Serialize, serde::Deserialize, TS)]
|
||||
#[ts(
|
||||
export,
|
||||
export_to = "../frontend/ts/bindings/Demo3LocalDexCorpusSearchRequest.ts"
|
||||
)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub(crate) struct Demo3LocalDexCorpusSearchRequest {
|
||||
/// Optional DEX code or decoded protocol name.
|
||||
pub dex_code: std::option::Option<std::string::String>,
|
||||
/// Optional Solana program id.
|
||||
pub program_id: std::option::Option<std::string::String>,
|
||||
/// Optional local pair id.
|
||||
#[ts(type = "number | null")]
|
||||
pub pair_id: std::option::Option<i64>,
|
||||
/// Optional pool account/address.
|
||||
pub pool_address: std::option::Option<std::string::String>,
|
||||
/// Optional token mint to match as base, quote or decoded mint.
|
||||
pub token_mint: std::option::Option<std::string::String>,
|
||||
/// Optional transaction signature.
|
||||
pub signature: std::option::Option<std::string::String>,
|
||||
/// Maximum number of rows to return per sample category.
|
||||
pub limit: u32,
|
||||
}
|
||||
|
||||
/// Response payload returned by Demo3 local DEX corpus search.
|
||||
#[derive(Clone, Debug, serde::Serialize, TS)]
|
||||
#[ts(
|
||||
export,
|
||||
export_to = "../frontend/ts/bindings/Demo3LocalDexCorpusSearchPayload.ts"
|
||||
)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub(crate) struct Demo3LocalDexCorpusSearchPayload {
|
||||
/// Open database URL.
|
||||
pub database_url: std::string::String,
|
||||
/// Pretty JSON representation of the search result.
|
||||
pub result_json: std::string::String,
|
||||
/// Structured local DEX corpus search result.
|
||||
pub result: Demo3LocalDexCorpusSearchResult,
|
||||
}
|
||||
|
||||
/// Structured local DEX corpus search result.
|
||||
#[derive(Clone, Debug, serde::Serialize, TS)]
|
||||
#[ts(export, export_to = "../frontend/ts/bindings/Demo3LocalDexCorpusSearchResult.ts")]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub(crate) struct Demo3LocalDexCorpusSearchResult {
|
||||
/// Normalized search request applied by the backend service.
|
||||
pub request: Demo3LocalDexCorpusSearchRequest,
|
||||
/// Aggregate counts for the matching local data.
|
||||
pub summary: Demo3LocalDexCorpusSearchSummary,
|
||||
/// Matching transaction samples.
|
||||
pub transaction_samples: std::vec::Vec<Demo3LocalDexCorpusTransactionSample>,
|
||||
/// Matching pool/pair samples.
|
||||
pub pool_pair_samples: std::vec::Vec<Demo3LocalDexCorpusPoolPairSample>,
|
||||
/// Matching decoded event samples.
|
||||
pub decoded_event_samples: std::vec::Vec<Demo3LocalDexCorpusDecodedEventSample>,
|
||||
}
|
||||
|
||||
/// Aggregate counts for one local DEX corpus search.
|
||||
#[derive(Clone, Debug, serde::Serialize, TS)]
|
||||
#[ts(
|
||||
export,
|
||||
export_to = "../frontend/ts/bindings/Demo3LocalDexCorpusSearchSummary.ts"
|
||||
)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub(crate) struct Demo3LocalDexCorpusSearchSummary {
|
||||
/// Number of distinct matching transactions.
|
||||
#[ts(type = "number")]
|
||||
pub transaction_count: i64,
|
||||
/// Number of distinct matching instructions.
|
||||
#[ts(type = "number")]
|
||||
pub instruction_count: i64,
|
||||
/// Number of distinct matching decoded DEX events.
|
||||
#[ts(type = "number")]
|
||||
pub decoded_event_count: i64,
|
||||
/// Number of distinct matching pools.
|
||||
#[ts(type = "number")]
|
||||
pub pool_count: i64,
|
||||
/// Number of distinct matching pairs.
|
||||
#[ts(type = "number")]
|
||||
pub pair_count: i64,
|
||||
/// Number of distinct matching trade events.
|
||||
#[ts(type = "number")]
|
||||
pub trade_event_count: i64,
|
||||
/// Number of distinct matching candle rows.
|
||||
#[ts(type = "number")]
|
||||
pub pair_candle_count: i64,
|
||||
/// Number of distinct matching protocol candidate rows.
|
||||
#[ts(type = "number")]
|
||||
pub protocol_candidate_count: i64,
|
||||
}
|
||||
|
||||
/// Matching transaction sample for corpus discovery.
|
||||
#[derive(Clone, Debug, serde::Serialize, TS)]
|
||||
#[ts(
|
||||
export,
|
||||
export_to = "../frontend/ts/bindings/Demo3LocalDexCorpusTransactionSample.ts"
|
||||
)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub(crate) struct Demo3LocalDexCorpusTransactionSample {
|
||||
/// Transaction id.
|
||||
#[ts(type = "number")]
|
||||
pub transaction_id: i64,
|
||||
/// Transaction signature.
|
||||
pub signature: std::string::String,
|
||||
/// Optional Solana slot.
|
||||
#[ts(type = "number | null")]
|
||||
pub slot: std::option::Option<i64>,
|
||||
/// Whether the transaction has a non-null error payload.
|
||||
pub failed: bool,
|
||||
/// Number of persisted instructions for the transaction.
|
||||
#[ts(type = "number")]
|
||||
pub instruction_count: i64,
|
||||
/// Number of persisted decoded DEX events for the transaction.
|
||||
#[ts(type = "number")]
|
||||
pub decoded_event_count: i64,
|
||||
/// Number of persisted trade events for the transaction.
|
||||
#[ts(type = "number")]
|
||||
pub trade_event_count: i64,
|
||||
/// Comma-separated distinct program ids seen in the transaction.
|
||||
pub program_ids_csv: std::string::String,
|
||||
}
|
||||
|
||||
/// Matching pool/pair sample for corpus discovery.
|
||||
#[derive(Clone, Debug, serde::Serialize, TS)]
|
||||
#[ts(
|
||||
export,
|
||||
export_to = "../frontend/ts/bindings/Demo3LocalDexCorpusPoolPairSample.ts"
|
||||
)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub(crate) struct Demo3LocalDexCorpusPoolPairSample {
|
||||
/// Optional pool id.
|
||||
#[ts(type = "number | null")]
|
||||
pub pool_id: std::option::Option<i64>,
|
||||
/// Optional pool address.
|
||||
pub pool_address: std::option::Option<std::string::String>,
|
||||
/// Optional pair id.
|
||||
#[ts(type = "number | null")]
|
||||
pub pair_id: std::option::Option<i64>,
|
||||
/// Optional DEX code.
|
||||
pub dex_code: std::option::Option<std::string::String>,
|
||||
/// Optional pair symbol.
|
||||
pub pair_symbol: std::option::Option<std::string::String>,
|
||||
/// Optional base token mint.
|
||||
pub base_mint: std::option::Option<std::string::String>,
|
||||
/// Optional base token symbol.
|
||||
pub base_symbol: std::option::Option<std::string::String>,
|
||||
/// Optional quote token mint.
|
||||
pub quote_mint: std::option::Option<std::string::String>,
|
||||
/// Optional quote token symbol.
|
||||
pub quote_symbol: std::option::Option<std::string::String>,
|
||||
/// Number of decoded events attached to the pool.
|
||||
#[ts(type = "number")]
|
||||
pub decoded_event_count: i64,
|
||||
/// Number of trade events attached to the pair.
|
||||
#[ts(type = "number")]
|
||||
pub trade_event_count: i64,
|
||||
/// Number of candle rows attached to the pair.
|
||||
#[ts(type = "number")]
|
||||
pub pair_candle_count: i64,
|
||||
/// Latest known founding or activity signature for the pool/pair.
|
||||
pub latest_signature: std::option::Option<std::string::String>,
|
||||
}
|
||||
|
||||
/// Matching decoded event sample for corpus discovery.
|
||||
#[derive(Clone, Debug, serde::Serialize, TS)]
|
||||
#[ts(
|
||||
export,
|
||||
export_to = "../frontend/ts/bindings/Demo3LocalDexCorpusDecodedEventSample.ts"
|
||||
)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub(crate) struct Demo3LocalDexCorpusDecodedEventSample {
|
||||
/// Decoded event id.
|
||||
#[ts(type = "number")]
|
||||
pub decoded_event_id: i64,
|
||||
/// Transaction id.
|
||||
#[ts(type = "number")]
|
||||
pub transaction_id: i64,
|
||||
/// Transaction signature.
|
||||
pub signature: std::string::String,
|
||||
/// Optional Solana slot.
|
||||
#[ts(type = "number | null")]
|
||||
pub slot: std::option::Option<i64>,
|
||||
/// Protocol name stored on the decoded event.
|
||||
pub protocol_name: std::string::String,
|
||||
/// Program id stored on the decoded event.
|
||||
pub program_id: std::string::String,
|
||||
/// Event kind.
|
||||
pub event_kind: std::string::String,
|
||||
/// Optional pool account.
|
||||
pub pool_account: std::option::Option<std::string::String>,
|
||||
/// Optional token A mint.
|
||||
pub token_a_mint: std::option::Option<std::string::String>,
|
||||
/// Optional token B mint.
|
||||
pub token_b_mint: std::option::Option<std::string::String>,
|
||||
/// Decoded event category extracted from payload JSON.
|
||||
pub event_category: std::option::Option<std::string::String>,
|
||||
/// Decoded event lifecycle kind extracted from payload JSON.
|
||||
pub event_lifecycle_kind: std::option::Option<std::string::String>,
|
||||
/// Decoded event actionability extracted from payload JSON.
|
||||
pub event_actionability: std::option::Option<std::string::String>,
|
||||
/// Whether the decoded event is a trade candidate.
|
||||
pub trade_candidate: bool,
|
||||
/// Whether the decoded event is a candle candidate.
|
||||
pub candle_candidate: bool,
|
||||
}
|
||||
|
||||
/// Opens the `Demo3` local DEX corpus search window.
|
||||
#[tauri::command]
|
||||
pub(crate) fn open_demo3_window(app_handle: tauri::AppHandle) -> Result<(), std::string::String> {
|
||||
let existing_window_option = app_handle.get_webview_window("demo3");
|
||||
|
||||
let demo_window = match existing_window_option {
|
||||
Some(demo_window) => demo_window,
|
||||
None => {
|
||||
let builder = tauri::WebviewWindowBuilder::new(
|
||||
&app_handle,
|
||||
"demo3",
|
||||
tauri::WebviewUrl::App("demo3.html".into()),
|
||||
)
|
||||
.title("Demo3 Local DEX Corpus")
|
||||
.inner_size(1480.0, 920.0)
|
||||
.min_inner_size(1100.0, 720.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 demo3 window: {error:?}"));
|
||||
},
|
||||
}
|
||||
},
|
||||
};
|
||||
let show_result = demo_window.show();
|
||||
if let Err(error) = show_result {
|
||||
return Err(format!("cannot show demo3 window: {error:?}"));
|
||||
}
|
||||
let focus_result = demo_window.set_focus();
|
||||
if let Err(error) = focus_result {
|
||||
return Err(format!("cannot focus demo3 window: {error:?}"));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Searches local DEX corpus candidates from the persisted SQLite pipeline.
|
||||
#[tauri::command]
|
||||
pub(crate) async fn demo3_search_local_dex_corpus(
|
||||
state: tauri::State<'_, crate::AppState>,
|
||||
request: Demo3LocalDexCorpusSearchRequest,
|
||||
) -> Result<Demo3LocalDexCorpusSearchPayload, std::string::String> {
|
||||
let database = state.database.clone();
|
||||
let service = kb_lib::LocalDexCorpusSearchService::new(database.clone());
|
||||
let search_result = service.search(to_lib_search_request(&request)).await;
|
||||
let lib_result = match search_result {
|
||||
Ok(result) => result,
|
||||
Err(error) => {
|
||||
return Err(format!("local DEX corpus search failed: {}", error));
|
||||
},
|
||||
};
|
||||
let ui_result = from_lib_search_result(lib_result);
|
||||
let result_json_result = serde_json::to_string_pretty(&ui_result);
|
||||
let result_json = match result_json_result {
|
||||
Ok(result_json) => result_json,
|
||||
Err(error) => {
|
||||
return Err(format!("cannot serialize local DEX corpus search result: {}", error));
|
||||
},
|
||||
};
|
||||
Ok(Demo3LocalDexCorpusSearchPayload {
|
||||
database_url: database.database_url().to_string(),
|
||||
result_json,
|
||||
result: ui_result,
|
||||
})
|
||||
}
|
||||
|
||||
fn to_lib_search_request(
|
||||
request: &Demo3LocalDexCorpusSearchRequest,
|
||||
) -> kb_lib::LocalDexCorpusSearchRequestDto {
|
||||
return kb_lib::LocalDexCorpusSearchRequestDto {
|
||||
dex_code: normalize_optional_text(request.dex_code.clone()),
|
||||
program_id: normalize_optional_text(request.program_id.clone()),
|
||||
pair_id: request.pair_id,
|
||||
pool_address: normalize_optional_text(request.pool_address.clone()),
|
||||
token_mint: normalize_optional_text(request.token_mint.clone()),
|
||||
signature: normalize_optional_text(request.signature.clone()),
|
||||
limit: request.limit,
|
||||
};
|
||||
}
|
||||
|
||||
fn normalize_optional_text(
|
||||
value: std::option::Option<std::string::String>,
|
||||
) -> std::option::Option<std::string::String> {
|
||||
let value = match value {
|
||||
Some(value) => value.trim().to_string(),
|
||||
None => return None,
|
||||
};
|
||||
if value.is_empty() {
|
||||
return None;
|
||||
}
|
||||
return Some(value);
|
||||
}
|
||||
|
||||
fn from_lib_search_result(
|
||||
result: kb_lib::LocalDexCorpusSearchResultDto,
|
||||
) -> Demo3LocalDexCorpusSearchResult {
|
||||
let mut transaction_samples = std::vec::Vec::new();
|
||||
for sample in result.transaction_samples {
|
||||
transaction_samples.push(from_lib_transaction_sample(sample));
|
||||
}
|
||||
let mut pool_pair_samples = std::vec::Vec::new();
|
||||
for sample in result.pool_pair_samples {
|
||||
pool_pair_samples.push(from_lib_pool_pair_sample(sample));
|
||||
}
|
||||
let mut decoded_event_samples = std::vec::Vec::new();
|
||||
for sample in result.decoded_event_samples {
|
||||
decoded_event_samples.push(from_lib_decoded_event_sample(sample));
|
||||
}
|
||||
return Demo3LocalDexCorpusSearchResult {
|
||||
request: from_lib_request(result.request),
|
||||
summary: from_lib_summary(result.summary),
|
||||
transaction_samples,
|
||||
pool_pair_samples,
|
||||
decoded_event_samples,
|
||||
};
|
||||
}
|
||||
|
||||
fn from_lib_request(
|
||||
request: kb_lib::LocalDexCorpusSearchRequestDto,
|
||||
) -> Demo3LocalDexCorpusSearchRequest {
|
||||
return Demo3LocalDexCorpusSearchRequest {
|
||||
dex_code: request.dex_code,
|
||||
program_id: request.program_id,
|
||||
pair_id: request.pair_id,
|
||||
pool_address: request.pool_address,
|
||||
token_mint: request.token_mint,
|
||||
signature: request.signature,
|
||||
limit: request.limit,
|
||||
};
|
||||
}
|
||||
|
||||
fn from_lib_summary(
|
||||
summary: kb_lib::LocalDexCorpusSearchSummaryDto,
|
||||
) -> Demo3LocalDexCorpusSearchSummary {
|
||||
return Demo3LocalDexCorpusSearchSummary {
|
||||
transaction_count: summary.transaction_count,
|
||||
instruction_count: summary.instruction_count,
|
||||
decoded_event_count: summary.decoded_event_count,
|
||||
pool_count: summary.pool_count,
|
||||
pair_count: summary.pair_count,
|
||||
trade_event_count: summary.trade_event_count,
|
||||
pair_candle_count: summary.pair_candle_count,
|
||||
protocol_candidate_count: summary.protocol_candidate_count,
|
||||
};
|
||||
}
|
||||
|
||||
fn from_lib_transaction_sample(
|
||||
sample: kb_lib::LocalDexCorpusTransactionSampleDto,
|
||||
) -> Demo3LocalDexCorpusTransactionSample {
|
||||
return Demo3LocalDexCorpusTransactionSample {
|
||||
transaction_id: sample.transaction_id,
|
||||
signature: sample.signature,
|
||||
slot: sample.slot,
|
||||
failed: sample.failed,
|
||||
instruction_count: sample.instruction_count,
|
||||
decoded_event_count: sample.decoded_event_count,
|
||||
trade_event_count: sample.trade_event_count,
|
||||
program_ids_csv: sample.program_ids_csv,
|
||||
};
|
||||
}
|
||||
|
||||
fn from_lib_pool_pair_sample(
|
||||
sample: kb_lib::LocalDexCorpusPoolPairSampleDto,
|
||||
) -> Demo3LocalDexCorpusPoolPairSample {
|
||||
return Demo3LocalDexCorpusPoolPairSample {
|
||||
pool_id: sample.pool_id,
|
||||
pool_address: sample.pool_address,
|
||||
pair_id: sample.pair_id,
|
||||
dex_code: sample.dex_code,
|
||||
pair_symbol: sample.pair_symbol,
|
||||
base_mint: sample.base_mint,
|
||||
base_symbol: sample.base_symbol,
|
||||
quote_mint: sample.quote_mint,
|
||||
quote_symbol: sample.quote_symbol,
|
||||
decoded_event_count: sample.decoded_event_count,
|
||||
trade_event_count: sample.trade_event_count,
|
||||
pair_candle_count: sample.pair_candle_count,
|
||||
latest_signature: sample.latest_signature,
|
||||
};
|
||||
}
|
||||
|
||||
fn from_lib_decoded_event_sample(
|
||||
sample: kb_lib::LocalDexCorpusDecodedEventSampleDto,
|
||||
) -> Demo3LocalDexCorpusDecodedEventSample {
|
||||
return Demo3LocalDexCorpusDecodedEventSample {
|
||||
decoded_event_id: sample.decoded_event_id,
|
||||
transaction_id: sample.transaction_id,
|
||||
signature: sample.signature,
|
||||
slot: sample.slot,
|
||||
protocol_name: sample.protocol_name,
|
||||
program_id: sample.program_id,
|
||||
event_kind: sample.event_kind,
|
||||
pool_account: sample.pool_account,
|
||||
token_a_mint: sample.token_a_mint,
|
||||
token_b_mint: sample.token_b_mint,
|
||||
event_category: sample.event_category,
|
||||
event_lifecycle_kind: sample.event_lifecycle_kind,
|
||||
event_actionability: sample.event_actionability,
|
||||
trade_candidate: sample.trade_candidate,
|
||||
candle_candidate: sample.candle_candidate,
|
||||
};
|
||||
}
|
||||
|
||||
/// Request payload for on-chain DEX pair/pool discovery.
|
||||
#[derive(Clone, Debug, serde::Serialize, serde::Deserialize, TS)]
|
||||
#[ts(export, export_to = "../frontend/ts/bindings/Demo3OnchainDexDiscoveryRequest.ts")]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub(crate) struct Demo3OnchainDexDiscoveryRequest {
|
||||
/// Optional DEX code from the support matrix.
|
||||
pub dex_code: std::option::Option<std::string::String>,
|
||||
/// Optional Solana program id. When absent, dex_code must resolve to a verified program id.
|
||||
pub program_id: std::option::Option<std::string::String>,
|
||||
/// HTTP role used to query Solana RPC.
|
||||
pub http_role: std::string::String,
|
||||
/// Maximum number of signatures to inspect.
|
||||
pub signature_limit: u32,
|
||||
/// Maximum number of transactions to fetch from the signature list.
|
||||
pub transaction_limit: u32,
|
||||
/// Maximum number of candidate rows to return.
|
||||
pub candidate_limit: u32,
|
||||
}
|
||||
|
||||
/// Response payload returned by Demo3 on-chain DEX discovery.
|
||||
#[derive(Clone, Debug, serde::Serialize, TS)]
|
||||
#[ts(export, export_to = "../frontend/ts/bindings/Demo3OnchainDexDiscoveryPayload.ts")]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub(crate) struct Demo3OnchainDexDiscoveryPayload {
|
||||
/// HTTP role used by the request.
|
||||
pub http_role: std::string::String,
|
||||
/// Pretty JSON representation of the discovery result.
|
||||
pub result_json: std::string::String,
|
||||
/// Structured discovery result.
|
||||
pub result: Demo3OnchainDexDiscoveryResult,
|
||||
}
|
||||
|
||||
/// Structured on-chain DEX discovery result.
|
||||
#[derive(Clone, Debug, serde::Serialize, TS)]
|
||||
#[ts(export, export_to = "../frontend/ts/bindings/Demo3OnchainDexDiscoveryResult.ts")]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub(crate) struct Demo3OnchainDexDiscoveryResult {
|
||||
/// Normalized request used by kb_lib.
|
||||
pub request: Demo3OnchainDexDiscoveryRequest,
|
||||
/// DEX code resolved from the support matrix when available.
|
||||
pub resolved_dex_code: std::option::Option<std::string::String>,
|
||||
/// Program id scanned with getSignaturesForAddress.
|
||||
pub resolved_program_id: std::string::String,
|
||||
/// Number of signatures returned by Solana RPC.
|
||||
#[ts(type = "number")]
|
||||
pub fetched_signature_count: usize,
|
||||
/// Number of fetched transactions.
|
||||
#[ts(type = "number")]
|
||||
pub fetched_transaction_count: usize,
|
||||
/// Number of getTransaction calls returning null.
|
||||
#[ts(type = "number")]
|
||||
pub missing_transaction_count: usize,
|
||||
/// Number of failed transactions encountered.
|
||||
#[ts(type = "number")]
|
||||
pub failed_transaction_count: usize,
|
||||
/// Number of candidate rows returned.
|
||||
#[ts(type = "number")]
|
||||
pub candidate_count: usize,
|
||||
/// Candidate on-chain rows.
|
||||
pub candidates: std::vec::Vec<Demo3OnchainDexPairCandidate>,
|
||||
}
|
||||
|
||||
/// Candidate on-chain transaction/instruction for a DEX program id.
|
||||
#[derive(Clone, Debug, serde::Serialize, TS)]
|
||||
#[ts(export, export_to = "../frontend/ts/bindings/Demo3OnchainDexPairCandidate.ts")]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub(crate) struct Demo3OnchainDexPairCandidate {
|
||||
/// Transaction signature.
|
||||
pub signature: std::string::String,
|
||||
/// Slot when available.
|
||||
#[ts(type = "number | null")]
|
||||
pub slot: std::option::Option<u64>,
|
||||
/// Block time when available.
|
||||
#[ts(type = "number | null")]
|
||||
pub block_time: std::option::Option<i64>,
|
||||
/// Whether the transaction failed.
|
||||
pub failed: bool,
|
||||
/// Program id matched by the candidate.
|
||||
pub program_id: std::string::String,
|
||||
/// DEX code when known.
|
||||
pub dex_code: std::option::Option<std::string::String>,
|
||||
/// Candidate kind inferred from data/logs.
|
||||
pub candidate_kind: std::string::String,
|
||||
/// Extraction confidence.
|
||||
pub confidence: std::string::String,
|
||||
/// Top-level instruction index.
|
||||
#[ts(type = "number | null")]
|
||||
pub instruction_index: std::option::Option<i64>,
|
||||
/// Inner instruction index.
|
||||
#[ts(type = "number | null")]
|
||||
pub inner_instruction_index: std::option::Option<i64>,
|
||||
/// Instruction name inferred from data/logs.
|
||||
pub instruction_name: std::option::Option<std::string::String>,
|
||||
/// Candidate pool address.
|
||||
pub pool_address: std::option::Option<std::string::String>,
|
||||
/// Candidate token A mint.
|
||||
pub token_a_mint: std::option::Option<std::string::String>,
|
||||
/// Candidate token B mint.
|
||||
pub token_b_mint: std::option::Option<std::string::String>,
|
||||
/// Verified pool address when a DEX decoder or stable layout proves it.
|
||||
pub verified_pool_address: std::option::Option<std::string::String>,
|
||||
/// Token mints observed generically from transaction token balances.
|
||||
pub observed_token_mints: std::vec::Vec<std::string::String>,
|
||||
/// Token balance deltas observed through transaction metadata.
|
||||
pub token_balance_deltas: std::vec::Vec<Demo3OnchainDexTokenBalanceDelta>,
|
||||
/// Program-owned or writable accounts that may be pool/config/state accounts.
|
||||
pub candidate_pool_accounts: std::vec::Vec<Demo3OnchainDexCandidateAccount>,
|
||||
/// Token accounts that may be pool vaults.
|
||||
pub candidate_token_vault_accounts: std::vec::Vec<Demo3OnchainDexCandidateAccount>,
|
||||
/// Other candidate accounts attached to the matched instruction.
|
||||
pub candidate_program_accounts: std::vec::Vec<Demo3OnchainDexCandidateAccount>,
|
||||
/// Short account sample.
|
||||
pub account_samples: std::vec::Vec<std::string::String>,
|
||||
/// Short log sample.
|
||||
pub log_samples: std::vec::Vec<std::string::String>,
|
||||
/// Suggested next action.
|
||||
pub backfill_hint: std::string::String,
|
||||
}
|
||||
|
||||
/// Token-balance delta observed in one on-chain candidate transaction.
|
||||
#[derive(Clone, Debug, serde::Serialize, TS)]
|
||||
#[ts(
|
||||
export,
|
||||
export_to = "../frontend/ts/bindings/Demo3OnchainDexTokenBalanceDelta.ts"
|
||||
)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub(crate) struct Demo3OnchainDexTokenBalanceDelta {
|
||||
/// Token account index in the transaction message when available.
|
||||
#[ts(type = "number | null")]
|
||||
pub account_index: std::option::Option<i64>,
|
||||
/// Token account address resolved from the transaction account keys.
|
||||
pub account_address: std::option::Option<std::string::String>,
|
||||
/// SPL Token or Token-2022 mint address.
|
||||
pub mint: std::string::String,
|
||||
/// Token account owner when Solana RPC exposes it.
|
||||
pub owner: std::option::Option<std::string::String>,
|
||||
/// Token program id when Solana RPC exposes it.
|
||||
pub token_program: std::option::Option<std::string::String>,
|
||||
/// Raw token amount before the transaction.
|
||||
pub pre_amount_raw: std::option::Option<std::string::String>,
|
||||
/// Raw token amount after the transaction.
|
||||
pub post_amount_raw: std::option::Option<std::string::String>,
|
||||
/// Signed raw delta when calculable.
|
||||
pub delta_raw: std::option::Option<std::string::String>,
|
||||
}
|
||||
|
||||
/// Candidate account inferred from generic on-chain transaction evidence.
|
||||
#[derive(Clone, Debug, serde::Serialize, TS)]
|
||||
#[ts(export, export_to = "../frontend/ts/bindings/Demo3OnchainDexCandidateAccount.ts")]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub(crate) struct Demo3OnchainDexCandidateAccount {
|
||||
/// Account address.
|
||||
pub address: std::string::String,
|
||||
/// Account index in the transaction message when known.
|
||||
#[ts(type = "number | null")]
|
||||
pub account_index: std::option::Option<i64>,
|
||||
/// Whether the account is writable in the transaction message when known.
|
||||
pub writable: std::option::Option<bool>,
|
||||
/// Whether the account is a signer in the transaction message when known.
|
||||
pub signer: std::option::Option<bool>,
|
||||
/// Generic role inferred by Demo3.
|
||||
pub inferred_role: std::string::String,
|
||||
/// Confidence of the generic account inference.
|
||||
pub confidence: std::string::String,
|
||||
/// Short reason explaining why the account is listed.
|
||||
pub reason: std::string::String,
|
||||
}
|
||||
|
||||
/// Discovers candidate DEX pairs/pools directly from Solana RPC.
|
||||
#[tauri::command]
|
||||
pub(crate) async fn demo3_discover_onchain_dex_pairs(
|
||||
state: tauri::State<'_, crate::AppState>,
|
||||
request: Demo3OnchainDexDiscoveryRequest,
|
||||
) -> Result<Demo3OnchainDexDiscoveryPayload, std::string::String> {
|
||||
let http_pool = std::sync::Arc::new(state.http_pool.clone());
|
||||
let service = kb_lib::OnchainDexPairDiscoveryService::new(http_pool);
|
||||
let lib_request = to_lib_onchain_request(&request);
|
||||
let discover_result = service.discover(lib_request).await;
|
||||
let lib_result = match discover_result {
|
||||
Ok(result) => result,
|
||||
Err(error) => {
|
||||
return Err(format!("on-chain DEX discovery failed: {}", error));
|
||||
},
|
||||
};
|
||||
let ui_result = from_lib_onchain_result(lib_result);
|
||||
let result_json_result = serde_json::to_string_pretty(&ui_result);
|
||||
let result_json = match result_json_result {
|
||||
Ok(result_json) => result_json,
|
||||
Err(error) => {
|
||||
return Err(format!("cannot serialize on-chain DEX discovery result: {}", error));
|
||||
},
|
||||
};
|
||||
Ok(Demo3OnchainDexDiscoveryPayload {
|
||||
http_role: ui_result.request.http_role.clone(),
|
||||
result_json,
|
||||
result: ui_result,
|
||||
})
|
||||
}
|
||||
|
||||
fn to_lib_onchain_request(
|
||||
request: &Demo3OnchainDexDiscoveryRequest,
|
||||
) -> kb_lib::OnchainDexPairDiscoveryRequestDto {
|
||||
return kb_lib::OnchainDexPairDiscoveryRequestDto {
|
||||
dex_code: normalize_optional_text(request.dex_code.clone()),
|
||||
program_id: normalize_optional_text(request.program_id.clone()),
|
||||
http_role: request.http_role.trim().to_string(),
|
||||
signature_limit: request.signature_limit,
|
||||
transaction_limit: request.transaction_limit,
|
||||
candidate_limit: request.candidate_limit,
|
||||
};
|
||||
}
|
||||
|
||||
fn from_lib_onchain_result(
|
||||
result: kb_lib::OnchainDexPairDiscoveryResultDto,
|
||||
) -> Demo3OnchainDexDiscoveryResult {
|
||||
let mut candidates = std::vec::Vec::new();
|
||||
for candidate in result.candidates {
|
||||
candidates.push(from_lib_onchain_candidate(candidate));
|
||||
}
|
||||
return Demo3OnchainDexDiscoveryResult {
|
||||
request: Demo3OnchainDexDiscoveryRequest {
|
||||
dex_code: result.request.dex_code,
|
||||
program_id: result.request.program_id,
|
||||
http_role: result.request.http_role,
|
||||
signature_limit: result.request.signature_limit,
|
||||
transaction_limit: result.request.transaction_limit,
|
||||
candidate_limit: result.request.candidate_limit,
|
||||
},
|
||||
resolved_dex_code: result.resolved_dex_code,
|
||||
resolved_program_id: result.resolved_program_id,
|
||||
fetched_signature_count: result.fetched_signature_count,
|
||||
fetched_transaction_count: result.fetched_transaction_count,
|
||||
missing_transaction_count: result.missing_transaction_count,
|
||||
failed_transaction_count: result.failed_transaction_count,
|
||||
candidate_count: result.candidate_count,
|
||||
candidates,
|
||||
};
|
||||
}
|
||||
|
||||
fn from_lib_onchain_candidate(
|
||||
candidate: kb_lib::OnchainDexPairCandidateDto,
|
||||
) -> Demo3OnchainDexPairCandidate {
|
||||
return Demo3OnchainDexPairCandidate {
|
||||
signature: candidate.signature,
|
||||
slot: candidate.slot,
|
||||
block_time: candidate.block_time,
|
||||
failed: candidate.failed,
|
||||
program_id: candidate.program_id,
|
||||
dex_code: candidate.dex_code,
|
||||
candidate_kind: candidate.candidate_kind,
|
||||
confidence: candidate.confidence,
|
||||
instruction_index: candidate.instruction_index,
|
||||
inner_instruction_index: candidate.inner_instruction_index,
|
||||
instruction_name: candidate.instruction_name,
|
||||
pool_address: candidate.pool_address,
|
||||
token_a_mint: candidate.token_a_mint,
|
||||
token_b_mint: candidate.token_b_mint,
|
||||
verified_pool_address: candidate.verified_pool_address,
|
||||
observed_token_mints: candidate.observed_token_mints,
|
||||
token_balance_deltas: from_lib_onchain_token_balance_deltas(candidate.token_balance_deltas),
|
||||
candidate_pool_accounts: from_lib_onchain_candidate_accounts(
|
||||
candidate.candidate_pool_accounts,
|
||||
),
|
||||
candidate_token_vault_accounts: from_lib_onchain_candidate_accounts(
|
||||
candidate.candidate_token_vault_accounts,
|
||||
),
|
||||
candidate_program_accounts: from_lib_onchain_candidate_accounts(
|
||||
candidate.candidate_program_accounts,
|
||||
),
|
||||
account_samples: candidate.account_samples,
|
||||
log_samples: candidate.log_samples,
|
||||
backfill_hint: candidate.backfill_hint,
|
||||
};
|
||||
}
|
||||
|
||||
fn from_lib_onchain_token_balance_deltas(
|
||||
values: std::vec::Vec<kb_lib::OnchainDexTokenBalanceDeltaDto>,
|
||||
) -> std::vec::Vec<Demo3OnchainDexTokenBalanceDelta> {
|
||||
let mut mapped = std::vec::Vec::new();
|
||||
for value in values {
|
||||
mapped.push(Demo3OnchainDexTokenBalanceDelta {
|
||||
account_index: value.account_index,
|
||||
account_address: value.account_address,
|
||||
mint: value.mint,
|
||||
owner: value.owner,
|
||||
token_program: value.token_program,
|
||||
pre_amount_raw: value.pre_amount_raw,
|
||||
post_amount_raw: value.post_amount_raw,
|
||||
delta_raw: value.delta_raw,
|
||||
});
|
||||
}
|
||||
return mapped;
|
||||
}
|
||||
|
||||
fn from_lib_onchain_candidate_accounts(
|
||||
values: std::vec::Vec<kb_lib::OnchainDexCandidateAccountDto>,
|
||||
) -> std::vec::Vec<Demo3OnchainDexCandidateAccount> {
|
||||
let mut mapped = std::vec::Vec::new();
|
||||
for value in values {
|
||||
mapped.push(Demo3OnchainDexCandidateAccount {
|
||||
address: value.address,
|
||||
account_index: value.account_index,
|
||||
writable: value.writable,
|
||||
signer: value.signer,
|
||||
inferred_role: value.inferred_role,
|
||||
confidence: value.confidence,
|
||||
reason: value.reason,
|
||||
});
|
||||
}
|
||||
return mapped;
|
||||
}
|
||||
428
kb_demo_app/src/demo3old.rs
Normal file
428
kb_demo_app/src/demo3old.rs
Normal file
@@ -0,0 +1,428 @@
|
||||
// file: kb_demo_app/src/demo3old.rs
|
||||
|
||||
//! Tauri commands for Demo3old local DEX corpus search.
|
||||
//!
|
||||
//! Demo3old is intentionally a thin UI-facing wrapper around `kb_lib` local
|
||||
//! corpus search services. It does not contain DEX decoding or protocol logic.
|
||||
|
||||
use tauri::Manager;
|
||||
use ts_rs::TS;
|
||||
|
||||
/// Request payload for a local DEX corpus search.
|
||||
#[derive(Clone, Debug, serde::Serialize, serde::Deserialize, TS)]
|
||||
#[ts(
|
||||
export,
|
||||
export_to = "../frontend/ts/bindings/Demo3oldLocalDexCorpusSearchRequest.ts"
|
||||
)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub(crate) struct Demo3oldLocalDexCorpusSearchRequest {
|
||||
/// Optional DEX code or decoded protocol name.
|
||||
pub dex_code: std::option::Option<std::string::String>,
|
||||
/// Optional Solana program id.
|
||||
pub program_id: std::option::Option<std::string::String>,
|
||||
/// Optional local pair id.
|
||||
#[ts(type = "number | null")]
|
||||
pub pair_id: std::option::Option<i64>,
|
||||
/// Optional pool account/address.
|
||||
pub pool_address: std::option::Option<std::string::String>,
|
||||
/// Optional token mint to match as base, quote or decoded mint.
|
||||
pub token_mint: std::option::Option<std::string::String>,
|
||||
/// Optional transaction signature.
|
||||
pub signature: std::option::Option<std::string::String>,
|
||||
/// Maximum number of rows to return per sample category.
|
||||
pub limit: u32,
|
||||
}
|
||||
|
||||
/// Response payload returned by Demo3old local DEX corpus search.
|
||||
#[derive(Clone, Debug, serde::Serialize, TS)]
|
||||
#[ts(
|
||||
export,
|
||||
export_to = "../frontend/ts/bindings/Demo3oldLocalDexCorpusSearchPayload.ts"
|
||||
)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub(crate) struct Demo3oldLocalDexCorpusSearchPayload {
|
||||
/// Open database URL.
|
||||
pub database_url: std::string::String,
|
||||
/// Pretty JSON representation of the search result.
|
||||
pub result_json: std::string::String,
|
||||
/// Structured local DEX corpus search result.
|
||||
pub result: Demo3oldLocalDexCorpusSearchResult,
|
||||
}
|
||||
|
||||
/// Structured local DEX corpus search result.
|
||||
#[derive(Clone, Debug, serde::Serialize, TS)]
|
||||
#[ts(
|
||||
export,
|
||||
export_to = "../frontend/ts/bindings/Demo3oldLocalDexCorpusSearchResult.ts"
|
||||
)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub(crate) struct Demo3oldLocalDexCorpusSearchResult {
|
||||
/// Normalized search request applied by the backend service.
|
||||
pub request: Demo3oldLocalDexCorpusSearchRequest,
|
||||
/// Aggregate counts for the matching local data.
|
||||
pub summary: Demo3oldLocalDexCorpusSearchSummary,
|
||||
/// Matching transaction samples.
|
||||
pub transaction_samples: std::vec::Vec<Demo3oldLocalDexCorpusTransactionSample>,
|
||||
/// Matching pool/pair samples.
|
||||
pub pool_pair_samples: std::vec::Vec<Demo3oldLocalDexCorpusPoolPairSample>,
|
||||
/// Matching decoded event samples.
|
||||
pub decoded_event_samples: std::vec::Vec<Demo3oldLocalDexCorpusDecodedEventSample>,
|
||||
}
|
||||
|
||||
/// Aggregate counts for one local DEX corpus search.
|
||||
#[derive(Clone, Debug, serde::Serialize, TS)]
|
||||
#[ts(
|
||||
export,
|
||||
export_to = "../frontend/ts/bindings/Demo3oldLocalDexCorpusSearchSummary.ts"
|
||||
)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub(crate) struct Demo3oldLocalDexCorpusSearchSummary {
|
||||
/// Number of distinct matching transactions.
|
||||
#[ts(type = "number")]
|
||||
pub transaction_count: i64,
|
||||
/// Number of distinct matching instructions.
|
||||
#[ts(type = "number")]
|
||||
pub instruction_count: i64,
|
||||
/// Number of distinct matching decoded DEX events.
|
||||
#[ts(type = "number")]
|
||||
pub decoded_event_count: i64,
|
||||
/// Number of distinct matching pools.
|
||||
#[ts(type = "number")]
|
||||
pub pool_count: i64,
|
||||
/// Number of distinct matching pairs.
|
||||
#[ts(type = "number")]
|
||||
pub pair_count: i64,
|
||||
/// Number of distinct matching trade events.
|
||||
#[ts(type = "number")]
|
||||
pub trade_event_count: i64,
|
||||
/// Number of distinct matching candle rows.
|
||||
#[ts(type = "number")]
|
||||
pub pair_candle_count: i64,
|
||||
/// Number of distinct matching protocol candidate rows.
|
||||
#[ts(type = "number")]
|
||||
pub protocol_candidate_count: i64,
|
||||
}
|
||||
|
||||
/// Matching transaction sample for corpus discovery.
|
||||
#[derive(Clone, Debug, serde::Serialize, TS)]
|
||||
#[ts(
|
||||
export,
|
||||
export_to = "../frontend/ts/bindings/Demo3oldLocalDexCorpusTransactionSample.ts"
|
||||
)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub(crate) struct Demo3oldLocalDexCorpusTransactionSample {
|
||||
/// Transaction id.
|
||||
#[ts(type = "number")]
|
||||
pub transaction_id: i64,
|
||||
/// Transaction signature.
|
||||
pub signature: std::string::String,
|
||||
/// Optional Solana slot.
|
||||
#[ts(type = "number | null")]
|
||||
pub slot: std::option::Option<i64>,
|
||||
/// Whether the transaction has a non-null error payload.
|
||||
pub failed: bool,
|
||||
/// Number of persisted instructions for the transaction.
|
||||
#[ts(type = "number")]
|
||||
pub instruction_count: i64,
|
||||
/// Number of persisted decoded DEX events for the transaction.
|
||||
#[ts(type = "number")]
|
||||
pub decoded_event_count: i64,
|
||||
/// Number of persisted trade events for the transaction.
|
||||
#[ts(type = "number")]
|
||||
pub trade_event_count: i64,
|
||||
/// Comma-separated distinct program ids seen in the transaction.
|
||||
pub program_ids_csv: std::string::String,
|
||||
}
|
||||
|
||||
/// Matching pool/pair sample for corpus discovery.
|
||||
#[derive(Clone, Debug, serde::Serialize, TS)]
|
||||
#[ts(
|
||||
export,
|
||||
export_to = "../frontend/ts/bindings/Demo3oldLocalDexCorpusPoolPairSample.ts"
|
||||
)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub(crate) struct Demo3oldLocalDexCorpusPoolPairSample {
|
||||
/// Optional pool id.
|
||||
#[ts(type = "number | null")]
|
||||
pub pool_id: std::option::Option<i64>,
|
||||
/// Optional pool address.
|
||||
pub pool_address: std::option::Option<std::string::String>,
|
||||
/// Optional pair id.
|
||||
#[ts(type = "number | null")]
|
||||
pub pair_id: std::option::Option<i64>,
|
||||
/// Optional DEX code.
|
||||
pub dex_code: std::option::Option<std::string::String>,
|
||||
/// Optional pair symbol.
|
||||
pub pair_symbol: std::option::Option<std::string::String>,
|
||||
/// Optional base token mint.
|
||||
pub base_mint: std::option::Option<std::string::String>,
|
||||
/// Optional base token symbol.
|
||||
pub base_symbol: std::option::Option<std::string::String>,
|
||||
/// Optional quote token mint.
|
||||
pub quote_mint: std::option::Option<std::string::String>,
|
||||
/// Optional quote token symbol.
|
||||
pub quote_symbol: std::option::Option<std::string::String>,
|
||||
/// Number of decoded events attached to the pool.
|
||||
#[ts(type = "number")]
|
||||
pub decoded_event_count: i64,
|
||||
/// Number of trade events attached to the pair.
|
||||
#[ts(type = "number")]
|
||||
pub trade_event_count: i64,
|
||||
/// Number of candle rows attached to the pair.
|
||||
#[ts(type = "number")]
|
||||
pub pair_candle_count: i64,
|
||||
/// Latest known founding or activity signature for the pool/pair.
|
||||
pub latest_signature: std::option::Option<std::string::String>,
|
||||
}
|
||||
|
||||
/// Matching decoded event sample for corpus discovery.
|
||||
#[derive(Clone, Debug, serde::Serialize, TS)]
|
||||
#[ts(
|
||||
export,
|
||||
export_to = "../frontend/ts/bindings/Demo3oldLocalDexCorpusDecodedEventSample.ts"
|
||||
)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub(crate) struct Demo3oldLocalDexCorpusDecodedEventSample {
|
||||
/// Decoded event id.
|
||||
#[ts(type = "number")]
|
||||
pub decoded_event_id: i64,
|
||||
/// Transaction id.
|
||||
#[ts(type = "number")]
|
||||
pub transaction_id: i64,
|
||||
/// Transaction signature.
|
||||
pub signature: std::string::String,
|
||||
/// Optional Solana slot.
|
||||
#[ts(type = "number | null")]
|
||||
pub slot: std::option::Option<i64>,
|
||||
/// Protocol name stored on the decoded event.
|
||||
pub protocol_name: std::string::String,
|
||||
/// Program id stored on the decoded event.
|
||||
pub program_id: std::string::String,
|
||||
/// Event kind.
|
||||
pub event_kind: std::string::String,
|
||||
/// Optional pool account.
|
||||
pub pool_account: std::option::Option<std::string::String>,
|
||||
/// Optional token A mint.
|
||||
pub token_a_mint: std::option::Option<std::string::String>,
|
||||
/// Optional token B mint.
|
||||
pub token_b_mint: std::option::Option<std::string::String>,
|
||||
/// Decoded event category extracted from payload JSON.
|
||||
pub event_category: std::option::Option<std::string::String>,
|
||||
/// Decoded event lifecycle kind extracted from payload JSON.
|
||||
pub event_lifecycle_kind: std::option::Option<std::string::String>,
|
||||
/// Decoded event actionability extracted from payload JSON.
|
||||
pub event_actionability: std::option::Option<std::string::String>,
|
||||
/// Whether the decoded event is a trade candidate.
|
||||
pub trade_candidate: bool,
|
||||
/// Whether the decoded event is a candle candidate.
|
||||
pub candle_candidate: bool,
|
||||
}
|
||||
|
||||
/// Opens the `Demo3old` local DEX corpus search window.
|
||||
#[tauri::command]
|
||||
pub(crate) fn open_demo3old_window(
|
||||
app_handle: tauri::AppHandle,
|
||||
) -> Result<(), std::string::String> {
|
||||
let existing_window_option = app_handle.get_webview_window("demo3old");
|
||||
|
||||
let demo_window = match existing_window_option {
|
||||
Some(demo_window) => demo_window,
|
||||
None => {
|
||||
let builder = tauri::WebviewWindowBuilder::new(
|
||||
&app_handle,
|
||||
"demo3old",
|
||||
tauri::WebviewUrl::App("demo3old.html".into()),
|
||||
)
|
||||
.title("Demo3old Local DEX Corpus")
|
||||
.inner_size(1480.0, 920.0)
|
||||
.min_inner_size(1100.0, 720.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 demo3old window: {error:?}"));
|
||||
},
|
||||
}
|
||||
},
|
||||
};
|
||||
let show_result = demo_window.show();
|
||||
if let Err(error) = show_result {
|
||||
return Err(format!("cannot show demo3old window: {error:?}"));
|
||||
}
|
||||
let focus_result = demo_window.set_focus();
|
||||
if let Err(error) = focus_result {
|
||||
return Err(format!("cannot focus demo3old window: {error:?}"));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Searches local DEX corpus candidates from the persisted SQLite pipeline.
|
||||
#[tauri::command]
|
||||
pub(crate) async fn demo3old_search_local_dex_corpus(
|
||||
state: tauri::State<'_, crate::AppState>,
|
||||
request: Demo3oldLocalDexCorpusSearchRequest,
|
||||
) -> Result<Demo3oldLocalDexCorpusSearchPayload, std::string::String> {
|
||||
let database = state.database.clone();
|
||||
let service = kb_lib::LocalDexCorpusSearchService::new(database.clone());
|
||||
let search_result = service.search(to_lib_search_request(&request)).await;
|
||||
let lib_result = match search_result {
|
||||
Ok(result) => result,
|
||||
Err(error) => {
|
||||
return Err(format!("local DEX corpus search failed: {}", error));
|
||||
},
|
||||
};
|
||||
let ui_result = from_lib_search_result(lib_result);
|
||||
let result_json_result = serde_json::to_string_pretty(&ui_result);
|
||||
let result_json = match result_json_result {
|
||||
Ok(result_json) => result_json,
|
||||
Err(error) => {
|
||||
return Err(format!("cannot serialize local DEX corpus search result: {}", error));
|
||||
},
|
||||
};
|
||||
Ok(Demo3oldLocalDexCorpusSearchPayload {
|
||||
database_url: database.database_url().to_string(),
|
||||
result_json,
|
||||
result: ui_result,
|
||||
})
|
||||
}
|
||||
|
||||
fn to_lib_search_request(
|
||||
request: &Demo3oldLocalDexCorpusSearchRequest,
|
||||
) -> kb_lib::LocalDexCorpusSearchRequestDto {
|
||||
return kb_lib::LocalDexCorpusSearchRequestDto {
|
||||
dex_code: normalize_optional_text(request.dex_code.clone()),
|
||||
program_id: normalize_optional_text(request.program_id.clone()),
|
||||
pair_id: request.pair_id,
|
||||
pool_address: normalize_optional_text(request.pool_address.clone()),
|
||||
token_mint: normalize_optional_text(request.token_mint.clone()),
|
||||
signature: normalize_optional_text(request.signature.clone()),
|
||||
limit: request.limit,
|
||||
};
|
||||
}
|
||||
|
||||
fn normalize_optional_text(
|
||||
value: std::option::Option<std::string::String>,
|
||||
) -> std::option::Option<std::string::String> {
|
||||
let value = match value {
|
||||
Some(value) => value.trim().to_string(),
|
||||
None => return None,
|
||||
};
|
||||
if value.is_empty() {
|
||||
return None;
|
||||
}
|
||||
return Some(value);
|
||||
}
|
||||
|
||||
fn from_lib_search_result(
|
||||
result: kb_lib::LocalDexCorpusSearchResultDto,
|
||||
) -> Demo3oldLocalDexCorpusSearchResult {
|
||||
let mut transaction_samples = std::vec::Vec::new();
|
||||
for sample in result.transaction_samples {
|
||||
transaction_samples.push(from_lib_transaction_sample(sample));
|
||||
}
|
||||
let mut pool_pair_samples = std::vec::Vec::new();
|
||||
for sample in result.pool_pair_samples {
|
||||
pool_pair_samples.push(from_lib_pool_pair_sample(sample));
|
||||
}
|
||||
let mut decoded_event_samples = std::vec::Vec::new();
|
||||
for sample in result.decoded_event_samples {
|
||||
decoded_event_samples.push(from_lib_decoded_event_sample(sample));
|
||||
}
|
||||
return Demo3oldLocalDexCorpusSearchResult {
|
||||
request: from_lib_request(result.request),
|
||||
summary: from_lib_summary(result.summary),
|
||||
transaction_samples,
|
||||
pool_pair_samples,
|
||||
decoded_event_samples,
|
||||
};
|
||||
}
|
||||
|
||||
fn from_lib_request(
|
||||
request: kb_lib::LocalDexCorpusSearchRequestDto,
|
||||
) -> Demo3oldLocalDexCorpusSearchRequest {
|
||||
return Demo3oldLocalDexCorpusSearchRequest {
|
||||
dex_code: request.dex_code,
|
||||
program_id: request.program_id,
|
||||
pair_id: request.pair_id,
|
||||
pool_address: request.pool_address,
|
||||
token_mint: request.token_mint,
|
||||
signature: request.signature,
|
||||
limit: request.limit,
|
||||
};
|
||||
}
|
||||
|
||||
fn from_lib_summary(
|
||||
summary: kb_lib::LocalDexCorpusSearchSummaryDto,
|
||||
) -> Demo3oldLocalDexCorpusSearchSummary {
|
||||
return Demo3oldLocalDexCorpusSearchSummary {
|
||||
transaction_count: summary.transaction_count,
|
||||
instruction_count: summary.instruction_count,
|
||||
decoded_event_count: summary.decoded_event_count,
|
||||
pool_count: summary.pool_count,
|
||||
pair_count: summary.pair_count,
|
||||
trade_event_count: summary.trade_event_count,
|
||||
pair_candle_count: summary.pair_candle_count,
|
||||
protocol_candidate_count: summary.protocol_candidate_count,
|
||||
};
|
||||
}
|
||||
|
||||
fn from_lib_transaction_sample(
|
||||
sample: kb_lib::LocalDexCorpusTransactionSampleDto,
|
||||
) -> Demo3oldLocalDexCorpusTransactionSample {
|
||||
return Demo3oldLocalDexCorpusTransactionSample {
|
||||
transaction_id: sample.transaction_id,
|
||||
signature: sample.signature,
|
||||
slot: sample.slot,
|
||||
failed: sample.failed,
|
||||
instruction_count: sample.instruction_count,
|
||||
decoded_event_count: sample.decoded_event_count,
|
||||
trade_event_count: sample.trade_event_count,
|
||||
program_ids_csv: sample.program_ids_csv,
|
||||
};
|
||||
}
|
||||
|
||||
fn from_lib_pool_pair_sample(
|
||||
sample: kb_lib::LocalDexCorpusPoolPairSampleDto,
|
||||
) -> Demo3oldLocalDexCorpusPoolPairSample {
|
||||
return Demo3oldLocalDexCorpusPoolPairSample {
|
||||
pool_id: sample.pool_id,
|
||||
pool_address: sample.pool_address,
|
||||
pair_id: sample.pair_id,
|
||||
dex_code: sample.dex_code,
|
||||
pair_symbol: sample.pair_symbol,
|
||||
base_mint: sample.base_mint,
|
||||
base_symbol: sample.base_symbol,
|
||||
quote_mint: sample.quote_mint,
|
||||
quote_symbol: sample.quote_symbol,
|
||||
decoded_event_count: sample.decoded_event_count,
|
||||
trade_event_count: sample.trade_event_count,
|
||||
pair_candle_count: sample.pair_candle_count,
|
||||
latest_signature: sample.latest_signature,
|
||||
};
|
||||
}
|
||||
|
||||
fn from_lib_decoded_event_sample(
|
||||
sample: kb_lib::LocalDexCorpusDecodedEventSampleDto,
|
||||
) -> Demo3oldLocalDexCorpusDecodedEventSample {
|
||||
return Demo3oldLocalDexCorpusDecodedEventSample {
|
||||
decoded_event_id: sample.decoded_event_id,
|
||||
transaction_id: sample.transaction_id,
|
||||
signature: sample.signature,
|
||||
slot: sample.slot,
|
||||
protocol_name: sample.protocol_name,
|
||||
program_id: sample.program_id,
|
||||
event_kind: sample.event_kind,
|
||||
pool_account: sample.pool_account,
|
||||
token_a_mint: sample.token_a_mint,
|
||||
token_b_mint: sample.token_b_mint,
|
||||
event_category: sample.event_category,
|
||||
event_lifecycle_kind: sample.event_lifecycle_kind,
|
||||
event_actionability: sample.event_actionability,
|
||||
trade_candidate: sample.trade_candidate,
|
||||
candle_candidate: sample.candle_candidate,
|
||||
};
|
||||
}
|
||||
@@ -1109,6 +1109,20 @@ pub(crate) struct DemoPipeline2BackfillPoolRequest {
|
||||
pub pool_signature_limit: u32,
|
||||
}
|
||||
|
||||
/// Request payload for single-signature backfill.
|
||||
#[derive(Clone, Debug, serde::Deserialize, TS)]
|
||||
#[ts(
|
||||
export,
|
||||
export_to = "../frontend/ts/bindings/DemoPipeline2BackfillSignatureRequest.ts"
|
||||
)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub(crate) struct DemoPipeline2BackfillSignatureRequest {
|
||||
/// Transaction signature to resolve and replay.
|
||||
pub signature: std::string::String,
|
||||
/// Optional HTTP role.
|
||||
pub http_role: std::option::Option<std::string::String>,
|
||||
}
|
||||
|
||||
/// Shared backfill response payload.
|
||||
#[derive(Clone, Debug, serde::Serialize, TS)]
|
||||
#[ts(export, export_to = "../frontend/ts/bindings/DemoPipeline2BackfillPayload.ts")]
|
||||
@@ -1116,7 +1130,7 @@ pub(crate) struct DemoPipeline2BackfillPoolRequest {
|
||||
pub(crate) struct DemoPipeline2BackfillPayload {
|
||||
/// Object key used by the backfill.
|
||||
pub object_key: std::string::String,
|
||||
/// Mode: `tokenMint` or `poolAddress`.
|
||||
/// Mode: `tokenMint`, `poolAddress` or `signature`.
|
||||
pub mode: std::string::String,
|
||||
/// HTTP role used.
|
||||
pub http_role: std::string::String,
|
||||
@@ -1265,7 +1279,7 @@ pub(crate) async fn demo_pipeline2_validate_local_pipeline(
|
||||
let service = kb_lib::LocalPipelineValidationService::new(database.clone());
|
||||
let profile_code = match request {
|
||||
Some(request) => request.profile_code,
|
||||
None => "0.7.39_dex_first_effective_swap_surfaces".to_string(),
|
||||
None => "0.7.40_raydium_effective_surfaces".to_string(),
|
||||
};
|
||||
let run_result = match profile_code.as_str() {
|
||||
"0.7.27" | "0.7.27_dexes_non_regression" => {
|
||||
@@ -1304,7 +1318,12 @@ pub(crate) async fn demo_pipeline2_validate_local_pipeline(
|
||||
"0.7.38" | "0.7.38_token_metadata_gap_prioritization" => {
|
||||
service.validate_v0_7_38_current_database().await
|
||||
},
|
||||
"0.7.39" | "0.7.39_dex_first_effective_swap_surfaces" | "0.7.39_launch_surface_origin_baseline" => {
|
||||
"0.7.39"
|
||||
| "0.7.39_dex_first_effective_swap_surfaces"
|
||||
| "0.7.39_launch_surface_origin_baseline" => {
|
||||
service.validate_v0_7_39_current_database().await
|
||||
},
|
||||
"0.7.40" | "0.7.40_raydium_effective_surfaces" => {
|
||||
service.validate_v0_7_39_current_database().await
|
||||
},
|
||||
other => Err(kb_lib::Error::InvalidState(format!(
|
||||
@@ -1495,6 +1514,54 @@ pub(crate) async fn demo_pipeline2_backfill_pool_address(
|
||||
})
|
||||
}
|
||||
|
||||
/// Runs a targeted single-signature backfill then returns the refreshed catalog.
|
||||
#[tauri::command]
|
||||
pub(crate) async fn demo_pipeline2_backfill_signature(
|
||||
state: tauri::State<'_, crate::AppState>,
|
||||
request: DemoPipeline2BackfillSignatureRequest,
|
||||
) -> Result<DemoPipeline2BackfillPayload, std::string::String> {
|
||||
let signature = request.signature.trim().to_string();
|
||||
if signature.is_empty() {
|
||||
return Err("signature must not be empty".to_string());
|
||||
}
|
||||
let http_role = demo_pipeline2_normalize_http_role(request.http_role);
|
||||
let database = state.database.clone();
|
||||
let http_pool = std::sync::Arc::new(state.http_pool.clone());
|
||||
let service = kb_lib::TokenBackfillService::new(http_pool, database.clone(), http_role.clone());
|
||||
let result = service.backfill_signature(signature.as_str()).await;
|
||||
let backfill = match result {
|
||||
Ok(backfill) => backfill,
|
||||
Err(error) => {
|
||||
return Err(format!(
|
||||
"cannot backfill signature '{}' with role '{}': {}",
|
||||
signature, http_role, error
|
||||
));
|
||||
},
|
||||
};
|
||||
let summary_json_result = serde_json::to_string_pretty(&backfill);
|
||||
let summary_json = match summary_json_result {
|
||||
Ok(summary_json) => summary_json,
|
||||
Err(error) => {
|
||||
return Err(format!(
|
||||
"cannot serialize signature backfill result for '{}': {}",
|
||||
signature, error
|
||||
));
|
||||
},
|
||||
};
|
||||
let catalog_result = demo_pipeline2_build_catalog(database).await;
|
||||
let catalog = match catalog_result {
|
||||
Ok(catalog) => catalog,
|
||||
Err(error) => return Err(error),
|
||||
};
|
||||
Ok(DemoPipeline2BackfillPayload {
|
||||
object_key: signature,
|
||||
mode: "signature".to_string(),
|
||||
http_role,
|
||||
summary_json,
|
||||
catalog,
|
||||
})
|
||||
}
|
||||
|
||||
/// Loads candles for one pair and one timeframe.
|
||||
#[tauri::command]
|
||||
pub(crate) async fn demo_pipeline2_get_pair_candles(
|
||||
|
||||
@@ -9,6 +9,8 @@
|
||||
#![deny(unreachable_pub)]
|
||||
#![warn(missing_docs)]
|
||||
|
||||
mod demo3;
|
||||
mod demo3old;
|
||||
mod demo_http;
|
||||
mod demo_pipeline;
|
||||
mod demo_pipeline2;
|
||||
@@ -121,6 +123,11 @@ pub async fn run() -> Result<(), kb_lib::Error> {
|
||||
tauri_builder = tauri_builder.invoke_handler(tauri::generate_handler![
|
||||
start_ws_clients,
|
||||
stop_ws_clients,
|
||||
crate::demo3::open_demo3_window,
|
||||
crate::demo3::demo3_search_local_dex_corpus,
|
||||
crate::demo3::demo3_discover_onchain_dex_pairs,
|
||||
crate::demo3old::open_demo3old_window,
|
||||
crate::demo3old::demo3old_search_local_dex_corpus,
|
||||
crate::demo_ws::open_demo_ws_window,
|
||||
crate::demo_ws::demo_ws_list_endpoints,
|
||||
crate::demo_ws::demo_ws_get_status,
|
||||
@@ -149,6 +156,7 @@ pub async fn run() -> Result<(), kb_lib::Error> {
|
||||
crate::demo_pipeline2::demo_pipeline2_get_catalog,
|
||||
crate::demo_pipeline2::demo_pipeline2_backfill_token_mint,
|
||||
crate::demo_pipeline2::demo_pipeline2_backfill_pool_address,
|
||||
crate::demo_pipeline2::demo_pipeline2_backfill_signature,
|
||||
crate::demo_pipeline2::demo_pipeline2_get_pair_candles,
|
||||
crate::demo_pipeline2::demo_pipeline2_replay_local_pipeline,
|
||||
crate::demo_pipeline2::demo_pipeline2_diagnose_local_pipeline,
|
||||
|
||||
Reference in New Issue
Block a user