0.7.29
This commit is contained in:
@@ -199,7 +199,7 @@ fn parse_accounts_json(accounts_json: &str) -> std::vec::Vec<std::string::String
|
||||
accounts.push(text.to_string());
|
||||
continue;
|
||||
}
|
||||
if let Some(pubkey) = item.get("pubkey").and_then(|value| value.as_str()) {
|
||||
if let Some(pubkey) = item.get("pubkey").and_then(|value| return value.as_str()) {
|
||||
accounts.push(pubkey.to_string());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -177,12 +177,12 @@ fn build_program_instruction_discriminator_summaries(
|
||||
});
|
||||
}
|
||||
summaries.sort_by(|left, right| {
|
||||
right
|
||||
return right
|
||||
.undecoded_occurrence_count
|
||||
.cmp(&left.undecoded_occurrence_count)
|
||||
.then(right.transaction_count.cmp(&left.transaction_count))
|
||||
.then(right.occurrence_count.cmp(&left.occurrence_count))
|
||||
.then(right.latest_instruction_id.cmp(&left.latest_instruction_id))
|
||||
.then(right.latest_instruction_id.cmp(&left.latest_instruction_id));
|
||||
});
|
||||
return Ok(summaries);
|
||||
}
|
||||
|
||||
@@ -26,178 +26,17 @@ pub(crate) struct DexCatalogItem {
|
||||
pub(crate) fn dex_catalog_item_by_code(
|
||||
code: &str,
|
||||
) -> std::option::Option<crate::dex_catalog::DexCatalogItem> {
|
||||
match code {
|
||||
"raydium" => {
|
||||
return Some(crate::dex_catalog::DexCatalogItem {
|
||||
code: "raydium",
|
||||
name: "Raydium AMM v4",
|
||||
program_id: Some(crate::RAYDIUM_AMM_V4_PROGRAM_ID),
|
||||
router_program_id: None,
|
||||
is_enabled: true,
|
||||
});
|
||||
},
|
||||
"raydium_cpmm" => {
|
||||
return Some(crate::dex_catalog::DexCatalogItem {
|
||||
code: "raydium_cpmm",
|
||||
name: "Raydium CPMM",
|
||||
program_id: Some(crate::RAYDIUM_CPMM_PROGRAM_ID),
|
||||
router_program_id: None,
|
||||
is_enabled: true,
|
||||
});
|
||||
},
|
||||
"raydium_clmm" => {
|
||||
return Some(crate::dex_catalog::DexCatalogItem {
|
||||
code: "raydium_clmm",
|
||||
name: "Raydium CLMM",
|
||||
program_id: Some(crate::RAYDIUM_CLMM_PROGRAM_ID),
|
||||
router_program_id: None,
|
||||
is_enabled: true,
|
||||
});
|
||||
},
|
||||
"pump_fun" => {
|
||||
return Some(crate::dex_catalog::DexCatalogItem {
|
||||
code: "pump_fun",
|
||||
name: "Pump.fun",
|
||||
program_id: Some(crate::PUMP_FUN_PROGRAM_ID),
|
||||
router_program_id: None,
|
||||
is_enabled: true,
|
||||
});
|
||||
},
|
||||
"pump_swap" => {
|
||||
return Some(crate::dex_catalog::DexCatalogItem {
|
||||
code: "pump_swap",
|
||||
name: "PumpSwap",
|
||||
program_id: Some(crate::PUMP_SWAP_PROGRAM_ID),
|
||||
router_program_id: None,
|
||||
is_enabled: true,
|
||||
});
|
||||
},
|
||||
"meteora_dbc" => {
|
||||
return Some(crate::dex_catalog::DexCatalogItem {
|
||||
code: "meteora_dbc",
|
||||
name: "Meteora DBC",
|
||||
program_id: Some(crate::METEORA_DBC_PROGRAM_ID),
|
||||
router_program_id: None,
|
||||
is_enabled: true,
|
||||
});
|
||||
},
|
||||
"meteora_dlmm" => {
|
||||
return Some(crate::dex_catalog::DexCatalogItem {
|
||||
code: "meteora_dlmm",
|
||||
name: "Meteora DLMM",
|
||||
program_id: Some(crate::METEORA_DLMM_PROGRAM_ID),
|
||||
router_program_id: None,
|
||||
is_enabled: true,
|
||||
});
|
||||
},
|
||||
"meteora_damm_v1" => {
|
||||
return Some(crate::dex_catalog::DexCatalogItem {
|
||||
code: "meteora_damm_v1",
|
||||
name: "Meteora DAMM v1",
|
||||
program_id: Some(crate::METEORA_DAMM_V1_PROGRAM_ID),
|
||||
router_program_id: None,
|
||||
is_enabled: true,
|
||||
});
|
||||
},
|
||||
"meteora_damm_v2" => {
|
||||
return Some(crate::dex_catalog::DexCatalogItem {
|
||||
code: "meteora_damm_v2",
|
||||
name: "Meteora DAMM v2",
|
||||
program_id: Some(crate::METEORA_DAMM_V2_PROGRAM_ID),
|
||||
router_program_id: None,
|
||||
is_enabled: true,
|
||||
});
|
||||
},
|
||||
"orca_whirlpools" => {
|
||||
return Some(crate::dex_catalog::DexCatalogItem {
|
||||
code: "orca_whirlpools",
|
||||
name: "Orca Whirlpools",
|
||||
program_id: Some(crate::ORCA_WHIRLPOOLS_PROGRAM_ID),
|
||||
router_program_id: None,
|
||||
is_enabled: true,
|
||||
});
|
||||
},
|
||||
"fluxbeam" => {
|
||||
return Some(crate::dex_catalog::DexCatalogItem {
|
||||
code: "fluxbeam",
|
||||
name: "FluxBeam",
|
||||
program_id: Some(crate::FLUXBEAM_PROGRAM_ID),
|
||||
router_program_id: None,
|
||||
is_enabled: true,
|
||||
});
|
||||
},
|
||||
"dexlab" => {
|
||||
return Some(crate::dex_catalog::DexCatalogItem {
|
||||
code: "dexlab",
|
||||
name: "DexLab Swap/Pool",
|
||||
program_id: Some(crate::DEXLAB_PROGRAM_ID),
|
||||
router_program_id: None,
|
||||
is_enabled: true,
|
||||
});
|
||||
},
|
||||
// Planned launch/swap surfaces.
|
||||
//
|
||||
// These entries are intentionally present before decoder support so that
|
||||
// the roadmap can evolve without duplicating DEX metadata later.
|
||||
//
|
||||
// Program ids should be filled only after validation against live
|
||||
// transactions and official or otherwise trustworthy references.
|
||||
"raydium_launchlab" => {
|
||||
return Some(crate::dex_catalog::DexCatalogItem {
|
||||
code: "raydium_launchlab",
|
||||
name: "Raydium LaunchLab",
|
||||
program_id: None,
|
||||
router_program_id: None,
|
||||
is_enabled: false,
|
||||
});
|
||||
},
|
||||
"letsbonk" => {
|
||||
return Some(crate::dex_catalog::DexCatalogItem {
|
||||
code: "letsbonk",
|
||||
name: "LetsBonk / Bonk.fun",
|
||||
program_id: None,
|
||||
router_program_id: None,
|
||||
is_enabled: false,
|
||||
});
|
||||
},
|
||||
"boop_fun" => {
|
||||
return Some(crate::dex_catalog::DexCatalogItem {
|
||||
code: "boop_fun",
|
||||
name: "Boop.fun",
|
||||
program_id: None,
|
||||
router_program_id: None,
|
||||
is_enabled: false,
|
||||
});
|
||||
},
|
||||
"moonshot" => {
|
||||
return Some(crate::dex_catalog::DexCatalogItem {
|
||||
code: "moonshot",
|
||||
name: "Moonshot",
|
||||
program_id: None,
|
||||
router_program_id: None,
|
||||
is_enabled: false,
|
||||
});
|
||||
},
|
||||
"believe" => {
|
||||
return Some(crate::dex_catalog::DexCatalogItem {
|
||||
code: "believe",
|
||||
name: "Believe",
|
||||
program_id: None,
|
||||
router_program_id: None,
|
||||
is_enabled: false,
|
||||
});
|
||||
},
|
||||
"heaven" => {
|
||||
return Some(crate::dex_catalog::DexCatalogItem {
|
||||
code: "heaven",
|
||||
name: "Heaven",
|
||||
program_id: None,
|
||||
router_program_id: None,
|
||||
is_enabled: false,
|
||||
});
|
||||
},
|
||||
_ => return None,
|
||||
}
|
||||
let matrix_entry = match crate::dex_support_matrix_entry_by_code(code) {
|
||||
Some(matrix_entry) => matrix_entry,
|
||||
None => return None,
|
||||
};
|
||||
return Some(crate::dex_catalog::DexCatalogItem {
|
||||
code: matrix_entry.code,
|
||||
name: matrix_entry.display_name,
|
||||
program_id: matrix_entry.program_id,
|
||||
router_program_id: matrix_entry.router_program_id,
|
||||
is_enabled: matrix_entry.catalog_enabled,
|
||||
});
|
||||
}
|
||||
|
||||
/// Ensures that one known DEX exists in storage and returns its internal id.
|
||||
@@ -283,7 +122,15 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn planned_launch_surfaces_are_present_but_disabled() {
|
||||
let codes = ["raydium_launchlab", "letsbonk", "boop_fun", "moonshot", "believe", "heaven"];
|
||||
let codes = [
|
||||
"raydium_launchlab",
|
||||
"raydium_launchpad",
|
||||
"letsbonk",
|
||||
"boop_fun",
|
||||
"moonshot",
|
||||
"believe",
|
||||
"heaven",
|
||||
];
|
||||
for code in codes {
|
||||
let item_option = crate::dex_catalog::dex_catalog_item_by_code(code);
|
||||
let item = match item_option {
|
||||
@@ -297,6 +144,23 @@ mod tests {
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn catalog_entries_are_derived_from_support_matrix() {
|
||||
let item = match crate::dex_catalog::dex_catalog_item_by_code("pump_swap") {
|
||||
Some(item) => item,
|
||||
None => panic!("expected pump_swap catalog item"),
|
||||
};
|
||||
let matrix_entry = match crate::dex_support_matrix_entry_by_code("pump_swap") {
|
||||
Some(matrix_entry) => matrix_entry,
|
||||
None => panic!("expected pump_swap matrix entry"),
|
||||
};
|
||||
assert_eq!(item.code, matrix_entry.code);
|
||||
assert_eq!(item.name, matrix_entry.display_name);
|
||||
assert_eq!(item.program_id, matrix_entry.program_id);
|
||||
assert_eq!(item.router_program_id, matrix_entry.router_program_id);
|
||||
assert_eq!(item.is_enabled, matrix_entry.catalog_enabled);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn unknown_dex_code_is_not_silently_accepted() {
|
||||
let item_option = crate::dex_catalog::dex_catalog_item_by_code("zora_solana");
|
||||
|
||||
910
kb_lib/src/dex_support_matrix.rs
Normal file
910
kb_lib/src/dex_support_matrix.rs
Normal file
@@ -0,0 +1,910 @@
|
||||
// file: kb_lib/src/dex_support_matrix.rs
|
||||
|
||||
//! Shared DEX support matrix.
|
||||
//!
|
||||
//! This module centralizes protocol metadata that was previously duplicated
|
||||
//! across catalog, transaction classification, candidate recording and roadmap
|
||||
//! surfaces. It intentionally does not decode instructions and does not decide
|
||||
//! whether one decoded event is actionable.
|
||||
|
||||
/// Support matrix entry for one DEX, router, aggregator or launch surface.
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq, serde::Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct DexSupportMatrixEntry {
|
||||
/// Stable internal protocol or surface code.
|
||||
pub code: &'static str,
|
||||
/// Human-readable protocol or surface name.
|
||||
pub display_name: &'static str,
|
||||
/// Protocol family, for example `raydium`, `meteora` or `pump`.
|
||||
pub family: &'static str,
|
||||
/// Protocol version or `unknown` when not verified locally.
|
||||
pub version: &'static str,
|
||||
/// Surface type: `launch`, `bonding_curve`, `AMM`, `CLMM`, `DLMM`, `router`, `aggregator` or `unknown`.
|
||||
pub surface_type: &'static str,
|
||||
/// Primary Solana program id, when verified in local constants or docs.
|
||||
pub program_id: std::option::Option<&'static str>,
|
||||
/// Optional router program id, when this entry uses a distinct router.
|
||||
pub router_program_id: std::option::Option<&'static str>,
|
||||
/// Program id confidence: `known`, `to_verify` or `unknown`.
|
||||
pub program_id_status: &'static str,
|
||||
/// Whether this protocol has been observed in the local replay corpus.
|
||||
pub observed: bool,
|
||||
/// Whether the code currently contains a decoder for this protocol.
|
||||
pub decoded: bool,
|
||||
/// Whether decoded events are currently materialized beyond raw decoded rows.
|
||||
pub materialized: bool,
|
||||
/// Whether this protocol can currently produce trade candidates.
|
||||
pub trade_candidate: bool,
|
||||
/// Whether this protocol can currently produce candle candidates.
|
||||
pub candle_candidate: bool,
|
||||
/// Whether this protocol can currently produce pair candidates.
|
||||
pub pair_candidate: bool,
|
||||
/// Whether this protocol can currently produce pool candidates.
|
||||
pub pool_candidate: bool,
|
||||
/// Operational support status: `supported`, `partial`, `planned`, `ignored`, `to_verify` or `unknown`.
|
||||
pub status: &'static str,
|
||||
/// Confidence level attached to this matrix entry.
|
||||
pub confidence: &'static str,
|
||||
/// Optional explicit skip reason for partial or ignored entries.
|
||||
pub skip_reason: std::option::Option<&'static str>,
|
||||
/// Whether the entry should be inserted as an enabled DEX in the storage catalog.
|
||||
pub catalog_enabled: bool,
|
||||
}
|
||||
|
||||
/// Owned DTO form of a DEX support matrix entry.
|
||||
#[derive(Debug, Clone, Eq, PartialEq, serde::Serialize, serde::Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct DexSupportMatrixEntryDto {
|
||||
/// Stable internal protocol or surface code.
|
||||
pub code: std::string::String,
|
||||
/// Human-readable protocol or surface name.
|
||||
pub display_name: std::string::String,
|
||||
/// Protocol family, for example `raydium`, `meteora` or `pump`.
|
||||
pub family: std::string::String,
|
||||
/// Protocol version or `unknown` when not verified locally.
|
||||
pub version: std::string::String,
|
||||
/// Surface type: `launch`, `bonding_curve`, `AMM`, `CLMM`, `DLMM`, `router`, `aggregator` or `unknown`.
|
||||
pub surface_type: std::string::String,
|
||||
/// Primary Solana program id, when verified in local constants or docs.
|
||||
pub program_id: std::option::Option<std::string::String>,
|
||||
/// Optional router program id, when this entry uses a distinct router.
|
||||
pub router_program_id: std::option::Option<std::string::String>,
|
||||
/// Program id confidence: `known`, `to_verify` or `unknown`.
|
||||
pub program_id_status: std::string::String,
|
||||
/// Whether this protocol has been observed in the local replay corpus.
|
||||
pub observed: bool,
|
||||
/// Whether the code currently contains a decoder for this protocol.
|
||||
pub decoded: bool,
|
||||
/// Whether decoded events are currently materialized beyond raw decoded rows.
|
||||
pub materialized: bool,
|
||||
/// Whether this protocol can currently produce trade candidates.
|
||||
pub trade_candidate: bool,
|
||||
/// Whether this protocol can currently produce candle candidates.
|
||||
pub candle_candidate: bool,
|
||||
/// Whether this protocol can currently produce pair candidates.
|
||||
pub pair_candidate: bool,
|
||||
/// Whether this protocol can currently produce pool candidates.
|
||||
pub pool_candidate: bool,
|
||||
/// Operational support status: `supported`, `partial`, `planned`, `ignored`, `to_verify` or `unknown`.
|
||||
pub status: std::string::String,
|
||||
/// Confidence level attached to this matrix entry.
|
||||
pub confidence: std::string::String,
|
||||
/// Optional explicit skip reason for partial or ignored entries.
|
||||
pub skip_reason: std::option::Option<std::string::String>,
|
||||
/// Whether the entry should be inserted as an enabled DEX in the storage catalog.
|
||||
pub catalog_enabled: bool,
|
||||
}
|
||||
|
||||
impl DexSupportMatrixEntryDto {
|
||||
/// Builds an owned DTO from one static support matrix entry.
|
||||
pub fn from_entry(entry: &crate::DexSupportMatrixEntry) -> Self {
|
||||
return Self {
|
||||
code: entry.code.to_string(),
|
||||
display_name: entry.display_name.to_string(),
|
||||
family: entry.family.to_string(),
|
||||
version: entry.version.to_string(),
|
||||
surface_type: entry.surface_type.to_string(),
|
||||
program_id: match entry.program_id {
|
||||
Some(program_id) => Some(program_id.to_string()),
|
||||
None => None,
|
||||
},
|
||||
router_program_id: match entry.router_program_id {
|
||||
Some(router_program_id) => Some(router_program_id.to_string()),
|
||||
None => None,
|
||||
},
|
||||
program_id_status: entry.program_id_status.to_string(),
|
||||
observed: entry.observed,
|
||||
decoded: entry.decoded,
|
||||
materialized: entry.materialized,
|
||||
trade_candidate: entry.trade_candidate,
|
||||
candle_candidate: entry.candle_candidate,
|
||||
pair_candidate: entry.pair_candidate,
|
||||
pool_candidate: entry.pool_candidate,
|
||||
status: entry.status.to_string(),
|
||||
confidence: entry.confidence.to_string(),
|
||||
skip_reason: match entry.skip_reason {
|
||||
Some(skip_reason) => Some(skip_reason.to_string()),
|
||||
None => None,
|
||||
},
|
||||
catalog_enabled: entry.catalog_enabled,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
const DEX_SUPPORT_MATRIX_ENTRIES: &[DexSupportMatrixEntry] = &[
|
||||
DexSupportMatrixEntry {
|
||||
code: "pump_fun",
|
||||
display_name: "Pump.fun",
|
||||
family: "pump",
|
||||
version: "bonding_curve",
|
||||
surface_type: "launch",
|
||||
program_id: Some(crate::PUMP_FUN_PROGRAM_ID),
|
||||
router_program_id: None,
|
||||
program_id_status: "known",
|
||||
observed: false,
|
||||
decoded: true,
|
||||
materialized: true,
|
||||
trade_candidate: true,
|
||||
candle_candidate: true,
|
||||
pair_candidate: true,
|
||||
pool_candidate: true,
|
||||
status: "partial",
|
||||
confidence: "high",
|
||||
skip_reason: Some("launch_surface_requires_migration_linking_before_live_trading"),
|
||||
catalog_enabled: true,
|
||||
},
|
||||
DexSupportMatrixEntry {
|
||||
code: "pump_swap",
|
||||
display_name: "PumpSwap",
|
||||
family: "pump",
|
||||
version: "amm",
|
||||
surface_type: "AMM",
|
||||
program_id: Some(crate::PUMP_SWAP_PROGRAM_ID),
|
||||
router_program_id: None,
|
||||
program_id_status: "known",
|
||||
observed: true,
|
||||
decoded: true,
|
||||
materialized: true,
|
||||
trade_candidate: true,
|
||||
candle_candidate: true,
|
||||
pair_candidate: true,
|
||||
pool_candidate: true,
|
||||
status: "supported",
|
||||
confidence: "high",
|
||||
skip_reason: None,
|
||||
catalog_enabled: true,
|
||||
},
|
||||
DexSupportMatrixEntry {
|
||||
code: "raydium_cpmm",
|
||||
display_name: "Raydium CPMM",
|
||||
family: "raydium",
|
||||
version: "cpmm",
|
||||
surface_type: "AMM",
|
||||
program_id: Some(crate::RAYDIUM_CPMM_PROGRAM_ID),
|
||||
router_program_id: None,
|
||||
program_id_status: "known",
|
||||
observed: true,
|
||||
decoded: true,
|
||||
materialized: true,
|
||||
trade_candidate: true,
|
||||
candle_candidate: true,
|
||||
pair_candidate: true,
|
||||
pool_candidate: true,
|
||||
status: "supported",
|
||||
confidence: "high",
|
||||
skip_reason: None,
|
||||
catalog_enabled: true,
|
||||
},
|
||||
DexSupportMatrixEntry {
|
||||
code: "raydium_clmm",
|
||||
display_name: "Raydium CLMM",
|
||||
family: "raydium",
|
||||
version: "clmm",
|
||||
surface_type: "CLMM",
|
||||
program_id: Some(crate::RAYDIUM_CLMM_PROGRAM_ID),
|
||||
router_program_id: None,
|
||||
program_id_status: "known",
|
||||
observed: true,
|
||||
decoded: true,
|
||||
materialized: true,
|
||||
trade_candidate: true,
|
||||
candle_candidate: true,
|
||||
pair_candidate: true,
|
||||
pool_candidate: true,
|
||||
status: "supported",
|
||||
confidence: "high",
|
||||
skip_reason: None,
|
||||
catalog_enabled: true,
|
||||
},
|
||||
DexSupportMatrixEntry {
|
||||
code: "raydium_amm_v4",
|
||||
display_name: "Raydium AMM v4",
|
||||
family: "raydium",
|
||||
version: "amm_v4",
|
||||
surface_type: "AMM",
|
||||
program_id: Some(crate::RAYDIUM_AMM_V4_PROGRAM_ID),
|
||||
router_program_id: None,
|
||||
program_id_status: "known",
|
||||
observed: false,
|
||||
decoded: true,
|
||||
materialized: true,
|
||||
trade_candidate: true,
|
||||
candle_candidate: true,
|
||||
pair_candidate: true,
|
||||
pool_candidate: true,
|
||||
status: "partial",
|
||||
confidence: "medium",
|
||||
skip_reason: Some("not_observed_in_0_7_28_replay"),
|
||||
catalog_enabled: true,
|
||||
},
|
||||
DexSupportMatrixEntry {
|
||||
code: "raydium",
|
||||
display_name: "Raydium AMM v4",
|
||||
family: "raydium",
|
||||
version: "amm_v4_alias",
|
||||
surface_type: "AMM",
|
||||
program_id: Some(crate::RAYDIUM_AMM_V4_PROGRAM_ID),
|
||||
router_program_id: None,
|
||||
program_id_status: "known",
|
||||
observed: false,
|
||||
decoded: true,
|
||||
materialized: true,
|
||||
trade_candidate: true,
|
||||
candle_candidate: true,
|
||||
pair_candidate: true,
|
||||
pool_candidate: true,
|
||||
status: "partial",
|
||||
confidence: "medium",
|
||||
skip_reason: Some("legacy_catalog_alias_for_raydium_amm_v4"),
|
||||
catalog_enabled: true,
|
||||
},
|
||||
DexSupportMatrixEntry {
|
||||
code: "raydium_launchlab",
|
||||
display_name: "Raydium LaunchLab",
|
||||
family: "raydium",
|
||||
version: "launchlab",
|
||||
surface_type: "launch",
|
||||
program_id: Some(crate::RAYDIUM_LAUNCHLAB_PROGRAM_ID),
|
||||
router_program_id: None,
|
||||
program_id_status: "known",
|
||||
observed: false,
|
||||
decoded: false,
|
||||
materialized: false,
|
||||
trade_candidate: false,
|
||||
candle_candidate: false,
|
||||
pair_candidate: true,
|
||||
pool_candidate: true,
|
||||
status: "planned",
|
||||
confidence: "medium",
|
||||
skip_reason: Some("decoder_and_materialization_not_enabled"),
|
||||
catalog_enabled: false,
|
||||
},
|
||||
DexSupportMatrixEntry {
|
||||
code: "raydium_launchpad",
|
||||
display_name: "Raydium Launchpad",
|
||||
family: "raydium",
|
||||
version: "unknown",
|
||||
surface_type: "launch",
|
||||
program_id: None,
|
||||
router_program_id: None,
|
||||
program_id_status: "to_verify",
|
||||
observed: false,
|
||||
decoded: false,
|
||||
materialized: false,
|
||||
trade_candidate: false,
|
||||
candle_candidate: false,
|
||||
pair_candidate: true,
|
||||
pool_candidate: true,
|
||||
status: "to_verify",
|
||||
confidence: "low",
|
||||
skip_reason: Some("program_id_to_verify"),
|
||||
catalog_enabled: false,
|
||||
},
|
||||
DexSupportMatrixEntry {
|
||||
code: "raydium_router",
|
||||
display_name: "Raydium Router",
|
||||
family: "raydium",
|
||||
version: "router",
|
||||
surface_type: "router",
|
||||
program_id: Some(crate::RAYDIUM_AMM_ROUTING_PROGRAM_ID),
|
||||
router_program_id: None,
|
||||
program_id_status: "known",
|
||||
observed: false,
|
||||
decoded: false,
|
||||
materialized: false,
|
||||
trade_candidate: false,
|
||||
candle_candidate: false,
|
||||
pair_candidate: false,
|
||||
pool_candidate: false,
|
||||
status: "partial",
|
||||
confidence: "medium",
|
||||
skip_reason: Some("router_not_materialized_as_direct_trade_surface"),
|
||||
catalog_enabled: false,
|
||||
},
|
||||
DexSupportMatrixEntry {
|
||||
code: "raydium_stable_swap",
|
||||
display_name: "Raydium Stable Swap AMM",
|
||||
family: "raydium",
|
||||
version: "stable_swap",
|
||||
surface_type: "AMM",
|
||||
program_id: Some(crate::RAYDIUM_STABLE_SWAP_AMM_PROGRAM_ID),
|
||||
router_program_id: None,
|
||||
program_id_status: "known",
|
||||
observed: false,
|
||||
decoded: false,
|
||||
materialized: false,
|
||||
trade_candidate: false,
|
||||
candle_candidate: false,
|
||||
pair_candidate: false,
|
||||
pool_candidate: false,
|
||||
status: "planned",
|
||||
confidence: "medium",
|
||||
skip_reason: Some("deprecated_program_not_prioritized"),
|
||||
catalog_enabled: false,
|
||||
},
|
||||
DexSupportMatrixEntry {
|
||||
code: "meteora_dlmm",
|
||||
display_name: "Meteora DLMM",
|
||||
family: "meteora",
|
||||
version: "dlmm",
|
||||
surface_type: "DLMM",
|
||||
program_id: Some(crate::METEORA_DLMM_PROGRAM_ID),
|
||||
router_program_id: None,
|
||||
program_id_status: "known",
|
||||
observed: true,
|
||||
decoded: true,
|
||||
materialized: true,
|
||||
trade_candidate: true,
|
||||
candle_candidate: true,
|
||||
pair_candidate: true,
|
||||
pool_candidate: true,
|
||||
status: "supported",
|
||||
confidence: "high",
|
||||
skip_reason: None,
|
||||
catalog_enabled: true,
|
||||
},
|
||||
DexSupportMatrixEntry {
|
||||
code: "meteora_dlc",
|
||||
display_name: "Meteora DLC",
|
||||
family: "meteora",
|
||||
version: "unknown",
|
||||
surface_type: "unknown",
|
||||
program_id: None,
|
||||
router_program_id: None,
|
||||
program_id_status: "to_verify",
|
||||
observed: false,
|
||||
decoded: false,
|
||||
materialized: false,
|
||||
trade_candidate: false,
|
||||
candle_candidate: false,
|
||||
pair_candidate: false,
|
||||
pool_candidate: false,
|
||||
status: "to_verify",
|
||||
confidence: "low",
|
||||
skip_reason: Some("surface_and_program_id_to_verify"),
|
||||
catalog_enabled: false,
|
||||
},
|
||||
DexSupportMatrixEntry {
|
||||
code: "meteora_damm_v1",
|
||||
display_name: "Meteora DAMM v1",
|
||||
family: "meteora",
|
||||
version: "damm_v1",
|
||||
surface_type: "AMM",
|
||||
program_id: Some(crate::METEORA_DAMM_V1_PROGRAM_ID),
|
||||
router_program_id: None,
|
||||
program_id_status: "known",
|
||||
observed: true,
|
||||
decoded: true,
|
||||
materialized: false,
|
||||
trade_candidate: false,
|
||||
candle_candidate: false,
|
||||
pair_candidate: true,
|
||||
pool_candidate: true,
|
||||
status: "partial",
|
||||
confidence: "medium",
|
||||
skip_reason: Some("meteora_damm_v1_swap_without_amount_payload"),
|
||||
catalog_enabled: true,
|
||||
},
|
||||
DexSupportMatrixEntry {
|
||||
code: "meteora_damm_v2",
|
||||
display_name: "Meteora DAMM v2",
|
||||
family: "meteora",
|
||||
version: "damm_v2",
|
||||
surface_type: "AMM",
|
||||
program_id: Some(crate::METEORA_DAMM_V2_PROGRAM_ID),
|
||||
router_program_id: None,
|
||||
program_id_status: "known",
|
||||
observed: false,
|
||||
decoded: true,
|
||||
materialized: true,
|
||||
trade_candidate: true,
|
||||
candle_candidate: true,
|
||||
pair_candidate: true,
|
||||
pool_candidate: true,
|
||||
status: "partial",
|
||||
confidence: "medium",
|
||||
skip_reason: Some("not_observed_in_0_7_28_replay"),
|
||||
catalog_enabled: true,
|
||||
},
|
||||
DexSupportMatrixEntry {
|
||||
code: "meteora_dbc",
|
||||
display_name: "Meteora DBC",
|
||||
family: "meteora",
|
||||
version: "dbc",
|
||||
surface_type: "bonding_curve",
|
||||
program_id: Some(crate::METEORA_DBC_PROGRAM_ID),
|
||||
router_program_id: None,
|
||||
program_id_status: "known",
|
||||
observed: false,
|
||||
decoded: true,
|
||||
materialized: true,
|
||||
trade_candidate: true,
|
||||
candle_candidate: true,
|
||||
pair_candidate: true,
|
||||
pool_candidate: true,
|
||||
status: "partial",
|
||||
confidence: "medium",
|
||||
skip_reason: Some("not_observed_in_0_7_28_replay"),
|
||||
catalog_enabled: true,
|
||||
},
|
||||
DexSupportMatrixEntry {
|
||||
code: "orca_whirlpools",
|
||||
display_name: "Orca Whirlpools",
|
||||
family: "orca",
|
||||
version: "whirlpools",
|
||||
surface_type: "CLMM",
|
||||
program_id: Some(crate::ORCA_WHIRLPOOLS_PROGRAM_ID),
|
||||
router_program_id: None,
|
||||
program_id_status: "known",
|
||||
observed: false,
|
||||
decoded: true,
|
||||
materialized: true,
|
||||
trade_candidate: true,
|
||||
candle_candidate: true,
|
||||
pair_candidate: true,
|
||||
pool_candidate: true,
|
||||
status: "partial",
|
||||
confidence: "medium",
|
||||
skip_reason: Some("not_observed_in_0_7_28_replay"),
|
||||
catalog_enabled: true,
|
||||
},
|
||||
DexSupportMatrixEntry {
|
||||
code: "fluxbeam",
|
||||
display_name: "FluxBeam",
|
||||
family: "fluxbeam",
|
||||
version: "unknown",
|
||||
surface_type: "AMM",
|
||||
program_id: Some(crate::FLUXBEAM_PROGRAM_ID),
|
||||
router_program_id: None,
|
||||
program_id_status: "known",
|
||||
observed: false,
|
||||
decoded: true,
|
||||
materialized: true,
|
||||
trade_candidate: true,
|
||||
candle_candidate: true,
|
||||
pair_candidate: true,
|
||||
pool_candidate: true,
|
||||
status: "partial",
|
||||
confidence: "medium",
|
||||
skip_reason: Some("not_observed_in_0_7_28_replay"),
|
||||
catalog_enabled: true,
|
||||
},
|
||||
DexSupportMatrixEntry {
|
||||
code: "dexlab",
|
||||
display_name: "DexLab Swap/Pool",
|
||||
family: "dexlab",
|
||||
version: "unknown",
|
||||
surface_type: "AMM",
|
||||
program_id: Some(crate::DEXLAB_PROGRAM_ID),
|
||||
router_program_id: None,
|
||||
program_id_status: "known",
|
||||
observed: false,
|
||||
decoded: true,
|
||||
materialized: true,
|
||||
trade_candidate: true,
|
||||
candle_candidate: true,
|
||||
pair_candidate: true,
|
||||
pool_candidate: true,
|
||||
status: "partial",
|
||||
confidence: "medium",
|
||||
skip_reason: Some("not_observed_in_0_7_28_replay"),
|
||||
catalog_enabled: true,
|
||||
},
|
||||
DexSupportMatrixEntry {
|
||||
code: "bags",
|
||||
display_name: "Bags",
|
||||
family: "bags",
|
||||
version: "unknown",
|
||||
surface_type: "launch",
|
||||
program_id: None,
|
||||
router_program_id: None,
|
||||
program_id_status: "unknown",
|
||||
observed: false,
|
||||
decoded: false,
|
||||
materialized: false,
|
||||
trade_candidate: false,
|
||||
candle_candidate: false,
|
||||
pair_candidate: true,
|
||||
pool_candidate: true,
|
||||
status: "planned",
|
||||
confidence: "low",
|
||||
skip_reason: Some("program_id_to_verify"),
|
||||
catalog_enabled: false,
|
||||
},
|
||||
DexSupportMatrixEntry {
|
||||
code: "letsbonk",
|
||||
display_name: "LetsBonk / Bonk.fun",
|
||||
family: "bonk",
|
||||
version: "unknown",
|
||||
surface_type: "launch",
|
||||
program_id: None,
|
||||
router_program_id: None,
|
||||
program_id_status: "unknown",
|
||||
observed: false,
|
||||
decoded: false,
|
||||
materialized: false,
|
||||
trade_candidate: false,
|
||||
candle_candidate: false,
|
||||
pair_candidate: true,
|
||||
pool_candidate: true,
|
||||
status: "planned",
|
||||
confidence: "low",
|
||||
skip_reason: Some("program_id_to_verify"),
|
||||
catalog_enabled: false,
|
||||
},
|
||||
DexSupportMatrixEntry {
|
||||
code: "bonk",
|
||||
display_name: "Bonk launch surface",
|
||||
family: "bonk",
|
||||
version: "unknown",
|
||||
surface_type: "launch",
|
||||
program_id: None,
|
||||
router_program_id: None,
|
||||
program_id_status: "unknown",
|
||||
observed: false,
|
||||
decoded: false,
|
||||
materialized: false,
|
||||
trade_candidate: false,
|
||||
candle_candidate: false,
|
||||
pair_candidate: true,
|
||||
pool_candidate: true,
|
||||
status: "planned",
|
||||
confidence: "low",
|
||||
skip_reason: Some("program_id_to_verify"),
|
||||
catalog_enabled: false,
|
||||
},
|
||||
DexSupportMatrixEntry {
|
||||
code: "okx_dex",
|
||||
display_name: "OKX DEX",
|
||||
family: "okx",
|
||||
version: "unknown",
|
||||
surface_type: "aggregator",
|
||||
program_id: None,
|
||||
router_program_id: None,
|
||||
program_id_status: "unknown",
|
||||
observed: false,
|
||||
decoded: false,
|
||||
materialized: false,
|
||||
trade_candidate: false,
|
||||
candle_candidate: false,
|
||||
pair_candidate: false,
|
||||
pool_candidate: false,
|
||||
status: "planned",
|
||||
confidence: "low",
|
||||
skip_reason: Some("program_id_to_verify"),
|
||||
catalog_enabled: false,
|
||||
},
|
||||
DexSupportMatrixEntry {
|
||||
code: "boop_fun",
|
||||
display_name: "Boop.fun",
|
||||
family: "boop",
|
||||
version: "unknown",
|
||||
surface_type: "launch",
|
||||
program_id: None,
|
||||
router_program_id: None,
|
||||
program_id_status: "unknown",
|
||||
observed: false,
|
||||
decoded: false,
|
||||
materialized: false,
|
||||
trade_candidate: false,
|
||||
candle_candidate: false,
|
||||
pair_candidate: true,
|
||||
pool_candidate: true,
|
||||
status: "planned",
|
||||
confidence: "low",
|
||||
skip_reason: Some("program_id_to_verify"),
|
||||
catalog_enabled: false,
|
||||
},
|
||||
DexSupportMatrixEntry {
|
||||
code: "moonshot",
|
||||
display_name: "Moonshot",
|
||||
family: "moonshot",
|
||||
version: "unknown",
|
||||
surface_type: "launch",
|
||||
program_id: None,
|
||||
router_program_id: None,
|
||||
program_id_status: "unknown",
|
||||
observed: false,
|
||||
decoded: false,
|
||||
materialized: false,
|
||||
trade_candidate: false,
|
||||
candle_candidate: false,
|
||||
pair_candidate: true,
|
||||
pool_candidate: true,
|
||||
status: "planned",
|
||||
confidence: "low",
|
||||
skip_reason: Some("program_id_to_verify"),
|
||||
catalog_enabled: false,
|
||||
},
|
||||
DexSupportMatrixEntry {
|
||||
code: "believe",
|
||||
display_name: "Believe",
|
||||
family: "believe",
|
||||
version: "unknown",
|
||||
surface_type: "launch",
|
||||
program_id: None,
|
||||
router_program_id: None,
|
||||
program_id_status: "unknown",
|
||||
observed: false,
|
||||
decoded: false,
|
||||
materialized: false,
|
||||
trade_candidate: false,
|
||||
candle_candidate: false,
|
||||
pair_candidate: true,
|
||||
pool_candidate: true,
|
||||
status: "planned",
|
||||
confidence: "low",
|
||||
skip_reason: Some("program_id_to_verify"),
|
||||
catalog_enabled: false,
|
||||
},
|
||||
DexSupportMatrixEntry {
|
||||
code: "zora",
|
||||
display_name: "Zora",
|
||||
family: "zora",
|
||||
version: "unknown",
|
||||
surface_type: "unknown",
|
||||
program_id: None,
|
||||
router_program_id: None,
|
||||
program_id_status: "to_verify",
|
||||
observed: false,
|
||||
decoded: false,
|
||||
materialized: false,
|
||||
trade_candidate: false,
|
||||
candle_candidate: false,
|
||||
pair_candidate: false,
|
||||
pool_candidate: false,
|
||||
status: "to_verify",
|
||||
confidence: "low",
|
||||
skip_reason: Some("surface_and_program_id_to_verify"),
|
||||
catalog_enabled: false,
|
||||
},
|
||||
DexSupportMatrixEntry {
|
||||
code: "moonit",
|
||||
display_name: "Moonit",
|
||||
family: "moonit",
|
||||
version: "unknown",
|
||||
surface_type: "launch",
|
||||
program_id: None,
|
||||
router_program_id: None,
|
||||
program_id_status: "unknown",
|
||||
observed: false,
|
||||
decoded: false,
|
||||
materialized: false,
|
||||
trade_candidate: false,
|
||||
candle_candidate: false,
|
||||
pair_candidate: true,
|
||||
pool_candidate: true,
|
||||
status: "planned",
|
||||
confidence: "low",
|
||||
skip_reason: Some("program_id_to_verify"),
|
||||
catalog_enabled: false,
|
||||
},
|
||||
DexSupportMatrixEntry {
|
||||
code: "launchbeam",
|
||||
display_name: "LaunchBeam",
|
||||
family: "launchbeam",
|
||||
version: "unknown",
|
||||
surface_type: "launch",
|
||||
program_id: None,
|
||||
router_program_id: None,
|
||||
program_id_status: "unknown",
|
||||
observed: false,
|
||||
decoded: false,
|
||||
materialized: false,
|
||||
trade_candidate: false,
|
||||
candle_candidate: false,
|
||||
pair_candidate: true,
|
||||
pool_candidate: true,
|
||||
status: "planned",
|
||||
confidence: "low",
|
||||
skip_reason: Some("program_id_to_verify"),
|
||||
catalog_enabled: false,
|
||||
},
|
||||
DexSupportMatrixEntry {
|
||||
code: "heaven",
|
||||
display_name: "Heaven",
|
||||
family: "heaven",
|
||||
version: "unknown",
|
||||
surface_type: "launch",
|
||||
program_id: None,
|
||||
router_program_id: None,
|
||||
program_id_status: "unknown",
|
||||
observed: false,
|
||||
decoded: false,
|
||||
materialized: false,
|
||||
trade_candidate: false,
|
||||
candle_candidate: false,
|
||||
pair_candidate: true,
|
||||
pool_candidate: true,
|
||||
status: "planned",
|
||||
confidence: "low",
|
||||
skip_reason: Some("program_id_to_verify"),
|
||||
catalog_enabled: false,
|
||||
},
|
||||
];
|
||||
|
||||
/// Returns all static DEX support matrix entries.
|
||||
pub fn dex_support_matrix_entries() -> &'static [crate::DexSupportMatrixEntry] {
|
||||
return DEX_SUPPORT_MATRIX_ENTRIES;
|
||||
}
|
||||
|
||||
/// Returns all DEX support matrix entries as owned DTOs.
|
||||
pub fn dex_support_matrix_entry_dtos() -> std::vec::Vec<crate::DexSupportMatrixEntryDto> {
|
||||
let mut entries = std::vec::Vec::new();
|
||||
for entry in crate::dex_support_matrix_entries() {
|
||||
entries.push(crate::DexSupportMatrixEntryDto::from_entry(entry));
|
||||
}
|
||||
return entries;
|
||||
}
|
||||
|
||||
/// Looks up one DEX support matrix entry by internal code.
|
||||
pub fn dex_support_matrix_entry_by_code(
|
||||
code: &str,
|
||||
) -> std::option::Option<&'static crate::DexSupportMatrixEntry> {
|
||||
for entry in crate::dex_support_matrix_entries() {
|
||||
if entry.code == code {
|
||||
return Some(entry);
|
||||
}
|
||||
}
|
||||
return None;
|
||||
}
|
||||
|
||||
/// Looks up one DEX support matrix entry by primary or router program id.
|
||||
pub fn dex_support_matrix_entry_by_program_id(
|
||||
program_id: &str,
|
||||
) -> std::option::Option<&'static crate::DexSupportMatrixEntry> {
|
||||
for entry in crate::dex_support_matrix_entries() {
|
||||
if let Some(entry_program_id) = entry.program_id {
|
||||
if entry_program_id == program_id {
|
||||
return Some(entry);
|
||||
}
|
||||
}
|
||||
if let Some(entry_router_program_id) = entry.router_program_id {
|
||||
if entry_router_program_id == program_id {
|
||||
return Some(entry);
|
||||
}
|
||||
}
|
||||
}
|
||||
return None;
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
#[test]
|
||||
fn matrix_contains_required_0_7_29_entries() {
|
||||
let codes = [
|
||||
"pump_fun",
|
||||
"pump_swap",
|
||||
"raydium_cpmm",
|
||||
"raydium_clmm",
|
||||
"raydium_amm_v4",
|
||||
"raydium_launchlab",
|
||||
"raydium_launchpad",
|
||||
"meteora_dlmm",
|
||||
"meteora_dlc",
|
||||
"meteora_damm_v1",
|
||||
"meteora_damm_v2",
|
||||
"bags",
|
||||
"bonk",
|
||||
"okx_dex",
|
||||
"boop_fun",
|
||||
"moonshot",
|
||||
"believe",
|
||||
"zora",
|
||||
"moonit",
|
||||
"launchbeam",
|
||||
"heaven",
|
||||
"dexlab",
|
||||
];
|
||||
for code in codes {
|
||||
let entry = crate::dex_support_matrix_entry_by_code(code);
|
||||
assert!(entry.is_some(), "missing matrix entry for {}", code);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn matrix_does_not_invent_program_ids_for_unverified_planned_surfaces() {
|
||||
let codes = [
|
||||
"bags",
|
||||
"bonk",
|
||||
"okx_dex",
|
||||
"boop_fun",
|
||||
"moonshot",
|
||||
"believe",
|
||||
"zora",
|
||||
"moonit",
|
||||
"launchbeam",
|
||||
"heaven",
|
||||
];
|
||||
for code in codes {
|
||||
let entry = match crate::dex_support_matrix_entry_by_code(code) {
|
||||
Some(entry) => entry,
|
||||
None => panic!("missing matrix entry for {}", code),
|
||||
};
|
||||
assert!(entry.program_id.is_none(), "{} should not have invented program id", code);
|
||||
assert!(!entry.catalog_enabled, "{} should not be enabled before verification", code);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn matrix_lookup_by_program_id_returns_expected_protocol() {
|
||||
let entry = match crate::dex_support_matrix_entry_by_program_id(crate::METEORA_DLMM_PROGRAM_ID)
|
||||
{
|
||||
Some(entry) => entry,
|
||||
None => panic!("expected meteora_dlmm program id lookup"),
|
||||
};
|
||||
assert_eq!(entry.code, "meteora_dlmm");
|
||||
|
||||
let raydium_entry = match crate::dex_support_matrix_entry_by_program_id(
|
||||
crate::RAYDIUM_AMM_V4_PROGRAM_ID,
|
||||
) {
|
||||
Some(entry) => entry,
|
||||
None => panic!("expected raydium AMM v4 program id lookup"),
|
||||
};
|
||||
assert_eq!(raydium_entry.code, "raydium_amm_v4");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn matrix_marks_partial_meteora_damm_v1_correctly() {
|
||||
let entry = match crate::dex_support_matrix_entry_by_code("meteora_damm_v1") {
|
||||
Some(entry) => entry,
|
||||
None => panic!("expected meteora_damm_v1 matrix entry"),
|
||||
};
|
||||
assert_eq!(entry.status, "partial");
|
||||
assert!(entry.observed);
|
||||
assert!(entry.decoded);
|
||||
assert!(!entry.trade_candidate);
|
||||
assert!(!entry.candle_candidate);
|
||||
assert_eq!(entry.skip_reason, Some("meteora_damm_v1_swap_without_amount_payload"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn matrix_marks_launch_surfaces_as_launch_or_bonding_curve() {
|
||||
let codes = ["pump_fun", "raydium_launchlab", "bags", "moonshot", "moonit"];
|
||||
for code in codes {
|
||||
let entry = match crate::dex_support_matrix_entry_by_code(code) {
|
||||
Some(entry) => entry,
|
||||
None => panic!("missing matrix entry for {}", code),
|
||||
};
|
||||
assert!(
|
||||
entry.surface_type == "launch" || entry.surface_type == "bonding_curve",
|
||||
"{} has unexpected surface type {}",
|
||||
code,
|
||||
entry.surface_type
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn matrix_dto_preserves_core_fields() {
|
||||
let entry = match crate::dex_support_matrix_entry_by_code("pump_swap") {
|
||||
Some(entry) => entry,
|
||||
None => panic!("expected pump_swap matrix entry"),
|
||||
};
|
||||
let dto = crate::DexSupportMatrixEntryDto::from_entry(entry);
|
||||
assert_eq!(dto.code, "pump_swap");
|
||||
assert_eq!(dto.program_id, Some(crate::PUMP_SWAP_PROGRAM_ID.to_string()));
|
||||
assert!(dto.trade_candidate);
|
||||
assert!(dto.candle_candidate);
|
||||
}
|
||||
}
|
||||
@@ -39,6 +39,8 @@ mod dex_detection_route;
|
||||
mod dex_event_classification;
|
||||
/// Shared DEX pool materialization helpers.
|
||||
mod dex_pool_materialization;
|
||||
/// Shared DEX support matrix.
|
||||
mod dex_support_matrix;
|
||||
/// Shared error type for `kb_lib`.
|
||||
mod error;
|
||||
/// Generic asynchronous HTTP JSON-RPC client.
|
||||
@@ -859,6 +861,18 @@ pub use dex_event_classification::is_dex_pool_lifecycle_event_kind;
|
||||
pub use dex_event_classification::is_dex_reward_event_kind;
|
||||
/// Returns true for swap-like DEX events.
|
||||
pub use dex_event_classification::is_dex_trade_event_kind;
|
||||
/// Static DEX support matrix entry.
|
||||
pub use dex_support_matrix::DexSupportMatrixEntry;
|
||||
/// Owned DEX support matrix entry DTO.
|
||||
pub use dex_support_matrix::DexSupportMatrixEntryDto;
|
||||
/// Returns all static DEX support matrix entries.
|
||||
pub use dex_support_matrix::dex_support_matrix_entries;
|
||||
/// Looks up one DEX support matrix entry by internal code.
|
||||
pub use dex_support_matrix::dex_support_matrix_entry_by_code;
|
||||
/// Looks up one DEX support matrix entry by primary or router program id.
|
||||
pub use dex_support_matrix::dex_support_matrix_entry_by_program_id;
|
||||
/// Returns all DEX support matrix entries as owned DTOs.
|
||||
pub use dex_support_matrix::dex_support_matrix_entry_dtos;
|
||||
/// Global error type used by the `kb_lib` crate.
|
||||
///
|
||||
/// The project intentionally avoids `anyhow` and `thiserror`, so this
|
||||
|
||||
@@ -64,7 +64,7 @@ impl LocalPipelineValidationConfig {
|
||||
/// Builds the strict validation config for `0.7.27` non-regression runs.
|
||||
pub fn v0_7_27_multi_dex_non_regression() -> Self {
|
||||
return Self {
|
||||
profile_code: "0.7.27_multi_dex_non_regression".to_string(),
|
||||
profile_code: "0.7.27_multi_dex_non_regression (obsolete)".to_string(),
|
||||
expected_dex_codes: vec![
|
||||
"pump_fun".to_string(),
|
||||
"pump_swap".to_string(),
|
||||
@@ -115,6 +115,37 @@ impl LocalPipelineValidationConfig {
|
||||
require_candles_per_dex: false,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
/// Builds the `0.7.29` DEX support matrix baseline validation config.
|
||||
///
|
||||
/// This profile preserves the `0.7.28` trade/candle non-regression checks
|
||||
/// while requiring the observed partial Meteora DAMM v1 surface to remain
|
||||
/// visible in diagnostics. It also exposes the DEX support matrix in the
|
||||
/// validation report without making planned DEXes blocking.
|
||||
pub fn v0_7_29_multi_dex_matrix_baseline() -> Self {
|
||||
return Self {
|
||||
profile_code: "0.7.29_multi_dex_matrix_baseline".to_string(),
|
||||
expected_dex_codes: vec![
|
||||
"pump_swap".to_string(),
|
||||
"raydium_cpmm".to_string(),
|
||||
"raydium_clmm".to_string(),
|
||||
"meteora_dlmm".to_string(),
|
||||
"meteora_damm_v1".to_string(),
|
||||
],
|
||||
require_all_expected_dexes: true,
|
||||
allow_unexpected_dexes: true,
|
||||
require_clean_diagnostics: false,
|
||||
require_ok_trade_candidates_fully_materialized: true,
|
||||
require_no_invalid_trade_events: true,
|
||||
require_no_duplicate_decoded_event_trades: true,
|
||||
require_no_duplicate_candle_buckets: true,
|
||||
require_no_pair_gaps: false,
|
||||
require_decoded_events_per_dex: true,
|
||||
require_trade_events_per_dex: false,
|
||||
require_candles_per_dex: false,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/// A single local pipeline validation issue.
|
||||
@@ -147,6 +178,10 @@ pub struct LocalPipelineValidationReportDto {
|
||||
pub expected_dex_codes: std::vec::Vec<std::string::String>,
|
||||
/// Observed DEX codes found in diagnostics.
|
||||
pub observed_dex_codes: std::vec::Vec<std::string::String>,
|
||||
/// Number of entries currently exposed by the DEX support matrix.
|
||||
pub dex_support_matrix_entry_count: i64,
|
||||
/// DEX support matrix snapshot exposed with the validation report.
|
||||
pub dex_support_matrix: std::vec::Vec<crate::DexSupportMatrixEntryDto>,
|
||||
/// Issues produced by validation.
|
||||
pub issues: std::vec::Vec<LocalPipelineValidationIssueDto>,
|
||||
}
|
||||
@@ -224,6 +259,15 @@ impl LocalPipelineValidationService {
|
||||
let config = crate::LocalPipelineValidationConfig::v0_7_28_multi_dex_non_regression();
|
||||
return self.validate_current_database(&config).await;
|
||||
}
|
||||
|
||||
|
||||
/// Diagnoses the current database with the `0.7.29` DEX matrix baseline profile.
|
||||
pub async fn validate_v0_7_29_current_database(
|
||||
&self,
|
||||
) -> Result<crate::LocalPipelineValidationRunDto, crate::Error> {
|
||||
let config = crate::LocalPipelineValidationConfig::v0_7_29_multi_dex_matrix_baseline();
|
||||
return self.validate_current_database(&config).await;
|
||||
}
|
||||
}
|
||||
|
||||
/// Validates a diagnostics summary without performing database access.
|
||||
@@ -383,6 +427,8 @@ pub fn validate_local_pipeline_diagnostics_summary(
|
||||
warning_count,
|
||||
expected_dex_codes,
|
||||
observed_dex_codes,
|
||||
dex_support_matrix_entry_count: crate::dex_support_matrix_entries().len() as i64,
|
||||
dex_support_matrix: crate::dex_support_matrix_entry_dtos(),
|
||||
issues,
|
||||
};
|
||||
}
|
||||
@@ -553,6 +599,38 @@ mod tests {
|
||||
assert!(report.observed_dex_codes.contains(&"meteora_damm_v1".to_string()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn validation_accepts_0_7_29_matrix_baseline_summary() {
|
||||
let summary = make_0_7_28_summary_with_meteora();
|
||||
let config = crate::LocalPipelineValidationConfig::v0_7_29_multi_dex_matrix_baseline();
|
||||
let report = crate::validate_local_pipeline_diagnostics_summary(&summary, &config);
|
||||
assert!(report.validation_passed);
|
||||
assert_eq!(report.validation_profile_code, "0.7.29_multi_dex_matrix_baseline");
|
||||
assert!(report.observed_dex_codes.contains(&"meteora_damm_v1".to_string()));
|
||||
assert!(report.expected_dex_codes.contains(&"meteora_damm_v1".to_string()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn validation_report_exposes_dex_support_matrix() {
|
||||
let summary = make_0_7_28_summary_with_meteora();
|
||||
let config = crate::LocalPipelineValidationConfig::v0_7_29_multi_dex_matrix_baseline();
|
||||
let report = crate::validate_local_pipeline_diagnostics_summary(&summary, &config);
|
||||
assert!(report.dex_support_matrix_entry_count > 0);
|
||||
assert_eq!(
|
||||
report.dex_support_matrix_entry_count,
|
||||
crate::dex_support_matrix_entries().len() as i64
|
||||
);
|
||||
let mut found_pump_swap = false;
|
||||
for entry in &report.dex_support_matrix {
|
||||
if entry.code == "pump_swap" {
|
||||
found_pump_swap = true;
|
||||
assert!(entry.trade_candidate);
|
||||
assert!(entry.candle_candidate);
|
||||
}
|
||||
}
|
||||
assert!(found_pump_swap);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn validation_rejects_missing_expected_dex() {
|
||||
let mut summary = make_clean_summary();
|
||||
|
||||
@@ -192,52 +192,11 @@ fn build_instruction_evidence_json(
|
||||
}
|
||||
|
||||
fn known_dex_protocol_name(program_id: &str) -> std::option::Option<&'static str> {
|
||||
if program_id == crate::RAYDIUM_AMM_V4_PROGRAM_ID {
|
||||
return Some("raydium_amm_v4");
|
||||
}
|
||||
if program_id == crate::RAYDIUM_CPMM_PROGRAM_ID {
|
||||
return Some("raydium_cpmm");
|
||||
}
|
||||
if program_id == crate::RAYDIUM_CLMM_PROGRAM_ID {
|
||||
return Some("raydium_clmm");
|
||||
}
|
||||
if program_id == crate::RAYDIUM_LAUNCHLAB_PROGRAM_ID {
|
||||
return Some("raydium_launchlab");
|
||||
}
|
||||
if program_id == crate::RAYDIUM_AMM_ROUTING_PROGRAM_ID {
|
||||
return Some("raydium_router");
|
||||
}
|
||||
if program_id == crate::RAYDIUM_STABLE_SWAP_AMM_PROGRAM_ID {
|
||||
return Some("raydium_stable_swap");
|
||||
}
|
||||
if program_id == crate::PUMP_FUN_PROGRAM_ID {
|
||||
return Some("pump_fun");
|
||||
}
|
||||
if program_id == crate::PUMP_SWAP_PROGRAM_ID {
|
||||
return Some("pump_swap");
|
||||
}
|
||||
if program_id == crate::METEORA_DBC_PROGRAM_ID {
|
||||
return Some("meteora_dbc");
|
||||
}
|
||||
if program_id == crate::METEORA_DLMM_PROGRAM_ID {
|
||||
return Some("meteora_dlmm");
|
||||
}
|
||||
if program_id == crate::METEORA_DAMM_V1_PROGRAM_ID {
|
||||
return Some("meteora_damm_v1");
|
||||
}
|
||||
if program_id == crate::METEORA_DAMM_V2_PROGRAM_ID {
|
||||
return Some("meteora_damm_v2");
|
||||
}
|
||||
if program_id == crate::ORCA_WHIRLPOOLS_PROGRAM_ID {
|
||||
return Some("orca_whirlpools");
|
||||
}
|
||||
if program_id == crate::FLUXBEAM_PROGRAM_ID {
|
||||
return Some("fluxbeam");
|
||||
}
|
||||
if program_id == crate::DEXLAB_PROGRAM_ID {
|
||||
return Some("dexlab");
|
||||
}
|
||||
return None;
|
||||
let matrix_entry = match crate::dex_support_matrix_entry_by_program_id(program_id) {
|
||||
Some(matrix_entry) => matrix_entry,
|
||||
None => return None,
|
||||
};
|
||||
return Some(matrix_entry.code);
|
||||
}
|
||||
|
||||
fn should_ignore_program_id(program_id: &str) -> bool {
|
||||
@@ -385,6 +344,24 @@ fn is_known_launch_surface_program_id(_program_id: &str) -> bool {
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
#[test]
|
||||
fn known_dex_candidate_uses_support_matrix_for_priority_dexes() {
|
||||
let samples = [
|
||||
(crate::PUMP_SWAP_PROGRAM_ID, "pump_swap"),
|
||||
(crate::RAYDIUM_CPMM_PROGRAM_ID, "raydium_cpmm"),
|
||||
(crate::RAYDIUM_CLMM_PROGRAM_ID, "raydium_clmm"),
|
||||
(crate::METEORA_DLMM_PROGRAM_ID, "meteora_dlmm"),
|
||||
(crate::METEORA_DAMM_V1_PROGRAM_ID, "meteora_damm_v1"),
|
||||
];
|
||||
for (program_id, expected_protocol) in samples {
|
||||
let protocol = match super::known_dex_protocol_name(program_id) {
|
||||
Some(protocol) => protocol,
|
||||
None => panic!("expected known protocol for {}", program_id),
|
||||
};
|
||||
assert_eq!(protocol, expected_protocol);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn associated_token_program_is_ignored() {
|
||||
let transaction = test_transaction();
|
||||
|
||||
@@ -139,7 +139,7 @@ impl TradeAggregationService {
|
||||
Ok(amount_resolution) => amount_resolution,
|
||||
Err(error) => return Err(error),
|
||||
};
|
||||
let trade_side = match amount_resolution.resolved_trade_side.clone() {
|
||||
let trade_side = match amount_resolution.resolved_trade_side {
|
||||
Some(resolved_trade_side) => resolved_trade_side,
|
||||
None => trade_side,
|
||||
};
|
||||
|
||||
@@ -1321,7 +1321,7 @@ fn collect_account_keys_from_candidate_path(
|
||||
target.push(text.to_string());
|
||||
continue;
|
||||
}
|
||||
if let Some(pubkey) = item.get("pubkey").and_then(|value| value.as_str()) {
|
||||
if let Some(pubkey) = item.get("pubkey").and_then(|value| return value.as_str()) {
|
||||
target.push(pubkey.to_string());
|
||||
}
|
||||
}
|
||||
@@ -1344,7 +1344,7 @@ fn collect_loaded_address_array(
|
||||
key: &str,
|
||||
target: &mut std::vec::Vec<std::string::String>,
|
||||
) {
|
||||
let array = match loaded_addresses.get(key).and_then(|value| value.as_array()) {
|
||||
let array = match loaded_addresses.get(key).and_then(|value| return value.as_array()) {
|
||||
Some(array) => array,
|
||||
None => return,
|
||||
};
|
||||
@@ -1391,12 +1391,12 @@ fn token_balance_amount_for_account_index(
|
||||
key: &str,
|
||||
account_index: usize,
|
||||
) -> std::option::Option<i128> {
|
||||
let balances = match meta.get(key).and_then(|value| value.as_array()) {
|
||||
let balances = match meta.get(key).and_then(|value| return value.as_array()) {
|
||||
Some(balances) => balances,
|
||||
None => return None,
|
||||
};
|
||||
for balance in balances {
|
||||
let balance_index = balance.get("accountIndex").and_then(|value| value.as_u64());
|
||||
let balance_index = balance.get("accountIndex").and_then(|value| return value.as_u64());
|
||||
let balance_index = match balance_index {
|
||||
Some(balance_index) => balance_index,
|
||||
None => continue,
|
||||
@@ -1410,8 +1410,8 @@ fn token_balance_amount_for_account_index(
|
||||
}
|
||||
let amount_text = balance
|
||||
.get("uiTokenAmount")
|
||||
.and_then(|value| value.get("amount"))
|
||||
.and_then(|value| value.as_str());
|
||||
.and_then(|value| return value.get("amount"))
|
||||
.and_then(|value| return value.as_str());
|
||||
let amount_text = match amount_text {
|
||||
Some(amount_text) => amount_text,
|
||||
None => return None,
|
||||
@@ -1504,7 +1504,7 @@ mod tests {
|
||||
stack_height,
|
||||
accounts_json: "[]".to_string(),
|
||||
data_json: None,
|
||||
parsed_type: parsed_type.map(|value| value.to_string()),
|
||||
parsed_type: parsed_type.map(|value| return value.to_string()),
|
||||
parsed_json,
|
||||
created_at: chrono::Utc::now(),
|
||||
};
|
||||
|
||||
@@ -352,41 +352,12 @@ fn known_dex_program_match(
|
||||
Some(program_id) => program_id,
|
||||
None => return None,
|
||||
};
|
||||
let protocol_name = if program_id == crate::RAYDIUM_AMM_V4_PROGRAM_ID {
|
||||
"raydium_amm_v4"
|
||||
} else if program_id == crate::RAYDIUM_CPMM_PROGRAM_ID {
|
||||
"raydium_cpmm"
|
||||
} else if program_id == crate::RAYDIUM_CLMM_PROGRAM_ID {
|
||||
"raydium_clmm"
|
||||
} else if program_id == crate::RAYDIUM_LAUNCHLAB_PROGRAM_ID {
|
||||
"raydium_launchlab"
|
||||
} else if program_id == crate::RAYDIUM_AMM_ROUTING_PROGRAM_ID {
|
||||
"raydium_router"
|
||||
} else if program_id == crate::RAYDIUM_STABLE_SWAP_AMM_PROGRAM_ID {
|
||||
"raydium_stable_swap"
|
||||
} else if program_id == crate::PUMP_FUN_PROGRAM_ID {
|
||||
"pump_fun"
|
||||
} else if program_id == crate::PUMP_SWAP_PROGRAM_ID {
|
||||
"pump_swap"
|
||||
} else if program_id == crate::METEORA_DBC_PROGRAM_ID {
|
||||
"meteora_dbc"
|
||||
} else if program_id == crate::METEORA_DLMM_PROGRAM_ID {
|
||||
"meteora_dlmm"
|
||||
} else if program_id == crate::METEORA_DAMM_V1_PROGRAM_ID {
|
||||
"meteora_damm_v1"
|
||||
} else if program_id == crate::METEORA_DAMM_V2_PROGRAM_ID {
|
||||
"meteora_damm_v2"
|
||||
} else if program_id == crate::ORCA_WHIRLPOOLS_PROGRAM_ID {
|
||||
"orca_whirlpools"
|
||||
} else if program_id == crate::FLUXBEAM_PROGRAM_ID {
|
||||
"fluxbeam"
|
||||
} else if program_id == crate::DEXLAB_PROGRAM_ID {
|
||||
"dexlab"
|
||||
} else {
|
||||
return None;
|
||||
let matrix_entry = match crate::dex_support_matrix_entry_by_program_id(program_id) {
|
||||
Some(matrix_entry) => matrix_entry,
|
||||
None => return None,
|
||||
};
|
||||
return Some(KnownDexProgramMatch {
|
||||
protocol_name,
|
||||
protocol_name: matrix_entry.code,
|
||||
program_id: program_id.to_string(),
|
||||
instruction_id: instruction.id,
|
||||
instruction_index: instruction.instruction_index,
|
||||
@@ -442,6 +413,26 @@ mod tests {
|
||||
assert_eq!(program_match.instruction_index, 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn known_program_match_uses_support_matrix_for_priority_dexes() {
|
||||
let samples = [
|
||||
(crate::PUMP_SWAP_PROGRAM_ID, "pump_swap"),
|
||||
(crate::RAYDIUM_CPMM_PROGRAM_ID, "raydium_cpmm"),
|
||||
(crate::RAYDIUM_CLMM_PROGRAM_ID, "raydium_clmm"),
|
||||
(crate::METEORA_DLMM_PROGRAM_ID, "meteora_dlmm"),
|
||||
(crate::METEORA_DAMM_V1_PROGRAM_ID, "meteora_damm_v1"),
|
||||
];
|
||||
for (program_id, expected_protocol) in samples {
|
||||
let instruction = test_instruction(Some(program_id.to_string()));
|
||||
let program_match = match super::known_dex_program_match(&instruction) {
|
||||
Some(program_match) => program_match,
|
||||
None => panic!("expected program match for {}", expected_protocol),
|
||||
};
|
||||
assert_eq!(program_match.protocol_name, expected_protocol);
|
||||
assert_eq!(program_match.program_id, program_id);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn unknown_program_id_is_not_matched() {
|
||||
let instruction =
|
||||
|
||||
Reference in New Issue
Block a user