diff --git a/CHANGELOG.md b/CHANGELOG.md
index 3d05580..b156089 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -69,4 +69,4 @@
0.7.36 - Consolidation de la famille Meteora : corpus mixte `meteora_damm_v1`, `meteora_damm_v2`, `meteora_dbc` et `meteora_dlmm`, correction des discriminants DAMM v2 / DBC, validation du profil `0.7.36_meteora_family_consolidation`, et reclassement explicite des swaps DAMM v2 / DBC sans payload montant/prix en `non_actionable_trade` afin d’éviter tout trade/candle artificiel.
0.7.37 - Première tranche metadata/catalog : ajout du profil `0.7.37_token_metadata_catalog_enrichment`, exposition des compteurs metadata dans diagnostics/validation et raccordement UI Demo Pipeline 2 sans rendre les metadata manquantes bloquantes.
0.7.38 - Priorisation des metadata manquantes : ajout du profil `0.7.38_token_metadata_gap_prioritization`, samples `tokenMetadataGapSamples`, priorités tradable/quote/catalog, raccordement UI Demo Pipeline 2 et maintien du caractère non bloquant des metadata incomplètes.
-0.7.39 - Tranche sûre launch surfaces : ensemencement contrôlé des origins LaunchLab/Launchpad/LetsBonk/Bonk.fun/Bags/Moonshot/Moonit/Boop.fun/Believe, Heaven préparé mais désactivé, mappings génériques vérifiés, samples diagnostics `launchOriginSamples` / `poolOriginSamples`, profil `0.7.39_launch_surface_origin_baseline`, raccordement Demo Pipeline 2 et maintien de l’invariant sans faux trade/candle ni program id fictif.
+0.7.39 - Réorientation DEX-first : ajout du rôle stratégique `surfaceRole` dans la matrice DEX (`dex_effective`, `aggregator_router`, `launch_surface`, `to_verify`), profil `0.7.39_dex_first_effective_swap_surfaces`, ajout de `metaDAO` et `Printr` comme entrées `to_verify` sans `program_id`, maintien des launch surfaces en état différé et des invariants sans faux trade/candle ni program id fictif.
diff --git a/README.md b/README.md
index 123017c..c861425 100644
--- a/README.md
+++ b/README.md
@@ -114,7 +114,7 @@ La distinction de travail à partir de `0.7.39` est la suivante :
### 4.1. Matrice de travail
-Depuis `0.7.29`, la matrice de support DEX est portée par `kb_lib/src/dex_support_matrix.rs`. Elle centralise le code interne, la famille, la version, le type de surface, les program ids vérifiés localement, le statut de support, les capacités actuelles et les raisons de skip.
+Depuis `0.7.29`, la matrice de support DEX est portée par `kb_lib/src/dex_support_matrix.rs`. Elle centralise le code interne, la famille, la version, le type technique de surface, le rôle stratégique de surface (`dex_effective`, `aggregator_router`, `launch_surface`, `to_verify`), les program ids vérifiés localement, le statut de support, les capacités actuelles et les raisons de skip.
Depuis `0.7.30`, les événements décodés reçoivent aussi une classification plus fine : `eventLifecycleKind`, `eventActionability` et `nonTradeUseful`. Cette classification sert aux diagnostics et prépare la matérialisation future des événements non-trade sans alimenter directement les trades/candles.
diff --git a/ROADMAP.md b/ROADMAP.md
index 017d7f2..7070c0f 100644
--- a/ROADMAP.md
+++ b/ROADMAP.md
@@ -787,7 +787,7 @@ Contraintes :
Réalisé :
- ajouter `kb_lib/src/dex_support_matrix.rs` comme source commune de metadata DEX/surfaces ;
-- exposer pour chaque entrée : code interne, famille, version, type de surface, program id connu ou à vérifier, support actuel, statut, confiance, raisons de skip et activation catalogue ;
+- exposer pour chaque entrée : code interne, famille, version, type technique de surface, rôle stratégique de surface, program id connu ou à vérifier, support actuel, statut, confiance, raisons de skip et activation catalogue ;
- raccorder `dex_catalog`, `transaction_classification` et `protocol_candidate_recording` à cette matrice ;
- ajouter le profil `0.7.29_multi_dex_matrix_baseline` ;
- exposer la matrice dans le rapport de validation local ;
@@ -947,9 +947,9 @@ Décision : `0.7.38` est clos. La clôture `0.7.38-B` conserve la logique de sta
### 6.071. Version `0.7.39` — Réorientation DEX-first et inventaire des DEX effectifs
Objectif : remplacer la priorité précédemment donnée aux launch surfaces par une consolidation des vrais DEX sur lesquels les swaps et événements de marché sont exécutés.
-À faire :
+Réalisé :
-- modifier la matrice DEX pour distinguer explicitement : `dex_effective`, `aggregator/router`, `launch_surface`, `to_verify` ;
+- modifier la matrice DEX pour distinguer explicitement les rôles `dex_effective`, `aggregator_router`, `launch_surface`, `to_verify` ;
- vérifier les DEX de swap principaux déjà connus : `pump_swap`, `raydium_cpmm`, `raydium_clmm`, `raydium_amm_v4`, `raydium_stable_swap`, `meteora_dlmm`, `meteora_damm_v1`, `meteora_damm_v2`, `meteora_dbc`, `orca_whirlpools`, `fluxbeam`, `dexlab` ;
- ajouter `metaDAO` et `Printr` comme entrées `to_verify` dans la matrice, sans `program_id` tant qu’ils ne sont pas prouvés ;
- rechercher ou confirmer les `program_id` inconnus depuis les corpus locaux, les protocol candidates, DEX Screener, les explorateurs et les transactions résolues ;
diff --git a/kb_demo_app/frontend/demo_pipeline2.html b/kb_demo_app/frontend/demo_pipeline2.html
index ba99b6a..7f6cf60 100644
--- a/kb_demo_app/frontend/demo_pipeline2.html
+++ b/kb_demo_app/frontend/demo_pipeline2.html
@@ -166,7 +166,7 @@
Validation profile
- 0.7.39 — launch surface origin baseline
+ 0.7.39 — DEX-first effective swap surfaces
0.7.38 — token metadata gap prioritization
0.7.37 — token metadata/catalog enrichment
0.7.36 — Meteora family consolidation
diff --git a/kb_demo_app/frontend/ts/bindings/DemoPipeline2DexSupportMatrixEntry.ts b/kb_demo_app/frontend/ts/bindings/DemoPipeline2DexSupportMatrixEntry.ts
index e0878d0..542d8e6 100644
--- a/kb_demo_app/frontend/ts/bindings/DemoPipeline2DexSupportMatrixEntry.ts
+++ b/kb_demo_app/frontend/ts/bindings/DemoPipeline2DexSupportMatrixEntry.ts
@@ -21,9 +21,13 @@ family: string,
*/
version: string,
/**
- * Surface type: launch, bonding curve, AMM, CLMM, DLMM, router, aggregator or unknown.
+ * Technical surface type: launch, bonding curve, AMM, CLMM, DLMM, router, aggregator or unknown.
*/
surfaceType: string,
+/**
+ * Strategic surface role: dex_effective, aggregator_router, launch_surface or to_verify.
+ */
+surfaceRole: string,
/**
* Primary Solana program id, when verified in local constants or docs.
*/
diff --git a/kb_demo_app/src/demo_pipeline2.rs b/kb_demo_app/src/demo_pipeline2.rs
index 1b3b0ba..77d45e2 100644
--- a/kb_demo_app/src/demo_pipeline2.rs
+++ b/kb_demo_app/src/demo_pipeline2.rs
@@ -225,8 +225,10 @@ pub(crate) struct DemoPipeline2DexSupportMatrixEntry {
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.
+ /// Technical surface type: launch, bonding curve, AMM, CLMM, DLMM, router, aggregator or unknown.
pub surface_type: std::string::String,
+ /// Strategic surface role: dex_effective, aggregator_router, launch_surface or to_verify.
+ pub surface_role: std::string::String,
/// Primary Solana program id, when verified in local constants or docs.
pub program_id: std::option::Option,
/// Optional router program id, when this entry uses a distinct router.
@@ -1263,7 +1265,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_launch_surface_origin_baseline".to_string(),
+ None => "0.7.39_dex_first_effective_swap_surfaces".to_string(),
};
let run_result = match profile_code.as_str() {
"0.7.27" | "0.7.27_dexes_non_regression" => {
@@ -1302,7 +1304,7 @@ 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_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
},
other => Err(kb_lib::Error::InvalidState(format!(
@@ -1762,6 +1764,7 @@ fn demo_pipeline2_map_dex_support_matrix_entry(
family: entry.family,
version: entry.version,
surface_type: entry.surface_type,
+ surface_role: entry.surface_role,
program_id: entry.program_id,
router_program_id: entry.router_program_id,
program_id_status: entry.program_id_status,
diff --git a/kb_lib/src/db/queries/dex.rs b/kb_lib/src/db/queries/dex.rs
index f19b330..c997918 100644
--- a/kb_lib/src/db/queries/dex.rs
+++ b/kb_lib/src/db/queries/dex.rs
@@ -186,16 +186,16 @@ mod tests {
.expect("database init must succeed");
let dex_id = crate::query_dexs_upsert(
&database,
- &crate::DexDto::new("raydium".to_string(), "Raydium".to_string(), None, None, true),
+ &crate::DexDto::new("raydium_amm_v4".to_string(), "Raydium AMM v4".to_string(), None, None, true),
)
.await
.expect("dex upsert must succeed");
assert!(dex_id > 0);
- let dex = crate::query_dexs_get_by_code(&database, "raydium")
+ let dex = crate::query_dexs_get_by_code(&database, "raydium_amm_v4")
.await
.expect("get dex must succeed");
assert!(dex.is_some());
- assert_eq!(dex.expect("dex must exist").name, "Raydium");
+ assert_eq!(dex.expect("dex must exist").name, "Raydium AMM v4");
let dexes = crate::query_dexs_list(&database).await.expect("list dexes must succeed");
assert_eq!(dexes.len(), 1);
}
diff --git a/kb_lib/src/db/queries/pair.rs b/kb_lib/src/db/queries/pair.rs
index 2446152..7f20b16 100644
--- a/kb_lib/src/db/queries/pair.rs
+++ b/kb_lib/src/db/queries/pair.rs
@@ -225,7 +225,7 @@ mod tests {
.expect("database init must succeed");
let dex_id = crate::query_dexs_upsert(
&database,
- &crate::DexDto::new("raydium".to_string(), "Raydium".to_string(), None, None, true),
+ &crate::DexDto::new("raydium_amm_v4".to_string(), "Raydium AMM v4".to_string(), None, None, true),
)
.await
.expect("dex upsert must succeed");
diff --git a/kb_lib/src/db/queries/pool.rs b/kb_lib/src/db/queries/pool.rs
index 1124d80..3179d33 100644
--- a/kb_lib/src/db/queries/pool.rs
+++ b/kb_lib/src/db/queries/pool.rs
@@ -181,7 +181,7 @@ mod tests {
.expect("database init must succeed");
let dex_id = crate::query_dexs_upsert(
&database,
- &crate::DexDto::new("raydium".to_string(), "Raydium".to_string(), None, None, true),
+ &crate::DexDto::new("raydium_amm_v4".to_string(), "Raydium AMM v4".to_string(), None, None, true),
)
.await
.expect("dex upsert must succeed");
diff --git a/kb_lib/src/db/queries/pool_listing.rs b/kb_lib/src/db/queries/pool_listing.rs
index dec1111..48ae893 100644
--- a/kb_lib/src/db/queries/pool_listing.rs
+++ b/kb_lib/src/db/queries/pool_listing.rs
@@ -202,7 +202,7 @@ mod tests {
.expect("database init must succeed");
let dex_id = crate::query_dexs_upsert(
&database,
- &crate::DexDto::new("raydium".to_string(), "Raydium".to_string(), None, None, true),
+ &crate::DexDto::new("raydium_amm_v4".to_string(), "Raydium AMM v4".to_string(), None, None, true),
)
.await
.expect("dex upsert must succeed");
diff --git a/kb_lib/src/db/queries/pool_token.rs b/kb_lib/src/db/queries/pool_token.rs
index 1539633..70a9201 100644
--- a/kb_lib/src/db/queries/pool_token.rs
+++ b/kb_lib/src/db/queries/pool_token.rs
@@ -140,7 +140,7 @@ mod tests {
.expect("database init must succeed");
let dex_id = crate::query_dexs_upsert(
&database,
- &crate::DexDto::new("raydium".to_string(), "Raydium".to_string(), None, None, true),
+ &crate::DexDto::new("raydium_amm_v4".to_string(), "Raydium AMM v4".to_string(), None, None, true),
)
.await
.expect("dex upsert must succeed");
diff --git a/kb_lib/src/db/queries/token_burn_event.rs b/kb_lib/src/db/queries/token_burn_event.rs
index 40b6cd0..1affc62 100644
--- a/kb_lib/src/db/queries/token_burn_event.rs
+++ b/kb_lib/src/db/queries/token_burn_event.rs
@@ -167,7 +167,7 @@ mod tests {
.expect("database init must succeed");
let dex_id = crate::query_dexs_upsert(
&database,
- &crate::DexDto::new("raydium".to_string(), "Raydium".to_string(), None, None, true),
+ &crate::DexDto::new("raydium_amm_v4".to_string(), "Raydium AMM v4".to_string(), None, None, true),
)
.await
.expect("dex upsert must succeed");
diff --git a/kb_lib/src/detect/service.rs b/kb_lib/src/detect/service.rs
index 7597d3a..7012f86 100644
--- a/kb_lib/src/detect/service.rs
+++ b/kb_lib/src/detect/service.rs
@@ -353,8 +353,8 @@ mod tests {
let dex_id = crate::query_dexs_upsert(
service.database().as_ref(),
&crate::DexDto::new(
- "raydium".to_string(),
- "Raydium".to_string(),
+ "raydium_amm_v4".to_string(),
+ "Raydium AMM v4".to_string(),
Some("DexProgram111111111111111111111111111111111".to_string()),
None,
true,
diff --git a/kb_lib/src/detect/solana_ws.rs b/kb_lib/src/detect/solana_ws.rs
index 643e587..c5d796a 100644
--- a/kb_lib/src/detect/solana_ws.rs
+++ b/kb_lib/src/detect/solana_ws.rs
@@ -1009,8 +1009,8 @@ mod tests {
let dex_id = crate::query_dexs_upsert(
persistence.database().as_ref(),
&crate::DexDto::new(
- "raydium".to_string(),
- "Raydium".to_string(),
+ "raydium_amm_v4".to_string(),
+ "Raydium AMM v4".to_string(),
Some("DexProgram111111111111111111111111111111111".to_string()),
None,
true,
diff --git a/kb_lib/src/dex_catalog.rs b/kb_lib/src/dex_catalog.rs
index dda43fe..948d771 100644
--- a/kb_lib/src/dex_catalog.rs
+++ b/kb_lib/src/dex_catalog.rs
@@ -92,7 +92,7 @@ mod tests {
#[test]
fn known_active_dexes_are_available_from_catalog() {
let codes = [
- "raydium",
+ "raydium_amm_v4",
"raydium_cpmm",
"raydium_clmm",
"pump_fun",
diff --git a/kb_lib/src/dex_detect.rs b/kb_lib/src/dex_detect.rs
index 22c6944..3afc4d4 100644
--- a/kb_lib/src/dex_detect.rs
+++ b/kb_lib/src/dex_detect.rs
@@ -149,7 +149,7 @@ impl DexDetectService {
decoded_event: &crate::DexDecodedEventDto,
) -> Result {
let dex_id_result =
- crate::dex_catalog::ensure_known_dex(self.database.as_ref(), "raydium").await;
+ crate::dex_catalog::ensure_known_dex(self.database.as_ref(), "raydium_amm_v4").await;
let dex_id = match dex_id_result {
Ok(dex_id) => dex_id,
Err(error) => return Err(error),
diff --git a/kb_lib/src/dex_support_matrix.rs b/kb_lib/src/dex_support_matrix.rs
index 5bea773..c3d7586 100644
--- a/kb_lib/src/dex_support_matrix.rs
+++ b/kb_lib/src/dex_support_matrix.rs
@@ -19,8 +19,10 @@ pub struct DexSupportMatrixEntry {
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`.
+ /// Technical surface type: `launch`, `bonding_curve`, `AMM`, `CLMM`, `DLMM`, `router`, `aggregator` or `unknown`.
pub surface_type: &'static str,
+ /// Strategic surface role used by the DEX-first roadmap: `dex_effective`, `aggregator_router`, `launch_surface` or `to_verify`.
+ pub surface_role: &'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.
@@ -63,8 +65,10 @@ pub struct DexSupportMatrixEntryDto {
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`.
+ /// Technical surface type: `launch`, `bonding_curve`, `AMM`, `CLMM`, `DLMM`, `router`, `aggregator` or `unknown`.
pub surface_type: std::string::String,
+ /// Strategic surface role used by the DEX-first roadmap: `dex_effective`, `aggregator_router`, `launch_surface` or `to_verify`.
+ pub surface_role: std::string::String,
/// Primary Solana program id, when verified in local constants or docs.
pub program_id: std::option::Option,
/// Optional router program id, when this entry uses a distinct router.
@@ -104,6 +108,7 @@ impl DexSupportMatrixEntryDto {
family: entry.family.to_string(),
version: entry.version.to_string(),
surface_type: entry.surface_type.to_string(),
+ surface_role: entry.surface_role.to_string(),
program_id: match entry.program_id {
Some(program_id) => Some(program_id.to_string()),
None => None,
@@ -138,6 +143,7 @@ const DEX_SUPPORT_MATRIX_ENTRIES: &[DexSupportMatrixEntry] = &[
family: "pump",
version: "bonding_curve",
surface_type: "launch",
+ surface_role: "launch_surface",
program_id: Some(crate::PUMP_FUN_PROGRAM_ID),
router_program_id: None,
program_id_status: "known",
@@ -159,6 +165,7 @@ const DEX_SUPPORT_MATRIX_ENTRIES: &[DexSupportMatrixEntry] = &[
family: "pump",
version: "amm",
surface_type: "AMM",
+ surface_role: "dex_effective",
program_id: Some(crate::PUMP_SWAP_PROGRAM_ID),
router_program_id: None,
program_id_status: "known",
@@ -180,6 +187,7 @@ const DEX_SUPPORT_MATRIX_ENTRIES: &[DexSupportMatrixEntry] = &[
family: "raydium",
version: "cpmm",
surface_type: "AMM",
+ surface_role: "dex_effective",
program_id: Some(crate::RAYDIUM_CPMM_PROGRAM_ID),
router_program_id: None,
program_id_status: "known",
@@ -201,6 +209,7 @@ const DEX_SUPPORT_MATRIX_ENTRIES: &[DexSupportMatrixEntry] = &[
family: "raydium",
version: "clmm",
surface_type: "CLMM",
+ surface_role: "dex_effective",
program_id: Some(crate::RAYDIUM_CLMM_PROGRAM_ID),
router_program_id: None,
program_id_status: "known",
@@ -222,6 +231,7 @@ const DEX_SUPPORT_MATRIX_ENTRIES: &[DexSupportMatrixEntry] = &[
family: "raydium",
version: "amm_v4",
surface_type: "AMM",
+ surface_role: "dex_effective",
program_id: Some(crate::RAYDIUM_AMM_V4_PROGRAM_ID),
router_program_id: None,
program_id_status: "known",
@@ -237,33 +247,13 @@ const DEX_SUPPORT_MATRIX_ENTRIES: &[DexSupportMatrixEntry] = &[
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",
+ surface_role: "launch_surface",
program_id: Some(crate::RAYDIUM_LAUNCHLAB_PROGRAM_ID),
router_program_id: None,
program_id_status: "known",
@@ -285,6 +275,7 @@ const DEX_SUPPORT_MATRIX_ENTRIES: &[DexSupportMatrixEntry] = &[
family: "raydium",
version: "unknown",
surface_type: "launch",
+ surface_role: "launch_surface",
program_id: None,
router_program_id: None,
program_id_status: "to_verify",
@@ -306,6 +297,7 @@ const DEX_SUPPORT_MATRIX_ENTRIES: &[DexSupportMatrixEntry] = &[
family: "raydium",
version: "router",
surface_type: "router",
+ surface_role: "aggregator_router",
program_id: Some(crate::RAYDIUM_AMM_ROUTING_PROGRAM_ID),
router_program_id: None,
program_id_status: "known",
@@ -327,6 +319,7 @@ const DEX_SUPPORT_MATRIX_ENTRIES: &[DexSupportMatrixEntry] = &[
family: "raydium",
version: "stable_swap",
surface_type: "AMM",
+ surface_role: "dex_effective",
program_id: Some(crate::RAYDIUM_STABLE_SWAP_AMM_PROGRAM_ID),
router_program_id: None,
program_id_status: "known",
@@ -348,6 +341,7 @@ const DEX_SUPPORT_MATRIX_ENTRIES: &[DexSupportMatrixEntry] = &[
family: "meteora",
version: "dlmm",
surface_type: "DLMM",
+ surface_role: "dex_effective",
program_id: Some(crate::METEORA_DLMM_PROGRAM_ID),
router_program_id: None,
program_id_status: "known",
@@ -369,6 +363,7 @@ const DEX_SUPPORT_MATRIX_ENTRIES: &[DexSupportMatrixEntry] = &[
family: "meteora",
version: "unknown",
surface_type: "unknown",
+ surface_role: "to_verify",
program_id: None,
router_program_id: None,
program_id_status: "to_verify",
@@ -390,6 +385,7 @@ const DEX_SUPPORT_MATRIX_ENTRIES: &[DexSupportMatrixEntry] = &[
family: "meteora",
version: "damm_v1",
surface_type: "AMM",
+ surface_role: "dex_effective",
program_id: Some(crate::METEORA_DAMM_V1_PROGRAM_ID),
router_program_id: None,
program_id_status: "known",
@@ -411,6 +407,7 @@ const DEX_SUPPORT_MATRIX_ENTRIES: &[DexSupportMatrixEntry] = &[
family: "meteora",
version: "damm_v2",
surface_type: "AMM",
+ surface_role: "dex_effective",
program_id: Some(crate::METEORA_DAMM_V2_PROGRAM_ID),
router_program_id: None,
program_id_status: "known",
@@ -432,6 +429,7 @@ const DEX_SUPPORT_MATRIX_ENTRIES: &[DexSupportMatrixEntry] = &[
family: "meteora",
version: "dbc",
surface_type: "bonding_curve",
+ surface_role: "dex_effective",
program_id: Some(crate::METEORA_DBC_PROGRAM_ID),
router_program_id: None,
program_id_status: "known",
@@ -453,6 +451,7 @@ const DEX_SUPPORT_MATRIX_ENTRIES: &[DexSupportMatrixEntry] = &[
family: "orca",
version: "whirlpools",
surface_type: "CLMM",
+ surface_role: "dex_effective",
program_id: Some(crate::ORCA_WHIRLPOOLS_PROGRAM_ID),
router_program_id: None,
program_id_status: "known",
@@ -474,6 +473,7 @@ const DEX_SUPPORT_MATRIX_ENTRIES: &[DexSupportMatrixEntry] = &[
family: "fluxbeam",
version: "unknown",
surface_type: "AMM",
+ surface_role: "dex_effective",
program_id: Some(crate::FLUXBEAM_PROGRAM_ID),
router_program_id: None,
program_id_status: "known",
@@ -495,6 +495,7 @@ const DEX_SUPPORT_MATRIX_ENTRIES: &[DexSupportMatrixEntry] = &[
family: "dexlab",
version: "unknown",
surface_type: "AMM",
+ surface_role: "dex_effective",
program_id: Some(crate::DEXLAB_PROGRAM_ID),
router_program_id: None,
program_id_status: "known",
@@ -516,6 +517,7 @@ const DEX_SUPPORT_MATRIX_ENTRIES: &[DexSupportMatrixEntry] = &[
family: "bags",
version: "unknown",
surface_type: "launch",
+ surface_role: "launch_surface",
program_id: None,
router_program_id: None,
program_id_status: "unknown",
@@ -537,6 +539,7 @@ const DEX_SUPPORT_MATRIX_ENTRIES: &[DexSupportMatrixEntry] = &[
family: "bonk",
version: "unknown",
surface_type: "launch",
+ surface_role: "launch_surface",
program_id: None,
router_program_id: None,
program_id_status: "unknown",
@@ -558,6 +561,7 @@ const DEX_SUPPORT_MATRIX_ENTRIES: &[DexSupportMatrixEntry] = &[
family: "bonk",
version: "unknown",
surface_type: "launch",
+ surface_role: "launch_surface",
program_id: None,
router_program_id: None,
program_id_status: "unknown",
@@ -579,6 +583,7 @@ const DEX_SUPPORT_MATRIX_ENTRIES: &[DexSupportMatrixEntry] = &[
family: "bonk",
version: "unknown",
surface_type: "launch",
+ surface_role: "launch_surface",
program_id: None,
router_program_id: None,
program_id_status: "unknown",
@@ -600,6 +605,7 @@ const DEX_SUPPORT_MATRIX_ENTRIES: &[DexSupportMatrixEntry] = &[
family: "okx",
version: "unknown",
surface_type: "aggregator",
+ surface_role: "aggregator_router",
program_id: None,
router_program_id: None,
program_id_status: "unknown",
@@ -621,6 +627,7 @@ const DEX_SUPPORT_MATRIX_ENTRIES: &[DexSupportMatrixEntry] = &[
family: "boop",
version: "unknown",
surface_type: "launch",
+ surface_role: "launch_surface",
program_id: None,
router_program_id: None,
program_id_status: "unknown",
@@ -642,6 +649,7 @@ const DEX_SUPPORT_MATRIX_ENTRIES: &[DexSupportMatrixEntry] = &[
family: "moonshot",
version: "unknown",
surface_type: "launch",
+ surface_role: "launch_surface",
program_id: None,
router_program_id: None,
program_id_status: "unknown",
@@ -663,6 +671,7 @@ const DEX_SUPPORT_MATRIX_ENTRIES: &[DexSupportMatrixEntry] = &[
family: "believe",
version: "unknown",
surface_type: "launch",
+ surface_role: "launch_surface",
program_id: None,
router_program_id: None,
program_id_status: "unknown",
@@ -678,12 +687,57 @@ const DEX_SUPPORT_MATRIX_ENTRIES: &[DexSupportMatrixEntry] = &[
skip_reason: Some("program_id_to_verify"),
catalog_enabled: false,
},
+ DexSupportMatrixEntry {
+ code: "metadao",
+ display_name: "metaDAO",
+ family: "metadao",
+ version: "unknown",
+ surface_type: "unknown",
+ surface_role: "to_verify",
+ 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: "printr",
+ display_name: "Printr",
+ family: "printr",
+ version: "unknown",
+ surface_type: "unknown",
+ surface_role: "to_verify",
+ 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: "zora",
display_name: "Zora",
family: "zora",
version: "unknown",
surface_type: "unknown",
+ surface_role: "to_verify",
program_id: None,
router_program_id: None,
program_id_status: "to_verify",
@@ -705,6 +759,7 @@ const DEX_SUPPORT_MATRIX_ENTRIES: &[DexSupportMatrixEntry] = &[
family: "moonit",
version: "unknown",
surface_type: "launch",
+ surface_role: "launch_surface",
program_id: None,
router_program_id: None,
program_id_status: "unknown",
@@ -726,6 +781,7 @@ const DEX_SUPPORT_MATRIX_ENTRIES: &[DexSupportMatrixEntry] = &[
family: "launchbeam",
version: "unknown",
surface_type: "launch",
+ surface_role: "launch_surface",
program_id: None,
router_program_id: None,
program_id_status: "unknown",
@@ -747,6 +803,7 @@ const DEX_SUPPORT_MATRIX_ENTRIES: &[DexSupportMatrixEntry] = &[
family: "heaven",
version: "unknown",
surface_type: "launch",
+ surface_role: "launch_surface",
program_id: None,
router_program_id: None,
program_id_status: "unknown",
@@ -812,7 +869,7 @@ pub fn dex_support_matrix_entry_by_program_id(
#[cfg(test)]
mod tests {
#[test]
- fn matrix_contains_required_0_7_29_entries() {
+ fn matrix_contains_required_0_7_39_dex_first_entries() {
let codes = [
"pump_fun",
"pump_swap",
@@ -837,6 +894,8 @@ mod tests {
"launchbeam",
"heaven",
"dexlab",
+ "metadao",
+ "printr",
];
for code in codes {
let entry = crate::dex_support_matrix_entry_by_code(code);
@@ -860,6 +919,8 @@ mod tests {
"moonit",
"launchbeam",
"heaven",
+ "metadao",
+ "printr",
];
for code in codes {
let entry = match crate::dex_support_matrix_entry_by_code(code) {
@@ -932,7 +993,7 @@ mod tests {
}
#[test]
- fn matrix_keeps_0_7_39_launch_surfaces_non_trade_materialized() {
+ fn matrix_keeps_0_7_39_deferred_surfaces_non_trade_materialized() {
let codes = [
"raydium_launchlab",
"raydium_launchpad",
@@ -958,6 +1019,50 @@ mod tests {
}
}
+ #[test]
+ fn matrix_marks_effective_dexes_for_dex_first_work() {
+ let codes = [
+ "pump_swap",
+ "raydium_cpmm",
+ "raydium_clmm",
+ "raydium_amm_v4",
+ "raydium_stable_swap",
+ "meteora_dlmm",
+ "meteora_damm_v1",
+ "meteora_damm_v2",
+ "meteora_dbc",
+ "orca_whirlpools",
+ "fluxbeam",
+ "dexlab",
+ ];
+ 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_eq!(entry.surface_role, "dex_effective", "{} must be DEX-first", code);
+ }
+ }
+
+ #[test]
+ fn matrix_keeps_metadao_and_printr_unverified_without_program_id() {
+ let codes = ["metadao", "printr"];
+ 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_eq!(entry.surface_role, "to_verify");
+ assert_eq!(entry.program_id_status, "to_verify");
+ assert!(entry.program_id.is_none());
+ assert!(!entry.decoded);
+ assert!(!entry.materialized);
+ assert!(!entry.trade_candidate);
+ assert!(!entry.candle_candidate);
+ assert!(!entry.catalog_enabled);
+ }
+ }
+
#[test]
fn matrix_dto_preserves_core_fields() {
let entry = match crate::dex_support_matrix_entry_by_code("pump_swap") {
@@ -967,6 +1072,7 @@ mod tests {
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_eq!(dto.surface_role, "dex_effective");
assert!(dto.trade_candidate);
assert!(dto.candle_candidate);
}
diff --git a/kb_lib/src/launch_origin.rs b/kb_lib/src/launch_origin.rs
index 8f03994..6d5b8ed 100644
--- a/kb_lib/src/launch_origin.rs
+++ b/kb_lib/src/launch_origin.rs
@@ -104,7 +104,7 @@ impl LaunchOriginService {
return Self { database, persistence };
}
- /// Ensures that every built-in launch surface tracked by `0.7.39` exists.
+ /// Ensures that every deferred built-in launch surface exists.
pub async fn ensure_target_launch_surfaces(&self) -> Result, crate::Error> {
let mut surface_ids = std::vec::Vec::new();
for spec in BUILTIN_LAUNCH_SURFACE_SPECS {
diff --git a/kb_lib/src/local_pipeline_validation.rs b/kb_lib/src/local_pipeline_validation.rs
index a5fda43..de08ff4 100644
--- a/kb_lib/src/local_pipeline_validation.rs
+++ b/kb_lib/src/local_pipeline_validation.rs
@@ -278,18 +278,26 @@ impl LocalPipelineValidationConfig {
return config;
}
- /// Builds the `0.7.39` launch surface origin baseline validation config.
+ /// Builds the `0.7.39` DEX-first effective swap surfaces validation config.
///
/// This profile keeps the `0.7.38` metadata-gap semantics and requires the
- /// static DEX matrix invariants that prevent planned launch surfaces from
- /// being treated as priced trade/candle producers before corpus-backed
- /// decoders are available.
- pub fn v0_7_39_launch_surface_origin_baseline() -> Self {
+ /// static DEX matrix invariants that distinguish effective swap surfaces,
+ /// routers/aggregators, deferred launch surfaces and unverified candidates.
+ pub fn v0_7_39_dex_first_effective_swap_surfaces() -> Self {
let mut config = Self::v0_7_38_token_metadata_gap_prioritization();
- config.profile_code = "0.7.39_launch_surface_origin_baseline".to_string();
+ config.profile_code = "0.7.39_dex_first_effective_swap_surfaces".to_string();
config.require_dex_support_matrix_semantics = true;
return config;
}
+
+ /// Builds the legacy `0.7.39` launch-surface validation alias.
+ ///
+ /// The implementation now delegates to the DEX-first profile so callers that
+ /// still pass the previous profile code do not accidentally revive the old
+ /// launch-surface priority.
+ pub fn v0_7_39_launch_surface_origin_baseline() -> Self {
+ return Self::v0_7_39_dex_first_effective_swap_surfaces();
+ }
}
/// A single local pipeline validation issue.
@@ -519,11 +527,11 @@ impl LocalPipelineValidationService {
return self.validate_current_database(&config).await;
}
- /// Diagnoses the current database with the `0.7.39` launch-origin baseline profile.
+ /// Diagnoses the current database with the `0.7.39` DEX-first profile.
pub async fn validate_v0_7_39_current_database(
&self,
) -> Result {
- let config = crate::LocalPipelineValidationConfig::v0_7_39_launch_surface_origin_baseline();
+ let config = crate::LocalPipelineValidationConfig::v0_7_39_dex_first_effective_swap_surfaces();
return self.validate_current_database(&config).await;
}
}
@@ -649,6 +657,7 @@ pub fn validate_local_pipeline_diagnostics_summary(
|| config.profile_code == "0.7.36_meteora_family_consolidation"
|| config.profile_code == "0.7.37_token_metadata_catalog_enrichment"
|| config.profile_code == "0.7.38_token_metadata_gap_prioritization"
+ || config.profile_code == "0.7.39_dex_first_effective_swap_surfaces"
|| config.profile_code == "0.7.39_launch_surface_origin_baseline";
if config.require_all_expected_dexes || missing_expected_dex_is_warning {
for expected_dex_code in &expected_dex_codes {
@@ -898,16 +907,48 @@ fn validate_dex_support_matrix_semantics(
});
}
if (entry.status == "planned" || entry.status == "to_verify" || entry.status == "unknown")
- && entry.surface_type == "launch"
+ && (entry.surface_role == "launch_surface" || entry.surface_role == "to_verify")
&& (entry.decoded
|| entry.materialized
|| entry.trade_candidate
|| entry.candle_candidate)
{
issues.push(crate::LocalPipelineValidationIssueDto {
- code: "inactive_launch_surface_matrix_entry_actionable".to_string(),
+ code: "inactive_deferred_surface_matrix_entry_actionable".to_string(),
message: format!(
- "inactive launch surface '{}' must not be decoded, materialized, or priced as trade/candle candidate",
+ "inactive deferred surface '{}' must not be decoded, materialized, or priced as trade/candle candidate",
+ entry_code
+ ),
+ subject: Some(entry_code.clone()),
+ blocking: true,
+ });
+ }
+ if entry.surface_role == "aggregator_router"
+ && (entry.trade_candidate || entry.candle_candidate || entry.catalog_enabled)
+ {
+ issues.push(crate::LocalPipelineValidationIssueDto {
+ code: "aggregator_router_matrix_entry_direct_trade_enabled".to_string(),
+ message: format!(
+ "aggregator/router '{}' must not be enabled as a direct trade/candle surface before proof",
+ entry_code
+ ),
+ subject: Some(entry_code.clone()),
+ blocking: true,
+ });
+ }
+ if entry.surface_role == "to_verify"
+ && (entry.program_id.is_some()
+ || entry.program_id_status != "to_verify"
+ || entry.catalog_enabled
+ || entry.decoded
+ || entry.materialized
+ || entry.trade_candidate
+ || entry.candle_candidate)
+ {
+ issues.push(crate::LocalPipelineValidationIssueDto {
+ code: "to_verify_matrix_entry_promoted_without_proof".to_string(),
+ message: format!(
+ "to-verify surface '{}' must not expose program id, catalog activation, decoding or trade/candle materialization without corpus proof",
entry_code
),
subject: Some(entry_code.clone()),
@@ -1397,12 +1438,12 @@ mod tests {
}
#[test]
- fn validation_accepts_0_7_39_launch_surface_origin_baseline() {
+ fn validation_accepts_0_7_39_dex_first_effective_swap_surfaces() {
let summary = make_0_7_28_summary_with_meteora();
- let config = crate::LocalPipelineValidationConfig::v0_7_39_launch_surface_origin_baseline();
+ let config = crate::LocalPipelineValidationConfig::v0_7_39_dex_first_effective_swap_surfaces();
let report = crate::validate_local_pipeline_diagnostics_summary(&summary, &config);
assert!(report.validation_passed);
- assert_eq!(report.validation_profile_code, "0.7.39_launch_surface_origin_baseline");
+ assert_eq!(report.validation_profile_code, "0.7.39_dex_first_effective_swap_surfaces");
assert_eq!(report.blocking_issue_count, 0);
}