This commit is contained in:
2026-05-19 11:14:20 +02:00
parent 3f6d2e9f7f
commit 3da01156a0
22 changed files with 1137 additions and 308 deletions

View File

@@ -69,3 +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 linvariant sans faux trade/candle ni program id fictif.

View File

@@ -8,7 +8,7 @@ members = [
]
[workspace.package]
version = "0.7.38"
version = "0.7.39"
edition = "2024"
license = "MIT"
repository = "https://git.sasedev.com/Sasedev/khadhroony-bobobot"

View File

@@ -4,15 +4,15 @@
`khadhroony-bobobot` est un workspace Rust destiné à la détection, au décodage, à lanalyse et, à terme, au trading semi-automatisé de tokens Solana.
Le README précédent décrivait surtout létat `0.3.1`. Ce fichier reflète létat de clôture `0.7.38` et la reprise prévue en `0.7.39` : le socle transport HTTP/WS, la résolution transactionnelle, le modèle SQLite, plusieurs connecteurs DEX, les candles, les signaux analytiques, la validation locale, la matrice DEX commune et les diagnostics de metadata prioritaires existent déjà.
Le README précédent décrivait surtout létat `0.3.1`. Ce fichier reflète létat de clôture `0.7.38-B` et la réorientation de travail `0.7.39 DEX-first` : le socle transport HTTP/WS, la résolution transactionnelle, le modèle SQLite, plusieurs connecteurs DEX, les candles, les signaux analytiques, la validation locale, la matrice DEX commune et les diagnostics de metadata prioritaires existent déjà. La prochaine phase donne la priorité aux DEX effectifs sur lesquels les swaps et événements de marché sont observables, avant de revenir aux launch surfaces.
## 1. Objectif
Lobjectif opérationnel est de construire progressivement une application capable de :
- détecter lapparition de nouveaux tokens, pools, paires et listings sur Solana ;
- identifier la première source de mint ou de lancement, même lorsque le token migre ensuite vers un autre DEX ;
- décoder les transactions pertinentes des DEX et launch surfaces ciblés ;
- identifier en priorité les DEX effectifs sur lesquels les swaps, liquidités et événements de marché sont réellement exécutés ;
- décoder les transactions pertinentes des DEX ciblés, puis seulement ensuite les launch surfaces et origines de mint/lancement ;
- séparer les swaps/candles des événements utiles seulement à lanalyse : liquidité, cycle de vie de pool, fees, rewards, administration, wallets observés ;
- produire des métriques exploitables : prix, volume, candles/OHLCV, activité, bursts, déséquilibres buy/sell, signaux analytiques ;
- préparer ensuite des règles déterministes de filtrage, dachat, de vente, de stop-loss et de trailing stop ;
@@ -31,7 +31,7 @@ Le workspace contient deux crates principales.
La logique métier doit rester dans `kb_lib`. `kb_demo_app` doit rester une façade UI/Tauri et ne doit pas récupérer de logique Solana ou DEX profonde.
## 3. État actuel après `0.7.38`
## 3. État actuel après `0.7.38-B`
### 3.1. Socle stabilisé à ne pas refactorer maintenant
@@ -74,16 +74,20 @@ Les connecteurs suivants sont les surfaces actuellement les plus importantes pou
- `meteora_damm_v2`, observé dans le corpus `0.7.36`, mais les swaps sans payload montant/prix exploitable sont maintenant `non_actionable_trade` ;
- `meteora_dbc`, observé dans le corpus `0.7.36`, mais les swaps sans payload montant/prix exploitable sont maintenant `non_actionable_trade`.
### 3.4. Connecteurs déjà présents mais à consolider par corpus
### 3.4. Connecteurs à consolider par corpus en priorité DEX-first
Les modules suivants existent ou sont partiellement représentés dans le code, mais doivent encore être consolidés par corpus local, invariants et documentation :
Les modules ou surfaces suivantes existent, sont partiellement représentés dans le code ou doivent être recherchés, mais doivent être consolidés par corpus local, invariants et documentation avant de reprendre les launch surfaces :
- `raydium_amm_v4` legacy ;
- `raydium_stable_swap` ;
- `orca_whirlpools` ;
- `fluxbeam` ;
- `dexlab` ;
- `raydium_amm_v4` legacy ;
- launch origins déjà amorcées : `meteora_fun_launch`, `bags`, `moonit` ;
- launch surfaces à venir : `raydium_launchlab`, `raydium_launchpad`, `letsbonk` / `bonk`, `boop_fun`, `moonshot`, `believe`, `heaven`.
- `meteora_damm_v1`, `meteora_damm_v2`, `meteora_dbc` et `meteora_dlmm` pour la couverture non-trade complète ;
- `metaDAO` et `printr`, apparus comme DEX à vérifier côté sources externes de découverte ;
- tout DEX ou programme récurrent détecté dans `k_sol_protocol_candidates`, DEX Screener ou les transactions locales.
Les launch origins déjà amorcées (`meteora_fun_launch`, `bags`, `moonit`) et les launch surfaces enregistrables (`raydium_launchlab`, `raydium_launchpad`, `letsbonk`, `bonk_fun`, `bags`, `moonshot`, `moonit`, `boop_fun`, `believe`, `heaven`) sont conservées dans la documentation, mais elles ne sont plus prioritaires tant que les DEX effectifs ne sont pas suffisamment couverts.
### 3.5. Validation acquise en `0.7.36`
@@ -100,13 +104,13 @@ La validation `0.7.36_meteora_family_consolidation` est considérée comme réal
Point important : `meteora_damm_v2` et `meteora_dbc` peuvent produire beaucoup dévénements `swap` décodés sans produire de `trade_events` lorsque les montants ou prix ne sont pas fiables. Ces événements doivent rester `non_actionable_trade` et ne doivent pas être comptés comme `tradeCandidate` ou `candleCandidate`.
## 4. Matrice DEX et launch surfaces
## 4. Matrice DEX : DEX effectifs dabord, launch surfaces ensuite
La distinction importante est la suivante :
La distinction de travail à partir de `0.7.39` est la suivante :
- un **DEX effectif** permet de détecter et décoder des swaps, pools, liquidité et candles ;
- une **launch surface** peut être la première source de mint ou de lancement, même si le token migre ensuite vers Raydium, Meteora ou un autre AMM ;
- pour le trading, la première source de mint est une information de filtrage et de ciblage aussi importante que le DEX final.
- un **DEX effectif** est un programme ou une famille de programmes sur lesquels des swaps, pools, liquidités, fees, rewards, admin/config, burns ou mints utiles sont réellement observables ;
- une **launch surface** peut être la première source de mint ou de lancement, mais elle ne doit pas masquer la priorité donnée aux programmes où le swap final est exécuté ;
- pour le trading, le DEX effectif, la qualité de décodage du swap et les événements de pool sont prioritaires ; la launch origin reste une information de filtrage à réintroduire après stabilisation des DEX.
### 4.1. Matrice de travail
@@ -118,31 +122,25 @@ Depuis `0.7.32`, les diagnostics distinguent explicitement les gaps littéraux d
Depuis `0.7.33`, les diagnostics ajoutent une classification `pairTradingReadiness` au niveau des paires et des résumés agrégés `pairTradingReadinessSummaries`. Cette classification sépare les paires directement lisibles/tradables contre WSOL ou stable, les paires inversées avec WSOL/stable en base, les paires cross-quote nécessitant un routeur/aggregator, les paires non matérialisées en trade et les cas de quote inconnue. Elle reste purement diagnostique : elle ne modifie ni le replay, ni les `trade_events`, ni les candles.
| Code cible | Type | Statut `0.7.29` | Prochaine action |
| Code cible | Type | Priorité `0.7.39+` | Prochaine action |
|---|---:|---|---|
| `pump_fun` | Launch + bonding curve | partiel | verrouiller le rattachement mint initial -> pools migrés |
| `pump_swap` | AMM / swap | supporté | conserver invariants trade/candle |
| `raydium_cpmm` | AMM | supporté | conserver invariants trade/candle |
| `raydium_clmm` | CLMM | supporté | conserver invariants trade/candle |
| `raydium_launchlab` | Launch surface | planifié, program id local connu | ajouter decoder/materialization dédiée |
| `raydium_launchpad` | Launch surface | à vérifier | ne pas inventer de program id |
| `raydium_amm_v4` | AMM legacy | partiel | traiter après les autres Raydium avec corpus dédié |
| `meteora_dlmm` | DLMM | supporté | verrouiller corpus et non-régression |
| `meteora_damm_v1` | AMM legacy | partiel validé | conserver le skip explicite des swaps sans montants exploitables |
| `meteora_damm_v2` | AMM | partiel validé `0.7.36` | conserver les swaps sans amounts en `non_actionable_trade`; ajouter extraction de montants seulement avec preuve |
| `meteora_dbc` | Launch / bonding curve | partiel validé `0.7.36` | conserver les swaps sans amounts en `non_actionable_trade`; étudier migration / launch origin |
| `meteora_dlc` | À vérifier | à vérifier | confirmer surface/program id avant intégration |
| `orca_whirlpools` | CLMM | partiel | corpus fiable et validation des instructions utiles |
| `fluxbeam` | AMM | partiel | corpus fiable avant validation |
| `dexlab` | AMM | partiel | corpus fiable avant validation |
| `bags` | Launch surface | planifié | conserver comme origine de lancement si corpus le prouve |
| `letsbonk` / `bonk` | Launch surface | planifié | ajouter comme origine de mint/lancement sans supposer un AMM autonome |
| `okx_dex` | Aggregator/router | planifié | classifier sans matérialiser en trade direct avant preuve |
| `boop_fun` | Launch surface | planifié | ajouter comme origine de mint/lancement et migration |
| `moonshot` / `moonit` | Launch surface | planifié | remplacer les heuristiques faibles par corpus et règles prouvées |
| `believe` | Launch surface | planifié | confirmer comptes, migration et rattachement |
| `heaven` | Launch + AMM candidat | planifié | ajouter corpus et déterminer séparation launch/swap |
| `zora` | À vérifier | à vérifier | ne pas intégrer avant preuve de programme Solana pertinent |
| `pump_swap` | AMM / swap | haute | conserver les invariants trade/candle et étendre les événements non-trade prouvés. |
| `raydium_cpmm` | AMM | haute | vérifier corpus swap/liquidité/admin et maintenir la matérialisation trade/candle. |
| `raydium_clmm` | CLMM | haute | vérifier corpus swap/liquidité/position et maintenir la matérialisation trade/candle. |
| `raydium_amm_v4` | AMM legacy | haute | rechercher des paires/pools réels pour `675kPX9MHTjS2zt1qfr1NYHuzeLXfQM9H24wFSUt1Mp8`, valider `initialize2` et identifier les swaps. |
| `raydium_stable_swap` | AMM legacy | moyenne | vérifier lusage réel de `5quBtoiQqxF9Jv6KYKctB59NT3gtJD2Y65kdnB1Uev3h` et ne lactiver quavec corpus. |
| `meteora_dlmm` | DLMM | haute | verrouiller swaps, positions, liquidité et lifecycle. |
| `meteora_damm_v1` | AMM legacy | haute | conserver le skip sans amounts exploitables et rechercher un corpus swap/liquidité exploitable. |
| `meteora_damm_v2` | AMM | haute | vérifier `cpamdpZCGKUy5JxQXB4dcpGPiikHawvSWAd6mEn1sGG`, les swaps, les configs dynamiques et les événements non-trade. |
| `meteora_dbc` | bonding curve / DEX effectif partiel | haute | séparer ce qui relève du swap, du bonding curve et de la migration sans faux trade/candle. |
| `orca_whirlpools` | CLMM | haute | consolider par corpus fiable : create_pool, swap, liquidité/positions. |
| `fluxbeam` | AMM | moyenne | vérifier program id, corpus réel et instructions utiles. |
| `dexlab` | AMM | moyenne | vérifier program id, corpus réel, swaps et liens OpenBook éventuels. |
| `metaDAO` | DEX à vérifier | haute | rechercher le ou les program ids, corpus, comptes et types dévénements avant toute promotion. |
| `printr` | DEX à vérifier | haute | rechercher le ou les program ids, corpus, comptes et types dévénements avant toute promotion. |
| `okx_dex` | Aggregator/router | basse DEX direct | classifier sans matérialiser en trade direct avant preuve. |
| Launch surfaces (`pump_fun`, `raydium_launchlab`, `raydium_launchpad`, `bags`, `letsbonk`, `bonk_fun`, `boop_fun`, `moonshot`, `moonit`, `believe`, `heaven`) | Launch / origine | reportée | reprendre après consolidation des DEX effectifs ; aucun `program_id` fictif. |
| `zora` | À vérifier | hors priorité | ne pas intégrer avant preuve de programme Solana pertinent. |
## 5. Base de données
@@ -232,18 +230,32 @@ Les tests peuvent rester plus souples lorsque cela clarifie le test.
## 8. Priorité immédiate
La phase `0.7.38_token_metadata_gap_prioritization` est considérée comme close. La clôture `0.7.38-B` ajoute un registre local minimal de tokens connus : `WSOL`, `USDC`, `USDT`, `JUP`, `RAY`, `BONK`. `USDC` et `USDT` restent les seuls ajouts stable-quote ; `JUP`, `RAY` et `BONK` enrichissent seulement laffichage metadata sans changer la classification de quote. La prochaine étape est `0.7.39_launch_surfaces`.
La phase `0.7.38_token_metadata_gap_prioritization` est considérée comme close. La clôture `0.7.38-B` ajoute un registre local minimal de tokens connus : `WSOL`, `USDC`, `USDT`, `JUP`, `RAY`, `BONK`. `USDC` et `USDT` restent les seuls ajouts stable-quote ; `JUP`, `RAY` et `BONK` enrichissent seulement laffichage metadata sans changer la classification de quote.
La prochaine étape est maintenant `0.7.39_dex_first_effective_swap_surfaces`. Elle remplace la priorité précédemment donnée aux launch surfaces. Lobjectif est de vérifier et consolider dabord les vrais DEX de swap et leurs événements observables.
Préconditions validées avant de reprendre le codage :
1. les invariants locaux restent sains : `blockingIssueCount = 0`, `actionableMissingTradeEventCount = 0`, `missingTradeEventCount = 0` ;
2. les profils `0.7.36_meteora_family_consolidation`, `0.7.37_token_metadata_catalog_enrichment` et `0.7.38_token_metadata_gap_prioritization` existent et passent sur le corpus local fourni ;
3. les metadata manquantes restent non bloquantes ;
4. les `tokenMetadataGapSamples` exposent maintenant une liste de travail priorisée ;
5. le registre local minimal contient `SOL`, `WSOL`, `USDC` et `USDT` ;
4. les `tokenMetadataGapSamples` exposent une liste de travail priorisée ;
5. le registre local minimal contient `SOL`, `WSOL`, `USDC`, `USDT`, `JUP`, `RAY` et `BONK` ;
6. le backfill metadata peut traiter les tokens déjà présents en base et rafraîchir les `pair_symbol` sans recréer les objets de marché.
Objectif de `0.7.39` : détecter et rattacher les surfaces de lancement sans inventer de program ids, sans produire de faux trades/candles et sans confondre `launch_origin`, `pool_origin`, `dex_effective` et `migration_target`.
Objectifs `0.7.39+` :
- inventorier et vérifier les DEX principaux sur lesquels des swaps peuvent réellement se produire ;
- rechercher ou confirmer les `program_id` inconnus sans en inventer ;
- ajouter `metaDAO` et `Printr` comme DEX à vérifier, notamment via DEX Screener et corpus local ;
- couvrir, par DEX, les swaps, liquidités, lifecycle, fees, rewards, admin/config, burns/mints utiles et autres événements non-trade prouvés ;
- ajouter `Demo3` pour rechercher les pools/paires/signatures par DEX ou `program_id` ;
- ajouter `Demo4` pour interroger DEX Screener et comparer les résultats avec la base locale ;
- préparer des démos spécialisées pour les launch surfaces seulement après stabilisation des DEX effectifs ;
- ajouter une démo temps réel type `Demo10` avec start/stop du client WebSocket, souscriptions aux programmes DEX et écriture en base via le pipeline existant ;
- passer ensuite à la création de wallets, aux fonctions wallet et aux transferts de fonds avant toute logique de swap/trading.
Les launch surfaces restent importantes, mais elles sont reportées après la consolidation des DEX effectifs. Elles ne doivent pas générer de faux trades/candles ni de `program_id` fictif.
## 9. Fichiers utiles pour reprendre dans une nouvelle session
@@ -271,4 +283,4 @@ Pour reprendre rapidement le codage dans une nouvelle session, fournir au minimu
- `kb_lib/src/db/dtos.rs` et `kb_lib/src/db/dtos/*` ;
- `kb_lib/src/db/queries.rs` et `kb_lib/src/db/queries/*`.
Ajouter `kb_demo_app/src/demo_pipeline*.rs` seulement si la tâche concerne lUI ou les diagnostics affichés.
Ajouter `kb_demo_app/src/demo_pipeline*.rs`, les fichiers frontend associés et les nouvelles démos (`Demo3`, `Demo4`, `Demo10`) seulement si la tâche concerne lUI, la recherche de corpus, les diagnostics affichés ou le watcher temps réel.

