0.7.29
This commit is contained in:
@@ -59,3 +59,4 @@
|
|||||||
0.7.26 - Diagnostics locaux du pipeline persisté, correction de l’agrégation instruction-scoped des swaps Raydium, clarification des compteurs de replay/upsert, et validation qu’aucun trade candidate issu d’une transaction OK n’est perdu
|
0.7.26 - Diagnostics locaux du pipeline persisté, correction de l’agrégation instruction-scoped des swaps Raydium, clarification des compteurs de replay/upsert, et validation qu’aucun trade candidate issu d’une transaction OK n’est perdu
|
||||||
0.7.27 - Validation multi-DEX et non-régression du pipeline sur Pump.fun, PumpSwap, Raydium CPMM et Raydium CLMM, avec corpus de tests, diagnostics de référence et garanties sur les événements non pricés
|
0.7.27 - Validation multi-DEX et non-régression du pipeline sur Pump.fun, PumpSwap, Raydium CPMM et Raydium CLMM, avec corpus de tests, diagnostics de référence et garanties sur les événements non pricés
|
||||||
0.7.28 - Refactor DEX commun et verrouillage des invariants de normalisation : séparation des événements décodés, actionnables, trade candidates et candle candidates ; conservation des transactions failed comme traçables mais non actionnables ; ajout de la règle bloquante empêchant tout trade/candle candidate sans payload de montants exploitable, notamment pour le cas partiel `meteora_damm_v1.swap` sans base/quote amount.
|
0.7.28 - Refactor DEX commun et verrouillage des invariants de normalisation : séparation des événements décodés, actionnables, trade candidates et candle candidates ; conservation des transactions failed comme traçables mais non actionnables ; ajout de la règle bloquante empêchant tout trade/candle candidate sans payload de montants exploitable, notamment pour le cas partiel `meteora_damm_v1.swap` sans base/quote amount.
|
||||||
|
0.7.29 - Ajout d’une matrice DEX commune (`dex_support_matrix`) utilisée par le catalogue DEX, la classification transactionnelle et l’enregistrement des protocol candidates ; ajout du profil de validation `0.7.29_multi_dex_matrix_baseline` exposant la matrice dans le rapport de validation ; préparation explicite des surfaces planifiées sans inventer de program ids non vérifiés.
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ members = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
[workspace.package]
|
[workspace.package]
|
||||||
version = "0.7.28"
|
version = "0.7.29"
|
||||||
edition = "2024"
|
edition = "2024"
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
repository = "https://git.sasedev.com/Sasedev/khadhroony-bobobot"
|
repository = "https://git.sasedev.com/Sasedev/khadhroony-bobobot"
|
||||||
@@ -87,3 +87,4 @@ manual_map = "allow"
|
|||||||
match_like_matches_macro = "allow"
|
match_like_matches_macro = "allow"
|
||||||
single_match = "allow"
|
single_match = "allow"
|
||||||
manual_unwrap_or_default = "allow"
|
manual_unwrap_or_default = "allow"
|
||||||
|
manual_find = "allow"
|
||||||
|
|||||||
87
README.md
87
README.md
@@ -4,7 +4,7 @@
|
|||||||
|
|
||||||
`khadhroony-bobobot` est un workspace Rust destiné à la détection, au décodage, à l’analyse et, à terme, au trading semi-automatisé de tokens Solana.
|
`khadhroony-bobobot` est un workspace Rust destiné à la détection, au décodage, à l’analyse 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 reprise autour de `0.7.27` : le socle transport HTTP/WS, la résolution transactionnelle, le modèle SQLite, plusieurs connecteurs DEX, les candles, les signaux analytiques et l’application de démonstration existent déjà.
|
Le README précédent décrivait surtout l’état `0.3.1`. Ce fichier reflète l’état de reprise autour de `0.7.29` : le socle transport HTTP/WS, la résolution transactionnelle, le modèle SQLite, plusieurs connecteurs DEX, les candles, les signaux analytiques, la validation locale et une matrice DEX commune existent déjà.
|
||||||
|
|
||||||
## 1. Objectif
|
## 1. Objectif
|
||||||
|
|
||||||
@@ -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.
|
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 autour de `0.7.27`
|
## 3. État actuel autour de `0.7.29`
|
||||||
|
|
||||||
### 3.1. Socle stabilisé à ne pas refactorer maintenant
|
### 3.1. Socle stabilisé à ne pas refactorer maintenant
|
||||||
|
|
||||||
@@ -63,12 +63,14 @@ Le pipeline `0.7.x` couvre déjà les étapes suivantes :
|
|||||||
|
|
||||||
### 3.3. Connecteurs validés manuellement via l’application de démo
|
### 3.3. Connecteurs validés manuellement via l’application de démo
|
||||||
|
|
||||||
Les connecteurs suivants ont déjà été testés via l’application de démonstration et doivent être verrouillés par corpus/replay avant d’ajouter de nouveaux DEX :
|
Les connecteurs suivants sont les surfaces prioritaires à verrouiller avant extension massive :
|
||||||
|
|
||||||
- `pump_fun` ;
|
- `pump_fun` comme surface de launch / mint initial ;
|
||||||
- `pump_swap` ;
|
- `pump_swap` pour les swaps post-migration Pump ;
|
||||||
- `raydium_cpmm` ;
|
- `raydium_cpmm` ;
|
||||||
- `raydium_clmm`.
|
- `raydium_clmm` ;
|
||||||
|
- `meteora_dlmm` ;
|
||||||
|
- `meteora_damm_v1`, actuellement partiel pour les swaps sans payload montant/prix exploitable.
|
||||||
|
|
||||||
### 3.4. Connecteurs déjà présents mais à consolider par corpus
|
### 3.4. Connecteurs déjà présents mais à consolider par corpus
|
||||||
|
|
||||||
@@ -93,28 +95,33 @@ La distinction importante est la suivante :
|
|||||||
|
|
||||||
### 4.1. Matrice de travail
|
### 4.1. Matrice de travail
|
||||||
|
|
||||||
| Code cible | Type | Statut actuel | Prochaine action |
|
Depuis `0.7.29`, la matrice de support DEX est portée par `kb_lib/src/dex_support_matrix.rs`. Elle centralise le code interne, la famille, la version, le type de surface, les program ids vérifiés localement, le statut de support, les capacités actuelles et les raisons de skip.
|
||||||
|
|
||||||
|
| Code cible | Type | Statut `0.7.29` | Prochaine action |
|
||||||
|---|---:|---|---|
|
|---|---:|---|---|
|
||||||
| `pump_fun` | Launch + bonding curve | testé via démo | verrouiller corpus, invariants et documentation |
|
| `pump_fun` | Launch + bonding curve | partiel | verrouiller le rattachement mint initial -> pools migrés |
|
||||||
| `pump_swap` | AMM / swap | testé via démo | verrouiller corpus, invariants et candles |
|
| `pump_swap` | AMM / swap | supporté | conserver invariants trade/candle |
|
||||||
| `raydium_cpmm` | AMM | testé via démo | verrouiller corpus, swaps et candles |
|
| `raydium_cpmm` | AMM | supporté | conserver invariants trade/candle |
|
||||||
| `raydium_clmm` | CLMM | testé via démo | verrouiller corpus, swaps et candles |
|
| `raydium_clmm` | CLMM | supporté | conserver invariants trade/candle |
|
||||||
| `raydium_launchlab` / `raydium_launchpad` | Launch surface + migration | manquant | ajouter comme origine de mint/lancement et migration vers Raydium |
|
| `raydium_launchlab` | Launch surface | planifié, program id local connu | ajouter decoder/materialization dédiée |
|
||||||
| `raydium_amm_v4` | AMM legacy | présent, à isoler | traiter après les autres Raydium avec corpus dédié |
|
| `raydium_launchpad` | Launch surface | à vérifier | ne pas inventer de program id |
|
||||||
| `meteora_dbc` | Launch / bonding curve | présent, à consolider | corpus, lifecycle, migration et swaps exploitables |
|
| `raydium_amm_v4` | AMM legacy | partiel | traiter après les autres Raydium avec corpus dédié |
|
||||||
| `meteora_damm_v1` | AMM legacy | présent, à consolider | corpus et séparation swaps/liquidité/events |
|
| `meteora_dlmm` | DLMM | supporté | verrouiller corpus et non-régression |
|
||||||
| `meteora_damm_v2` | AMM | présent, à consolider | corpus et séparation swaps/liquidité/events |
|
| `meteora_damm_v1` | AMM legacy | partiel | conserver le skip explicite des swaps sans montants exploitables |
|
||||||
| `meteora_dlmm` | DLMM | manquant | ajouter à la matrice, puis corpus avant décodage |
|
| `meteora_damm_v2` | AMM | partiel | corpus et séparation swaps/liquidité/events |
|
||||||
| `orca_whirlpools` | CLMM | présent, à consolider | corpus fiable et validation des instructions utiles |
|
| `meteora_dbc` | Launch / bonding curve | partiel | lifecycle, migration et swaps exploitables |
|
||||||
| `fluxbeam` | DEX | présent, à consolider | corpus fiable avant validation |
|
| `meteora_dlc` | À vérifier | à vérifier | confirmer surface/program id avant intégration |
|
||||||
| `dexlab` | DEX | présent, à consolider | corpus fiable avant validation |
|
| `orca_whirlpools` | CLMM | partiel | corpus fiable et validation des instructions utiles |
|
||||||
| `bags` | Launch surface / attribution | amorcé | conserver comme origine de lancement, relier à Meteora si prouvé |
|
| `fluxbeam` | AMM | partiel | corpus fiable avant validation |
|
||||||
| `letsbonk` / `bonk_fun` | Launch surface | manquant | ajouter comme origine LaunchLab/Raydium, pas comme AMM autonome tant que non prouvé |
|
| `dexlab` | AMM | partiel | corpus fiable avant validation |
|
||||||
| `boop_fun` | Launch surface | manquant | ajouter comme origine de mint/lancement et migration |
|
| `bags` | Launch surface | planifié | conserver comme origine de lancement si corpus le prouve |
|
||||||
| `moonshot` / `moonit` | Launch surface | amorcé partiellement | remplacer les heuristiques faibles par corpus et règles prouvées |
|
| `letsbonk` / `bonk` | Launch surface | planifié | ajouter comme origine de mint/lancement sans supposer un AMM autonome |
|
||||||
| `believe` | Launch surface | manquant | ajouter comme origine associée à Meteora DBC si les comptes l’attestent |
|
| `okx_dex` | Aggregator/router | planifié | classifier sans matérialiser en trade direct avant preuve |
|
||||||
| `heaven` | Launch + AMM candidat | manquant | ajouter corpus et déterminer séparation launch/swap |
|
| `boop_fun` | Launch surface | planifié | ajouter comme origine de mint/lancement et migration |
|
||||||
| `zora_solana` | À vérifier | écarté maintenant | ne pas intégrer avant preuve de programme Solana pertinent |
|
| `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 |
|
||||||
|
|
||||||
## 5. Base de données
|
## 5. Base de données
|
||||||
|
|
||||||
@@ -147,9 +154,9 @@ Le modèle actuel contient déjà notamment :
|
|||||||
- metrics et analytic signals ;
|
- metrics et analytic signals ;
|
||||||
- diagnostics locaux.
|
- diagnostics locaux.
|
||||||
|
|
||||||
### 5.2. Tables futures prioritaires
|
### 5.2. Tables existantes à stabiliser pour les cas non-trade et inconnus
|
||||||
|
|
||||||
Avant d’étendre trop agressivement les DEX, le modèle doit prévoir les cas non directement tradables :
|
Avant d’étendre trop agressivement les DEX, ces tables doivent être stabilisées et raccordées progressivement aux diagnostics et matérialisations :
|
||||||
|
|
||||||
| Table cible | Rôle |
|
| Table cible | Rôle |
|
||||||
|---|---|
|
|---|---|
|
||||||
@@ -206,18 +213,16 @@ Les tests peuvent rester plus souples lorsque cela clarifie le test.
|
|||||||
|
|
||||||
La reprise doit suivre cet ordre :
|
La reprise doit suivre cet ordre :
|
||||||
|
|
||||||
1. finir/verrouiller `0.7.27` sur `pump_fun`, `pump_swap`, `raydium_cpmm`, `raydium_clmm` ;
|
1. conserver la non-régression `0.7.28` : transactions failed traçables mais non actionnables, aucun trade/candle candidate sans montant exploitable ;
|
||||||
2. démarrer `0.7.28` par un refactor commun DEX sans toucher au transport ;
|
2. utiliser la matrice `0.7.29` comme source commune pour le catalogue, la classification et les protocol candidates ;
|
||||||
3. ajouter la matrice DEX documentée et les corpus de validation ;
|
3. relier progressivement les événements non-trade aux tables existantes : lifecycle, liquidité, fees, rewards, admin ;
|
||||||
4. ajouter les tables de classification des transactions inconnues et protocol candidates ;
|
4. consolider Meteora, surtout `meteora_dlmm` et le cas partiel `meteora_damm_v1` ;
|
||||||
5. matérialiser les événements non-trade : lifecycle, liquidité, fees, rewards, admin ;
|
5. ajouter les launch surfaces manquantes comme origines de mint : LaunchLab/Launchpad, LetsBonk/Bonk.fun, Boop.fun, Moonshot/Moonit, Believe, Bags ;
|
||||||
6. consolider Meteora, y compris `meteora_dlmm` dans la matrice ;
|
6. traiter Heaven ;
|
||||||
7. ajouter les launch surfaces manquantes comme origines de mint : LaunchLab/Launchpad, LetsBonk/Bonk.fun, Boop.fun, Moonshot/Moonit, Believe ;
|
7. consolider Orca/FluxBeam/DexLab ;
|
||||||
8. traiter Heaven ;
|
8. isoler Raydium AMM v4 legacy ;
|
||||||
9. consolider Orca/FluxBeam/DexLab ;
|
9. effectuer une validation DEX v1 consolidée ;
|
||||||
10. isoler Raydium AMM v4 legacy ;
|
10. reprendre ensuite l’UI analytique et les vues token/pair/pool.
|
||||||
11. effectuer une validation DEX v1 consolidée ;
|
|
||||||
12. reprendre ensuite l’UI analytique et les vues token/pair/pool.
|
|
||||||
|
|
||||||
## 9. Fichiers utiles pour reprendre dans une nouvelle session
|
## 9. Fichiers utiles pour reprendre dans une nouvelle session
|
||||||
|
|
||||||
|
|||||||
122
ROADMAP.md
122
ROADMAP.md
@@ -766,9 +766,7 @@ Objectif : verrouiller la non-régression du pipeline actuel avant d’ajouter d
|
|||||||
- valider que les transactions échouées restent traçables dans les événements décodés sans produire de `k_sol_trade_events`.
|
- valider que les transactions échouées restent traçables dans les événements décodés sans produire de `k_sol_trade_events`.
|
||||||
|
|
||||||
### 6.060. Version `0.7.28` — Refactor DEX commun et préparation extension
|
### 6.060. Version `0.7.28` — Refactor DEX commun et préparation extension
|
||||||
Objectif : nettoyer la couche DEX avant d’ajouter de nouveaux protocoles, sans modifier le transport HTTP/WS déjà stabilisé.
|
Réalisé :
|
||||||
|
|
||||||
À faire :
|
|
||||||
|
|
||||||
- ne pas toucher à `ws_client.rs`, `ws_manager.rs`, `http_client.rs`, `http_pool.rs` ni aux couches JSON-RPC déjà stabilisées,
|
- ne pas toucher à `ws_client.rs`, `ws_manager.rs`, `http_client.rs`, `http_pool.rs` ni aux couches JSON-RPC déjà stabilisées,
|
||||||
- extraire depuis `dex_decode.rs` les catégories communes d’événements : trade, candle candidate, liquidity candidate, fee candidate, reward candidate, admin candidate, pool lifecycle candidate,
|
- extraire depuis `dex_decode.rs` les catégories communes d’événements : trade, candle candidate, liquidity candidate, fee candidate, reward candidate, admin candidate, pool lifecycle candidate,
|
||||||
@@ -787,52 +785,54 @@ Contraintes :
|
|||||||
- aucun changement de comportement métier volontaire dans cette version,
|
- aucun changement de comportement métier volontaire dans cette version,
|
||||||
- aucun événement non price-action ne doit devenir un trade ou une candle.
|
- aucun événement non price-action ne doit devenir un trade ou une candle.
|
||||||
|
|
||||||
### 6.061. Version `0.7.29` — Matrice DEX, corpus et documentation de support
|
### 6.061. Version `0.7.29` — Matrice DEX commune et validation baseline
|
||||||
Objectif : rendre explicite ce qui est validé, présent, partiel, manquant ou volontairement ignoré.
|
Réalisé :
|
||||||
|
|
||||||
À faire :
|
- ajouter `kb_lib/src/dex_support_matrix.rs` comme source commune de metadata DEX/surfaces ;
|
||||||
|
- exposer pour chaque entrée : code interne, famille, version, type de surface, program id connu ou à vérifier, support actuel, statut, confiance, raisons de skip et activation catalogue ;
|
||||||
- ajouter et maintenir une matrice DEX dans `README.md` et `ROADMAP.md`,
|
- raccorder `dex_catalog`, `transaction_classification` et `protocol_candidate_recording` à cette matrice ;
|
||||||
- distinguer clairement `DEX effectif`, `launch surface`, `pool origin`, `launch origin` et `migration target`,
|
- ajouter le profil `0.7.29_multi_dex_matrix_baseline` ;
|
||||||
- documenter que les launch surfaces sont importantes comme première source de mint/lancement même lorsqu’un token migre ensuite vers un autre DEX,
|
- exposer la matrice dans le rapport de validation local ;
|
||||||
- constituer une liste de corpus par DEX/surface : signatures, pools, token mints, résultat attendu,
|
- conserver explicitement le comportement `0.7.28` : transactions failed traçables mais non actionnables, et `meteora_damm_v1.swap` sans payload montant/prix non candidat trade/candle.
|
||||||
- indiquer pour chaque protocole : statut, source de preuve, type d’événements couverts, tables alimentées, limites connues,
|
|
||||||
- retirer `zora_solana` du phasage actif tant qu’aucun programme Solana pertinent n’est prouvé,
|
|
||||||
- ajouter `meteora_dlmm` à la matrice comme variante Meteora manquante à couvrir plus tard,
|
|
||||||
- préparer les diagnostics SQL de référence par protocole et par table métier.
|
|
||||||
|
|
||||||
Matrice cible initiale :
|
Matrice cible initiale :
|
||||||
|
|
||||||
| Code cible | Type | Statut | Objectif immédiat |
|
| Code cible | Type | Statut `0.7.29` | Objectif immédiat |
|
||||||
|---|---:|---|---|
|
|---|---:|---|---|
|
||||||
| `pump_fun` | launch + bonding curve | testé via démo | verrouiller corpus et invariants |
|
| `pump_fun` | launch + bonding curve | partiel | rattacher mint initial, bonding curve et migration |
|
||||||
| `pump_swap` | AMM / swap | testé via démo | verrouiller trades/candles |
|
| `pump_swap` | AMM / swap | supporté | conserver trades/candles |
|
||||||
| `raydium_cpmm` | AMM | testé via démo | verrouiller trades/candles |
|
| `raydium_cpmm` | AMM | supporté | conserver trades/candles |
|
||||||
| `raydium_clmm` | CLMM | testé via démo | verrouiller trades/candles |
|
| `raydium_clmm` | CLMM | supporté | conserver trades/candles |
|
||||||
| `raydium_launchlab` / `raydium_launchpad` | launch surface | manquant | détecter mint, launch, migration |
|
| `raydium_launchlab` | launch surface | planifié, program id local connu | ajouter decoder/materialization dédiée |
|
||||||
| `raydium_amm_v4` | AMM legacy | présent, à isoler | corpus dédié après autres DEX |
|
| `raydium_launchpad` | launch surface | à vérifier | ne pas inventer de program id |
|
||||||
| `meteora_dbc` | launch / bonding curve | présent, à consolider | lifecycle, migration, swaps utiles |
|
| `raydium_amm_v4` | AMM legacy | partiel | corpus dédié après autres Raydium |
|
||||||
| `meteora_damm_v1` | AMM legacy | présent, à consolider | corpus et séparation events |
|
| `raydium_router` | router | partiel | ne pas matérialiser en trade direct avant preuve |
|
||||||
| `meteora_damm_v2` | AMM | présent, à consolider | corpus et séparation events |
|
| `raydium_stable_swap` | AMM legacy | planifié | traiter seulement si corpus pertinent |
|
||||||
| `meteora_dlmm` | DLMM | manquant | ajouter corpus avant décodeur |
|
| `meteora_dlmm` | DLMM | supporté | verrouiller corpus et non-régression |
|
||||||
| `orca_whirlpools` | CLMM | présent, à consolider | validation par corpus |
|
| `meteora_damm_v1` | AMM legacy | partiel | garder skip explicite sans payload montant/prix |
|
||||||
| `fluxbeam` | DEX | présent, à consolider | validation par corpus |
|
| `meteora_damm_v2` | AMM | partiel | corpus et séparation events |
|
||||||
| `dexlab` | DEX | présent, à consolider | validation par corpus |
|
| `meteora_dbc` | launch / bonding curve | partiel | lifecycle, migration, swaps utiles |
|
||||||
| `bags` | launch surface | amorcé | attribution fiable, migration si prouvée |
|
| `meteora_dlc` | à vérifier | à vérifier | confirmer surface/program id avant intégration |
|
||||||
| `letsbonk` / `bonk_fun` | launch surface | manquant | origine LaunchLab/Raydium |
|
| `orca_whirlpools` | CLMM | partiel | validation par corpus |
|
||||||
| `boop_fun` | launch surface | manquant | origine mint/lancement/migration |
|
| `fluxbeam` | AMM | partiel | validation par corpus |
|
||||||
| `moonshot` / `moonit` | launch surface | amorcé partiellement | corpus, éviter heuristique faible |
|
| `dexlab` | AMM | partiel | validation par corpus |
|
||||||
| `believe` | launch surface | manquant | origine Meteora DBC si comptes probants |
|
| `bags` | launch surface | planifié | attribution fiable, migration si prouvée |
|
||||||
| `heaven` | launch + AMM candidat | manquant | corpus et séparation launch/swap |
|
| `letsbonk` / `bonk` | launch surface | planifié | origine mint/lancement, sans supposer un AMM autonome |
|
||||||
|
| `okx_dex` | aggregator/router | planifié | classifier sans trade direct avant preuve |
|
||||||
|
| `boop_fun` | launch surface | planifié | origine mint/lancement/migration |
|
||||||
|
| `moonshot` / `moonit` | launch surface | planifié | corpus, éviter heuristique faible |
|
||||||
|
| `believe` | launch surface | planifié | confirmer comptes et migrations |
|
||||||
|
| `heaven` | launch + AMM candidat | planifié | corpus et séparation launch/swap |
|
||||||
|
| `zora` | à vérifier | à vérifier | hors phasage actif avant preuve Solana |
|
||||||
|
|
||||||
### 6.062. Version `0.7.30` — Transactions inconnues et protocol candidates
|
### 6.062. Version `0.7.30` — Transactions inconnues et protocol candidates
|
||||||
Objectif : ne plus perdre les transactions utiles qui ne correspondent pas encore à un DEX connu.
|
Objectif : ne plus perdre les transactions utiles qui ne correspondent pas encore à un DEX connu.
|
||||||
|
|
||||||
À faire :
|
À faire :
|
||||||
|
|
||||||
- ajouter `k_sol_transaction_classifications`,
|
- consolider `k_sol_transaction_classifications`, déjà présente, avec les catégories utiles au suivi DEX,
|
||||||
- ajouter `k_sol_protocol_candidates`,
|
- consolider `k_sol_protocol_candidates`, déjà présente, pour prioriser les programmes inconnus ou partiellement reconnus,
|
||||||
- classifier les transactions résolues en catégories : known dex, known launch surface, unknown program, non-dex, failed transaction, partial decode, ignored technical transaction,
|
- classifier les transactions résolues en catégories : known supported, known partial, known non-trade, unknown program, unknown protocol candidate, unknown event kind, failed transaction, non-actionable trade,
|
||||||
- conserver les `program_id`, comptes, signatures, préfixes de `data`, logs et indices d’instructions utiles à l’analyse,
|
- conserver les `program_id`, comptes, signatures, préfixes de `data`, logs et indices d’instructions utiles à l’analyse,
|
||||||
- créer des requêtes de diagnostic pour repérer les programmes inconnus fréquents,
|
- créer des requêtes de diagnostic pour repérer les programmes inconnus fréquents,
|
||||||
- permettre de promouvoir plus tard un protocol candidate vers un vrai DEX/surface sans perdre l’historique,
|
- permettre de promouvoir plus tard un protocol candidate vers un vrai DEX/surface sans perdre l’historique,
|
||||||
@@ -1161,6 +1161,19 @@ Plus tard, ce comportement pourra devenir configurable dans `config.json` et pil
|
|||||||
6. laisser se terminer le flux de lecture,
|
6. laisser se terminer le flux de lecture,
|
||||||
7. journaliser clairement les cas dégradés.
|
7. journaliser clairement les cas dégradés.
|
||||||
|
|
||||||
|
## 10.5. Jalons `0.7.29`
|
||||||
|
|
||||||
|
Réalisé / à maintenir :
|
||||||
|
|
||||||
|
- matrice DEX commune dans `kb_lib/src/dex_support_matrix.rs` ;
|
||||||
|
- raccordement minimal du catalogue DEX à cette matrice ;
|
||||||
|
- raccordement des mappings program id -> protocole utilisés par la classification transactionnelle et les protocol candidates ;
|
||||||
|
- profil `0.7.29_multi_dex_matrix_baseline` ;
|
||||||
|
- exposition de la matrice dans le rapport de validation local ;
|
||||||
|
- aucune modification volontaire du comportement trade/candle validé en `0.7.28`.
|
||||||
|
|
||||||
|
À poursuivre en `0.7.30` : matérialisation contrôlée des événements non-trade utiles et des transactions inconnues/partielles, sans alimenter les trades/candles actionnables.
|
||||||
|
|
||||||
## 11. Documentation et livrables de référence
|
## 11. Documentation et livrables de référence
|
||||||
Le projet doit maintenir au minimum :
|
Le projet doit maintenir au minimum :
|
||||||
|
|
||||||
@@ -1175,21 +1188,18 @@ Le projet doit maintenir au minimum :
|
|||||||
|
|
||||||
La priorité immédiate est désormais la suivante :
|
La priorité immédiate est désormais la suivante :
|
||||||
|
|
||||||
1. terminer la validation `0.7.27` sur `pump_fun`, `pump_swap`, `raydium_cpmm` et `raydium_clmm`, sans ajouter de nouveau DEX dans cette étape,
|
1. conserver la validation acquise `0.7.28` : transactions failed traçables mais non actionnables, aucun trade/candle candidate sans payload montant/prix exploitable, aucun diagnostic bloquant masqué,
|
||||||
2. vérifier sur bases neuves et après replay local les invariants bloquants du pipeline : `diagnosticsClean = true`, `blockingIssueCount = 0`, aucun trade candidate exploitable perdu, aucun trade event invalide, aucun doublon réel par `decoded_event_id`, aucune candle dupliquée par bucket,
|
2. utiliser la matrice `0.7.29` (`kb_lib/src/dex_support_matrix.rs`) comme source commune pour le catalogue DEX, les mappings program id -> protocole, la classification transactionnelle et les protocol candidates,
|
||||||
3. démarrer `0.7.28` par le refactor DEX commun, sans toucher aux clients HTTP/WS ni aux managers réseau stabilisés,
|
3. garder les clients HTTP/WS et managers réseau hors du refactor DEX tant qu’ils ne bloquent pas le pipeline,
|
||||||
4. ajouter la matrice DEX et launch surfaces, avec statut, corpus, limites et prochaine action pour chaque protocole,
|
4. consolider les événements non-trade sans les confondre avec les trades/candles : lifecycle de pool, liquidité, fees, rewards, admin/config, migration et launch/mint,
|
||||||
5. ajouter les tables de classification des transactions inconnues et des protocol candidates afin de ne plus perdre les transactions utiles non encore décodables,
|
5. rattacher les launch surfaces aux tokens et aux pools migrés : Raydium LaunchLab/Launchpad, LetsBonk/Bonk.fun, Boop.fun, Moonshot/Moonit, Believe, Bags et Heaven,
|
||||||
6. matérialiser ensuite les événements non-trade : liquidité, cycle de vie des pools, fees, rewards et administration,
|
6. consolider Meteora avec corpus fiable : `meteora_dlmm`, `meteora_damm_v1`, `meteora_damm_v2`, `meteora_dbc` et `meteora_dlc` si le programme est confirmé,
|
||||||
7. garantir que ces événements non-trade restent séparés des `k_sol_trade_events` et des candles tout en restant rattachés aux transactions, decoded events, pools, pairs et wallets observés,
|
7. consolider Orca, FluxBeam et DexLab sur corpus,
|
||||||
8. consolider Meteora, y compris `meteora_dlmm`, après corpus fiable,
|
8. traiter `raydium_amm_v4` legacy seulement après les autres Raydium, avec corpus dédié prouvant le programme `675kPX9MHTjS2zt1qfr1NYHuzeLXfQM9H24wFSUt1Mp8`,
|
||||||
9. ajouter les launch surfaces manquantes comme premières sources de mint/lancement : Raydium LaunchLab/Launchpad, LetsBonk/Bonk.fun, Boop.fun, Moonshot/Moonit, Believe et Bags,
|
9. ajouter une matérialisation dédiée des transactions inconnues ou partiellement décodées pour analyser les DEX manquants sans polluer les trades/candles,
|
||||||
10. traiter Heaven avec séparation launch/swap,
|
10. effectuer une validation DEX v1 consolidée sur tous les connecteurs supportés avant de considérer la couche DEX `0.7.x` comme stable,
|
||||||
11. consolider Orca, FluxBeam et DexLab sur corpus,
|
11. ajouter ensuite les overlays des signaux analytiques sur les candles,
|
||||||
12. traiter `raydium_amm_v4` legacy seulement après les autres Raydium, avec corpus dédié prouvant le programme `675kPX9MHTjS2zt1qfr1NYHuzeLXfQM9H24wFSUt1Mp8`,
|
12. consolider les vues métier `token / pair / pool` dans `kb_demo_app`, y compris les événements liquidité, lifecycle, fees, rewards et admin,
|
||||||
13. effectuer une validation DEX v1 consolidée sur tous les connecteurs supportés avant de considérer la couche DEX `0.7.x` comme stable,
|
13. stabiliser l’ergonomie, les filtres, la pagination et la navigation de l’UI d’inspection,
|
||||||
14. ajouter ensuite les overlays des signaux analytiques sur les candles,
|
14. préparer ensuite l’ouverture de `0.8.x` pour l’analyse, les filtres, les patterns et les projections graphiques,
|
||||||
15. consolider les vues métier `token / pair / pool` dans `kb_demo_app`, y compris les événements liquidité, lifecycle, fees, rewards et admin,
|
15. préparer enfin Yellowstone gRPC comme extension de capacité, et non comme remplacement du socle HTTP / WS existant.
|
||||||
16. stabiliser l’ergonomie, les filtres, la pagination et la navigation de l’UI d’inspection,
|
|
||||||
17. préparer ensuite l’ouverture de `0.8.x` pour l’analyse, les filtres, les patterns et les projections graphiques,
|
|
||||||
18. préparer enfin Yellowstone gRPC comme extension de capacité, et non comme remplacement du socle HTTP / WS existant.
|
|
||||||
|
|||||||
@@ -163,6 +163,15 @@
|
|||||||
Analyse les données déjà persistées : transactions, decoded events, trades, candles, tokens, pools et pairs.
|
Analyse les données déjà persistées : transactions, decoded events, trades, candles, tokens, pools et pairs.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="demoPipeline2ValidationProfileSelect" class="form-label">Validation profile</label>
|
||||||
|
<select id="demoPipeline2ValidationProfileSelect" class="form-select">
|
||||||
|
<option value="0.7.29_multi_dex_matrix_baseline" selected>0.7.29 — DEX matrix baseline</option>
|
||||||
|
<option value="0.7.28_multi_dex_non_regression">0.7.28 — multi-DEX non-regression</option>
|
||||||
|
<option value="0.7.27_dexes_non_regression" selected>0.7.27 — first DEXes non-regression</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="d-flex gap-2">
|
<div class="d-flex gap-2">
|
||||||
<button id="demoPipeline2DiagnoseLocalPipelineButton" type="button" class="btn btn-outline-primary">
|
<button id="demoPipeline2DiagnoseLocalPipelineButton" type="button" class="btn btn-outline-primary">
|
||||||
Diagnose local pipeline
|
Diagnose local pipeline
|
||||||
|
|||||||
@@ -0,0 +1,82 @@
|
|||||||
|
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
|
||||||
|
|
||||||
|
/**
|
||||||
|
* One DEX support matrix entry for the UI.
|
||||||
|
*/
|
||||||
|
export type DemoPipeline2DexSupportMatrixEntry = {
|
||||||
|
/**
|
||||||
|
* Stable internal protocol or surface code.
|
||||||
|
*/
|
||||||
|
code: string,
|
||||||
|
/**
|
||||||
|
* Human-readable protocol or surface name.
|
||||||
|
*/
|
||||||
|
displayName: string,
|
||||||
|
/**
|
||||||
|
* Protocol family.
|
||||||
|
*/
|
||||||
|
family: string,
|
||||||
|
/**
|
||||||
|
* Protocol version or `unknown` when not verified locally.
|
||||||
|
*/
|
||||||
|
version: string,
|
||||||
|
/**
|
||||||
|
* Surface type: launch, bonding curve, AMM, CLMM, DLMM, router, aggregator or unknown.
|
||||||
|
*/
|
||||||
|
surfaceType: string,
|
||||||
|
/**
|
||||||
|
* Primary Solana program id, when verified in local constants or docs.
|
||||||
|
*/
|
||||||
|
programId: string | null,
|
||||||
|
/**
|
||||||
|
* Optional router program id, when this entry uses a distinct router.
|
||||||
|
*/
|
||||||
|
routerProgramId: string | null,
|
||||||
|
/**
|
||||||
|
* Program id confidence: known, to_verify or unknown.
|
||||||
|
*/
|
||||||
|
programIdStatus: string,
|
||||||
|
/**
|
||||||
|
* Whether this protocol has been observed in the local replay corpus.
|
||||||
|
*/
|
||||||
|
observed: boolean,
|
||||||
|
/**
|
||||||
|
* Whether the code currently contains a decoder for this protocol.
|
||||||
|
*/
|
||||||
|
decoded: boolean,
|
||||||
|
/**
|
||||||
|
* Whether decoded events are currently materialized beyond raw decoded rows.
|
||||||
|
*/
|
||||||
|
materialized: boolean,
|
||||||
|
/**
|
||||||
|
* Whether this protocol can currently produce trade candidates.
|
||||||
|
*/
|
||||||
|
tradeCandidate: boolean,
|
||||||
|
/**
|
||||||
|
* Whether this protocol can currently produce candle candidates.
|
||||||
|
*/
|
||||||
|
candleCandidate: boolean,
|
||||||
|
/**
|
||||||
|
* Whether this protocol can currently produce pair candidates.
|
||||||
|
*/
|
||||||
|
pairCandidate: boolean,
|
||||||
|
/**
|
||||||
|
* Whether this protocol can currently produce pool candidates.
|
||||||
|
*/
|
||||||
|
poolCandidate: boolean,
|
||||||
|
/**
|
||||||
|
* Operational support status.
|
||||||
|
*/
|
||||||
|
status: string,
|
||||||
|
/**
|
||||||
|
* Confidence level attached to this matrix entry.
|
||||||
|
*/
|
||||||
|
confidence: string,
|
||||||
|
/**
|
||||||
|
* Optional explicit skip reason for partial or ignored entries.
|
||||||
|
*/
|
||||||
|
skipReason: string | null,
|
||||||
|
/**
|
||||||
|
* Whether the entry should be inserted as an enabled DEX in the storage catalog.
|
||||||
|
*/
|
||||||
|
catalogEnabled: boolean, };
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
|
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
|
||||||
|
import type { DemoPipeline2DexSupportMatrixEntry } from "./DemoPipeline2DexSupportMatrixEntry";
|
||||||
import type { DemoPipeline2LocalPipelineValidationIssue } from "./DemoPipeline2LocalPipelineValidationIssue";
|
import type { DemoPipeline2LocalPipelineValidationIssue } from "./DemoPipeline2LocalPipelineValidationIssue";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -29,6 +30,14 @@ expectedDexCodes: Array<string>,
|
|||||||
* Observed DEX codes found in diagnostics.
|
* Observed DEX codes found in diagnostics.
|
||||||
*/
|
*/
|
||||||
observedDexCodes: Array<string>,
|
observedDexCodes: Array<string>,
|
||||||
|
/**
|
||||||
|
* Number of entries currently exposed by the DEX support matrix.
|
||||||
|
*/
|
||||||
|
dexSupportMatrixEntryCount: number,
|
||||||
|
/**
|
||||||
|
* DEX support matrix snapshot exposed with the validation report.
|
||||||
|
*/
|
||||||
|
dexSupportMatrix: Array<DemoPipeline2DexSupportMatrixEntry>,
|
||||||
/**
|
/**
|
||||||
* Issues produced by validation.
|
* Issues produced by validation.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -0,0 +1,10 @@
|
|||||||
|
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Request payload for local pipeline validation.
|
||||||
|
*/
|
||||||
|
export type DemoPipeline2LocalValidationRequest = {
|
||||||
|
/**
|
||||||
|
* Stable validation profile code to execute.
|
||||||
|
*/
|
||||||
|
profileCode: string, };
|
||||||
@@ -14,6 +14,7 @@ import type { DemoPipeline2BackfillPayload } from "./bindings/DemoPipeline2Backf
|
|||||||
import type { DemoPipeline2PairCandlesRequest } from "./bindings/DemoPipeline2PairCandlesRequest.ts";
|
import type { DemoPipeline2PairCandlesRequest } from "./bindings/DemoPipeline2PairCandlesRequest.ts";
|
||||||
import type { DemoPipeline2PairCandlesPayload } from "./bindings/DemoPipeline2PairCandlesPayload.ts";
|
import type { DemoPipeline2PairCandlesPayload } from "./bindings/DemoPipeline2PairCandlesPayload.ts";
|
||||||
import type { DemoPipeline2LocalDiagnosticsPayload } from "./bindings/DemoPipeline2LocalDiagnosticsPayload.ts";
|
import type { DemoPipeline2LocalDiagnosticsPayload } from "./bindings/DemoPipeline2LocalDiagnosticsPayload.ts";
|
||||||
|
import type { DemoPipeline2LocalValidationRequest } from "./bindings/DemoPipeline2LocalValidationRequest.ts";
|
||||||
import type { DemoPipeline2LocalValidationPayload } from "./bindings/DemoPipeline2LocalValidationPayload.ts";
|
import type { DemoPipeline2LocalValidationPayload } from "./bindings/DemoPipeline2LocalValidationPayload.ts";
|
||||||
import type { DemoPipeline2ProgramInstructionDiscriminatorSummaryRequest } from "./bindings/DemoPipeline2ProgramInstructionDiscriminatorSummaryRequest.ts";
|
import type { DemoPipeline2ProgramInstructionDiscriminatorSummaryRequest } from "./bindings/DemoPipeline2ProgramInstructionDiscriminatorSummaryRequest.ts";
|
||||||
import type { DemoPipeline2ProgramInstructionDiscriminatorSummaryPayload } from "./bindings/DemoPipeline2ProgramInstructionDiscriminatorSummaryPayload.ts";
|
import type { DemoPipeline2ProgramInstructionDiscriminatorSummaryPayload } from "./bindings/DemoPipeline2ProgramInstructionDiscriminatorSummaryPayload.ts";
|
||||||
@@ -356,6 +357,7 @@ document.addEventListener("DOMContentLoaded", async () => {
|
|||||||
const replayMetadataLimitInput = document.querySelector<HTMLInputElement>("#demoPipeline2ReplayMetadataLimitInput");
|
const replayMetadataLimitInput = document.querySelector<HTMLInputElement>("#demoPipeline2ReplayMetadataLimitInput");
|
||||||
const replayLocalPipelineButton = document.querySelector<HTMLButtonElement>("#demoPipeline2ReplayLocalPipelineButton");
|
const replayLocalPipelineButton = document.querySelector<HTMLButtonElement>("#demoPipeline2ReplayLocalPipelineButton");
|
||||||
const diagnoseLocalPipelineButton = document.querySelector<HTMLButtonElement>("#demoPipeline2DiagnoseLocalPipelineButton");
|
const diagnoseLocalPipelineButton = document.querySelector<HTMLButtonElement>("#demoPipeline2DiagnoseLocalPipelineButton");
|
||||||
|
const validationProfileSelect = document.querySelector<HTMLSelectElement>("#demoPipeline2ValidationProfileSelect");
|
||||||
const validateLocalPipelineButton = document.querySelector<HTMLButtonElement>("#demoPipeline2ValidateLocalPipelineButton");
|
const validateLocalPipelineButton = document.querySelector<HTMLButtonElement>("#demoPipeline2ValidateLocalPipelineButton");
|
||||||
|
|
||||||
const discriminatorProgramIdInput = document.querySelector<HTMLInputElement>("#demoPipeline2DiscriminatorProgramIdInput");
|
const discriminatorProgramIdInput = document.querySelector<HTMLInputElement>("#demoPipeline2DiscriminatorProgramIdInput");
|
||||||
@@ -402,6 +404,7 @@ document.addEventListener("DOMContentLoaded", async () => {
|
|||||||
!replayMetadataLimitInput ||
|
!replayMetadataLimitInput ||
|
||||||
!replayLocalPipelineButton ||
|
!replayLocalPipelineButton ||
|
||||||
!diagnoseLocalPipelineButton ||
|
!diagnoseLocalPipelineButton ||
|
||||||
|
!validationProfileSelect ||
|
||||||
!validateLocalPipelineButton ||
|
!validateLocalPipelineButton ||
|
||||||
!discriminatorProgramIdInput ||
|
!discriminatorProgramIdInput ||
|
||||||
!discriminatorLimitInput ||
|
!discriminatorLimitInput ||
|
||||||
@@ -647,11 +650,16 @@ document.addEventListener("DOMContentLoaded", async () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
validateLocalPipelineButton.addEventListener("click", async () => {
|
validateLocalPipelineButton.addEventListener("click", async () => {
|
||||||
appendLogLine(logTextarea, "[ui] validating local pipeline with 0.7.28 profile");
|
const request: DemoPipeline2LocalValidationRequest = {
|
||||||
|
profileCode: validationProfileSelect.value,
|
||||||
|
};
|
||||||
|
|
||||||
|
appendLogLine(logTextarea, `[ui] validating local pipeline with ${request.profileCode} profile`);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const payload = await invoke<DemoPipeline2LocalValidationPayload>(
|
const payload = await invoke<DemoPipeline2LocalValidationPayload>(
|
||||||
"demo_pipeline2_validate_local_pipeline",
|
"demo_pipeline2_validate_local_pipeline",
|
||||||
|
{ request },
|
||||||
);
|
);
|
||||||
|
|
||||||
localValidationTextarea.value = payload.validationJson;
|
localValidationTextarea.value = payload.validationJson;
|
||||||
@@ -659,7 +667,7 @@ document.addEventListener("DOMContentLoaded", async () => {
|
|||||||
|
|
||||||
appendLogLine(
|
appendLogLine(
|
||||||
logTextarea,
|
logTextarea,
|
||||||
`[ui] local pipeline validation completed: profile='${payload.run.validationProfileCode}' passed='${payload.run.validationPassed ? "yes" : "no"}' blocking='${payload.run.blockingIssueCount.toString()}' warnings='${payload.run.warningCount.toString()}'`,
|
`[ui] local pipeline validation completed: profile='${payload.run.validationProfileCode}' passed='${payload.run.validationPassed ? "yes" : "no"}' blocking='${payload.run.blockingIssueCount.toString()}' warnings='${payload.run.warningCount.toString()}' dexMatrix='${payload.run.report.dexSupportMatrixEntryCount.toString()}'`,
|
||||||
);
|
);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
appendLogLine(logTextarea, `[ui] local pipeline validation error: ${String(error)}`);
|
appendLogLine(logTextarea, `[ui] local pipeline validation error: ${String(error)}`);
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "kb-demo-app",
|
"name": "kb-demo-app",
|
||||||
"private": true,
|
"private": true,
|
||||||
"version": "0.7.28",
|
"version": "0.7.29",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "vite",
|
"dev": "vite",
|
||||||
|
|||||||
@@ -76,6 +76,18 @@ pub(crate) struct DemoPipeline2LocalDiagnosticsPayload {
|
|||||||
pub summary: DemoPipeline2LocalPipelineDiagnosticSummary,
|
pub summary: DemoPipeline2LocalPipelineDiagnosticSummary,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Request payload for local pipeline validation.
|
||||||
|
#[derive(Clone, Debug, serde::Deserialize, TS)]
|
||||||
|
#[ts(
|
||||||
|
export,
|
||||||
|
export_to = "../frontend/ts/bindings/DemoPipeline2LocalValidationRequest.ts"
|
||||||
|
)]
|
||||||
|
#[serde(rename_all = "camelCase")]
|
||||||
|
pub(crate) struct DemoPipeline2LocalValidationRequest {
|
||||||
|
/// Stable validation profile code to execute.
|
||||||
|
pub profile_code: std::string::String,
|
||||||
|
}
|
||||||
|
|
||||||
/// Local validation payload returned to the UI.
|
/// Local validation payload returned to the UI.
|
||||||
#[derive(Clone, Debug, serde::Serialize, TS)]
|
#[derive(Clone, Debug, serde::Serialize, TS)]
|
||||||
#[ts(
|
#[ts(
|
||||||
@@ -140,10 +152,63 @@ pub(crate) struct DemoPipeline2LocalPipelineValidationReport {
|
|||||||
pub expected_dex_codes: std::vec::Vec<std::string::String>,
|
pub expected_dex_codes: std::vec::Vec<std::string::String>,
|
||||||
/// Observed DEX codes found in diagnostics.
|
/// Observed DEX codes found in diagnostics.
|
||||||
pub observed_dex_codes: std::vec::Vec<std::string::String>,
|
pub observed_dex_codes: std::vec::Vec<std::string::String>,
|
||||||
|
/// Number of entries currently exposed by the DEX support matrix.
|
||||||
|
#[ts(type = "number")]
|
||||||
|
pub dex_support_matrix_entry_count: i64,
|
||||||
|
/// DEX support matrix snapshot exposed with the validation report.
|
||||||
|
pub dex_support_matrix: std::vec::Vec<DemoPipeline2DexSupportMatrixEntry>,
|
||||||
/// Issues produced by validation.
|
/// Issues produced by validation.
|
||||||
pub issues: std::vec::Vec<DemoPipeline2LocalPipelineValidationIssue>,
|
pub issues: std::vec::Vec<DemoPipeline2LocalPipelineValidationIssue>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// One DEX support matrix entry for the UI.
|
||||||
|
#[derive(Clone, Debug, serde::Serialize, TS)]
|
||||||
|
#[ts(
|
||||||
|
export,
|
||||||
|
export_to = "../frontend/ts/bindings/DemoPipeline2DexSupportMatrixEntry.ts"
|
||||||
|
)]
|
||||||
|
#[serde(rename_all = "camelCase")]
|
||||||
|
pub(crate) struct DemoPipeline2DexSupportMatrixEntry {
|
||||||
|
/// Stable internal protocol or surface code.
|
||||||
|
pub code: std::string::String,
|
||||||
|
/// Human-readable protocol or surface name.
|
||||||
|
pub display_name: std::string::String,
|
||||||
|
/// Protocol family.
|
||||||
|
pub family: std::string::String,
|
||||||
|
/// Protocol version or `unknown` when not verified locally.
|
||||||
|
pub version: std::string::String,
|
||||||
|
/// Surface type: launch, bonding curve, AMM, CLMM, DLMM, router, aggregator or unknown.
|
||||||
|
pub surface_type: std::string::String,
|
||||||
|
/// Primary Solana program id, when verified in local constants or docs.
|
||||||
|
pub program_id: std::option::Option<std::string::String>,
|
||||||
|
/// Optional router program id, when this entry uses a distinct router.
|
||||||
|
pub router_program_id: std::option::Option<std::string::String>,
|
||||||
|
/// Program id confidence: known, to_verify or unknown.
|
||||||
|
pub program_id_status: std::string::String,
|
||||||
|
/// Whether this protocol has been observed in the local replay corpus.
|
||||||
|
pub observed: bool,
|
||||||
|
/// Whether the code currently contains a decoder for this protocol.
|
||||||
|
pub decoded: bool,
|
||||||
|
/// Whether decoded events are currently materialized beyond raw decoded rows.
|
||||||
|
pub materialized: bool,
|
||||||
|
/// Whether this protocol can currently produce trade candidates.
|
||||||
|
pub trade_candidate: bool,
|
||||||
|
/// Whether this protocol can currently produce candle candidates.
|
||||||
|
pub candle_candidate: bool,
|
||||||
|
/// Whether this protocol can currently produce pair candidates.
|
||||||
|
pub pair_candidate: bool,
|
||||||
|
/// Whether this protocol can currently produce pool candidates.
|
||||||
|
pub pool_candidate: bool,
|
||||||
|
/// Operational support status.
|
||||||
|
pub status: std::string::String,
|
||||||
|
/// Confidence level attached to this matrix entry.
|
||||||
|
pub confidence: std::string::String,
|
||||||
|
/// Optional explicit skip reason for partial or ignored entries.
|
||||||
|
pub skip_reason: std::option::Option<std::string::String>,
|
||||||
|
/// Whether the entry should be inserted as an enabled DEX in the storage catalog.
|
||||||
|
pub catalog_enabled: bool,
|
||||||
|
}
|
||||||
|
|
||||||
/// One local pipeline validation issue for the UI.
|
/// One local pipeline validation issue for the UI.
|
||||||
#[derive(Clone, Debug, serde::Serialize, TS)]
|
#[derive(Clone, Debug, serde::Serialize, TS)]
|
||||||
#[ts(
|
#[ts(
|
||||||
@@ -839,14 +904,32 @@ pub(crate) async fn demo_pipeline2_diagnose_local_pipeline(
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Validates the local pipeline with the `0.7.28` multi-DEX non-regression profile.
|
/// Validates the local pipeline with a selected multi-DEX validation profile.
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
pub(crate) async fn demo_pipeline2_validate_local_pipeline(
|
pub(crate) async fn demo_pipeline2_validate_local_pipeline(
|
||||||
state: tauri::State<'_, crate::AppState>,
|
state: tauri::State<'_, crate::AppState>,
|
||||||
|
request: std::option::Option<DemoPipeline2LocalValidationRequest>,
|
||||||
) -> Result<DemoPipeline2LocalValidationPayload, std::string::String> {
|
) -> Result<DemoPipeline2LocalValidationPayload, std::string::String> {
|
||||||
let database = state.database.clone();
|
let database = state.database.clone();
|
||||||
let service = kb_lib::LocalPipelineValidationService::new(database.clone());
|
let service = kb_lib::LocalPipelineValidationService::new(database.clone());
|
||||||
let run_result = service.validate_v0_7_28_current_database().await;
|
let profile_code = match request {
|
||||||
|
Some(request) => request.profile_code,
|
||||||
|
None => "0.7.29_multi_dex_matrix_baseline".to_string(),
|
||||||
|
};
|
||||||
|
let run_result = match profile_code.as_str() {
|
||||||
|
"0.7.27" | "0.7.27_dexes_non_regression" => {
|
||||||
|
service.validate_v0_7_27_current_database().await
|
||||||
|
},
|
||||||
|
"0.7.28" | "0.7.28_multi_dex_non_regression" => {
|
||||||
|
service.validate_v0_7_28_current_database().await
|
||||||
|
},
|
||||||
|
"0.7.29" | "0.7.29_multi_dex_matrix_baseline" => {
|
||||||
|
service.validate_v0_7_29_current_database().await
|
||||||
|
},
|
||||||
|
other => Err(kb_lib::Error::InvalidState(format!(
|
||||||
|
"unsupported local pipeline validation profile: {other}"
|
||||||
|
))),
|
||||||
|
};
|
||||||
let run = match run_result {
|
let run = match run_result {
|
||||||
Ok(run) => run,
|
Ok(run) => run,
|
||||||
Err(error) => {
|
Err(error) => {
|
||||||
@@ -1257,6 +1340,10 @@ fn demo_pipeline2_map_local_validation_report(
|
|||||||
for issue in report.issues {
|
for issue in report.issues {
|
||||||
issues.push(demo_pipeline2_map_local_validation_issue(issue));
|
issues.push(demo_pipeline2_map_local_validation_issue(issue));
|
||||||
}
|
}
|
||||||
|
let mut dex_support_matrix = std::vec::Vec::new();
|
||||||
|
for entry in report.dex_support_matrix {
|
||||||
|
dex_support_matrix.push(demo_pipeline2_map_dex_support_matrix_entry(entry));
|
||||||
|
}
|
||||||
return DemoPipeline2LocalPipelineValidationReport {
|
return DemoPipeline2LocalPipelineValidationReport {
|
||||||
validation_profile_code: report.validation_profile_code,
|
validation_profile_code: report.validation_profile_code,
|
||||||
validation_passed: report.validation_passed,
|
validation_passed: report.validation_passed,
|
||||||
@@ -1264,10 +1351,38 @@ fn demo_pipeline2_map_local_validation_report(
|
|||||||
warning_count: report.warning_count,
|
warning_count: report.warning_count,
|
||||||
expected_dex_codes: report.expected_dex_codes,
|
expected_dex_codes: report.expected_dex_codes,
|
||||||
observed_dex_codes: report.observed_dex_codes,
|
observed_dex_codes: report.observed_dex_codes,
|
||||||
|
dex_support_matrix_entry_count: report.dex_support_matrix_entry_count,
|
||||||
|
dex_support_matrix,
|
||||||
issues,
|
issues,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn demo_pipeline2_map_dex_support_matrix_entry(
|
||||||
|
entry: kb_lib::DexSupportMatrixEntryDto,
|
||||||
|
) -> DemoPipeline2DexSupportMatrixEntry {
|
||||||
|
return DemoPipeline2DexSupportMatrixEntry {
|
||||||
|
code: entry.code,
|
||||||
|
display_name: entry.display_name,
|
||||||
|
family: entry.family,
|
||||||
|
version: entry.version,
|
||||||
|
surface_type: entry.surface_type,
|
||||||
|
program_id: entry.program_id,
|
||||||
|
router_program_id: entry.router_program_id,
|
||||||
|
program_id_status: entry.program_id_status,
|
||||||
|
observed: entry.observed,
|
||||||
|
decoded: entry.decoded,
|
||||||
|
materialized: entry.materialized,
|
||||||
|
trade_candidate: entry.trade_candidate,
|
||||||
|
candle_candidate: entry.candle_candidate,
|
||||||
|
pair_candidate: entry.pair_candidate,
|
||||||
|
pool_candidate: entry.pool_candidate,
|
||||||
|
status: entry.status,
|
||||||
|
confidence: entry.confidence,
|
||||||
|
skip_reason: entry.skip_reason,
|
||||||
|
catalog_enabled: entry.catalog_enabled,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
fn demo_pipeline2_map_local_validation_issue(
|
fn demo_pipeline2_map_local_validation_issue(
|
||||||
issue: kb_lib::LocalPipelineValidationIssueDto,
|
issue: kb_lib::LocalPipelineValidationIssueDto,
|
||||||
) -> DemoPipeline2LocalPipelineValidationIssue {
|
) -> DemoPipeline2LocalPipelineValidationIssue {
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"$schema": "https://schema.tauri.app/config/2",
|
"$schema": "https://schema.tauri.app/config/2",
|
||||||
"productName": "kb-demo-app",
|
"productName": "kb-demo-app",
|
||||||
"version": "0.7.28",
|
"version": "0.7.29",
|
||||||
"identifier": "com.sasedev.kb-demo-app",
|
"identifier": "com.sasedev.kb-demo-app",
|
||||||
"build": {
|
"build": {
|
||||||
"beforeDevCommand": "npm run dev",
|
"beforeDevCommand": "npm run dev",
|
||||||
|
|||||||
@@ -199,7 +199,7 @@ fn parse_accounts_json(accounts_json: &str) -> std::vec::Vec<std::string::String
|
|||||||
accounts.push(text.to_string());
|
accounts.push(text.to_string());
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if let Some(pubkey) = item.get("pubkey").and_then(|value| value.as_str()) {
|
if let Some(pubkey) = item.get("pubkey").and_then(|value| return value.as_str()) {
|
||||||
accounts.push(pubkey.to_string());
|
accounts.push(pubkey.to_string());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -177,12 +177,12 @@ fn build_program_instruction_discriminator_summaries(
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
summaries.sort_by(|left, right| {
|
summaries.sort_by(|left, right| {
|
||||||
right
|
return right
|
||||||
.undecoded_occurrence_count
|
.undecoded_occurrence_count
|
||||||
.cmp(&left.undecoded_occurrence_count)
|
.cmp(&left.undecoded_occurrence_count)
|
||||||
.then(right.transaction_count.cmp(&left.transaction_count))
|
.then(right.transaction_count.cmp(&left.transaction_count))
|
||||||
.then(right.occurrence_count.cmp(&left.occurrence_count))
|
.then(right.occurrence_count.cmp(&left.occurrence_count))
|
||||||
.then(right.latest_instruction_id.cmp(&left.latest_instruction_id))
|
.then(right.latest_instruction_id.cmp(&left.latest_instruction_id));
|
||||||
});
|
});
|
||||||
return Ok(summaries);
|
return Ok(summaries);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,178 +26,17 @@ pub(crate) struct DexCatalogItem {
|
|||||||
pub(crate) fn dex_catalog_item_by_code(
|
pub(crate) fn dex_catalog_item_by_code(
|
||||||
code: &str,
|
code: &str,
|
||||||
) -> std::option::Option<crate::dex_catalog::DexCatalogItem> {
|
) -> std::option::Option<crate::dex_catalog::DexCatalogItem> {
|
||||||
match code {
|
let matrix_entry = match crate::dex_support_matrix_entry_by_code(code) {
|
||||||
"raydium" => {
|
Some(matrix_entry) => matrix_entry,
|
||||||
return Some(crate::dex_catalog::DexCatalogItem {
|
None => return None,
|
||||||
code: "raydium",
|
};
|
||||||
name: "Raydium AMM v4",
|
return Some(crate::dex_catalog::DexCatalogItem {
|
||||||
program_id: Some(crate::RAYDIUM_AMM_V4_PROGRAM_ID),
|
code: matrix_entry.code,
|
||||||
router_program_id: None,
|
name: matrix_entry.display_name,
|
||||||
is_enabled: true,
|
program_id: matrix_entry.program_id,
|
||||||
});
|
router_program_id: matrix_entry.router_program_id,
|
||||||
},
|
is_enabled: matrix_entry.catalog_enabled,
|
||||||
"raydium_cpmm" => {
|
});
|
||||||
return Some(crate::dex_catalog::DexCatalogItem {
|
|
||||||
code: "raydium_cpmm",
|
|
||||||
name: "Raydium CPMM",
|
|
||||||
program_id: Some(crate::RAYDIUM_CPMM_PROGRAM_ID),
|
|
||||||
router_program_id: None,
|
|
||||||
is_enabled: true,
|
|
||||||
});
|
|
||||||
},
|
|
||||||
"raydium_clmm" => {
|
|
||||||
return Some(crate::dex_catalog::DexCatalogItem {
|
|
||||||
code: "raydium_clmm",
|
|
||||||
name: "Raydium CLMM",
|
|
||||||
program_id: Some(crate::RAYDIUM_CLMM_PROGRAM_ID),
|
|
||||||
router_program_id: None,
|
|
||||||
is_enabled: true,
|
|
||||||
});
|
|
||||||
},
|
|
||||||
"pump_fun" => {
|
|
||||||
return Some(crate::dex_catalog::DexCatalogItem {
|
|
||||||
code: "pump_fun",
|
|
||||||
name: "Pump.fun",
|
|
||||||
program_id: Some(crate::PUMP_FUN_PROGRAM_ID),
|
|
||||||
router_program_id: None,
|
|
||||||
is_enabled: true,
|
|
||||||
});
|
|
||||||
},
|
|
||||||
"pump_swap" => {
|
|
||||||
return Some(crate::dex_catalog::DexCatalogItem {
|
|
||||||
code: "pump_swap",
|
|
||||||
name: "PumpSwap",
|
|
||||||
program_id: Some(crate::PUMP_SWAP_PROGRAM_ID),
|
|
||||||
router_program_id: None,
|
|
||||||
is_enabled: true,
|
|
||||||
});
|
|
||||||
},
|
|
||||||
"meteora_dbc" => {
|
|
||||||
return Some(crate::dex_catalog::DexCatalogItem {
|
|
||||||
code: "meteora_dbc",
|
|
||||||
name: "Meteora DBC",
|
|
||||||
program_id: Some(crate::METEORA_DBC_PROGRAM_ID),
|
|
||||||
router_program_id: None,
|
|
||||||
is_enabled: true,
|
|
||||||
});
|
|
||||||
},
|
|
||||||
"meteora_dlmm" => {
|
|
||||||
return Some(crate::dex_catalog::DexCatalogItem {
|
|
||||||
code: "meteora_dlmm",
|
|
||||||
name: "Meteora DLMM",
|
|
||||||
program_id: Some(crate::METEORA_DLMM_PROGRAM_ID),
|
|
||||||
router_program_id: None,
|
|
||||||
is_enabled: true,
|
|
||||||
});
|
|
||||||
},
|
|
||||||
"meteora_damm_v1" => {
|
|
||||||
return Some(crate::dex_catalog::DexCatalogItem {
|
|
||||||
code: "meteora_damm_v1",
|
|
||||||
name: "Meteora DAMM v1",
|
|
||||||
program_id: Some(crate::METEORA_DAMM_V1_PROGRAM_ID),
|
|
||||||
router_program_id: None,
|
|
||||||
is_enabled: true,
|
|
||||||
});
|
|
||||||
},
|
|
||||||
"meteora_damm_v2" => {
|
|
||||||
return Some(crate::dex_catalog::DexCatalogItem {
|
|
||||||
code: "meteora_damm_v2",
|
|
||||||
name: "Meteora DAMM v2",
|
|
||||||
program_id: Some(crate::METEORA_DAMM_V2_PROGRAM_ID),
|
|
||||||
router_program_id: None,
|
|
||||||
is_enabled: true,
|
|
||||||
});
|
|
||||||
},
|
|
||||||
"orca_whirlpools" => {
|
|
||||||
return Some(crate::dex_catalog::DexCatalogItem {
|
|
||||||
code: "orca_whirlpools",
|
|
||||||
name: "Orca Whirlpools",
|
|
||||||
program_id: Some(crate::ORCA_WHIRLPOOLS_PROGRAM_ID),
|
|
||||||
router_program_id: None,
|
|
||||||
is_enabled: true,
|
|
||||||
});
|
|
||||||
},
|
|
||||||
"fluxbeam" => {
|
|
||||||
return Some(crate::dex_catalog::DexCatalogItem {
|
|
||||||
code: "fluxbeam",
|
|
||||||
name: "FluxBeam",
|
|
||||||
program_id: Some(crate::FLUXBEAM_PROGRAM_ID),
|
|
||||||
router_program_id: None,
|
|
||||||
is_enabled: true,
|
|
||||||
});
|
|
||||||
},
|
|
||||||
"dexlab" => {
|
|
||||||
return Some(crate::dex_catalog::DexCatalogItem {
|
|
||||||
code: "dexlab",
|
|
||||||
name: "DexLab Swap/Pool",
|
|
||||||
program_id: Some(crate::DEXLAB_PROGRAM_ID),
|
|
||||||
router_program_id: None,
|
|
||||||
is_enabled: true,
|
|
||||||
});
|
|
||||||
},
|
|
||||||
// Planned launch/swap surfaces.
|
|
||||||
//
|
|
||||||
// These entries are intentionally present before decoder support so that
|
|
||||||
// the roadmap can evolve without duplicating DEX metadata later.
|
|
||||||
//
|
|
||||||
// Program ids should be filled only after validation against live
|
|
||||||
// transactions and official or otherwise trustworthy references.
|
|
||||||
"raydium_launchlab" => {
|
|
||||||
return Some(crate::dex_catalog::DexCatalogItem {
|
|
||||||
code: "raydium_launchlab",
|
|
||||||
name: "Raydium LaunchLab",
|
|
||||||
program_id: None,
|
|
||||||
router_program_id: None,
|
|
||||||
is_enabled: false,
|
|
||||||
});
|
|
||||||
},
|
|
||||||
"letsbonk" => {
|
|
||||||
return Some(crate::dex_catalog::DexCatalogItem {
|
|
||||||
code: "letsbonk",
|
|
||||||
name: "LetsBonk / Bonk.fun",
|
|
||||||
program_id: None,
|
|
||||||
router_program_id: None,
|
|
||||||
is_enabled: false,
|
|
||||||
});
|
|
||||||
},
|
|
||||||
"boop_fun" => {
|
|
||||||
return Some(crate::dex_catalog::DexCatalogItem {
|
|
||||||
code: "boop_fun",
|
|
||||||
name: "Boop.fun",
|
|
||||||
program_id: None,
|
|
||||||
router_program_id: None,
|
|
||||||
is_enabled: false,
|
|
||||||
});
|
|
||||||
},
|
|
||||||
"moonshot" => {
|
|
||||||
return Some(crate::dex_catalog::DexCatalogItem {
|
|
||||||
code: "moonshot",
|
|
||||||
name: "Moonshot",
|
|
||||||
program_id: None,
|
|
||||||
router_program_id: None,
|
|
||||||
is_enabled: false,
|
|
||||||
});
|
|
||||||
},
|
|
||||||
"believe" => {
|
|
||||||
return Some(crate::dex_catalog::DexCatalogItem {
|
|
||||||
code: "believe",
|
|
||||||
name: "Believe",
|
|
||||||
program_id: None,
|
|
||||||
router_program_id: None,
|
|
||||||
is_enabled: false,
|
|
||||||
});
|
|
||||||
},
|
|
||||||
"heaven" => {
|
|
||||||
return Some(crate::dex_catalog::DexCatalogItem {
|
|
||||||
code: "heaven",
|
|
||||||
name: "Heaven",
|
|
||||||
program_id: None,
|
|
||||||
router_program_id: None,
|
|
||||||
is_enabled: false,
|
|
||||||
});
|
|
||||||
},
|
|
||||||
_ => return None,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Ensures that one known DEX exists in storage and returns its internal id.
|
/// Ensures that one known DEX exists in storage and returns its internal id.
|
||||||
@@ -283,7 +122,15 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn planned_launch_surfaces_are_present_but_disabled() {
|
fn planned_launch_surfaces_are_present_but_disabled() {
|
||||||
let codes = ["raydium_launchlab", "letsbonk", "boop_fun", "moonshot", "believe", "heaven"];
|
let codes = [
|
||||||
|
"raydium_launchlab",
|
||||||
|
"raydium_launchpad",
|
||||||
|
"letsbonk",
|
||||||
|
"boop_fun",
|
||||||
|
"moonshot",
|
||||||
|
"believe",
|
||||||
|
"heaven",
|
||||||
|
];
|
||||||
for code in codes {
|
for code in codes {
|
||||||
let item_option = crate::dex_catalog::dex_catalog_item_by_code(code);
|
let item_option = crate::dex_catalog::dex_catalog_item_by_code(code);
|
||||||
let item = match item_option {
|
let item = match item_option {
|
||||||
@@ -297,6 +144,23 @@ mod tests {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn catalog_entries_are_derived_from_support_matrix() {
|
||||||
|
let item = match crate::dex_catalog::dex_catalog_item_by_code("pump_swap") {
|
||||||
|
Some(item) => item,
|
||||||
|
None => panic!("expected pump_swap catalog item"),
|
||||||
|
};
|
||||||
|
let matrix_entry = match crate::dex_support_matrix_entry_by_code("pump_swap") {
|
||||||
|
Some(matrix_entry) => matrix_entry,
|
||||||
|
None => panic!("expected pump_swap matrix entry"),
|
||||||
|
};
|
||||||
|
assert_eq!(item.code, matrix_entry.code);
|
||||||
|
assert_eq!(item.name, matrix_entry.display_name);
|
||||||
|
assert_eq!(item.program_id, matrix_entry.program_id);
|
||||||
|
assert_eq!(item.router_program_id, matrix_entry.router_program_id);
|
||||||
|
assert_eq!(item.is_enabled, matrix_entry.catalog_enabled);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn unknown_dex_code_is_not_silently_accepted() {
|
fn unknown_dex_code_is_not_silently_accepted() {
|
||||||
let item_option = crate::dex_catalog::dex_catalog_item_by_code("zora_solana");
|
let item_option = crate::dex_catalog::dex_catalog_item_by_code("zora_solana");
|
||||||
|
|||||||
910
kb_lib/src/dex_support_matrix.rs
Normal file
910
kb_lib/src/dex_support_matrix.rs
Normal file
@@ -0,0 +1,910 @@
|
|||||||
|
// file: kb_lib/src/dex_support_matrix.rs
|
||||||
|
|
||||||
|
//! Shared DEX support matrix.
|
||||||
|
//!
|
||||||
|
//! This module centralizes protocol metadata that was previously duplicated
|
||||||
|
//! across catalog, transaction classification, candidate recording and roadmap
|
||||||
|
//! surfaces. It intentionally does not decode instructions and does not decide
|
||||||
|
//! whether one decoded event is actionable.
|
||||||
|
|
||||||
|
/// Support matrix entry for one DEX, router, aggregator or launch surface.
|
||||||
|
#[derive(Debug, Copy, Clone, Eq, PartialEq, serde::Serialize)]
|
||||||
|
#[serde(rename_all = "camelCase")]
|
||||||
|
pub struct DexSupportMatrixEntry {
|
||||||
|
/// Stable internal protocol or surface code.
|
||||||
|
pub code: &'static str,
|
||||||
|
/// Human-readable protocol or surface name.
|
||||||
|
pub display_name: &'static str,
|
||||||
|
/// Protocol family, for example `raydium`, `meteora` or `pump`.
|
||||||
|
pub family: &'static str,
|
||||||
|
/// Protocol version or `unknown` when not verified locally.
|
||||||
|
pub version: &'static str,
|
||||||
|
/// Surface type: `launch`, `bonding_curve`, `AMM`, `CLMM`, `DLMM`, `router`, `aggregator` or `unknown`.
|
||||||
|
pub surface_type: &'static str,
|
||||||
|
/// Primary Solana program id, when verified in local constants or docs.
|
||||||
|
pub program_id: std::option::Option<&'static str>,
|
||||||
|
/// Optional router program id, when this entry uses a distinct router.
|
||||||
|
pub router_program_id: std::option::Option<&'static str>,
|
||||||
|
/// Program id confidence: `known`, `to_verify` or `unknown`.
|
||||||
|
pub program_id_status: &'static str,
|
||||||
|
/// Whether this protocol has been observed in the local replay corpus.
|
||||||
|
pub observed: bool,
|
||||||
|
/// Whether the code currently contains a decoder for this protocol.
|
||||||
|
pub decoded: bool,
|
||||||
|
/// Whether decoded events are currently materialized beyond raw decoded rows.
|
||||||
|
pub materialized: bool,
|
||||||
|
/// Whether this protocol can currently produce trade candidates.
|
||||||
|
pub trade_candidate: bool,
|
||||||
|
/// Whether this protocol can currently produce candle candidates.
|
||||||
|
pub candle_candidate: bool,
|
||||||
|
/// Whether this protocol can currently produce pair candidates.
|
||||||
|
pub pair_candidate: bool,
|
||||||
|
/// Whether this protocol can currently produce pool candidates.
|
||||||
|
pub pool_candidate: bool,
|
||||||
|
/// Operational support status: `supported`, `partial`, `planned`, `ignored`, `to_verify` or `unknown`.
|
||||||
|
pub status: &'static str,
|
||||||
|
/// Confidence level attached to this matrix entry.
|
||||||
|
pub confidence: &'static str,
|
||||||
|
/// Optional explicit skip reason for partial or ignored entries.
|
||||||
|
pub skip_reason: std::option::Option<&'static str>,
|
||||||
|
/// Whether the entry should be inserted as an enabled DEX in the storage catalog.
|
||||||
|
pub catalog_enabled: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Owned DTO form of a DEX support matrix entry.
|
||||||
|
#[derive(Debug, Clone, Eq, PartialEq, serde::Serialize, serde::Deserialize)]
|
||||||
|
#[serde(rename_all = "camelCase")]
|
||||||
|
pub struct DexSupportMatrixEntryDto {
|
||||||
|
/// Stable internal protocol or surface code.
|
||||||
|
pub code: std::string::String,
|
||||||
|
/// Human-readable protocol or surface name.
|
||||||
|
pub display_name: std::string::String,
|
||||||
|
/// Protocol family, for example `raydium`, `meteora` or `pump`.
|
||||||
|
pub family: std::string::String,
|
||||||
|
/// Protocol version or `unknown` when not verified locally.
|
||||||
|
pub version: std::string::String,
|
||||||
|
/// Surface type: `launch`, `bonding_curve`, `AMM`, `CLMM`, `DLMM`, `router`, `aggregator` or `unknown`.
|
||||||
|
pub surface_type: std::string::String,
|
||||||
|
/// Primary Solana program id, when verified in local constants or docs.
|
||||||
|
pub program_id: std::option::Option<std::string::String>,
|
||||||
|
/// Optional router program id, when this entry uses a distinct router.
|
||||||
|
pub router_program_id: std::option::Option<std::string::String>,
|
||||||
|
/// Program id confidence: `known`, `to_verify` or `unknown`.
|
||||||
|
pub program_id_status: std::string::String,
|
||||||
|
/// Whether this protocol has been observed in the local replay corpus.
|
||||||
|
pub observed: bool,
|
||||||
|
/// Whether the code currently contains a decoder for this protocol.
|
||||||
|
pub decoded: bool,
|
||||||
|
/// Whether decoded events are currently materialized beyond raw decoded rows.
|
||||||
|
pub materialized: bool,
|
||||||
|
/// Whether this protocol can currently produce trade candidates.
|
||||||
|
pub trade_candidate: bool,
|
||||||
|
/// Whether this protocol can currently produce candle candidates.
|
||||||
|
pub candle_candidate: bool,
|
||||||
|
/// Whether this protocol can currently produce pair candidates.
|
||||||
|
pub pair_candidate: bool,
|
||||||
|
/// Whether this protocol can currently produce pool candidates.
|
||||||
|
pub pool_candidate: bool,
|
||||||
|
/// Operational support status: `supported`, `partial`, `planned`, `ignored`, `to_verify` or `unknown`.
|
||||||
|
pub status: std::string::String,
|
||||||
|
/// Confidence level attached to this matrix entry.
|
||||||
|
pub confidence: std::string::String,
|
||||||
|
/// Optional explicit skip reason for partial or ignored entries.
|
||||||
|
pub skip_reason: std::option::Option<std::string::String>,
|
||||||
|
/// Whether the entry should be inserted as an enabled DEX in the storage catalog.
|
||||||
|
pub catalog_enabled: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DexSupportMatrixEntryDto {
|
||||||
|
/// Builds an owned DTO from one static support matrix entry.
|
||||||
|
pub fn from_entry(entry: &crate::DexSupportMatrixEntry) -> Self {
|
||||||
|
return Self {
|
||||||
|
code: entry.code.to_string(),
|
||||||
|
display_name: entry.display_name.to_string(),
|
||||||
|
family: entry.family.to_string(),
|
||||||
|
version: entry.version.to_string(),
|
||||||
|
surface_type: entry.surface_type.to_string(),
|
||||||
|
program_id: match entry.program_id {
|
||||||
|
Some(program_id) => Some(program_id.to_string()),
|
||||||
|
None => None,
|
||||||
|
},
|
||||||
|
router_program_id: match entry.router_program_id {
|
||||||
|
Some(router_program_id) => Some(router_program_id.to_string()),
|
||||||
|
None => None,
|
||||||
|
},
|
||||||
|
program_id_status: entry.program_id_status.to_string(),
|
||||||
|
observed: entry.observed,
|
||||||
|
decoded: entry.decoded,
|
||||||
|
materialized: entry.materialized,
|
||||||
|
trade_candidate: entry.trade_candidate,
|
||||||
|
candle_candidate: entry.candle_candidate,
|
||||||
|
pair_candidate: entry.pair_candidate,
|
||||||
|
pool_candidate: entry.pool_candidate,
|
||||||
|
status: entry.status.to_string(),
|
||||||
|
confidence: entry.confidence.to_string(),
|
||||||
|
skip_reason: match entry.skip_reason {
|
||||||
|
Some(skip_reason) => Some(skip_reason.to_string()),
|
||||||
|
None => None,
|
||||||
|
},
|
||||||
|
catalog_enabled: entry.catalog_enabled,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const DEX_SUPPORT_MATRIX_ENTRIES: &[DexSupportMatrixEntry] = &[
|
||||||
|
DexSupportMatrixEntry {
|
||||||
|
code: "pump_fun",
|
||||||
|
display_name: "Pump.fun",
|
||||||
|
family: "pump",
|
||||||
|
version: "bonding_curve",
|
||||||
|
surface_type: "launch",
|
||||||
|
program_id: Some(crate::PUMP_FUN_PROGRAM_ID),
|
||||||
|
router_program_id: None,
|
||||||
|
program_id_status: "known",
|
||||||
|
observed: false,
|
||||||
|
decoded: true,
|
||||||
|
materialized: true,
|
||||||
|
trade_candidate: true,
|
||||||
|
candle_candidate: true,
|
||||||
|
pair_candidate: true,
|
||||||
|
pool_candidate: true,
|
||||||
|
status: "partial",
|
||||||
|
confidence: "high",
|
||||||
|
skip_reason: Some("launch_surface_requires_migration_linking_before_live_trading"),
|
||||||
|
catalog_enabled: true,
|
||||||
|
},
|
||||||
|
DexSupportMatrixEntry {
|
||||||
|
code: "pump_swap",
|
||||||
|
display_name: "PumpSwap",
|
||||||
|
family: "pump",
|
||||||
|
version: "amm",
|
||||||
|
surface_type: "AMM",
|
||||||
|
program_id: Some(crate::PUMP_SWAP_PROGRAM_ID),
|
||||||
|
router_program_id: None,
|
||||||
|
program_id_status: "known",
|
||||||
|
observed: true,
|
||||||
|
decoded: true,
|
||||||
|
materialized: true,
|
||||||
|
trade_candidate: true,
|
||||||
|
candle_candidate: true,
|
||||||
|
pair_candidate: true,
|
||||||
|
pool_candidate: true,
|
||||||
|
status: "supported",
|
||||||
|
confidence: "high",
|
||||||
|
skip_reason: None,
|
||||||
|
catalog_enabled: true,
|
||||||
|
},
|
||||||
|
DexSupportMatrixEntry {
|
||||||
|
code: "raydium_cpmm",
|
||||||
|
display_name: "Raydium CPMM",
|
||||||
|
family: "raydium",
|
||||||
|
version: "cpmm",
|
||||||
|
surface_type: "AMM",
|
||||||
|
program_id: Some(crate::RAYDIUM_CPMM_PROGRAM_ID),
|
||||||
|
router_program_id: None,
|
||||||
|
program_id_status: "known",
|
||||||
|
observed: true,
|
||||||
|
decoded: true,
|
||||||
|
materialized: true,
|
||||||
|
trade_candidate: true,
|
||||||
|
candle_candidate: true,
|
||||||
|
pair_candidate: true,
|
||||||
|
pool_candidate: true,
|
||||||
|
status: "supported",
|
||||||
|
confidence: "high",
|
||||||
|
skip_reason: None,
|
||||||
|
catalog_enabled: true,
|
||||||
|
},
|
||||||
|
DexSupportMatrixEntry {
|
||||||
|
code: "raydium_clmm",
|
||||||
|
display_name: "Raydium CLMM",
|
||||||
|
family: "raydium",
|
||||||
|
version: "clmm",
|
||||||
|
surface_type: "CLMM",
|
||||||
|
program_id: Some(crate::RAYDIUM_CLMM_PROGRAM_ID),
|
||||||
|
router_program_id: None,
|
||||||
|
program_id_status: "known",
|
||||||
|
observed: true,
|
||||||
|
decoded: true,
|
||||||
|
materialized: true,
|
||||||
|
trade_candidate: true,
|
||||||
|
candle_candidate: true,
|
||||||
|
pair_candidate: true,
|
||||||
|
pool_candidate: true,
|
||||||
|
status: "supported",
|
||||||
|
confidence: "high",
|
||||||
|
skip_reason: None,
|
||||||
|
catalog_enabled: true,
|
||||||
|
},
|
||||||
|
DexSupportMatrixEntry {
|
||||||
|
code: "raydium_amm_v4",
|
||||||
|
display_name: "Raydium AMM v4",
|
||||||
|
family: "raydium",
|
||||||
|
version: "amm_v4",
|
||||||
|
surface_type: "AMM",
|
||||||
|
program_id: Some(crate::RAYDIUM_AMM_V4_PROGRAM_ID),
|
||||||
|
router_program_id: None,
|
||||||
|
program_id_status: "known",
|
||||||
|
observed: false,
|
||||||
|
decoded: true,
|
||||||
|
materialized: true,
|
||||||
|
trade_candidate: true,
|
||||||
|
candle_candidate: true,
|
||||||
|
pair_candidate: true,
|
||||||
|
pool_candidate: true,
|
||||||
|
status: "partial",
|
||||||
|
confidence: "medium",
|
||||||
|
skip_reason: Some("not_observed_in_0_7_28_replay"),
|
||||||
|
catalog_enabled: true,
|
||||||
|
},
|
||||||
|
DexSupportMatrixEntry {
|
||||||
|
code: "raydium",
|
||||||
|
display_name: "Raydium AMM v4",
|
||||||
|
family: "raydium",
|
||||||
|
version: "amm_v4_alias",
|
||||||
|
surface_type: "AMM",
|
||||||
|
program_id: Some(crate::RAYDIUM_AMM_V4_PROGRAM_ID),
|
||||||
|
router_program_id: None,
|
||||||
|
program_id_status: "known",
|
||||||
|
observed: false,
|
||||||
|
decoded: true,
|
||||||
|
materialized: true,
|
||||||
|
trade_candidate: true,
|
||||||
|
candle_candidate: true,
|
||||||
|
pair_candidate: true,
|
||||||
|
pool_candidate: true,
|
||||||
|
status: "partial",
|
||||||
|
confidence: "medium",
|
||||||
|
skip_reason: Some("legacy_catalog_alias_for_raydium_amm_v4"),
|
||||||
|
catalog_enabled: true,
|
||||||
|
},
|
||||||
|
DexSupportMatrixEntry {
|
||||||
|
code: "raydium_launchlab",
|
||||||
|
display_name: "Raydium LaunchLab",
|
||||||
|
family: "raydium",
|
||||||
|
version: "launchlab",
|
||||||
|
surface_type: "launch",
|
||||||
|
program_id: Some(crate::RAYDIUM_LAUNCHLAB_PROGRAM_ID),
|
||||||
|
router_program_id: None,
|
||||||
|
program_id_status: "known",
|
||||||
|
observed: false,
|
||||||
|
decoded: false,
|
||||||
|
materialized: false,
|
||||||
|
trade_candidate: false,
|
||||||
|
candle_candidate: false,
|
||||||
|
pair_candidate: true,
|
||||||
|
pool_candidate: true,
|
||||||
|
status: "planned",
|
||||||
|
confidence: "medium",
|
||||||
|
skip_reason: Some("decoder_and_materialization_not_enabled"),
|
||||||
|
catalog_enabled: false,
|
||||||
|
},
|
||||||
|
DexSupportMatrixEntry {
|
||||||
|
code: "raydium_launchpad",
|
||||||
|
display_name: "Raydium Launchpad",
|
||||||
|
family: "raydium",
|
||||||
|
version: "unknown",
|
||||||
|
surface_type: "launch",
|
||||||
|
program_id: None,
|
||||||
|
router_program_id: None,
|
||||||
|
program_id_status: "to_verify",
|
||||||
|
observed: false,
|
||||||
|
decoded: false,
|
||||||
|
materialized: false,
|
||||||
|
trade_candidate: false,
|
||||||
|
candle_candidate: false,
|
||||||
|
pair_candidate: true,
|
||||||
|
pool_candidate: true,
|
||||||
|
status: "to_verify",
|
||||||
|
confidence: "low",
|
||||||
|
skip_reason: Some("program_id_to_verify"),
|
||||||
|
catalog_enabled: false,
|
||||||
|
},
|
||||||
|
DexSupportMatrixEntry {
|
||||||
|
code: "raydium_router",
|
||||||
|
display_name: "Raydium Router",
|
||||||
|
family: "raydium",
|
||||||
|
version: "router",
|
||||||
|
surface_type: "router",
|
||||||
|
program_id: Some(crate::RAYDIUM_AMM_ROUTING_PROGRAM_ID),
|
||||||
|
router_program_id: None,
|
||||||
|
program_id_status: "known",
|
||||||
|
observed: false,
|
||||||
|
decoded: false,
|
||||||
|
materialized: false,
|
||||||
|
trade_candidate: false,
|
||||||
|
candle_candidate: false,
|
||||||
|
pair_candidate: false,
|
||||||
|
pool_candidate: false,
|
||||||
|
status: "partial",
|
||||||
|
confidence: "medium",
|
||||||
|
skip_reason: Some("router_not_materialized_as_direct_trade_surface"),
|
||||||
|
catalog_enabled: false,
|
||||||
|
},
|
||||||
|
DexSupportMatrixEntry {
|
||||||
|
code: "raydium_stable_swap",
|
||||||
|
display_name: "Raydium Stable Swap AMM",
|
||||||
|
family: "raydium",
|
||||||
|
version: "stable_swap",
|
||||||
|
surface_type: "AMM",
|
||||||
|
program_id: Some(crate::RAYDIUM_STABLE_SWAP_AMM_PROGRAM_ID),
|
||||||
|
router_program_id: None,
|
||||||
|
program_id_status: "known",
|
||||||
|
observed: false,
|
||||||
|
decoded: false,
|
||||||
|
materialized: false,
|
||||||
|
trade_candidate: false,
|
||||||
|
candle_candidate: false,
|
||||||
|
pair_candidate: false,
|
||||||
|
pool_candidate: false,
|
||||||
|
status: "planned",
|
||||||
|
confidence: "medium",
|
||||||
|
skip_reason: Some("deprecated_program_not_prioritized"),
|
||||||
|
catalog_enabled: false,
|
||||||
|
},
|
||||||
|
DexSupportMatrixEntry {
|
||||||
|
code: "meteora_dlmm",
|
||||||
|
display_name: "Meteora DLMM",
|
||||||
|
family: "meteora",
|
||||||
|
version: "dlmm",
|
||||||
|
surface_type: "DLMM",
|
||||||
|
program_id: Some(crate::METEORA_DLMM_PROGRAM_ID),
|
||||||
|
router_program_id: None,
|
||||||
|
program_id_status: "known",
|
||||||
|
observed: true,
|
||||||
|
decoded: true,
|
||||||
|
materialized: true,
|
||||||
|
trade_candidate: true,
|
||||||
|
candle_candidate: true,
|
||||||
|
pair_candidate: true,
|
||||||
|
pool_candidate: true,
|
||||||
|
status: "supported",
|
||||||
|
confidence: "high",
|
||||||
|
skip_reason: None,
|
||||||
|
catalog_enabled: true,
|
||||||
|
},
|
||||||
|
DexSupportMatrixEntry {
|
||||||
|
code: "meteora_dlc",
|
||||||
|
display_name: "Meteora DLC",
|
||||||
|
family: "meteora",
|
||||||
|
version: "unknown",
|
||||||
|
surface_type: "unknown",
|
||||||
|
program_id: None,
|
||||||
|
router_program_id: None,
|
||||||
|
program_id_status: "to_verify",
|
||||||
|
observed: false,
|
||||||
|
decoded: false,
|
||||||
|
materialized: false,
|
||||||
|
trade_candidate: false,
|
||||||
|
candle_candidate: false,
|
||||||
|
pair_candidate: false,
|
||||||
|
pool_candidate: false,
|
||||||
|
status: "to_verify",
|
||||||
|
confidence: "low",
|
||||||
|
skip_reason: Some("surface_and_program_id_to_verify"),
|
||||||
|
catalog_enabled: false,
|
||||||
|
},
|
||||||
|
DexSupportMatrixEntry {
|
||||||
|
code: "meteora_damm_v1",
|
||||||
|
display_name: "Meteora DAMM v1",
|
||||||
|
family: "meteora",
|
||||||
|
version: "damm_v1",
|
||||||
|
surface_type: "AMM",
|
||||||
|
program_id: Some(crate::METEORA_DAMM_V1_PROGRAM_ID),
|
||||||
|
router_program_id: None,
|
||||||
|
program_id_status: "known",
|
||||||
|
observed: true,
|
||||||
|
decoded: true,
|
||||||
|
materialized: false,
|
||||||
|
trade_candidate: false,
|
||||||
|
candle_candidate: false,
|
||||||
|
pair_candidate: true,
|
||||||
|
pool_candidate: true,
|
||||||
|
status: "partial",
|
||||||
|
confidence: "medium",
|
||||||
|
skip_reason: Some("meteora_damm_v1_swap_without_amount_payload"),
|
||||||
|
catalog_enabled: true,
|
||||||
|
},
|
||||||
|
DexSupportMatrixEntry {
|
||||||
|
code: "meteora_damm_v2",
|
||||||
|
display_name: "Meteora DAMM v2",
|
||||||
|
family: "meteora",
|
||||||
|
version: "damm_v2",
|
||||||
|
surface_type: "AMM",
|
||||||
|
program_id: Some(crate::METEORA_DAMM_V2_PROGRAM_ID),
|
||||||
|
router_program_id: None,
|
||||||
|
program_id_status: "known",
|
||||||
|
observed: false,
|
||||||
|
decoded: true,
|
||||||
|
materialized: true,
|
||||||
|
trade_candidate: true,
|
||||||
|
candle_candidate: true,
|
||||||
|
pair_candidate: true,
|
||||||
|
pool_candidate: true,
|
||||||
|
status: "partial",
|
||||||
|
confidence: "medium",
|
||||||
|
skip_reason: Some("not_observed_in_0_7_28_replay"),
|
||||||
|
catalog_enabled: true,
|
||||||
|
},
|
||||||
|
DexSupportMatrixEntry {
|
||||||
|
code: "meteora_dbc",
|
||||||
|
display_name: "Meteora DBC",
|
||||||
|
family: "meteora",
|
||||||
|
version: "dbc",
|
||||||
|
surface_type: "bonding_curve",
|
||||||
|
program_id: Some(crate::METEORA_DBC_PROGRAM_ID),
|
||||||
|
router_program_id: None,
|
||||||
|
program_id_status: "known",
|
||||||
|
observed: false,
|
||||||
|
decoded: true,
|
||||||
|
materialized: true,
|
||||||
|
trade_candidate: true,
|
||||||
|
candle_candidate: true,
|
||||||
|
pair_candidate: true,
|
||||||
|
pool_candidate: true,
|
||||||
|
status: "partial",
|
||||||
|
confidence: "medium",
|
||||||
|
skip_reason: Some("not_observed_in_0_7_28_replay"),
|
||||||
|
catalog_enabled: true,
|
||||||
|
},
|
||||||
|
DexSupportMatrixEntry {
|
||||||
|
code: "orca_whirlpools",
|
||||||
|
display_name: "Orca Whirlpools",
|
||||||
|
family: "orca",
|
||||||
|
version: "whirlpools",
|
||||||
|
surface_type: "CLMM",
|
||||||
|
program_id: Some(crate::ORCA_WHIRLPOOLS_PROGRAM_ID),
|
||||||
|
router_program_id: None,
|
||||||
|
program_id_status: "known",
|
||||||
|
observed: false,
|
||||||
|
decoded: true,
|
||||||
|
materialized: true,
|
||||||
|
trade_candidate: true,
|
||||||
|
candle_candidate: true,
|
||||||
|
pair_candidate: true,
|
||||||
|
pool_candidate: true,
|
||||||
|
status: "partial",
|
||||||
|
confidence: "medium",
|
||||||
|
skip_reason: Some("not_observed_in_0_7_28_replay"),
|
||||||
|
catalog_enabled: true,
|
||||||
|
},
|
||||||
|
DexSupportMatrixEntry {
|
||||||
|
code: "fluxbeam",
|
||||||
|
display_name: "FluxBeam",
|
||||||
|
family: "fluxbeam",
|
||||||
|
version: "unknown",
|
||||||
|
surface_type: "AMM",
|
||||||
|
program_id: Some(crate::FLUXBEAM_PROGRAM_ID),
|
||||||
|
router_program_id: None,
|
||||||
|
program_id_status: "known",
|
||||||
|
observed: false,
|
||||||
|
decoded: true,
|
||||||
|
materialized: true,
|
||||||
|
trade_candidate: true,
|
||||||
|
candle_candidate: true,
|
||||||
|
pair_candidate: true,
|
||||||
|
pool_candidate: true,
|
||||||
|
status: "partial",
|
||||||
|
confidence: "medium",
|
||||||
|
skip_reason: Some("not_observed_in_0_7_28_replay"),
|
||||||
|
catalog_enabled: true,
|
||||||
|
},
|
||||||
|
DexSupportMatrixEntry {
|
||||||
|
code: "dexlab",
|
||||||
|
display_name: "DexLab Swap/Pool",
|
||||||
|
family: "dexlab",
|
||||||
|
version: "unknown",
|
||||||
|
surface_type: "AMM",
|
||||||
|
program_id: Some(crate::DEXLAB_PROGRAM_ID),
|
||||||
|
router_program_id: None,
|
||||||
|
program_id_status: "known",
|
||||||
|
observed: false,
|
||||||
|
decoded: true,
|
||||||
|
materialized: true,
|
||||||
|
trade_candidate: true,
|
||||||
|
candle_candidate: true,
|
||||||
|
pair_candidate: true,
|
||||||
|
pool_candidate: true,
|
||||||
|
status: "partial",
|
||||||
|
confidence: "medium",
|
||||||
|
skip_reason: Some("not_observed_in_0_7_28_replay"),
|
||||||
|
catalog_enabled: true,
|
||||||
|
},
|
||||||
|
DexSupportMatrixEntry {
|
||||||
|
code: "bags",
|
||||||
|
display_name: "Bags",
|
||||||
|
family: "bags",
|
||||||
|
version: "unknown",
|
||||||
|
surface_type: "launch",
|
||||||
|
program_id: None,
|
||||||
|
router_program_id: None,
|
||||||
|
program_id_status: "unknown",
|
||||||
|
observed: false,
|
||||||
|
decoded: false,
|
||||||
|
materialized: false,
|
||||||
|
trade_candidate: false,
|
||||||
|
candle_candidate: false,
|
||||||
|
pair_candidate: true,
|
||||||
|
pool_candidate: true,
|
||||||
|
status: "planned",
|
||||||
|
confidence: "low",
|
||||||
|
skip_reason: Some("program_id_to_verify"),
|
||||||
|
catalog_enabled: false,
|
||||||
|
},
|
||||||
|
DexSupportMatrixEntry {
|
||||||
|
code: "letsbonk",
|
||||||
|
display_name: "LetsBonk / Bonk.fun",
|
||||||
|
family: "bonk",
|
||||||
|
version: "unknown",
|
||||||
|
surface_type: "launch",
|
||||||
|
program_id: None,
|
||||||
|
router_program_id: None,
|
||||||
|
program_id_status: "unknown",
|
||||||
|
observed: false,
|
||||||
|
decoded: false,
|
||||||
|
materialized: false,
|
||||||
|
trade_candidate: false,
|
||||||
|
candle_candidate: false,
|
||||||
|
pair_candidate: true,
|
||||||
|
pool_candidate: true,
|
||||||
|
status: "planned",
|
||||||
|
confidence: "low",
|
||||||
|
skip_reason: Some("program_id_to_verify"),
|
||||||
|
catalog_enabled: false,
|
||||||
|
},
|
||||||
|
DexSupportMatrixEntry {
|
||||||
|
code: "bonk",
|
||||||
|
display_name: "Bonk launch surface",
|
||||||
|
family: "bonk",
|
||||||
|
version: "unknown",
|
||||||
|
surface_type: "launch",
|
||||||
|
program_id: None,
|
||||||
|
router_program_id: None,
|
||||||
|
program_id_status: "unknown",
|
||||||
|
observed: false,
|
||||||
|
decoded: false,
|
||||||
|
materialized: false,
|
||||||
|
trade_candidate: false,
|
||||||
|
candle_candidate: false,
|
||||||
|
pair_candidate: true,
|
||||||
|
pool_candidate: true,
|
||||||
|
status: "planned",
|
||||||
|
confidence: "low",
|
||||||
|
skip_reason: Some("program_id_to_verify"),
|
||||||
|
catalog_enabled: false,
|
||||||
|
},
|
||||||
|
DexSupportMatrixEntry {
|
||||||
|
code: "okx_dex",
|
||||||
|
display_name: "OKX DEX",
|
||||||
|
family: "okx",
|
||||||
|
version: "unknown",
|
||||||
|
surface_type: "aggregator",
|
||||||
|
program_id: None,
|
||||||
|
router_program_id: None,
|
||||||
|
program_id_status: "unknown",
|
||||||
|
observed: false,
|
||||||
|
decoded: false,
|
||||||
|
materialized: false,
|
||||||
|
trade_candidate: false,
|
||||||
|
candle_candidate: false,
|
||||||
|
pair_candidate: false,
|
||||||
|
pool_candidate: false,
|
||||||
|
status: "planned",
|
||||||
|
confidence: "low",
|
||||||
|
skip_reason: Some("program_id_to_verify"),
|
||||||
|
catalog_enabled: false,
|
||||||
|
},
|
||||||
|
DexSupportMatrixEntry {
|
||||||
|
code: "boop_fun",
|
||||||
|
display_name: "Boop.fun",
|
||||||
|
family: "boop",
|
||||||
|
version: "unknown",
|
||||||
|
surface_type: "launch",
|
||||||
|
program_id: None,
|
||||||
|
router_program_id: None,
|
||||||
|
program_id_status: "unknown",
|
||||||
|
observed: false,
|
||||||
|
decoded: false,
|
||||||
|
materialized: false,
|
||||||
|
trade_candidate: false,
|
||||||
|
candle_candidate: false,
|
||||||
|
pair_candidate: true,
|
||||||
|
pool_candidate: true,
|
||||||
|
status: "planned",
|
||||||
|
confidence: "low",
|
||||||
|
skip_reason: Some("program_id_to_verify"),
|
||||||
|
catalog_enabled: false,
|
||||||
|
},
|
||||||
|
DexSupportMatrixEntry {
|
||||||
|
code: "moonshot",
|
||||||
|
display_name: "Moonshot",
|
||||||
|
family: "moonshot",
|
||||||
|
version: "unknown",
|
||||||
|
surface_type: "launch",
|
||||||
|
program_id: None,
|
||||||
|
router_program_id: None,
|
||||||
|
program_id_status: "unknown",
|
||||||
|
observed: false,
|
||||||
|
decoded: false,
|
||||||
|
materialized: false,
|
||||||
|
trade_candidate: false,
|
||||||
|
candle_candidate: false,
|
||||||
|
pair_candidate: true,
|
||||||
|
pool_candidate: true,
|
||||||
|
status: "planned",
|
||||||
|
confidence: "low",
|
||||||
|
skip_reason: Some("program_id_to_verify"),
|
||||||
|
catalog_enabled: false,
|
||||||
|
},
|
||||||
|
DexSupportMatrixEntry {
|
||||||
|
code: "believe",
|
||||||
|
display_name: "Believe",
|
||||||
|
family: "believe",
|
||||||
|
version: "unknown",
|
||||||
|
surface_type: "launch",
|
||||||
|
program_id: None,
|
||||||
|
router_program_id: None,
|
||||||
|
program_id_status: "unknown",
|
||||||
|
observed: false,
|
||||||
|
decoded: false,
|
||||||
|
materialized: false,
|
||||||
|
trade_candidate: false,
|
||||||
|
candle_candidate: false,
|
||||||
|
pair_candidate: true,
|
||||||
|
pool_candidate: true,
|
||||||
|
status: "planned",
|
||||||
|
confidence: "low",
|
||||||
|
skip_reason: Some("program_id_to_verify"),
|
||||||
|
catalog_enabled: false,
|
||||||
|
},
|
||||||
|
DexSupportMatrixEntry {
|
||||||
|
code: "zora",
|
||||||
|
display_name: "Zora",
|
||||||
|
family: "zora",
|
||||||
|
version: "unknown",
|
||||||
|
surface_type: "unknown",
|
||||||
|
program_id: None,
|
||||||
|
router_program_id: None,
|
||||||
|
program_id_status: "to_verify",
|
||||||
|
observed: false,
|
||||||
|
decoded: false,
|
||||||
|
materialized: false,
|
||||||
|
trade_candidate: false,
|
||||||
|
candle_candidate: false,
|
||||||
|
pair_candidate: false,
|
||||||
|
pool_candidate: false,
|
||||||
|
status: "to_verify",
|
||||||
|
confidence: "low",
|
||||||
|
skip_reason: Some("surface_and_program_id_to_verify"),
|
||||||
|
catalog_enabled: false,
|
||||||
|
},
|
||||||
|
DexSupportMatrixEntry {
|
||||||
|
code: "moonit",
|
||||||
|
display_name: "Moonit",
|
||||||
|
family: "moonit",
|
||||||
|
version: "unknown",
|
||||||
|
surface_type: "launch",
|
||||||
|
program_id: None,
|
||||||
|
router_program_id: None,
|
||||||
|
program_id_status: "unknown",
|
||||||
|
observed: false,
|
||||||
|
decoded: false,
|
||||||
|
materialized: false,
|
||||||
|
trade_candidate: false,
|
||||||
|
candle_candidate: false,
|
||||||
|
pair_candidate: true,
|
||||||
|
pool_candidate: true,
|
||||||
|
status: "planned",
|
||||||
|
confidence: "low",
|
||||||
|
skip_reason: Some("program_id_to_verify"),
|
||||||
|
catalog_enabled: false,
|
||||||
|
},
|
||||||
|
DexSupportMatrixEntry {
|
||||||
|
code: "launchbeam",
|
||||||
|
display_name: "LaunchBeam",
|
||||||
|
family: "launchbeam",
|
||||||
|
version: "unknown",
|
||||||
|
surface_type: "launch",
|
||||||
|
program_id: None,
|
||||||
|
router_program_id: None,
|
||||||
|
program_id_status: "unknown",
|
||||||
|
observed: false,
|
||||||
|
decoded: false,
|
||||||
|
materialized: false,
|
||||||
|
trade_candidate: false,
|
||||||
|
candle_candidate: false,
|
||||||
|
pair_candidate: true,
|
||||||
|
pool_candidate: true,
|
||||||
|
status: "planned",
|
||||||
|
confidence: "low",
|
||||||
|
skip_reason: Some("program_id_to_verify"),
|
||||||
|
catalog_enabled: false,
|
||||||
|
},
|
||||||
|
DexSupportMatrixEntry {
|
||||||
|
code: "heaven",
|
||||||
|
display_name: "Heaven",
|
||||||
|
family: "heaven",
|
||||||
|
version: "unknown",
|
||||||
|
surface_type: "launch",
|
||||||
|
program_id: None,
|
||||||
|
router_program_id: None,
|
||||||
|
program_id_status: "unknown",
|
||||||
|
observed: false,
|
||||||
|
decoded: false,
|
||||||
|
materialized: false,
|
||||||
|
trade_candidate: false,
|
||||||
|
candle_candidate: false,
|
||||||
|
pair_candidate: true,
|
||||||
|
pool_candidate: true,
|
||||||
|
status: "planned",
|
||||||
|
confidence: "low",
|
||||||
|
skip_reason: Some("program_id_to_verify"),
|
||||||
|
catalog_enabled: false,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
/// Returns all static DEX support matrix entries.
|
||||||
|
pub fn dex_support_matrix_entries() -> &'static [crate::DexSupportMatrixEntry] {
|
||||||
|
return DEX_SUPPORT_MATRIX_ENTRIES;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns all DEX support matrix entries as owned DTOs.
|
||||||
|
pub fn dex_support_matrix_entry_dtos() -> std::vec::Vec<crate::DexSupportMatrixEntryDto> {
|
||||||
|
let mut entries = std::vec::Vec::new();
|
||||||
|
for entry in crate::dex_support_matrix_entries() {
|
||||||
|
entries.push(crate::DexSupportMatrixEntryDto::from_entry(entry));
|
||||||
|
}
|
||||||
|
return entries;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Looks up one DEX support matrix entry by internal code.
|
||||||
|
pub fn dex_support_matrix_entry_by_code(
|
||||||
|
code: &str,
|
||||||
|
) -> std::option::Option<&'static crate::DexSupportMatrixEntry> {
|
||||||
|
for entry in crate::dex_support_matrix_entries() {
|
||||||
|
if entry.code == code {
|
||||||
|
return Some(entry);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Looks up one DEX support matrix entry by primary or router program id.
|
||||||
|
pub fn dex_support_matrix_entry_by_program_id(
|
||||||
|
program_id: &str,
|
||||||
|
) -> std::option::Option<&'static crate::DexSupportMatrixEntry> {
|
||||||
|
for entry in crate::dex_support_matrix_entries() {
|
||||||
|
if let Some(entry_program_id) = entry.program_id {
|
||||||
|
if entry_program_id == program_id {
|
||||||
|
return Some(entry);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if let Some(entry_router_program_id) = entry.router_program_id {
|
||||||
|
if entry_router_program_id == program_id {
|
||||||
|
return Some(entry);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
#[test]
|
||||||
|
fn matrix_contains_required_0_7_29_entries() {
|
||||||
|
let codes = [
|
||||||
|
"pump_fun",
|
||||||
|
"pump_swap",
|
||||||
|
"raydium_cpmm",
|
||||||
|
"raydium_clmm",
|
||||||
|
"raydium_amm_v4",
|
||||||
|
"raydium_launchlab",
|
||||||
|
"raydium_launchpad",
|
||||||
|
"meteora_dlmm",
|
||||||
|
"meteora_dlc",
|
||||||
|
"meteora_damm_v1",
|
||||||
|
"meteora_damm_v2",
|
||||||
|
"bags",
|
||||||
|
"bonk",
|
||||||
|
"okx_dex",
|
||||||
|
"boop_fun",
|
||||||
|
"moonshot",
|
||||||
|
"believe",
|
||||||
|
"zora",
|
||||||
|
"moonit",
|
||||||
|
"launchbeam",
|
||||||
|
"heaven",
|
||||||
|
"dexlab",
|
||||||
|
];
|
||||||
|
for code in codes {
|
||||||
|
let entry = crate::dex_support_matrix_entry_by_code(code);
|
||||||
|
assert!(entry.is_some(), "missing matrix entry for {}", code);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn matrix_does_not_invent_program_ids_for_unverified_planned_surfaces() {
|
||||||
|
let codes = [
|
||||||
|
"bags",
|
||||||
|
"bonk",
|
||||||
|
"okx_dex",
|
||||||
|
"boop_fun",
|
||||||
|
"moonshot",
|
||||||
|
"believe",
|
||||||
|
"zora",
|
||||||
|
"moonit",
|
||||||
|
"launchbeam",
|
||||||
|
"heaven",
|
||||||
|
];
|
||||||
|
for code in codes {
|
||||||
|
let entry = match crate::dex_support_matrix_entry_by_code(code) {
|
||||||
|
Some(entry) => entry,
|
||||||
|
None => panic!("missing matrix entry for {}", code),
|
||||||
|
};
|
||||||
|
assert!(entry.program_id.is_none(), "{} should not have invented program id", code);
|
||||||
|
assert!(!entry.catalog_enabled, "{} should not be enabled before verification", code);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn matrix_lookup_by_program_id_returns_expected_protocol() {
|
||||||
|
let entry = match crate::dex_support_matrix_entry_by_program_id(crate::METEORA_DLMM_PROGRAM_ID)
|
||||||
|
{
|
||||||
|
Some(entry) => entry,
|
||||||
|
None => panic!("expected meteora_dlmm program id lookup"),
|
||||||
|
};
|
||||||
|
assert_eq!(entry.code, "meteora_dlmm");
|
||||||
|
|
||||||
|
let raydium_entry = match crate::dex_support_matrix_entry_by_program_id(
|
||||||
|
crate::RAYDIUM_AMM_V4_PROGRAM_ID,
|
||||||
|
) {
|
||||||
|
Some(entry) => entry,
|
||||||
|
None => panic!("expected raydium AMM v4 program id lookup"),
|
||||||
|
};
|
||||||
|
assert_eq!(raydium_entry.code, "raydium_amm_v4");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn matrix_marks_partial_meteora_damm_v1_correctly() {
|
||||||
|
let entry = match crate::dex_support_matrix_entry_by_code("meteora_damm_v1") {
|
||||||
|
Some(entry) => entry,
|
||||||
|
None => panic!("expected meteora_damm_v1 matrix entry"),
|
||||||
|
};
|
||||||
|
assert_eq!(entry.status, "partial");
|
||||||
|
assert!(entry.observed);
|
||||||
|
assert!(entry.decoded);
|
||||||
|
assert!(!entry.trade_candidate);
|
||||||
|
assert!(!entry.candle_candidate);
|
||||||
|
assert_eq!(entry.skip_reason, Some("meteora_damm_v1_swap_without_amount_payload"));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn matrix_marks_launch_surfaces_as_launch_or_bonding_curve() {
|
||||||
|
let codes = ["pump_fun", "raydium_launchlab", "bags", "moonshot", "moonit"];
|
||||||
|
for code in codes {
|
||||||
|
let entry = match crate::dex_support_matrix_entry_by_code(code) {
|
||||||
|
Some(entry) => entry,
|
||||||
|
None => panic!("missing matrix entry for {}", code),
|
||||||
|
};
|
||||||
|
assert!(
|
||||||
|
entry.surface_type == "launch" || entry.surface_type == "bonding_curve",
|
||||||
|
"{} has unexpected surface type {}",
|
||||||
|
code,
|
||||||
|
entry.surface_type
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn matrix_dto_preserves_core_fields() {
|
||||||
|
let entry = match crate::dex_support_matrix_entry_by_code("pump_swap") {
|
||||||
|
Some(entry) => entry,
|
||||||
|
None => panic!("expected pump_swap matrix entry"),
|
||||||
|
};
|
||||||
|
let dto = crate::DexSupportMatrixEntryDto::from_entry(entry);
|
||||||
|
assert_eq!(dto.code, "pump_swap");
|
||||||
|
assert_eq!(dto.program_id, Some(crate::PUMP_SWAP_PROGRAM_ID.to_string()));
|
||||||
|
assert!(dto.trade_candidate);
|
||||||
|
assert!(dto.candle_candidate);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -39,6 +39,8 @@ mod dex_detection_route;
|
|||||||
mod dex_event_classification;
|
mod dex_event_classification;
|
||||||
/// Shared DEX pool materialization helpers.
|
/// Shared DEX pool materialization helpers.
|
||||||
mod dex_pool_materialization;
|
mod dex_pool_materialization;
|
||||||
|
/// Shared DEX support matrix.
|
||||||
|
mod dex_support_matrix;
|
||||||
/// Shared error type for `kb_lib`.
|
/// Shared error type for `kb_lib`.
|
||||||
mod error;
|
mod error;
|
||||||
/// Generic asynchronous HTTP JSON-RPC client.
|
/// Generic asynchronous HTTP JSON-RPC client.
|
||||||
@@ -859,6 +861,18 @@ pub use dex_event_classification::is_dex_pool_lifecycle_event_kind;
|
|||||||
pub use dex_event_classification::is_dex_reward_event_kind;
|
pub use dex_event_classification::is_dex_reward_event_kind;
|
||||||
/// Returns true for swap-like DEX events.
|
/// Returns true for swap-like DEX events.
|
||||||
pub use dex_event_classification::is_dex_trade_event_kind;
|
pub use dex_event_classification::is_dex_trade_event_kind;
|
||||||
|
/// Static DEX support matrix entry.
|
||||||
|
pub use dex_support_matrix::DexSupportMatrixEntry;
|
||||||
|
/// Owned DEX support matrix entry DTO.
|
||||||
|
pub use dex_support_matrix::DexSupportMatrixEntryDto;
|
||||||
|
/// Returns all static DEX support matrix entries.
|
||||||
|
pub use dex_support_matrix::dex_support_matrix_entries;
|
||||||
|
/// Looks up one DEX support matrix entry by internal code.
|
||||||
|
pub use dex_support_matrix::dex_support_matrix_entry_by_code;
|
||||||
|
/// Looks up one DEX support matrix entry by primary or router program id.
|
||||||
|
pub use dex_support_matrix::dex_support_matrix_entry_by_program_id;
|
||||||
|
/// Returns all DEX support matrix entries as owned DTOs.
|
||||||
|
pub use dex_support_matrix::dex_support_matrix_entry_dtos;
|
||||||
/// Global error type used by the `kb_lib` crate.
|
/// Global error type used by the `kb_lib` crate.
|
||||||
///
|
///
|
||||||
/// The project intentionally avoids `anyhow` and `thiserror`, so this
|
/// The project intentionally avoids `anyhow` and `thiserror`, so this
|
||||||
|
|||||||
@@ -64,7 +64,7 @@ impl LocalPipelineValidationConfig {
|
|||||||
/// Builds the strict validation config for `0.7.27` non-regression runs.
|
/// Builds the strict validation config for `0.7.27` non-regression runs.
|
||||||
pub fn v0_7_27_multi_dex_non_regression() -> Self {
|
pub fn v0_7_27_multi_dex_non_regression() -> Self {
|
||||||
return Self {
|
return Self {
|
||||||
profile_code: "0.7.27_multi_dex_non_regression".to_string(),
|
profile_code: "0.7.27_multi_dex_non_regression (obsolete)".to_string(),
|
||||||
expected_dex_codes: vec![
|
expected_dex_codes: vec![
|
||||||
"pump_fun".to_string(),
|
"pump_fun".to_string(),
|
||||||
"pump_swap".to_string(),
|
"pump_swap".to_string(),
|
||||||
@@ -115,6 +115,37 @@ impl LocalPipelineValidationConfig {
|
|||||||
require_candles_per_dex: false,
|
require_candles_per_dex: false,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// Builds the `0.7.29` DEX support matrix baseline validation config.
|
||||||
|
///
|
||||||
|
/// This profile preserves the `0.7.28` trade/candle non-regression checks
|
||||||
|
/// while requiring the observed partial Meteora DAMM v1 surface to remain
|
||||||
|
/// visible in diagnostics. It also exposes the DEX support matrix in the
|
||||||
|
/// validation report without making planned DEXes blocking.
|
||||||
|
pub fn v0_7_29_multi_dex_matrix_baseline() -> Self {
|
||||||
|
return Self {
|
||||||
|
profile_code: "0.7.29_multi_dex_matrix_baseline".to_string(),
|
||||||
|
expected_dex_codes: vec![
|
||||||
|
"pump_swap".to_string(),
|
||||||
|
"raydium_cpmm".to_string(),
|
||||||
|
"raydium_clmm".to_string(),
|
||||||
|
"meteora_dlmm".to_string(),
|
||||||
|
"meteora_damm_v1".to_string(),
|
||||||
|
],
|
||||||
|
require_all_expected_dexes: true,
|
||||||
|
allow_unexpected_dexes: true,
|
||||||
|
require_clean_diagnostics: false,
|
||||||
|
require_ok_trade_candidates_fully_materialized: true,
|
||||||
|
require_no_invalid_trade_events: true,
|
||||||
|
require_no_duplicate_decoded_event_trades: true,
|
||||||
|
require_no_duplicate_candle_buckets: true,
|
||||||
|
require_no_pair_gaps: false,
|
||||||
|
require_decoded_events_per_dex: true,
|
||||||
|
require_trade_events_per_dex: false,
|
||||||
|
require_candles_per_dex: false,
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A single local pipeline validation issue.
|
/// A single local pipeline validation issue.
|
||||||
@@ -147,6 +178,10 @@ pub struct LocalPipelineValidationReportDto {
|
|||||||
pub expected_dex_codes: std::vec::Vec<std::string::String>,
|
pub expected_dex_codes: std::vec::Vec<std::string::String>,
|
||||||
/// Observed DEX codes found in diagnostics.
|
/// Observed DEX codes found in diagnostics.
|
||||||
pub observed_dex_codes: std::vec::Vec<std::string::String>,
|
pub observed_dex_codes: std::vec::Vec<std::string::String>,
|
||||||
|
/// Number of entries currently exposed by the DEX support matrix.
|
||||||
|
pub dex_support_matrix_entry_count: i64,
|
||||||
|
/// DEX support matrix snapshot exposed with the validation report.
|
||||||
|
pub dex_support_matrix: std::vec::Vec<crate::DexSupportMatrixEntryDto>,
|
||||||
/// Issues produced by validation.
|
/// Issues produced by validation.
|
||||||
pub issues: std::vec::Vec<LocalPipelineValidationIssueDto>,
|
pub issues: std::vec::Vec<LocalPipelineValidationIssueDto>,
|
||||||
}
|
}
|
||||||
@@ -224,6 +259,15 @@ impl LocalPipelineValidationService {
|
|||||||
let config = crate::LocalPipelineValidationConfig::v0_7_28_multi_dex_non_regression();
|
let config = crate::LocalPipelineValidationConfig::v0_7_28_multi_dex_non_regression();
|
||||||
return self.validate_current_database(&config).await;
|
return self.validate_current_database(&config).await;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// Diagnoses the current database with the `0.7.29` DEX matrix baseline profile.
|
||||||
|
pub async fn validate_v0_7_29_current_database(
|
||||||
|
&self,
|
||||||
|
) -> Result<crate::LocalPipelineValidationRunDto, crate::Error> {
|
||||||
|
let config = crate::LocalPipelineValidationConfig::v0_7_29_multi_dex_matrix_baseline();
|
||||||
|
return self.validate_current_database(&config).await;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Validates a diagnostics summary without performing database access.
|
/// Validates a diagnostics summary without performing database access.
|
||||||
@@ -383,6 +427,8 @@ pub fn validate_local_pipeline_diagnostics_summary(
|
|||||||
warning_count,
|
warning_count,
|
||||||
expected_dex_codes,
|
expected_dex_codes,
|
||||||
observed_dex_codes,
|
observed_dex_codes,
|
||||||
|
dex_support_matrix_entry_count: crate::dex_support_matrix_entries().len() as i64,
|
||||||
|
dex_support_matrix: crate::dex_support_matrix_entry_dtos(),
|
||||||
issues,
|
issues,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -553,6 +599,38 @@ mod tests {
|
|||||||
assert!(report.observed_dex_codes.contains(&"meteora_damm_v1".to_string()));
|
assert!(report.observed_dex_codes.contains(&"meteora_damm_v1".to_string()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn validation_accepts_0_7_29_matrix_baseline_summary() {
|
||||||
|
let summary = make_0_7_28_summary_with_meteora();
|
||||||
|
let config = crate::LocalPipelineValidationConfig::v0_7_29_multi_dex_matrix_baseline();
|
||||||
|
let report = crate::validate_local_pipeline_diagnostics_summary(&summary, &config);
|
||||||
|
assert!(report.validation_passed);
|
||||||
|
assert_eq!(report.validation_profile_code, "0.7.29_multi_dex_matrix_baseline");
|
||||||
|
assert!(report.observed_dex_codes.contains(&"meteora_damm_v1".to_string()));
|
||||||
|
assert!(report.expected_dex_codes.contains(&"meteora_damm_v1".to_string()));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn validation_report_exposes_dex_support_matrix() {
|
||||||
|
let summary = make_0_7_28_summary_with_meteora();
|
||||||
|
let config = crate::LocalPipelineValidationConfig::v0_7_29_multi_dex_matrix_baseline();
|
||||||
|
let report = crate::validate_local_pipeline_diagnostics_summary(&summary, &config);
|
||||||
|
assert!(report.dex_support_matrix_entry_count > 0);
|
||||||
|
assert_eq!(
|
||||||
|
report.dex_support_matrix_entry_count,
|
||||||
|
crate::dex_support_matrix_entries().len() as i64
|
||||||
|
);
|
||||||
|
let mut found_pump_swap = false;
|
||||||
|
for entry in &report.dex_support_matrix {
|
||||||
|
if entry.code == "pump_swap" {
|
||||||
|
found_pump_swap = true;
|
||||||
|
assert!(entry.trade_candidate);
|
||||||
|
assert!(entry.candle_candidate);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
assert!(found_pump_swap);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn validation_rejects_missing_expected_dex() {
|
fn validation_rejects_missing_expected_dex() {
|
||||||
let mut summary = make_clean_summary();
|
let mut summary = make_clean_summary();
|
||||||
|
|||||||
@@ -192,52 +192,11 @@ fn build_instruction_evidence_json(
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn known_dex_protocol_name(program_id: &str) -> std::option::Option<&'static str> {
|
fn known_dex_protocol_name(program_id: &str) -> std::option::Option<&'static str> {
|
||||||
if program_id == crate::RAYDIUM_AMM_V4_PROGRAM_ID {
|
let matrix_entry = match crate::dex_support_matrix_entry_by_program_id(program_id) {
|
||||||
return Some("raydium_amm_v4");
|
Some(matrix_entry) => matrix_entry,
|
||||||
}
|
None => return None,
|
||||||
if program_id == crate::RAYDIUM_CPMM_PROGRAM_ID {
|
};
|
||||||
return Some("raydium_cpmm");
|
return Some(matrix_entry.code);
|
||||||
}
|
|
||||||
if program_id == crate::RAYDIUM_CLMM_PROGRAM_ID {
|
|
||||||
return Some("raydium_clmm");
|
|
||||||
}
|
|
||||||
if program_id == crate::RAYDIUM_LAUNCHLAB_PROGRAM_ID {
|
|
||||||
return Some("raydium_launchlab");
|
|
||||||
}
|
|
||||||
if program_id == crate::RAYDIUM_AMM_ROUTING_PROGRAM_ID {
|
|
||||||
return Some("raydium_router");
|
|
||||||
}
|
|
||||||
if program_id == crate::RAYDIUM_STABLE_SWAP_AMM_PROGRAM_ID {
|
|
||||||
return Some("raydium_stable_swap");
|
|
||||||
}
|
|
||||||
if program_id == crate::PUMP_FUN_PROGRAM_ID {
|
|
||||||
return Some("pump_fun");
|
|
||||||
}
|
|
||||||
if program_id == crate::PUMP_SWAP_PROGRAM_ID {
|
|
||||||
return Some("pump_swap");
|
|
||||||
}
|
|
||||||
if program_id == crate::METEORA_DBC_PROGRAM_ID {
|
|
||||||
return Some("meteora_dbc");
|
|
||||||
}
|
|
||||||
if program_id == crate::METEORA_DLMM_PROGRAM_ID {
|
|
||||||
return Some("meteora_dlmm");
|
|
||||||
}
|
|
||||||
if program_id == crate::METEORA_DAMM_V1_PROGRAM_ID {
|
|
||||||
return Some("meteora_damm_v1");
|
|
||||||
}
|
|
||||||
if program_id == crate::METEORA_DAMM_V2_PROGRAM_ID {
|
|
||||||
return Some("meteora_damm_v2");
|
|
||||||
}
|
|
||||||
if program_id == crate::ORCA_WHIRLPOOLS_PROGRAM_ID {
|
|
||||||
return Some("orca_whirlpools");
|
|
||||||
}
|
|
||||||
if program_id == crate::FLUXBEAM_PROGRAM_ID {
|
|
||||||
return Some("fluxbeam");
|
|
||||||
}
|
|
||||||
if program_id == crate::DEXLAB_PROGRAM_ID {
|
|
||||||
return Some("dexlab");
|
|
||||||
}
|
|
||||||
return None;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn should_ignore_program_id(program_id: &str) -> bool {
|
fn should_ignore_program_id(program_id: &str) -> bool {
|
||||||
@@ -385,6 +344,24 @@ fn is_known_launch_surface_program_id(_program_id: &str) -> bool {
|
|||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
|
#[test]
|
||||||
|
fn known_dex_candidate_uses_support_matrix_for_priority_dexes() {
|
||||||
|
let samples = [
|
||||||
|
(crate::PUMP_SWAP_PROGRAM_ID, "pump_swap"),
|
||||||
|
(crate::RAYDIUM_CPMM_PROGRAM_ID, "raydium_cpmm"),
|
||||||
|
(crate::RAYDIUM_CLMM_PROGRAM_ID, "raydium_clmm"),
|
||||||
|
(crate::METEORA_DLMM_PROGRAM_ID, "meteora_dlmm"),
|
||||||
|
(crate::METEORA_DAMM_V1_PROGRAM_ID, "meteora_damm_v1"),
|
||||||
|
];
|
||||||
|
for (program_id, expected_protocol) in samples {
|
||||||
|
let protocol = match super::known_dex_protocol_name(program_id) {
|
||||||
|
Some(protocol) => protocol,
|
||||||
|
None => panic!("expected known protocol for {}", program_id),
|
||||||
|
};
|
||||||
|
assert_eq!(protocol, expected_protocol);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn associated_token_program_is_ignored() {
|
fn associated_token_program_is_ignored() {
|
||||||
let transaction = test_transaction();
|
let transaction = test_transaction();
|
||||||
|
|||||||
@@ -139,7 +139,7 @@ impl TradeAggregationService {
|
|||||||
Ok(amount_resolution) => amount_resolution,
|
Ok(amount_resolution) => amount_resolution,
|
||||||
Err(error) => return Err(error),
|
Err(error) => return Err(error),
|
||||||
};
|
};
|
||||||
let trade_side = match amount_resolution.resolved_trade_side.clone() {
|
let trade_side = match amount_resolution.resolved_trade_side {
|
||||||
Some(resolved_trade_side) => resolved_trade_side,
|
Some(resolved_trade_side) => resolved_trade_side,
|
||||||
None => trade_side,
|
None => trade_side,
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1321,7 +1321,7 @@ fn collect_account_keys_from_candidate_path(
|
|||||||
target.push(text.to_string());
|
target.push(text.to_string());
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if let Some(pubkey) = item.get("pubkey").and_then(|value| value.as_str()) {
|
if let Some(pubkey) = item.get("pubkey").and_then(|value| return value.as_str()) {
|
||||||
target.push(pubkey.to_string());
|
target.push(pubkey.to_string());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1344,7 +1344,7 @@ fn collect_loaded_address_array(
|
|||||||
key: &str,
|
key: &str,
|
||||||
target: &mut std::vec::Vec<std::string::String>,
|
target: &mut std::vec::Vec<std::string::String>,
|
||||||
) {
|
) {
|
||||||
let array = match loaded_addresses.get(key).and_then(|value| value.as_array()) {
|
let array = match loaded_addresses.get(key).and_then(|value| return value.as_array()) {
|
||||||
Some(array) => array,
|
Some(array) => array,
|
||||||
None => return,
|
None => return,
|
||||||
};
|
};
|
||||||
@@ -1391,12 +1391,12 @@ fn token_balance_amount_for_account_index(
|
|||||||
key: &str,
|
key: &str,
|
||||||
account_index: usize,
|
account_index: usize,
|
||||||
) -> std::option::Option<i128> {
|
) -> std::option::Option<i128> {
|
||||||
let balances = match meta.get(key).and_then(|value| value.as_array()) {
|
let balances = match meta.get(key).and_then(|value| return value.as_array()) {
|
||||||
Some(balances) => balances,
|
Some(balances) => balances,
|
||||||
None => return None,
|
None => return None,
|
||||||
};
|
};
|
||||||
for balance in balances {
|
for balance in balances {
|
||||||
let balance_index = balance.get("accountIndex").and_then(|value| value.as_u64());
|
let balance_index = balance.get("accountIndex").and_then(|value| return value.as_u64());
|
||||||
let balance_index = match balance_index {
|
let balance_index = match balance_index {
|
||||||
Some(balance_index) => balance_index,
|
Some(balance_index) => balance_index,
|
||||||
None => continue,
|
None => continue,
|
||||||
@@ -1410,8 +1410,8 @@ fn token_balance_amount_for_account_index(
|
|||||||
}
|
}
|
||||||
let amount_text = balance
|
let amount_text = balance
|
||||||
.get("uiTokenAmount")
|
.get("uiTokenAmount")
|
||||||
.and_then(|value| value.get("amount"))
|
.and_then(|value| return value.get("amount"))
|
||||||
.and_then(|value| value.as_str());
|
.and_then(|value| return value.as_str());
|
||||||
let amount_text = match amount_text {
|
let amount_text = match amount_text {
|
||||||
Some(amount_text) => amount_text,
|
Some(amount_text) => amount_text,
|
||||||
None => return None,
|
None => return None,
|
||||||
@@ -1504,7 +1504,7 @@ mod tests {
|
|||||||
stack_height,
|
stack_height,
|
||||||
accounts_json: "[]".to_string(),
|
accounts_json: "[]".to_string(),
|
||||||
data_json: None,
|
data_json: None,
|
||||||
parsed_type: parsed_type.map(|value| value.to_string()),
|
parsed_type: parsed_type.map(|value| return value.to_string()),
|
||||||
parsed_json,
|
parsed_json,
|
||||||
created_at: chrono::Utc::now(),
|
created_at: chrono::Utc::now(),
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -352,41 +352,12 @@ fn known_dex_program_match(
|
|||||||
Some(program_id) => program_id,
|
Some(program_id) => program_id,
|
||||||
None => return None,
|
None => return None,
|
||||||
};
|
};
|
||||||
let protocol_name = if program_id == crate::RAYDIUM_AMM_V4_PROGRAM_ID {
|
let matrix_entry = match crate::dex_support_matrix_entry_by_program_id(program_id) {
|
||||||
"raydium_amm_v4"
|
Some(matrix_entry) => matrix_entry,
|
||||||
} else if program_id == crate::RAYDIUM_CPMM_PROGRAM_ID {
|
None => return None,
|
||||||
"raydium_cpmm"
|
|
||||||
} else if program_id == crate::RAYDIUM_CLMM_PROGRAM_ID {
|
|
||||||
"raydium_clmm"
|
|
||||||
} else if program_id == crate::RAYDIUM_LAUNCHLAB_PROGRAM_ID {
|
|
||||||
"raydium_launchlab"
|
|
||||||
} else if program_id == crate::RAYDIUM_AMM_ROUTING_PROGRAM_ID {
|
|
||||||
"raydium_router"
|
|
||||||
} else if program_id == crate::RAYDIUM_STABLE_SWAP_AMM_PROGRAM_ID {
|
|
||||||
"raydium_stable_swap"
|
|
||||||
} else if program_id == crate::PUMP_FUN_PROGRAM_ID {
|
|
||||||
"pump_fun"
|
|
||||||
} else if program_id == crate::PUMP_SWAP_PROGRAM_ID {
|
|
||||||
"pump_swap"
|
|
||||||
} else if program_id == crate::METEORA_DBC_PROGRAM_ID {
|
|
||||||
"meteora_dbc"
|
|
||||||
} else if program_id == crate::METEORA_DLMM_PROGRAM_ID {
|
|
||||||
"meteora_dlmm"
|
|
||||||
} else if program_id == crate::METEORA_DAMM_V1_PROGRAM_ID {
|
|
||||||
"meteora_damm_v1"
|
|
||||||
} else if program_id == crate::METEORA_DAMM_V2_PROGRAM_ID {
|
|
||||||
"meteora_damm_v2"
|
|
||||||
} else if program_id == crate::ORCA_WHIRLPOOLS_PROGRAM_ID {
|
|
||||||
"orca_whirlpools"
|
|
||||||
} else if program_id == crate::FLUXBEAM_PROGRAM_ID {
|
|
||||||
"fluxbeam"
|
|
||||||
} else if program_id == crate::DEXLAB_PROGRAM_ID {
|
|
||||||
"dexlab"
|
|
||||||
} else {
|
|
||||||
return None;
|
|
||||||
};
|
};
|
||||||
return Some(KnownDexProgramMatch {
|
return Some(KnownDexProgramMatch {
|
||||||
protocol_name,
|
protocol_name: matrix_entry.code,
|
||||||
program_id: program_id.to_string(),
|
program_id: program_id.to_string(),
|
||||||
instruction_id: instruction.id,
|
instruction_id: instruction.id,
|
||||||
instruction_index: instruction.instruction_index,
|
instruction_index: instruction.instruction_index,
|
||||||
@@ -442,6 +413,26 @@ mod tests {
|
|||||||
assert_eq!(program_match.instruction_index, 0);
|
assert_eq!(program_match.instruction_index, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn known_program_match_uses_support_matrix_for_priority_dexes() {
|
||||||
|
let samples = [
|
||||||
|
(crate::PUMP_SWAP_PROGRAM_ID, "pump_swap"),
|
||||||
|
(crate::RAYDIUM_CPMM_PROGRAM_ID, "raydium_cpmm"),
|
||||||
|
(crate::RAYDIUM_CLMM_PROGRAM_ID, "raydium_clmm"),
|
||||||
|
(crate::METEORA_DLMM_PROGRAM_ID, "meteora_dlmm"),
|
||||||
|
(crate::METEORA_DAMM_V1_PROGRAM_ID, "meteora_damm_v1"),
|
||||||
|
];
|
||||||
|
for (program_id, expected_protocol) in samples {
|
||||||
|
let instruction = test_instruction(Some(program_id.to_string()));
|
||||||
|
let program_match = match super::known_dex_program_match(&instruction) {
|
||||||
|
Some(program_match) => program_match,
|
||||||
|
None => panic!("expected program match for {}", expected_protocol),
|
||||||
|
};
|
||||||
|
assert_eq!(program_match.protocol_name, expected_protocol);
|
||||||
|
assert_eq!(program_match.program_id, program_id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn unknown_program_id_is_not_matched() {
|
fn unknown_program_id_is_not_matched() {
|
||||||
let instruction =
|
let instruction =
|
||||||
|
|||||||
Reference in New Issue
Block a user