View File

@@ -942,195 +942,182 @@ Réalisé :
- validation locale confirmée avec `validationPassed = true`, `blockingIssueCount = 0`, `missingTradeEventCount = 0`, `decodedTradeCandidateWithoutTradeEventCount = 0` ;
- les samples permettent de sélectionner les prochains mints à enrichir via registre local, payloads DEX, Token-2022, Metaplex ou backfill HTTP.
Décision : `0.7.38` est clos. La clôture `0.7.38-B` conserve la logique de stable quotes limitée à `USDC`/`USDT` et ajoute un registre metadata local pour `JUP`, `RAY` et `BONK` sans les classer automatiquement comme quotes. La suite de développement commence à `0.7.39` avec les launch surfaces.
Décision : `0.7.38` est clos. La clôture `0.7.38-B` conserve la logique de stable quotes limitée à `USDC`/`USDT` et ajoute un registre metadata local pour `JUP`, `RAY` et `BONK` sans les classer automatiquement comme quotes. La suite de développement commence à `0.7.39` avec une priorité DEX-first : consolider les DEX effectifs de swap avant de revenir aux launch surfaces.
### 6.071. Version `0.7.39` — Launch surfaces : LaunchLab, LetsBonk, Bags, Moonshot/Moonit, Boop.fun, Believe
Objectif : détecter la première source de mint/lancement des tokens même lorsque le swap final se fait ailleurs.
### 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 :
- ajouter ou stabiliser `raydium_launchlab` / `raydium_launchpad`,
- ajouter `letsbonk` / `bonk_fun` comme surface dorigine rattachée à LaunchLab/Raydium si le corpus le prouve,
- ajouter `boop_fun` comme surface dorigine et suivre ses migrations,
- consolider `moonshot` / `moonit` avec corpus au lieu de simples heuristiques faibles,
- consolider `bags` comme surface dorigine, notamment lorsque le token passe par Meteora DBC/DAMM,
- ajouter `believe` comme surface dorigine associée à Meteora DBC si les comptes/authorities le prouvent,
- distinguer `launch_origin`, `pool_origin`, `dex_effective` et `migration_target`,
- rattacher les launch origins aux pools et paires lorsque les comptes permettent un matching fiable,
- exposer les origins dans les diagnostics et lUI dinspection.
- modifier la matrice DEX pour distinguer explicitement : `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 quils 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 ;
- ne promouvoir aucune entrée vers `partial` ou `supported` sans corpus transactionnel vérifiable ;
- conserver les invariants `0.7.36` à `0.7.38` : aucun faux trade, aucune fausse candle, aucun événement non price-action transformé en trade/candle.
### 6.072. Version `0.7.39` — Heaven : corpus, launch et AMM
Objectif : ajouter Heaven sans le classer trop tôt comme simple DEX ou simple launchpad.
Résultat attendu : une matrice DEX réorientée vers les surfaces de swap effectives, avec les launch surfaces explicitement reportées.
### 6.072. Version `0.7.40` — Raydium effectif : AMM v4, Stable Swap, CPMM, CLMM
Objectif : consolider la famille Raydium côté DEX effectifs avant les launch surfaces Raydium.
À faire :
- vérifier le ou les programmes Heaven réellement observés sur mainnet,
- constituer un corpus local de mints, pools et swaps Heaven,
- séparer les événements de lancement des événements de swap,
- ajouter les decoded events, launch origins, pool/pair/listing et trade events seulement lorsque les instructions sont prouvées,
- documenter les limites si le corpus ne permet pas encore de matérialiser tous les événements,
- vérifier que Heaven ne crée pas de candles invalides en cas dévénement de launch non pricé.
- vérifier `raydium_cpmm` et `raydium_clmm` comme références déjà supportées ;
- rechercher des pools réellement rattachés au programme AMM v4 `675kPX9MHTjS2zt1qfr1NYHuzeLXfQM9H24wFSUt1Mp8` ;
- vérifier lusage réel de `raydium_stable_swap` et du programme `5quBtoiQqxF9Jv6KYKctB59NT3gtJD2Y65kdnB1Uev3h` ;
- distinguer clairement `raydium_amm_v4`, `raydium_cpmm`, `raydium_clmm`, `raydium_router`, `raydium_stable_swap`, `raydium_launchlab` et `raydium_launchpad` ;
- identifier les instructions swap, initialize/create pool, liquidité, fees/admin réellement observées ;
- ajouter des requêtes diagnostics par `program_id`, `accounts_json`, `data_json`, signature et pool address ;
- documenter les limites si le corpus Raydium legacy reste faible.
### 6.073. Version `0.7.40` — Orca / FluxBeam / DexLab : corpus et validation ciblée
Objectif : consolider les connecteurs déjà présents à partir de corpus locaux vérifiables.
### 6.073. Version `0.7.41` — Meteora effectif : DLMM, DAMM v1/v2, DBC
Objectif : compléter la famille Meteora en couvrant les événements réellement utiles au DEX effectif.
À faire :
- constituer des corpus locaux pour `Orca Whirlpools`, `FluxBeam` et `DexLab`,
- vérifier les `program_id`, comptes, préfixes `data_json` et familles dinstructions utiles,
- stabiliser les événements `create_pool`, `swap` et liquidité réellement observés,
- alimenter les mêmes tables métier et diagnostics que les connecteurs déjà validés,
- marquer explicitement les variantes partiellement supportées ou heuristiques,
- rejouer les corpus plusieurs fois pour vérifier lidempotence et labsence de trades/candles invalides.
- conserver la validation `0.7.36` : les swaps DAMM v2 / DBC sans amounts fiables restent `non_actionable_trade` ;
- vérifier `cpamdpZCGKUy5JxQXB4dcpGPiikHawvSWAd6mEn1sGG` et les autres programmes Meteora dans le corpus local ;
- consolider `meteora_dlmm` pour swaps, positions, add/remove liquidity, lifecycle et non-trade events ;
- consolider `meteora_damm_v1` et `meteora_damm_v2` pour pools, swaps exploitables, configs dynamiques, fees/admin ;
- clarifier la part `launch/bonding_curve` et la part `dex_effective` de `meteora_dbc` ;
- éviter tout trade/candle artificiel lorsque les montants ou prix ne sont pas prouvés.
### 6.074. Version `0.7.41` — Raydium AMM v4 legacy : corpus et validation ciblée
Objectif : traiter le vrai Raydium AMM v4 historique après les autres Raydium, afin de lisoler de `raydium_cpmm`, `raydium_clmm` et des labels Raydium génériques.
### 6.074. Version `0.7.42` — Autres DEX effectifs : Orca, FluxBeam, DexLab, metaDAO, Printr
Objectif : consolider les DEX non-Ray­dium/Meteora et intégrer les DEX récemment observés comme candidats vérifiables.
À faire :
- rechercher des pools réellement rattachés au programme `675kPX9MHTjS2zt1qfr1NYHuzeLXfQM9H24wFSUt1Mp8`,
- constituer un petit corpus local de signatures/pools AMM v4 fiables,
- vérifier que les adresses issues dexplorateurs ne sont pas seulement catégorisées globalement comme `Raydium`,
- ajouter des requêtes de diagnostic par `program_id`, `accounts_json` et préfixe `data_json`,
- valider `initialize2` et identifier les instructions de swap AMM v4 à supporter si elles apparaissent dans le corpus,
- renommer/stabiliser les fonctions internes autour de `raydium_amm_v4` pour éviter lambiguïté avec `raydium_cpmm` et `raydium_clmm`,
- documenter les limites connues si le corpus AMM v4 reste faible.
- constituer des corpus locaux pour `orca_whirlpools`, `fluxbeam` et `dexlab` ;
- vérifier les `program_id`, comptes, préfixes `data_json` et familles dinstructions utiles ;
- stabiliser les événements `create_pool`, `swap`, liquidité, positions et admin lorsque prouvés ;
- ajouter `metaDAO` et `Printr` en `to_verify` avec recherche explicite de program ids, signatures, comptes et pools ;
- ne pas confondre source externe de découverte et preuve on-chain ;
- marquer explicitement les variantes partiellement supportées ou heuristiques.
### 6.075. Version `0.7.42` — Validation DEX v1 consolidée
Objectif : rejouer tous les DEX et launch surfaces supportés et valider les invariants du pipeline complet.
### 6.075. Version `0.7.43` — Couverture événementielle DEX : swap, liquidité, fees, rewards, admin, burns
Objectif : sassurer que chaque DEX effectif expose les événements utiles au scoring et au risque sans polluer les trades/candles.
À faire :
- rejouer des bases neuves couvrant tous les connecteurs DEX supportés,
- vérifier les compteurs globaux et par DEX : decoded events, trade events, liquidity events, lifecycle events, fee events, reward events, admin events, candles et analytic signals,
- contrôler que chaque famille dévénements alimente uniquement les tables métier prévues,
- vérifier les diagnostics bloquants et les samples danomalie,
- documenter les corpus utilisés pour chaque DEX/surface,
- conserver une matrice de support par DEX, variante, instruction et type dévénement,
- vérifier par DEX la couverture `swap` / `tradeCandidate` / `candleCandidate` ;
- vérifier par DEX la couverture liquidité : add/remove/increase/decrease/open/close position ;
- vérifier par DEX les événements lifecycle : create/init/migrate/pause/resume/close ;
- vérifier par DEX les fees, rewards, creator fees, protocol fees et admin/config ;
- vérifier les burns/mints utiles au suivi token/pool sans les transformer en price-action ;
- matérialiser uniquement les événements prouvés dans les tables dédiées ;
- ajouter des compteurs et samples diagnostics par DEX et par type dévénement ;
- conserver linvariant : aucun fee/reward/admin/liquidity/lifecycle/burn non price-action ne produit de trade, metric ou candle.
### 6.076. Version `0.7.44` — `kb_demo_app` Demo3 : recherche de paires/pools par DEX ou program id
Objectif : ajouter une fenêtre ou vue de démonstration dédiée à la constitution de corpus par DEX.
À faire :
- ajouter une `Demo3` dans `kb_demo_app` ;
- permettre la saisie dun `program_id`, dun `dex_code`, dun `pool_address`, dun `pair_id` ou dun token mint ;
- rechercher dans la base locale les transactions, instructions, decoded events, pools, paires, listings et candidates associés ;
- fournir des presets pour les programmes à vérifier, par exemple `675kPX9MHTjS2zt1qfr1NYHuzeLXfQM9H24wFSUt1Mp8`, `5quBtoiQqxF9Jv6KYKctB59NT3gtJD2Y65kdnB1Uev3h`, `cpamdpZCGKUy5JxQXB4dcpGPiikHawvSWAd6mEn1sGG` ;
- afficher les signatures candidates à backfill/replay ;
- garder `kb_demo_app` comme façade UI : toute requête métier doit rester dans `kb_lib`.
### 6.077. Version `0.7.45` — `kb_demo_app` Demo4 : DEX Screener et sources externes de découverte
Objectif : utiliser des sources externes comme aides à la découverte de corpus sans les traiter comme vérité métier.
À faire :
- ajouter une `Demo4` pour interroger DEX Screener ou une source équivalente ;
- rechercher des paires par token mint, chain, DEX name, pool address ou program id lorsque disponible ;
- comparer les résultats externes avec les objets locaux : tokens, pools, pairs, listings, decoded events ;
- afficher les écarts : paire externe absente localement, pool local sans source externe, DEX label ambigu, program id manquant ;
- permettre de copier les signatures/adresses candidates pour backfill ;
- ne jamais promouvoir automatiquement un DEX, un `program_id` ou une paire sur la seule base dune réponse externe.
### 6.078. Version `0.7.46` — Démos spécialisées launch surfaces après DEX effectifs
Objectif : préparer des vues spécialisées pour les launch surfaces, mais seulement après stabilisation des DEX effectifs.
À faire plus tard :
- ajouter des démos dédiées à `pump_fun`, `raydium_launchpad` / `raydium_launchlab`, `believe`, `bags`, `moonshot` / `moonit`, `boop_fun`, `letsbonk` / `bonk_fun`, `heaven` ;
- distinguer `launch_origin`, `pool_origin`, `dex_effective` et `migration_target` ;
- rattacher les launch origins aux pools et paires uniquement lorsque les comptes permettent un matching fiable ;
- exposer les origins dans les diagnostics et lUI dinspection ;
- maintenir linterdiction de faux program ids, faux trades et fausses candles.
### 6.079. Version `0.7.47` — `kb_demo_app` Demo10 : watcher WebSocket live DEX
Objectif : valider le passage du replay/backfill vers lobservation temps réel contrôlée.
À faire :
- ajouter une démo temps réel type `Demo10` avec bouton `start` / `stop` ;
- permettre de sélectionner les endpoints WS/HTTP et les DEX/program ids à souscrire ;
- lancer le client WebSocket existant sans refactorer inutilement `ws_client.rs` / `ws_manager.rs` ;
- effectuer les `logsSubscribe`, `programSubscribe` ou `accountSubscribe` nécessaires selon le DEX ;
- détecter en temps réel mints, swaps, liquidités et autres événements utiles ;
- écrire en base via le pipeline existant : observations, transactions résolues, decoded events, pools/pairs/listings, trade events, candles et non-trade events ;
- afficher les compteurs live, erreurs, subscriptions actives et derniers objets persistés ;
- prévoir un arrêt propre avec unsubscribe avant close.
### 6.080. Version `0.7.48` — Validation DEX v1 consolidée
Objectif : rejouer tous les DEX effectifs supportés et valider les invariants du pipeline complet avant de revenir aux launch surfaces ou à lanalyse `0.8.x`.
À faire :
- rejouer des bases neuves couvrant tous les connecteurs DEX supportés ;
- vérifier les compteurs globaux et par DEX : decoded events, trade events, liquidity events, lifecycle events, fee events, reward events, admin events, burns/mints utiles, candles et analytic signals ;
- contrôler que chaque famille dévénements alimente uniquement les tables métier prévues ;
- vérifier les diagnostics bloquants et les samples danomalie ;
- documenter les corpus utilisés pour chaque DEX/surface ;
- conserver une matrice de support par DEX, variante, instruction et type dévénement ;
- verrouiller les invariants avant douvrir lanalyse `0.8.x`.
### 6.076. Version `0.7.43` — `kb_demo_app` : overlays analytiques
Objectif : rendre visibles les signaux analytiques directement sur les graphes et vues de marché.
À faire :
- afficher les signaux analytiques par bucket au-dessus ou autour des candles,
- ajouter des marqueurs pour `first_trade_seen`, `trade_burst_60s`, `buy_sell_imbalance_60s`, `price_jump_up_60s`, `price_jump_down_60s` et `volume_spike_60s`,
- permettre le filtrage par type de signal et par sévérité,
- afficher un panneau latéral listant les signaux liés à une paire et à un timeframe,
- préparer lextension future vers Ichimoku, Kumo, projections ABCD et égalités temps/prix sans les mélanger au pipeline de décodage DEX.
### 6.077. Version `0.7.44` — `kb_demo_app` : vues consolidées token / pair / pool
Objectif : fournir une lecture métier plus confortable du modèle `0.7.x`.
À faire :
- ajouter une fiche token avec mint, programme token, metadata, pools, paires et historique de découverte,
- ajouter une fiche paire avec base/quote, DEX, pool, métriques, candles, signaux et derniers trades,
- ajouter une fiche pool avec composition, vaults, origine, première signature vue, programme DEX et statut de décodage,
- relier dans lUI les launch origins, pool origins, wallets observés, holdings observés, événements de liquidité, lifecycle, fees, rewards, admin, candles et analytic signals,
- préparer une navigation transversale entre objets techniques et objets métier,
- rendre explicites les cas `tradeCount = null`, `lastPriceQuotePerBase = null`, tokens non enrichis et événements conservés uniquement pour analyse.
### 6.078. Version `0.7.45` — Finition UI `0.7.x`
Objectif : stabiliser la couche desktop de validation avant louverture de `0.8.x`.
À faire :
- consolider les vues ajoutées dans `kb_demo_app`,
- améliorer la navigation, les filtres et la pagination,
- ajouter les derniers raffinements de confort et de lisibilité,
- préparer une base UI suffisamment stable pour la future phase danalyse et filtrage `0.8.x`,
- vérifier que les commandes Tauri restent de simples façades vers `kb_lib`.
### 6.079. Version `0.7.x` — Couverture DEX v1
Objectif : structurer les connecteurs DEX autour dun pipeline complet de résolution, décodage, normalisation métier et classification des événements non-trade.
Protocoles et surfaces cibles :
- Pump.fun,
- PumpSwap,
- Raydium CPMM,
- Raydium CLMM,
- Raydium LaunchLab / Launchpad,
- Raydium AMM v4 legacy,
- Meteora DBC,
- Meteora DAMM v1,
- Meteora DAMM v2,
- Meteora DLMM,
- Orca Whirlpools,
- FluxBeam,
- DexLab,
- Bags,
- LetsBonk / Bonk.fun,
- Boop.fun,
- Moonshot / Moonit,
- Believe,
- Heaven.
Hors périmètre immédiat :
- `zora_solana`, tant quaucun programme Solana pertinent et exploitable nest prouvé.
Résultat attendu :
- identification fiable des programmes et versions,
- résolution des signatures pertinentes,
- décodage des transactions utiles,
- conservation des transactions inconnues ou candidates sans perte dinformation,
- création dobjets métier riches pour tokens, pools, paires, listings, participants, origins et holdings observés,
- distinction claire entre première source de mint, launch origin, pool origin, DEX effectif et migration target,
- enrichissement metadata des tokens découverts,
- séparation stricte entre événements candle/trade et événements utiles seulement à lanalyse,
- matérialisation progressive des événements non-trade dans des tables métier dédiées,
- préparation dune détection temps réel hybride et dun backfill ciblé compatible avec les mêmes objets métier,
- préparation dagrégats DEX plus riches, de candles/OHLCV et dune UI dinspection du pipeline `0.7.x`.
### 6.080. Version `0.8.x` — Analyse et filtrage
### 6.081. Version `0.8.x` — Analyse et filtrage
Objectif : transformer les événements bruts en signaux exploitables.
À faire :
- agrégation des métriques,
- règles de filtrage,
- exclusions des tokens non tradables,
- statistiques de comportement,
- premiers patterns,
- enrichissement des signaux analytiques préparés en fin de `0.7.x`,
- indicateurs graphiques optionnels comme Ichimoku / Kumo,
- outils de sélection manuelle de points ABC et projection dun point D selon des règles temps/prix explicites,
- agrégation des métriques ;
- règles de filtrage ;
- exclusions des tokens non tradables ;
- statistiques de comportement ;
- premiers patterns ;
- enrichissement des signaux analytiques préparés en fin de `0.7.x` ;
- indicateurs graphiques optionnels comme Ichimoku / Kumo ;
- outils de sélection manuelle de points ABC et projection dun point D selon des règles temps/prix explicites ;
- séparation stricte entre signaux analytiques observés, projections hypothétiques et décisions de trading.
### 6.081. Version `1.x.y` — Wallets et swap préparatoire
Objectif : préparer la couche daction.
### 6.082. Version `1.x.y` — Wallets, comptes et transferts
Objectif : préparer la couche daction sans encore brancher lachat/vente automatique.
À faire :
- gestion du répertoire wallets,
- chargement sécurisé des keypairs,
- abstraction wallet,
- préparation dordres et de swaps,
- simulation et garde-fous.
- gestion du répertoire wallets ;
- création de wallet/keypair ;
- chargement sécurisé des keypairs ;
- inspection des informations wallet ;
- transfert de fonds depuis cette wallet vers un autre account ;
- garde-fous daffichage, confirmation et simulation ;
- préparation dordres et de swaps seulement après stabilisation des transferts de base.
### 6.082. Version `2.x.y` — Trading semi-automatisé
### 6.083. Version `2.x.y` — Trading semi-automatisé
Objectif : brancher lanalyse à laction tout en gardant des garde-fous explicites.
À faire :
- scénarios dachat/vente,
- règles dentrée/sortie,
- limites de risque,
- confirmations explicites ou semi-automatiques,
- scénarios dachat/vente ;
- règles dentrée/sortie ;
- limites de risque ;
- confirmations explicites ou semi-automatiques ;
- journaux dexécution.
### 6.083. Version `3.x.y` — Yellowstone gRPC
### 6.084. Version `3.x.y` — Yellowstone gRPC
Objectif : ajouter le connecteur gRPC dédié.
À faire :
- `GrpcClient` basé sur `yellowstone-grpc-client`,
- adaptation du pipeline dévénements,
- coexistence HTTP / WS / gRPC,
- `GrpcClient` basé sur `yellowstone-grpc-client` ;
- adaptation du pipeline dévénements ;
- coexistence HTTP / WS / gRPC ;
- politique de répartition par source.
## 7. Organisation des modules ciblés
@@ -1186,6 +1173,9 @@ Responsabilités cibles :
- réception des événements venant de `kb_lib`,
- fenêtres de démonstration / diagnostic isolées,
- inspection du pipeline persisté,
- `Demo3` pour la recherche de paires/pools par DEX ou `program_id`,
- `Demo4` pour les requêtes DEX Screener et la comparaison avec la base locale,
- `Demo10` pour le watcher WebSocket live DEX avec start/stop,
- affichage candles et futurs overlays analytiques.
`kb_demo_app` ne doit pas contenir de logique métier DEX profonde.
@@ -1253,7 +1243,7 @@ Le projet doit maintenir au minimum :
## 12. Priorité immédiate
La priorité immédiate est `0.7.39_launch_surfaces` : détecter et rattacher les origines de lancement sans casser les invariants `0.7.36` à `0.7.38`.
La priorité immédiate après `0.7.38-B` est `0.7.39_dex_first_effective_swap_surfaces`. La phase launch surfaces est volontairement reportée : elle sera reprise après consolidation des DEX effectifs et des outils de constitution de corpus.
Préconditions validées avant `0.7.39` :
@@ -1261,13 +1251,28 @@ Préconditions validées avant `0.7.39` :
2. validation `0.7.37` acquise : compteurs metadata/catalog exposés, backfill metadata idempotent, `pair_symbol` rafraîchissables, metadata manquantes non bloquantes ;
3. validation `0.7.38` acquise : `tokenMetadataGapSamples` priorisés, Demo Pipeline 2 raccordé, `validationPassed = true`, `blockingIssueCount = 0`, registre local `WSOL`/`USDC`/`USDT`/`JUP`/`RAY`/`BONK` disponible ;
4. registre local minimal disponible : `SOL`, `WSOL`, `USDC`, `USDT` ;
5. les diagnostics locaux restent loutil de vérité pour décider si une surface peut passer de `planned` à `partial` ou `supported`.
5. les diagnostics locaux restent loutil de vérité pour décider si un DEX peut passer de `planned` ou `to_verify` à `partial` ou `supported`.
Ordre de travail recommandé pour `0.7.39` :
Ordre de travail recommandé pour `0.7.39+` :
1. scanner `launch_origin.rs`, `pool_origin.rs`, `protocol_candidate_recording.rs`, `dex_support_matrix.rs`, `transaction_classification.rs`, `dex_decode.rs` et `dex_detect.rs` ;
2. identifier les surfaces candidates déjà présentes dans la matrice : Raydium LaunchLab/Launchpad, LetsBonk/Bonk.fun, Bags, Moonshot/Moonit, Boop.fun et Believe ;
3. ne promouvoir une surface que si le corpus prouve le program id, les comptes/authorities et la migration vers le DEX effectif ;
4. distinguer explicitement `launch_origin`, `pool_origin`, `dex_effective` et `migration_target` ;
5. exposer les origins dans les diagnostics et lUI dinspection ;
6. conserver les garde-fous : pas de faux trade, pas de fausse candle, pas de program id inventé, pas de metadata manquante bloquante.
1. scanner `dex_support_matrix.rs`, `dex.rs`, `dex/*.rs`, `dex_decode.rs`, `dex_detect.rs`, `trade_aggregation.rs`, `local_pipeline_validation.rs`, `local_pipeline_diagnostics.rs`, `transaction_classification.rs` et `protocol_candidate_recording.rs` ;
2. produire un état des DEX effectifs déjà couverts, partiels, à vérifier ou absents ;
3. ajouter `metaDAO` et `Printr` comme DEX `to_verify` sans `program_id` inventé ;
4. vérifier les DEX principaux de swap : PumpSwap, Raydium CPMM/CLMM/AMM v4/Stable Swap, Meteora DLMM/DAMM/DBC, Orca Whirlpools, FluxBeam, DexLab ;
5. rechercher des corpus par `program_id`, pair/pool address, signature et token mint ;
6. ajouter `Demo3` pour rechercher localement les paires/pools/signatures par DEX ou `program_id` ;
7. ajouter `Demo4` pour interroger DEX Screener ou sources externes et comparer avec la base locale sans promotion automatique ;
8. compléter la couverture par DEX des swaps, liquidités, lifecycle, fees, rewards, admin/config, burns/mints utiles ;
9. ajouter ensuite `Demo10` pour le watcher WebSocket live DEX avec start/stop, subscribe/unsubscribe et écriture en base via le pipeline existant ;
10. reprendre seulement après cela les launch surfaces : Raydium LaunchLab/Launchpad, LetsBonk/Bonk.fun, Bags, Moonshot/Moonit, Boop.fun, Believe, Heaven ;
11. passer ensuite à la couche wallet : création de wallet/keypair, inspection et transfert de fonds vers un autre account.
Garde-fous constants :
- pas de faux trade ;
- pas de fausse candle ;
- pas de `program_id` fictif ;
- pas de promotion dun DEX sans corpus transactionnel ;
- pas de logique métier DEX profonde dans `kb_demo_app` ;
- pas de metadata manquante bloquante ;
- pas de refactor réseau inutile tant que les clients HTTP/WS existants suffisent.

View File

@@ -166,7 +166,8 @@
<div class="mb-3">
<label for="demoPipeline2ValidationProfileSelect" class="form-label">Validation profile</label>
<select id="demoPipeline2ValidationProfileSelect" class="form-select">
<option value="0.7.38_token_metadata_gap_prioritization" selected>0.7.38token metadata gap prioritization</option>
<option value="0.7.39_launch_surface_origin_baseline" selected>0.7.39launch surface origin baseline</option>
<option value="0.7.38_token_metadata_gap_prioritization">0.7.38 — token metadata gap prioritization</option>
<option value="0.7.37_token_metadata_catalog_enrichment">0.7.37 — token metadata/catalog enrichment</option>
<option value="0.7.36_meteora_family_consolidation">0.7.36 — Meteora family consolidation</option>
<option value="0.7.35_non_trade_fee_reward_admin">0.7.35 — non-trade fee/reward admin</option>

View File

@@ -0,0 +1,54 @@
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
/**
* Local launch-origin diagnostic sample for the UI.
*/
export type DemoPipeline2LocalLaunchOriginDiagnosticSample = {
/**
* Launch attribution id.
*/
launchAttributionId: number,
/**
* Launch surface code.
*/
launchSurfaceCode: string,
/**
* Launch surface display name.
*/
launchSurfaceName: string,
/**
* Transaction signature.
*/
transactionSignature: string,
/**
* Decoded event id.
*/
decodedEventId: number,
/**
* Effective protocol recorded on the decoded event.
*/
protocolName: string,
/**
* Match kind used for attribution.
*/
matchKind: string,
/**
* Matched key value.
*/
matchedValue: string,
/**
* Optional pool id.
*/
poolId: number | null,
/**
* Optional pool address.
*/
poolAddress: string | null,
/**
* Optional pair id.
*/
pairId: number | null,
/**
* Optional pair symbol.
*/
pairSymbol: string | null, };

View File

@@ -3,6 +3,7 @@ import type { DemoPipeline2LocalDecodedEventDiagnosticSummary } from "./DemoPipe
import type { DemoPipeline2LocalDexDiagnosticSummary } from "./DemoPipeline2LocalDexDiagnosticSummary";
import type { DemoPipeline2LocalDuplicateDecodedEventTradeDiagnosticSample } from "./DemoPipeline2LocalDuplicateDecodedEventTradeDiagnosticSample";
import type { DemoPipeline2LocalEventClassificationDiagnosticSummary } from "./DemoPipeline2LocalEventClassificationDiagnosticSummary";
import type { DemoPipeline2LocalLaunchOriginDiagnosticSample } from "./DemoPipeline2LocalLaunchOriginDiagnosticSample";
import type { DemoPipeline2LocalMissingTradeEventDiagnosticSample } from "./DemoPipeline2LocalMissingTradeEventDiagnosticSample";
import type { DemoPipeline2LocalMissingTradeEventReasonSummary } from "./DemoPipeline2LocalMissingTradeEventReasonSummary";
import type { DemoPipeline2LocalMultiTradeSignaturePairDiagnosticSample } from "./DemoPipeline2LocalMultiTradeSignaturePairDiagnosticSample";
@@ -11,6 +12,7 @@ import type { DemoPipeline2LocalPairActionabilityDiagnosticSummary } from "./Dem
import type { DemoPipeline2LocalPairDiagnosticSummary } from "./DemoPipeline2LocalPairDiagnosticSummary";
import type { DemoPipeline2LocalPairGapDiagnosticSample } from "./DemoPipeline2LocalPairGapDiagnosticSample";
import type { DemoPipeline2LocalPairTradingReadinessDiagnosticSummary } from "./DemoPipeline2LocalPairTradingReadinessDiagnosticSummary";
import type { DemoPipeline2LocalPoolOriginDiagnosticSample } from "./DemoPipeline2LocalPoolOriginDiagnosticSample";
import type { DemoPipeline2LocalTokenMetadataGapDiagnosticSample } from "./DemoPipeline2LocalTokenMetadataGapDiagnosticSample";
/**
@@ -246,6 +248,14 @@ eventClassificationSummaries: Array<DemoPipeline2LocalEventClassificationDiagnos
* Missing trade events grouped by diagnostic reason.
*/
missingTradeEventReasonSummaries: Array<DemoPipeline2LocalMissingTradeEventReasonSummary>,
/**
* Samples of launch-origin attributions.
*/
launchOriginSamples: Array<DemoPipeline2LocalLaunchOriginDiagnosticSample>,
/**
* Samples of pool-origin rows and their optional launch linkage.
*/
poolOriginSamples: Array<DemoPipeline2LocalPoolOriginDiagnosticSample>,
/**
* Prioritized samples of tokens whose display metadata is still incomplete.
*/

View File

@@ -0,0 +1,46 @@
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
/**
* Local pool-origin diagnostic sample for the UI.
*/
export type DemoPipeline2LocalPoolOriginDiagnosticSample = {
/**
* Pool-origin id.
*/
poolOriginId: number,
/**
* Effective DEX code attached to the pool.
*/
dexCode: string,
/**
* Pool id.
*/
poolId: number,
/**
* Pool address.
*/
poolAddress: string,
/**
* Optional pair id.
*/
pairId: number | null,
/**
* Optional pair symbol.
*/
pairSymbol: string | null,
/**
* Optional launch surface code.
*/
launchSurfaceCode: string | null,
/**
* Founding transaction signature.
*/
foundingSignature: string,
/**
* Founding effective protocol.
*/
foundingProtocolName: string,
/**
* Founding decoded event kind.
*/
foundingEventKind: string, };

View File

@@ -15,6 +15,7 @@ import type { DemoPipeline2PairCandlesRequest } from "./bindings/DemoPipeline2Pa
import type { DemoPipeline2PairCandlesPayload } from "./bindings/DemoPipeline2PairCandlesPayload.ts";
import type { DemoPipeline2LocalDiagnosticsPayload } from "./bindings/DemoPipeline2LocalDiagnosticsPayload.ts";
import type { DemoPipeline2LocalValidationPayload } from "./bindings/DemoPipeline2LocalValidationPayload.ts";
import type { DemoPipeline2LocalValidationRequest } from "./bindings/DemoPipeline2LocalValidationRequest.ts";
import type { DemoPipeline2ProgramInstructionDiscriminatorSummaryRequest } from "./bindings/DemoPipeline2ProgramInstructionDiscriminatorSummaryRequest.ts";
import type { DemoPipeline2ProgramInstructionDiscriminatorSummaryPayload } from "./bindings/DemoPipeline2ProgramInstructionDiscriminatorSummaryPayload.ts";
import { DemoPipeline2ProtocolCandidateSummaryRequest } from './bindings/DemoPipeline2ProtocolCandidateSummaryRequest.ts';
@@ -363,6 +364,7 @@ document.addEventListener("DOMContentLoaded", async () => {
const replayLocalPipelineButton = document.querySelector<HTMLButtonElement>("#demoPipeline2ReplayLocalPipelineButton");
const diagnoseLocalPipelineButton = document.querySelector<HTMLButtonElement>("#demoPipeline2DiagnoseLocalPipelineButton");
const validateLocalPipelineButton = document.querySelector<HTMLButtonElement>("#demoPipeline2ValidateLocalPipelineButton");
const validationProfileSelect = document.querySelector<HTMLSelectElement>("#demoPipeline2ValidationProfileSelect");
const discriminatorProgramIdInput = document.querySelector<HTMLInputElement>("#demoPipeline2DiscriminatorProgramIdInput");
const discriminatorLimitInput = document.querySelector<HTMLInputElement>("#demoPipeline2DiscriminatorLimitInput");
@@ -409,6 +411,7 @@ document.addEventListener("DOMContentLoaded", async () => {
!replayLocalPipelineButton ||
!diagnoseLocalPipelineButton ||
!validateLocalPipelineButton ||
!validationProfileSelect ||
!discriminatorProgramIdInput ||
!discriminatorLimitInput ||
!loadDiscriminatorSummariesButton ||
@@ -653,11 +656,19 @@ document.addEventListener("DOMContentLoaded", async () => {
});
validateLocalPipelineButton.addEventListener("click", async () => {
appendLogLine(logTextarea, "[ui] validating local pipeline");
const request: DemoPipeline2LocalValidationRequest = {
profileCode: validationProfileSelect.value,
};
appendLogLine(
logTextarea,
`[ui] validating local pipeline with '${request.profileCode}' profile`,
);
try {
const payload = await invoke<DemoPipeline2LocalValidationPayload>(
"demo_pipeline2_validate_local_pipeline",
{ request },
);
localValidationTextarea.value = payload.validationJson;

View File

@@ -1,7 +1,7 @@
{
"name": "kb-demo-app",
"private": true,
"version": "0.7.38",
"version": "0.7.39",
"type": "module",
"scripts": {
"dev": "vite",

View File

@@ -451,6 +451,10 @@ pub(crate) struct DemoPipeline2LocalPipelineDiagnosticSummary {
/// Missing trade events grouped by diagnostic reason.
pub missing_trade_event_reason_summaries:
std::vec::Vec<DemoPipeline2LocalMissingTradeEventReasonSummary>,
/// Samples of launch-origin attributions.
pub launch_origin_samples: std::vec::Vec<DemoPipeline2LocalLaunchOriginDiagnosticSample>,
/// Samples of pool-origin rows and their optional launch linkage.
pub pool_origin_samples: std::vec::Vec<DemoPipeline2LocalPoolOriginDiagnosticSample>,
/// Prioritized samples of tokens whose display metadata is still incomplete.
pub token_metadata_gap_samples:
std::vec::Vec<DemoPipeline2LocalTokenMetadataGapDiagnosticSample>,
@@ -896,6 +900,77 @@ pub(crate) struct DemoPipeline2LocalPairGapDiagnosticSample {
pub pair_candle_count: i64,
}
/// Local launch-origin diagnostic sample for the UI.
#[derive(Clone, Debug, serde::Serialize, TS)]
#[ts(
export,
export_to = "../frontend/ts/bindings/DemoPipeline2LocalLaunchOriginDiagnosticSample.ts"
)]
#[serde(rename_all = "camelCase")]
pub(crate) struct DemoPipeline2LocalLaunchOriginDiagnosticSample {
/// Launch attribution id.
#[ts(type = "number")]
pub launch_attribution_id: i64,
/// Launch surface code.
pub launch_surface_code: std::string::String,
/// Launch surface display name.
pub launch_surface_name: std::string::String,
/// Transaction signature.
pub transaction_signature: std::string::String,
/// Decoded event id.
#[ts(type = "number")]
pub decoded_event_id: i64,
/// Effective protocol recorded on the decoded event.
pub protocol_name: std::string::String,
/// Match kind used for attribution.
pub match_kind: std::string::String,
/// Matched key value.
pub matched_value: std::string::String,
/// 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 pair symbol.
pub pair_symbol: std::option::Option<std::string::String>,
}
/// Local pool-origin diagnostic sample for the UI.
#[derive(Clone, Debug, serde::Serialize, TS)]
#[ts(
export,
export_to = "../frontend/ts/bindings/DemoPipeline2LocalPoolOriginDiagnosticSample.ts"
)]
#[serde(rename_all = "camelCase")]
pub(crate) struct DemoPipeline2LocalPoolOriginDiagnosticSample {
/// Pool-origin id.
#[ts(type = "number")]
pub pool_origin_id: i64,
/// Effective DEX code attached to the pool.
pub dex_code: std::string::String,
/// Pool id.
#[ts(type = "number")]
pub pool_id: i64,
/// Pool address.
pub pool_address: std::string::String,
/// Optional pair id.
#[ts(type = "number | null")]
pub pair_id: std::option::Option<i64>,
/// Optional pair symbol.
pub pair_symbol: std::option::Option<std::string::String>,
/// Optional launch surface code.
pub launch_surface_code: std::option::Option<std::string::String>,
/// Founding transaction signature.
pub founding_signature: std::string::String,
/// Founding effective protocol.
pub founding_protocol_name: std::string::String,
/// Founding decoded event kind.
pub founding_event_kind: std::string::String,
}
/// Local token metadata gap sample for the UI.
#[derive(Clone, Debug, serde::Serialize, TS)]
#[ts(
@@ -1188,7 +1263,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.38_token_metadata_gap_prioritization".to_string(),
None => "0.7.39_launch_surface_origin_baseline".to_string(),
};
let run_result = match profile_code.as_str() {
"0.7.27" | "0.7.27_dexes_non_regression" => {
@@ -1227,6 +1302,9 @@ 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" => {
service.validate_v0_7_39_current_database().await
},
other => Err(kb_lib::Error::InvalidState(format!(
"unsupported local pipeline validation profile: {other}"
))),
@@ -1777,6 +1855,14 @@ fn demo_pipeline2_map_local_diagnostics_summary(
multi_trade_signature_pair_samples
.push(demo_pipeline2_map_multi_trade_signature_pair_sample(sample));
}
let mut launch_origin_samples = std::vec::Vec::new();
for sample in summary.launch_origin_samples {
launch_origin_samples.push(demo_pipeline2_map_launch_origin_sample(sample));
}
let mut pool_origin_samples = std::vec::Vec::new();
for sample in summary.pool_origin_samples {
pool_origin_samples.push(demo_pipeline2_map_pool_origin_sample(sample));
}
let mut token_metadata_gap_samples = std::vec::Vec::new();
for sample in summary.token_metadata_gap_samples {
token_metadata_gap_samples.push(demo_pipeline2_map_token_metadata_gap_sample(sample));
@@ -1853,6 +1939,8 @@ fn demo_pipeline2_map_local_diagnostics_summary(
decoded_event_summaries,
event_classification_summaries,
missing_trade_event_reason_summaries,
launch_origin_samples,
pool_origin_samples,
token_metadata_gap_samples,
non_actionable_pair_count: summary.non_actionable_pair_count,
non_actionable_pair_summaries,
@@ -2052,6 +2140,42 @@ fn demo_pipeline2_map_multi_trade_signature_pair_sample(
};
}
fn demo_pipeline2_map_launch_origin_sample(
sample: kb_lib::LocalLaunchOriginDiagnosticSampleDto,
) -> DemoPipeline2LocalLaunchOriginDiagnosticSample {
return DemoPipeline2LocalLaunchOriginDiagnosticSample {
launch_attribution_id: sample.launch_attribution_id,
launch_surface_code: sample.launch_surface_code,
launch_surface_name: sample.launch_surface_name,
transaction_signature: sample.transaction_signature,
decoded_event_id: sample.decoded_event_id,
protocol_name: sample.protocol_name,
match_kind: sample.match_kind,
matched_value: sample.matched_value,
pool_id: sample.pool_id,
pool_address: sample.pool_address,
pair_id: sample.pair_id,
pair_symbol: sample.pair_symbol,
};
}
fn demo_pipeline2_map_pool_origin_sample(
sample: kb_lib::LocalPoolOriginDiagnosticSampleDto,
) -> DemoPipeline2LocalPoolOriginDiagnosticSample {
return DemoPipeline2LocalPoolOriginDiagnosticSample {
pool_origin_id: sample.pool_origin_id,
dex_code: sample.dex_code,
pool_id: sample.pool_id,
pool_address: sample.pool_address,
pair_id: sample.pair_id,
pair_symbol: sample.pair_symbol,
launch_surface_code: sample.launch_surface_code,
founding_signature: sample.founding_signature,
founding_protocol_name: sample.founding_protocol_name,
founding_event_kind: sample.founding_event_kind,
};
}
fn demo_pipeline2_map_token_metadata_gap_sample(
sample: kb_lib::LocalTokenMetadataGapDiagnosticSampleDto,
) -> DemoPipeline2LocalTokenMetadataGapDiagnosticSample {

View File

@@ -1,7 +1,7 @@
{
"$schema": "https://schema.tauri.app/config/2",
"productName": "kb-demo-app",
"version": "0.7.38",
"version": "0.7.39",
"identifier": "com.sasedev.kb-demo-app",
"build": {
"beforeDevCommand": "npm run dev",

View File

@@ -34,6 +34,7 @@ pub use dtos::LocalDecodedEventDiagnosticSummaryDto;
pub use dtos::LocalDexDiagnosticSummaryDto;
pub use dtos::LocalDuplicateDecodedEventTradeDiagnosticSampleDto;
pub use dtos::LocalEventClassificationDiagnosticSummaryDto;
pub use dtos::LocalLaunchOriginDiagnosticSampleDto;
pub use dtos::LocalMissingTradeEventDiagnosticSampleDto;
pub use dtos::LocalMissingTradeEventReasonSummaryDto;
pub use dtos::LocalMultiTradeSignaturePairDiagnosticSampleDto;
@@ -44,6 +45,7 @@ pub use dtos::LocalPairGapDiagnosticSampleDto;
pub use dtos::LocalPairTradingReadinessDiagnosticSummaryDto;
pub use dtos::LocalPipelineDiagnosticCountersDto;
pub use dtos::LocalPipelineDiagnosticSummaryDto;
pub use dtos::LocalPoolOriginDiagnosticSampleDto;
pub use dtos::LocalTokenMetadataGapDiagnosticSampleDto;
pub use dtos::ObservedTokenDto;
pub use dtos::OnchainObservationDto;
@@ -160,6 +162,7 @@ pub use queries::query_liquidity_events_upsert;
pub use queries::query_local_decoded_event_diagnostic_list_summaries;
pub use queries::query_local_duplicate_decoded_event_trade_diagnostic_list_samples;
pub use queries::query_local_event_classification_diagnostic_list_summaries;
pub use queries::query_local_launch_origin_diagnostic_list_samples;
pub use queries::query_local_missing_trade_event_diagnostic_list_samples;
pub use queries::query_local_missing_trade_event_reason_list_summaries;
pub use queries::query_local_multi_trade_signature_pair_diagnostic_list_samples;
@@ -171,6 +174,7 @@ pub use queries::query_local_pair_without_candle_diagnostic_list_samples;
pub use queries::query_local_pair_without_trade_diagnostic_list_samples;
pub use queries::query_local_pipeline_diagnostic_get_counters;
pub use queries::query_local_pipeline_diagnostic_list_summaries;
pub use queries::query_local_pool_origin_diagnostic_list_samples;
pub use queries::query_local_token_metadata_gap_diagnostic_list_samples;
pub use queries::query_observed_tokens_get_by_mint;
pub use queries::query_observed_tokens_list;

View File

@@ -49,6 +49,7 @@ pub(crate) use local_pipeline_diagnostics::LocalDecodedEventDiagnosticSummaryRow
pub(crate) use local_pipeline_diagnostics::LocalDexDiagnosticSummaryRow;
pub(crate) use local_pipeline_diagnostics::LocalDuplicateDecodedEventTradeDiagnosticSampleRow;
pub(crate) use local_pipeline_diagnostics::LocalEventClassificationDiagnosticSummaryRow;
pub(crate) use local_pipeline_diagnostics::LocalLaunchOriginDiagnosticSampleRow;
pub(crate) use local_pipeline_diagnostics::LocalMissingTradeEventDiagnosticSampleRow;
pub(crate) use local_pipeline_diagnostics::LocalMissingTradeEventReasonSummaryRow;
pub(crate) use local_pipeline_diagnostics::LocalMultiTradeSignaturePairDiagnosticSampleRow;
@@ -58,6 +59,7 @@ pub(crate) use local_pipeline_diagnostics::LocalPairDiagnosticSummaryRow;
pub(crate) use local_pipeline_diagnostics::LocalPairGapDiagnosticSampleRow;
pub(crate) use local_pipeline_diagnostics::LocalPairTradingReadinessDiagnosticSummaryRow;
pub(crate) use local_pipeline_diagnostics::LocalPipelineDiagnosticCountersRow;
pub(crate) use local_pipeline_diagnostics::LocalPoolOriginDiagnosticSampleRow;
pub(crate) use local_pipeline_diagnostics::LocalTokenMetadataGapDiagnosticSampleRow;
pub use analysis_signal::AnalysisSignalDto;
@@ -79,6 +81,7 @@ pub use local_pipeline_diagnostics::LocalDecodedEventDiagnosticSummaryDto;
pub use local_pipeline_diagnostics::LocalDexDiagnosticSummaryDto;
pub use local_pipeline_diagnostics::LocalDuplicateDecodedEventTradeDiagnosticSampleDto;
pub use local_pipeline_diagnostics::LocalEventClassificationDiagnosticSummaryDto;
pub use local_pipeline_diagnostics::LocalLaunchOriginDiagnosticSampleDto;
pub use local_pipeline_diagnostics::LocalMissingTradeEventDiagnosticSampleDto;
pub use local_pipeline_diagnostics::LocalMissingTradeEventReasonSummaryDto;
pub use local_pipeline_diagnostics::LocalMultiTradeSignaturePairDiagnosticSampleDto;
@@ -89,6 +92,7 @@ pub use local_pipeline_diagnostics::LocalPairGapDiagnosticSampleDto;
pub use local_pipeline_diagnostics::LocalPairTradingReadinessDiagnosticSummaryDto;
pub use local_pipeline_diagnostics::LocalPipelineDiagnosticCountersDto;
pub use local_pipeline_diagnostics::LocalPipelineDiagnosticSummaryDto;
pub use local_pipeline_diagnostics::LocalPoolOriginDiagnosticSampleDto;
pub use local_pipeline_diagnostics::LocalTokenMetadataGapDiagnosticSampleDto;
pub use observed_token::ObservedTokenDto;
pub use onchain_observation::OnchainObservationDto;

View File

@@ -138,6 +138,10 @@ pub struct LocalPipelineDiagnosticSummaryDto {
/// Missing trade events grouped by diagnostic reason.
pub missing_trade_event_reason_summaries:
std::vec::Vec<crate::LocalMissingTradeEventReasonSummaryDto>,
/// Samples of launch-origin attributions.
pub launch_origin_samples: std::vec::Vec<crate::LocalLaunchOriginDiagnosticSampleDto>,
/// Samples of pool-origin rows and their optional launch linkage.
pub pool_origin_samples: std::vec::Vec<crate::LocalPoolOriginDiagnosticSampleDto>,
/// Prioritized samples of tokens whose display metadata is still incomplete.
pub token_metadata_gap_samples: std::vec::Vec<crate::LocalTokenMetadataGapDiagnosticSampleDto>,
/// Total pairs with only non-actionable missing trade events.
@@ -714,6 +718,60 @@ pub struct LocalPairGapDiagnosticSampleDto {
pub pair_candle_count: i64,
}
/// Sample of a launch-origin attribution.
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct LocalLaunchOriginDiagnosticSampleDto {
/// Launch attribution id.
pub launch_attribution_id: i64,
/// Launch surface code.
pub launch_surface_code: std::string::String,
/// Launch surface display name.
pub launch_surface_name: std::string::String,
/// Transaction signature.
pub transaction_signature: std::string::String,
/// Decoded event id.
pub decoded_event_id: i64,
/// Protocol that materialized the decoded event.
pub protocol_name: std::string::String,
/// Match kind used to attribute the launch surface.
pub match_kind: std::string::String,
/// Matched key value used to attribute the launch surface.
pub matched_value: std::string::String,
/// Optional related pool id.
pub pool_id: std::option::Option<i64>,
/// Optional related pool address.
pub pool_address: std::option::Option<std::string::String>,
/// Optional related pair id.
pub pair_id: std::option::Option<i64>,
/// Optional related pair symbol.
pub pair_symbol: std::option::Option<std::string::String>,
}
/// Sample of a pool-origin row.
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct LocalPoolOriginDiagnosticSampleDto {
/// Pool-origin id.
pub pool_origin_id: i64,
/// Effective DEX code attached to the pool.
pub dex_code: std::string::String,
/// Pool id.
pub pool_id: i64,
/// Pool address.
pub pool_address: std::string::String,
/// Optional pair id.
pub pair_id: std::option::Option<i64>,
/// Optional pair symbol.
pub pair_symbol: std::option::Option<std::string::String>,
/// Optional launch surface code linked through launch attribution.
pub launch_surface_code: std::option::Option<std::string::String>,
/// Founding transaction signature.
pub founding_signature: std::string::String,
/// Founding effective DEX/protocol name.
pub founding_protocol_name: std::string::String,
/// Founding decoded event kind.
pub founding_event_kind: std::string::String,
}
/// Prioritized sample of an incomplete token metadata row.
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct LocalTokenMetadataGapDiagnosticSampleDto {
@@ -835,6 +893,38 @@ pub(crate) struct LocalPairGapDiagnosticSampleRow {
pub(crate) pair_candle_count: i64,
}
/// SQL row for launch-origin diagnostic samples.
#[derive(Debug, Clone, sqlx::FromRow)]
pub(crate) struct LocalLaunchOriginDiagnosticSampleRow {
pub(crate) launch_attribution_id: i64,
pub(crate) launch_surface_code: std::string::String,
pub(crate) launch_surface_name: std::string::String,
pub(crate) transaction_signature: std::string::String,
pub(crate) decoded_event_id: i64,
pub(crate) protocol_name: std::string::String,
pub(crate) match_kind: std::string::String,
pub(crate) matched_value: std::string::String,
pub(crate) pool_id: std::option::Option<i64>,
pub(crate) pool_address: std::option::Option<std::string::String>,
pub(crate) pair_id: std::option::Option<i64>,
pub(crate) pair_symbol: std::option::Option<std::string::String>,
}
/// SQL row for pool-origin diagnostic samples.
#[derive(Debug, Clone, sqlx::FromRow)]
pub(crate) struct LocalPoolOriginDiagnosticSampleRow {
pub(crate) pool_origin_id: i64,
pub(crate) dex_code: std::string::String,
pub(crate) pool_id: i64,
pub(crate) pool_address: std::string::String,
pub(crate) pair_id: std::option::Option<i64>,
pub(crate) pair_symbol: std::option::Option<std::string::String>,
pub(crate) launch_surface_code: std::option::Option<std::string::String>,
pub(crate) founding_signature: std::string::String,
pub(crate) founding_protocol_name: std::string::String,
pub(crate) founding_event_kind: std::string::String,
}
/// SQL row for incomplete token metadata samples.
#[derive(Debug, Clone, sqlx::FromRow)]
pub(crate) struct LocalTokenMetadataGapDiagnosticSampleRow {

View File

@@ -91,6 +91,7 @@ pub use liquidity_event::query_liquidity_events_upsert;
pub use local_pipeline_diagnostics::query_local_decoded_event_diagnostic_list_summaries;
pub use local_pipeline_diagnostics::query_local_duplicate_decoded_event_trade_diagnostic_list_samples;
pub use local_pipeline_diagnostics::query_local_event_classification_diagnostic_list_summaries;
pub use local_pipeline_diagnostics::query_local_launch_origin_diagnostic_list_samples;
pub use local_pipeline_diagnostics::query_local_missing_trade_event_diagnostic_list_samples;
pub use local_pipeline_diagnostics::query_local_missing_trade_event_reason_list_summaries;
pub use local_pipeline_diagnostics::query_local_multi_trade_signature_pair_diagnostic_list_samples;
@@ -102,6 +103,7 @@ pub use local_pipeline_diagnostics::query_local_pair_without_candle_diagnostic_l
pub use local_pipeline_diagnostics::query_local_pair_without_trade_diagnostic_list_samples;
pub use local_pipeline_diagnostics::query_local_pipeline_diagnostic_get_counters;
pub use local_pipeline_diagnostics::query_local_pipeline_diagnostic_list_summaries;
pub use local_pipeline_diagnostics::query_local_pool_origin_diagnostic_list_samples;
pub use local_pipeline_diagnostics::query_local_token_metadata_gap_diagnostic_list_samples;
pub use observed_token::query_observed_tokens_get_by_mint;
pub use observed_token::query_observed_tokens_list;

View File

@@ -1678,6 +1678,139 @@ LIMIT ?
}
}
/// Lists samples of launch-origin attributions.
pub async fn query_local_launch_origin_diagnostic_list_samples(
database: &crate::Database,
limit: i64,
) -> Result<std::vec::Vec<crate::LocalLaunchOriginDiagnosticSampleDto>, crate::Error> {
match database.connection() {
crate::DatabaseConnection::Sqlite(pool) => {
let rows_result = sqlx::query_as::<
sqlx::Sqlite,
crate::db::dtos::LocalLaunchOriginDiagnosticSampleRow,
>(
r#"
SELECT
la.id AS launch_attribution_id,
ls.code AS launch_surface_code,
ls.name AS launch_surface_name,
ct.signature AS transaction_signature,
la.decoded_event_id AS decoded_event_id,
la.protocol_name AS protocol_name,
la.match_kind AS match_kind,
la.matched_value AS matched_value,
la.pool_id AS pool_id,
p.address AS pool_address,
la.pair_id AS pair_id,
pair.symbol AS pair_symbol
FROM k_sol_launch_attributions la
JOIN k_sol_launch_surfaces ls ON ls.id = la.launch_surface_id
JOIN k_sol_chain_transactions ct ON ct.id = la.transaction_id
LEFT JOIN k_sol_pools p ON p.id = la.pool_id
LEFT JOIN k_sol_pairs pair ON pair.id = la.pair_id
ORDER BY la.id DESC
LIMIT ?
"#,
)
.bind(limit)
.fetch_all(pool)
.await;
let rows = match rows_result {
Ok(rows) => rows,
Err(error) => {
return Err(crate::Error::Db(format!(
"cannot list launch-origin diagnostic samples on sqlite: {}",
error
)));
},
};
let mut samples = std::vec::Vec::new();
for row in rows {
samples.push(crate::LocalLaunchOriginDiagnosticSampleDto {
launch_attribution_id: row.launch_attribution_id,
launch_surface_code: row.launch_surface_code,
launch_surface_name: row.launch_surface_name,
transaction_signature: row.transaction_signature,
decoded_event_id: row.decoded_event_id,
protocol_name: row.protocol_name,
match_kind: row.match_kind,
matched_value: row.matched_value,
pool_id: row.pool_id,
pool_address: row.pool_address,
pair_id: row.pair_id,
pair_symbol: row.pair_symbol,
});
}
return Ok(samples);
},
}
}
/// Lists samples of pool-origin rows and their optional launch linkage.
pub async fn query_local_pool_origin_diagnostic_list_samples(
database: &crate::Database,
limit: i64,
) -> Result<std::vec::Vec<crate::LocalPoolOriginDiagnosticSampleDto>, crate::Error> {
match database.connection() {
crate::DatabaseConnection::Sqlite(pool) => {
let rows_result = sqlx::query_as::<
sqlx::Sqlite,
crate::db::dtos::LocalPoolOriginDiagnosticSampleRow,
>(
r#"
SELECT
po.id AS pool_origin_id,
d.code AS dex_code,
po.pool_id AS pool_id,
p.address AS pool_address,
po.pair_id AS pair_id,
pair.symbol AS pair_symbol,
ls.code AS launch_surface_code,
po.founding_signature AS founding_signature,
po.founding_protocol_name AS founding_protocol_name,
po.founding_event_kind AS founding_event_kind
FROM k_sol_pool_origins po
JOIN k_sol_dexes d ON d.id = po.dex_id
JOIN k_sol_pools p ON p.id = po.pool_id
LEFT JOIN k_sol_pairs pair ON pair.id = po.pair_id
LEFT JOIN k_sol_launch_attributions la ON la.id = po.launch_attribution_id
LEFT JOIN k_sol_launch_surfaces ls ON ls.id = la.launch_surface_id
ORDER BY po.id DESC
LIMIT ?
"#,
)
.bind(limit)
.fetch_all(pool)
.await;
let rows = match rows_result {
Ok(rows) => rows,
Err(error) => {
return Err(crate::Error::Db(format!(
"cannot list pool-origin diagnostic samples on sqlite: {}",
error
)));
},
};
let mut samples = std::vec::Vec::new();
for row in rows {
samples.push(crate::LocalPoolOriginDiagnosticSampleDto {
pool_origin_id: row.pool_origin_id,
dex_code: row.dex_code,
pool_id: row.pool_id,
pool_address: row.pool_address,
pair_id: row.pair_id,
pair_symbol: row.pair_symbol,
launch_surface_code: row.launch_surface_code,
founding_signature: row.founding_signature,
founding_protocol_name: row.founding_protocol_name,
founding_event_kind: row.founding_event_kind,
});
}
return Ok(samples);
},
}
}
/// Lists prioritized samples of tokens whose metadata is still incomplete.
pub async fn query_local_token_metadata_gap_diagnostic_list_samples(
database: &crate::Database,

View File

@@ -573,6 +573,27 @@ const DEX_SUPPORT_MATRIX_ENTRIES: &[DexSupportMatrixEntry] = &[
skip_reason: Some("program_id_to_verify"),
catalog_enabled: false,
},
DexSupportMatrixEntry {
code: "bonk_fun",
display_name: "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: "okx_dex",
display_name: "OKX DEX",
@@ -806,6 +827,7 @@ mod tests {
"meteora_damm_v2",
"bags",
"bonk",
"bonk_fun",
"okx_dex",
"boop_fun",
"moonshot",
@@ -825,8 +847,11 @@ mod tests {
#[test]
fn matrix_does_not_invent_program_ids_for_unverified_planned_surfaces() {
let codes = [
"raydium_launchpad",
"bags",
"letsbonk",
"bonk",
"bonk_fun",
"okx_dex",
"boop_fun",
"moonshot",
@@ -879,7 +904,19 @@ mod tests {
#[test]
fn matrix_marks_launch_surfaces_as_launch_or_bonding_curve() {
let codes = ["pump_fun", "raydium_launchlab", "bags", "moonshot", "moonit"];
let codes = [
"pump_fun",
"raydium_launchlab",
"raydium_launchpad",
"letsbonk",
"bonk_fun",
"bags",
"moonshot",
"moonit",
"boop_fun",
"believe",
"heaven",
];
for code in codes {
let entry = match crate::dex_support_matrix_entry_by_code(code) {
Some(entry) => entry,
@@ -894,6 +931,33 @@ mod tests {
}
}
#[test]
fn matrix_keeps_0_7_39_launch_surfaces_non_trade_materialized() {
let codes = [
"raydium_launchlab",
"raydium_launchpad",
"letsbonk",
"bonk_fun",
"bags",
"moonshot",
"moonit",
"boop_fun",
"believe",
"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.decoded, "{} must not be decoded without corpus", code);
assert!(!entry.materialized, "{} must not materialize launch rows as trades", code);
assert!(!entry.trade_candidate, "{} must not create trade candidates", code);
assert!(!entry.candle_candidate, "{} must not create candle candidates", code);
assert!(!entry.catalog_enabled, "{} must not be inserted as enabled DEX", code);
}
}
#[test]
fn matrix_dto_preserves_core_fields() {
let entry = match crate::dex_support_matrix_entry_by_code("pump_swap") {

View File

@@ -2,6 +2,77 @@
//! Launch surface attribution service.
#[derive(Debug, Copy, Clone)]
struct BuiltinLaunchSurfaceSpec {
code: &'static str,
name: &'static str,
protocol_family: &'static str,
enabled: bool,
}
const BUILTIN_LAUNCH_SURFACE_SPECS: &[BuiltinLaunchSurfaceSpec] = &[
BuiltinLaunchSurfaceSpec {
code: "raydium_launchlab",
name: "Raydium LaunchLab",
protocol_family: "raydium",
enabled: true,
},
BuiltinLaunchSurfaceSpec {
code: "raydium_launchpad",
name: "Raydium Launchpad",
protocol_family: "raydium",
enabled: true,
},
BuiltinLaunchSurfaceSpec {
code: "letsbonk",
name: "LetsBonk / Bonk.fun",
protocol_family: "bonk",
enabled: true,
},
BuiltinLaunchSurfaceSpec {
code: "bonk_fun",
name: "Bonk.fun",
protocol_family: "bonk",
enabled: true,
},
BuiltinLaunchSurfaceSpec {
code: "bags",
name: "Bags",
protocol_family: "bags",
enabled: true,
},
BuiltinLaunchSurfaceSpec {
code: "moonshot",
name: "Moonshot",
protocol_family: "moonshot",
enabled: true,
},
BuiltinLaunchSurfaceSpec {
code: "moonit",
name: "Moonit",
protocol_family: "moonit",
enabled: true,
},
BuiltinLaunchSurfaceSpec {
code: "boop_fun",
name: "Boop.fun",
protocol_family: "boop",
enabled: true,
},
BuiltinLaunchSurfaceSpec {
code: "believe",
name: "Believe",
protocol_family: "believe",
enabled: true,
},
BuiltinLaunchSurfaceSpec {
code: "heaven",
name: "Heaven",
protocol_family: "heaven",
enabled: false,
},
];
/// Result of one launch surface attribution.
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct LaunchAttributionResult {
@@ -33,57 +104,121 @@ impl LaunchOriginService {
return Self { database, persistence };
}
/// Ensures that the built-in `moonit` launch surface exists and returns its id.
pub async fn ensure_moonit_surface(&self) -> Result<i64, crate::Error> {
let existing_result =
crate::query_launch_surfaces_get_by_code(self.database.as_ref(), "moonit").await;
let existing_option = match existing_result {
Ok(existing_option) => existing_option,
/// Ensures that every built-in launch surface tracked by `0.7.39` exists.
pub async fn ensure_target_launch_surfaces(&self) -> Result<std::vec::Vec<i64>, crate::Error> {
let mut surface_ids = std::vec::Vec::new();
for spec in BUILTIN_LAUNCH_SURFACE_SPECS {
let surface_id_result = self.ensure_builtin_launch_surface(spec.code).await;
let surface_id = match surface_id_result {
Ok(surface_id) => surface_id,
Err(error) => return Err(error),
};
surface_ids.push(surface_id);
}
return Ok(surface_ids);
}
/// Ensures that one built-in launch surface exists and returns its id.
pub async fn ensure_builtin_launch_surface(&self, code: &str) -> Result<i64, crate::Error> {
let spec = match find_builtin_launch_surface_spec(code) {
Some(spec) => spec,
None => {
return Err(crate::Error::InvalidState(format!(
"unknown built-in launch surface '{}'",
code
)));
},
};
let surface_id_result = self.ensure_launch_surface_from_spec(spec).await;
let surface_id = match surface_id_result {
Ok(surface_id) => surface_id,
Err(error) => return Err(error),
};
let surface_id = match existing_option {
Some(existing) => match existing.id {
Some(surface_id) => surface_id,
None => {
return Err(crate::Error::InvalidState(
"moonit launch surface has no internal id".to_string(),
));
},
},
None => {
let dto = crate::LaunchSurfaceDto::new(
"moonit".to_string(),
"Moonit".to_string(),
Some("launchpad".to_string()),
true,
);
let insert_result =
crate::query_launch_surfaces_upsert(self.database.as_ref(), &dto).await;
match insert_result {
Ok(surface_id) => surface_id,
Err(error) => return Err(error),
}
},
};
let suffix_key_result = crate::query_launch_surface_keys_upsert(
self.database.as_ref(),
&crate::LaunchSurfaceKeyDto::new(
surface_id,
"token_mint_suffix".to_string(),
"moon".to_string(),
),
)
.await;
if let Err(error) = suffix_key_result {
return Err(error);
if spec.code == "moonit" {
let suffix_key_result = crate::query_launch_surface_keys_upsert(
self.database.as_ref(),
&crate::LaunchSurfaceKeyDto::new(
surface_id,
"token_mint_suffix".to_string(),
"moon".to_string(),
),
)
.await;
if let Err(error) = suffix_key_result {
return Err(error);
}
}
return Ok(surface_id);
}
/// Ensures that the built-in `moonit` launch surface exists and returns its id.
pub async fn ensure_moonit_surface(&self) -> Result<i64, crate::Error> {
return self.ensure_builtin_launch_surface("moonit").await;
}
/// Ensures that the built-in `bags` launch surface exists and returns its id.
pub async fn ensure_bags_surface(&self) -> Result<i64, crate::Error> {
return self.ensure_builtin_launch_surface("bags").await;
}
/// Registers one generic launch-surface mapping from verified corpus/API evidence.
pub async fn register_launch_surface_mapping(
&self,
surface_code: &str,
token_mint: std::option::Option<&str>,
config_account: std::option::Option<&str>,
pool_account: std::option::Option<&str>,
creator: std::option::Option<&str>,
) -> Result<i64, crate::Error> {
let surface_id_result = self.ensure_builtin_launch_surface(surface_code).await;
let surface_id = match surface_id_result {
Ok(surface_id) => surface_id,
Err(error) => return Err(error),
};
let key_specs = [
("token_mint", token_mint),
("config_account", config_account),
("pool_account", pool_account),
("creator", creator),
];
let mut inserted_key_count = 0_u32;
for (match_kind, match_value) in key_specs {
let match_value = match match_value {
Some(match_value) => match_value,
None => continue,
};
if match_value.trim().is_empty() {
continue;
}
let key_result = crate::query_launch_surface_keys_upsert(
self.database.as_ref(),
&crate::LaunchSurfaceKeyDto::new(
surface_id,
match_kind.to_string(),
match_value.to_string(),
),
)
.await;
if let Err(error) = key_result {
return Err(error);
}
inserted_key_count += 1;
}
if inserted_key_count == 0 {
return Err(crate::Error::InvalidState(format!(
"launch surface '{}' mapping must include at least one non-empty key",
surface_code
)));
}
return Ok(surface_id);
}
async fn ensure_launch_surface_from_spec(
&self,
spec: BuiltinLaunchSurfaceSpec,
) -> Result<i64, crate::Error> {
let existing_result =
crate::query_launch_surfaces_get_by_code(self.database.as_ref(), "bags").await;
crate::query_launch_surfaces_get_by_code(self.database.as_ref(), spec.code).await;
let existing_option = match existing_result {
Ok(existing_option) => existing_option,
Err(error) => return Err(error),
@@ -92,17 +227,18 @@ impl LaunchOriginService {
Some(existing) => match existing.id {
Some(surface_id) => return Ok(surface_id),
None => {
return Err(crate::Error::InvalidState(
"bags launch surface has no internal id".to_string(),
));
return Err(crate::Error::InvalidState(format!(
"{} launch surface has no internal id",
spec.code
)));
},
},
None => {
let dto = crate::LaunchSurfaceDto::new(
"bags".to_string(),
"Bags".to_string(),
Some("launchpad".to_string()),
true,
spec.code.to_string(),
spec.name.to_string(),
Some(spec.protocol_family.to_string()),
spec.enabled,
);
return crate::query_launch_surfaces_upsert(self.database.as_ref(), &dto).await;
},
@@ -117,51 +253,19 @@ impl LaunchOriginService {
dbc_pool_key: std::option::Option<std::string::String>,
damm_v2_pool_key: std::option::Option<std::string::String>,
) -> Result<i64, crate::Error> {
let surface_id_result = self.ensure_bags_surface().await;
let surface_id_result = self
.register_launch_surface_mapping(
"bags",
Some(token_mint),
dbc_config_key.as_deref(),
dbc_pool_key.as_deref(),
None,
)
.await;
let surface_id = match surface_id_result {
Ok(surface_id) => surface_id,
Err(error) => return Err(error),
};
let token_key_result = crate::query_launch_surface_keys_upsert(
self.database.as_ref(),
&crate::LaunchSurfaceKeyDto::new(
surface_id,
"token_mint".to_string(),
token_mint.to_string(),
),
)
.await;
if let Err(error) = token_key_result {
return Err(error);
}
if let Some(dbc_config_key) = dbc_config_key {
let key_result = crate::query_launch_surface_keys_upsert(
self.database.as_ref(),
&crate::LaunchSurfaceKeyDto::new(
surface_id,
"config_account".to_string(),
dbc_config_key,
),
)
.await;
if let Err(error) = key_result {
return Err(error);
}
}
if let Some(dbc_pool_key) = dbc_pool_key {
let key_result = crate::query_launch_surface_keys_upsert(
self.database.as_ref(),
&crate::LaunchSurfaceKeyDto::new(
surface_id,
"pool_account".to_string(),
dbc_pool_key,
),
)
.await;
if let Err(error) = key_result {
return Err(error);
}
}
if let Some(damm_v2_pool_key) = damm_v2_pool_key {
let key_result = crate::query_launch_surface_keys_upsert(
self.database.as_ref(),
@@ -209,8 +313,8 @@ impl LaunchOriginService {
)));
},
};
let ensure_moonit_result = self.ensure_moonit_surface().await;
if let Err(error) = ensure_moonit_result {
let ensure_surfaces_result = self.ensure_target_launch_surfaces().await;
if let Err(error) = ensure_surfaces_result {
return Err(error);
}
let decoded_events_result = crate::query_dex_decoded_events_list_by_transaction_id(
@@ -512,6 +616,15 @@ impl LaunchOriginService {
}
}
fn find_builtin_launch_surface_spec(code: &str) -> std::option::Option<BuiltinLaunchSurfaceSpec> {
for spec in BUILTIN_LAUNCH_SURFACE_SPECS {
if spec.code == code {
return Some(*spec);
}
}
return None;
}
#[derive(Debug, Clone)]
struct MatchedLaunchSurface {
launch_surface_id: i64,
@@ -866,4 +979,79 @@ mod tests {
assert_eq!(listed[0].match_kind, "token_mint_suffix".to_string());
assert_eq!(listed[0].matched_value, "ExampleTokenmoon".to_string());
}
#[tokio::test]
async fn ensure_target_launch_surfaces_seeds_0_7_39_surfaces_without_program_ids() {
let database = make_database().await;
let service = crate::LaunchOriginService::new(database.clone());
let result = service.ensure_target_launch_surfaces().await;
let surface_ids = match result {
Ok(surface_ids) => surface_ids,
Err(error) => panic!("target launch surfaces must seed: {}", error),
};
assert_eq!(surface_ids.len(), 10);
let required_codes = [
"raydium_launchlab",
"raydium_launchpad",
"letsbonk",
"bonk_fun",
"bags",
"moonshot",
"moonit",
"boop_fun",
"believe",
"heaven",
];
for code in required_codes {
let surface_result =
crate::query_launch_surfaces_get_by_code(database.as_ref(), code).await;
let surface_option = match surface_result {
Ok(surface_option) => surface_option,
Err(error) => panic!("surface fetch must succeed: {}", error),
};
assert!(surface_option.is_some(), "missing launch surface {}", code);
}
let heaven_surface =
match crate::query_launch_surfaces_get_by_code(database.as_ref(), "heaven").await {
Ok(Some(surface)) => surface,
Ok(None) => panic!("heaven surface must exist"),
Err(error) => panic!("heaven surface fetch must succeed: {}", error),
};
assert!(!heaven_surface.is_enabled);
}
#[tokio::test]
async fn attribute_transaction_by_signature_detects_generic_launch_surface_mapping() {
let database = make_database().await;
let service = crate::LaunchOriginService::new(database.clone());
let register_result = service
.register_launch_surface_mapping(
"raydium_launchlab",
Some("DbcDetectTokenA111"),
Some("DbcDetectConfig111"),
None,
None,
)
.await;
if let Err(error) = register_result {
panic!("launchlab mapping registration must succeed: {}", error);
}
seed_decoded_meteora_dbc_event(database.clone(), "sig-launch-origin-launchlab-1").await;
let result = service
.attribute_transaction_by_signature("sig-launch-origin-launchlab-1")
.await;
let results = match result {
Ok(results) => results,
Err(error) => panic!("launchlab attribution must succeed: {}", error),
};
assert_eq!(results.len(), 1);
assert!(results[0].created_attribution);
let surface_result =
crate::query_launch_surfaces_get_by_code(database.as_ref(), "raydium_launchlab").await;
let surface = match surface_result {
Ok(Some(surface)) => surface,
Ok(None) => panic!("raydium_launchlab surface must exist"),
Err(error) => panic!("surface fetch must succeed: {}", error),
};
assert_eq!(surface.id, Some(results[0].launch_surface_id));
}
}

View File

@@ -146,6 +146,8 @@ pub use constants::ARBITRAGE_BOT_6MWVT_PROGRAM_ID;
/// Associated Token Account program identifier. ("ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL").
/// @see solana_sdk::pubkey::Pubkey = spl_associated_token_account_interface::program::ID
pub use constants::ASSOCIATED_TOKEN_PROGRAM_ID;
/// Canonical Bonk token mint identifier.
pub use constants::BONK_MINT_ID;
/// BPF Loader program identifier. ("BPFLoader1111111111111111111111111111111111").
/// @see solana_sdk::pubkey::Pubkey = solana_sdk_ids::bpf_loader_deprecated::ID
pub use constants::BPF_LOADER_DEPRECATED_PROGRAM_ID;
@@ -171,6 +173,8 @@ pub use constants::FLUXBEAM_PROGRAM_ID;
/// Incinerator program identifier. ("1nc1nerator11111111111111111111111111111111").
/// @see solana_sdk::pubkey::Pubkey = solana_sdk_ids::incinerator::ID
pub use constants::INCINERATOR_PROGRAM_ID;
/// Canonical Jupiter governance token mint identifier.
pub use constants::JUP_MINT_ID;
/// Loader V4 program identifier. ("LoaderV411111111111111111111111111111111111").
/// @see solana_sdk::pubkey::Pubkey = solana_sdk_ids::loader_v4::ID
pub use constants::LOADER_V4_PROGRAM_ID;
@@ -191,6 +195,8 @@ pub use constants::ORCA_WHIRLPOOLS_PROGRAM_ID;
pub use constants::PUMP_FUN_PROGRAM_ID;
/// PumpSwap / PumpAMM program id. ("pAMMBay6oceH9fJKBRHGP5D4bD4sWpmSwMn52FMfXEA").
pub use constants::PUMP_SWAP_PROGRAM_ID;
/// Canonical Raydium token mint identifier.
pub use constants::RAY_MINT_ID;
/// Raydium AMM routing program id. ("routeUGWgWzqBWFcrCfv8tritsqukccJPu3q5GPP3xS").
pub use constants::RAYDIUM_AMM_ROUTING_PROGRAM_ID;
/// Raydium AmmV4 program id. ("675kPX9MHTjS2zt1qfr1NYHuzeLXfQM9H24wFSUt1Mp8").
@@ -263,19 +269,13 @@ pub use constants::SYSVAR_SLOT_HISTORY_PROGRAM_ID;
/// Sysvar Stake History program identifier. ("SysvarStakeHistory1111111111111111111111111").
/// @see solana_sdk::pubkey::Pubkey = solana_sdk_ids::sysvar::stake_history::ID
pub use constants::SYSVAR_STAKE_HISTORY_PROGRAM_ID;
/// Vote program identifier. ("Vote111111111111111111111111111111111111111").
/// @see solana_sdk::pubkey::Pubkey = solana_sdk_ids::vote::ID
pub use constants::VOTE_PROGRAM_ID;
/// Canonical Bonk token mint identifier.
pub use constants::BONK_MINT_ID;
/// Canonical Jupiter governance token mint identifier.
pub use constants::JUP_MINT_ID;
/// Canonical Raydium token mint identifier.
pub use constants::RAY_MINT_ID;
/// Canonical Solana USDC mint identifier.
pub use constants::USDC_MINT_ID;
/// Canonical Solana USDT mint identifier.
pub use constants::USDT_MINT_ID;
/// Vote program identifier. ("Vote111111111111111111111111111111111111111").
/// @see solana_sdk::pubkey::Pubkey = solana_sdk_ids::vote::ID
pub use constants::VOTE_PROGRAM_ID;
/// Wrapped SOL mint identifier. ("So11111111111111111111111111111111111111112").
/// @see solana_sdk::pubkey::Pubkey = spl_token_interface::native_mint::ID
pub use constants::WSOL_MINT_ID;
@@ -365,6 +365,8 @@ pub use db::LocalDexDiagnosticSummaryDto;
pub use db::LocalDuplicateDecodedEventTradeDiagnosticSampleDto;
/// Local decoded-event classification diagnostics summary.
pub use db::LocalEventClassificationDiagnosticSummaryDto;
/// Sample of a launch-origin attribution.
pub use db::LocalLaunchOriginDiagnosticSampleDto;
/// Sample of a decoded trade candidate without linked trade event.
pub use db::LocalMissingTradeEventDiagnosticSampleDto;
/// Missing trade event diagnostics grouped by reason.
@@ -385,6 +387,8 @@ pub use db::LocalPairTradingReadinessDiagnosticSummaryDto;
pub use db::LocalPipelineDiagnosticCountersDto;
/// Local pipeline diagnostics summary.
pub use db::LocalPipelineDiagnosticSummaryDto;
/// Sample of a pool-origin row and optional launch linkage.
pub use db::LocalPoolOriginDiagnosticSampleDto;
/// Prioritized sample of an incomplete token metadata row.
pub use db::LocalTokenMetadataGapDiagnosticSampleDto;
/// Source family for one on-chain observation.
@@ -603,6 +607,8 @@ pub use db::query_local_decoded_event_diagnostic_list_summaries;
pub use db::query_local_duplicate_decoded_event_trade_diagnostic_list_samples;
/// Lists local decoded-event classification diagnostic summaries.
pub use db::query_local_event_classification_diagnostic_list_summaries;
/// Lists launch-origin diagnostic samples.
pub use db::query_local_launch_origin_diagnostic_list_samples;
/// Lists samples of decoded trade candidates without linked trade event.
pub use db::query_local_missing_trade_event_diagnostic_list_samples;
/// Lists missing trade events grouped by diagnostic reason.
@@ -625,6 +631,8 @@ pub use db::query_local_pair_without_trade_diagnostic_list_samples;
pub use db::query_local_pipeline_diagnostic_get_counters;
/// Lists local DEX diagnostic summaries.
pub use db::query_local_pipeline_diagnostic_list_summaries;
/// Lists pool-origin diagnostic samples.
pub use db::query_local_pool_origin_diagnostic_list_samples;
/// Lists prioritized token metadata gap diagnostic samples.
pub use db::query_local_token_metadata_gap_diagnostic_list_samples;
/// Reads one observed token by mint.

View File

@@ -75,6 +75,25 @@ impl LocalPipelineDiagnosticsService {
Ok(summaries) => summaries,
Err(error) => return Err(error),
};
let launch_origin_samples_result =
crate::query_local_launch_origin_diagnostic_list_samples(
self.database.as_ref(),
sample_limit,
)
.await;
let launch_origin_samples = match launch_origin_samples_result {
Ok(samples) => samples,
Err(error) => return Err(error),
};
let pool_origin_samples_result = crate::query_local_pool_origin_diagnostic_list_samples(
self.database.as_ref(),
sample_limit,
)
.await;
let pool_origin_samples = match pool_origin_samples_result {
Ok(samples) => samples,
Err(error) => return Err(error),
};
let token_metadata_gap_samples_result =
crate::query_local_token_metadata_gap_diagnostic_list_samples(
self.database.as_ref(),
@@ -216,6 +235,8 @@ impl LocalPipelineDiagnosticsService {
decoded_event_summaries,
event_classification_summaries,
missing_trade_event_reason_summaries,
launch_origin_samples,
pool_origin_samples,
token_metadata_gap_samples,
non_actionable_pair_count: counters.non_actionable_pair_count,
non_actionable_pair_summaries,

View File

@@ -277,6 +277,19 @@ impl LocalPipelineValidationConfig {
config.profile_code = "0.7.38_token_metadata_gap_prioritization".to_string();
return config;
}
/// Builds the `0.7.39` launch surface origin baseline 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 {
let mut config = Self::v0_7_38_token_metadata_gap_prioritization();
config.profile_code = "0.7.39_launch_surface_origin_baseline".to_string();
config.require_dex_support_matrix_semantics = true;
return config;
}
}
/// A single local pipeline validation issue.
@@ -505,6 +518,14 @@ impl LocalPipelineValidationService {
crate::LocalPipelineValidationConfig::v0_7_38_token_metadata_gap_prioritization();
return self.validate_current_database(&config).await;
}
/// Diagnoses the current database with the `0.7.39` launch-origin baseline profile.
pub async fn validate_v0_7_39_current_database(
&self,
) -> Result<crate::LocalPipelineValidationRunDto, crate::Error> {
let config = crate::LocalPipelineValidationConfig::v0_7_39_launch_surface_origin_baseline();
return self.validate_current_database(&config).await;
}
}
/// Validates a diagnostics summary without performing database access.
@@ -627,7 +648,8 @@ pub fn validate_local_pipeline_diagnostics_summary(
|| config.profile_code == "0.7.35_non_trade_fee_reward_admin"
|| 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.38_token_metadata_gap_prioritization"
|| 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 {
if !observed_dex_codes.contains(expected_dex_code) {
@@ -875,6 +897,23 @@ fn validate_dex_support_matrix_semantics(
blocking: true,
});
}
if (entry.status == "planned" || entry.status == "to_verify" || entry.status == "unknown")
&& entry.surface_type == "launch"
&& (entry.decoded
|| entry.materialized
|| entry.trade_candidate
|| entry.candle_candidate)
{
issues.push(crate::LocalPipelineValidationIssueDto {
code: "inactive_launch_surface_matrix_entry_actionable".to_string(),
message: format!(
"inactive launch surface '{}' must not be decoded, materialized, or priced as trade/candle candidate",
entry_code
),
subject: Some(entry_code.clone()),
blocking: true,
});
}
}
}
@@ -1061,6 +1100,8 @@ mod tests {
decoded_event_summaries: vec![],
event_classification_summaries: vec![],
missing_trade_event_reason_summaries: vec![],
launch_origin_samples: vec![],
pool_origin_samples: vec![],
token_metadata_gap_samples: vec![],
non_actionable_pair_summaries: vec![],
missing_trade_event_samples: vec![],
@@ -1355,6 +1396,16 @@ mod tests {
assert_eq!(summary.token_metadata_gap_samples.len(), 1);
}
#[test]
fn validation_accepts_0_7_39_launch_surface_origin_baseline() {
let summary = make_0_7_28_summary_with_meteora();
let config = crate::LocalPipelineValidationConfig::v0_7_39_launch_surface_origin_baseline();
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.blocking_issue_count, 0);
}
#[test]
fn validation_rejects_0_7_33_pair_trading_readiness_mismatch() {
let mut summary = make_0_7_28_summary_with_meteora();