0.7.38
This commit is contained in:
@@ -68,3 +68,4 @@
|
|||||||
0.7.35 - Ajout du profil `0.7.35_non_trade_fee_reward_admin`, matérialisation des événements non-trade fees/rewards/admin, raccordement aux diagnostics locaux et maintien strict de l’invariant : aucun fee/reward/admin ne peut produire de trade, metric ou candle.
|
0.7.35 - Ajout du profil `0.7.35_non_trade_fee_reward_admin`, matérialisation des événements non-trade fees/rewards/admin, raccordement aux diagnostics locaux et maintien strict de l’invariant : aucun fee/reward/admin ne peut produire de trade, metric ou candle.
|
||||||
0.7.36 - Consolidation de la famille Meteora : corpus mixte `meteora_damm_v1`, `meteora_damm_v2`, `meteora_dbc` et `meteora_dlmm`, correction des discriminants DAMM v2 / DBC, validation du profil `0.7.36_meteora_family_consolidation`, et reclassement explicite des swaps DAMM v2 / DBC sans payload montant/prix en `non_actionable_trade` afin d’éviter tout trade/candle artificiel.
|
0.7.36 - Consolidation de la famille Meteora : corpus mixte `meteora_damm_v1`, `meteora_damm_v2`, `meteora_dbc` et `meteora_dlmm`, correction des discriminants DAMM v2 / DBC, validation du profil `0.7.36_meteora_family_consolidation`, et reclassement explicite des swaps DAMM v2 / DBC sans payload montant/prix en `non_actionable_trade` afin d’éviter tout trade/candle artificiel.
|
||||||
0.7.37 - Première tranche metadata/catalog : ajout du profil `0.7.37_token_metadata_catalog_enrichment`, exposition des compteurs metadata dans diagnostics/validation et raccordement UI Demo Pipeline 2 sans rendre les metadata manquantes bloquantes.
|
0.7.37 - Première tranche metadata/catalog : ajout du profil `0.7.37_token_metadata_catalog_enrichment`, exposition des compteurs metadata dans diagnostics/validation et raccordement UI Demo Pipeline 2 sans rendre les metadata manquantes bloquantes.
|
||||||
|
0.7.38 - Priorisation des metadata manquantes : ajout du profil `0.7.38_token_metadata_gap_prioritization`, samples `tokenMetadataGapSamples`, priorités tradable/quote/catalog, raccordement UI Demo Pipeline 2 et maintien du caractère non bloquant des metadata incomplètes.
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ members = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
[workspace.package]
|
[workspace.package]
|
||||||
version = "0.7.37"
|
version = "0.7.38"
|
||||||
edition = "2024"
|
edition = "2024"
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
repository = "https://git.sasedev.com/Sasedev/khadhroony-bobobot"
|
repository = "https://git.sasedev.com/Sasedev/khadhroony-bobobot"
|
||||||
|
|||||||
24
README.md
24
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.37-A` : 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à.
|
Le README précédent décrivait surtout l’état `0.3.1`. Ce fichier reflète l’état de clôture `0.7.38` et la reprise prévue en `0.7.39` : le socle transport HTTP/WS, la résolution transactionnelle, le modèle SQLite, plusieurs connecteurs DEX, les candles, les signaux analytiques, la validation locale, la matrice DEX commune et les diagnostics de metadata prioritaires existent déjà.
|
||||||
|
|
||||||
## 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.37-A`
|
## 3. État actuel après `0.7.38`
|
||||||
|
|
||||||
### 3.1. Socle stabilisé à ne pas refactorer maintenant
|
### 3.1. Socle stabilisé à ne pas refactorer maintenant
|
||||||
|
|
||||||
@@ -232,20 +232,18 @@ Les tests peuvent rester plus souples lorsque cela clarifie le test.
|
|||||||
|
|
||||||
## 8. Priorité immédiate
|
## 8. Priorité immédiate
|
||||||
|
|
||||||
La phase actuelle est `0.7.37_token_metadata_catalog_enrichment`.
|
La phase `0.7.38_token_metadata_gap_prioritization` est considérée comme close. La clôture `0.7.38-B` ajoute un registre local minimal de tokens connus : `WSOL`, `USDC`, `USDT`, `JUP`, `RAY`, `BONK`. `USDC` et `USDT` restent les seuls ajouts stable-quote ; `JUP`, `RAY` et `BONK` enrichissent seulement l’affichage metadata sans changer la classification de quote. La prochaine étape est `0.7.39_launch_surfaces`.
|
||||||
|
|
||||||
Objectif : rendre le catalogue local exploitable visuellement et analytiquement sans toucher aux invariants de décodage/trade validés en `0.7.36`.
|
Préconditions validées avant de reprendre le codage :
|
||||||
|
|
||||||
À faire en priorité :
|
1. les invariants locaux restent sains : `blockingIssueCount = 0`, `actionableMissingTradeEventCount = 0`, `missingTradeEventCount = 0` ;
|
||||||
|
2. les profils `0.7.36_meteora_family_consolidation`, `0.7.37_token_metadata_catalog_enrichment` et `0.7.38_token_metadata_gap_prioritization` existent et passent sur le corpus local fourni ;
|
||||||
|
3. les metadata manquantes restent non bloquantes ;
|
||||||
|
4. les `tokenMetadataGapSamples` exposent maintenant une liste de travail priorisée ;
|
||||||
|
5. le registre local minimal contient `SOL`, `WSOL`, `USDC` et `USDT` ;
|
||||||
|
6. le backfill metadata peut traiter les tokens déjà présents en base et rafraîchir les `pair_symbol` sans recréer les objets de marché.
|
||||||
|
|
||||||
1. ajouter ou compléter un registre local des mints connus : `SOL`, `WSOL`, `USDC`, `USDT`, puis autres mints fréquents si vérifiés ;
|
Objectif de `0.7.39` : détecter et rattacher les surfaces de lancement sans inventer de program ids, sans produire de faux trades/candles et sans confondre `launch_origin`, `pool_origin`, `dex_effective` et `migration_target`.
|
||||||
2. améliorer le service de backfill metadata pour traiter les tokens déjà présents en base ;
|
|
||||||
3. exposer un résumé de metadata manquantes par asset class, protocole d’origine, DEX et quote asset ;
|
|
||||||
4. rafraîchir automatiquement ou explicitement les `pair_symbol` après mise à jour des tokens ;
|
|
||||||
5. ajouter une commande UI ou clarifier la commande existante pour relancer le metadata backfill ;
|
|
||||||
6. vérifier l’idempotence : relancer le backfill metadata ne doit pas recréer tokens/pools/pairs/trades ;
|
|
||||||
7. conserver les compteurs DEX propres : `blockingIssueCount = 0`, `actionableMissingTradeEventCount = 0`, `missingTradeEventCount = 0` ;
|
|
||||||
8. préparer ensuite les launch surfaces, qui deviennent l’étape suivante de la roadmap.
|
|
||||||
|
|
||||||
## 9. Fichiers utiles pour reprendre dans une nouvelle session
|
## 9. Fichiers utiles pour reprendre dans une nouvelle session
|
||||||
|
|
||||||
|
|||||||
134
ROADMAP.md
134
ROADMAP.md
@@ -751,19 +751,17 @@ Réalisé :
|
|||||||
### 6.059. Version `0.7.27` — Validation multi-DEX des connecteurs déjà branchés
|
### 6.059. Version `0.7.27` — Validation multi-DEX des connecteurs déjà branchés
|
||||||
Objectif : verrouiller la non-régression du pipeline actuel avant d’ajouter de nouveaux DEX ou d’ouvrir la phase d’analyse `0.8.x`.
|
Objectif : verrouiller la non-régression du pipeline actuel avant d’ajouter de nouveaux DEX ou d’ouvrir la phase d’analyse `0.8.x`.
|
||||||
|
|
||||||
À faire :
|
Réalisé / validé :
|
||||||
|
|
||||||
- rejouer des bases neuves de test pour `pump_fun`, `pump_swap`, `raydium_cpmm` et `raydium_clmm`,
|
- replay local et bases neuves de test utilisés pour stabiliser `pump_fun`, `pump_swap`, `raydium_cpmm` et `raydium_clmm` ;
|
||||||
- ne pas ajouter de nouveau DEX dans cette version ; cette version sert uniquement à valider les connecteurs déjà branchés,
|
- aucun nouveau DEX ajouté dans cette étape : la version a bien servi de verrou de non-régression ;
|
||||||
- vérifier pour chaque DEX le triptyque `decoded_event_count / trade_event_count / pair_candle_count`,
|
- vérification du triptyque `decoded_event_count / trade_event_count / pair_candle_count` par DEX ;
|
||||||
- vérifier que les compteurs `diagnosticsClean`, `blockingIssueCount`, `actionableMissingTradeEventCount`, `duplicateDecodedEventTradeCount` et `duplicateCandleBucketCount` restent cohérents après replay,
|
- garde-fous maintenus sur `diagnosticsClean`, `blockingIssueCount`, `actionableMissingTradeEventCount`, `duplicateDecodedEventTradeCount` et `duplicateCandleBucketCount` ;
|
||||||
- garantir que les événements non pricés ou non candle ne produisent pas de trade event invalide,
|
- refus des trades sans montant ou prix exploitable ;
|
||||||
- conserver l’enrichissement `eventCategory`, `tradeCandidate`, `candleCandidate`, `liquidityCandidate`, `feeCandidate`, `rewardCandidate`, `adminCandidate` et `poolLifecycleCandidate` dans `payload_json`,
|
- conservation des transactions échouées comme decoded events traçables sans produire de `k_sol_trade_events` ;
|
||||||
- documenter les familles d’événements utilisées pour les candles et celles conservées seulement pour l’analyse, la liquidité, les frais, les rewards, l’administration ou la traçabilité,
|
- maintien des champs d’enrichissement dans `payload_json` : `eventCategory`, `tradeCandidate`, `candleCandidate`, `liquidityCandidate`, `feeCandidate`, `rewardCandidate`, `adminCandidate`, `poolLifecycleCandidate` ;
|
||||||
- ajouter ou compléter les tests unitaires sur `dex_decode`, `dex_detect`, `trade_aggregation`, `pair_candle_aggregation`, `pair_analytic_signal`, `local_pipeline_replay` et `local_pipeline_diagnostics`,
|
- couverture testée dans les zones critiques : `dex_decode`, `dex_detect`, `trade_aggregation`, `pair_candle_aggregation`, `pair_analytic_signal`, `local_pipeline_replay`, `local_pipeline_diagnostics` ;
|
||||||
- ajouter des requêtes SQL de diagnostic de référence pour contrôler rapidement les tables clés après backfill ou replay local,
|
- requêtes SQL de diagnostic conservées comme contrôle manuel après backfill ou replay local.
|
||||||
- conserver la tolérance aux événements DEX partiels tout en refusant les trades sans montant ou prix exploitable,
|
|
||||||
- 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
|
||||||
Réalisé :
|
Réalisé :
|
||||||
@@ -888,16 +886,17 @@ Réalisé :
|
|||||||
### 6.067. Version `0.7.35` — Événements non-trade v2 : fees, rewards et administration
|
### 6.067. Version `0.7.35` — Événements non-trade v2 : fees, rewards et administration
|
||||||
Objectif : conserver les événements utiles au risque, au scoring, à l’économie du pool et à la traçabilité opérationnelle.
|
Objectif : conserver les événements utiles au risque, au scoring, à l’économie du pool et à la traçabilité opérationnelle.
|
||||||
|
|
||||||
À faire :
|
Réalisé :
|
||||||
|
|
||||||
- ajouter `k_sol_fee_events`,
|
- ajout des tables `k_sol_fee_events`, `k_sol_reward_events` et `k_sol_pool_admin_events` ;
|
||||||
- ajouter `k_sol_reward_events`,
|
- ajout des DTO, entities, requêtes, index et re-exports associés ;
|
||||||
- ajouter `k_sol_pool_admin_events`,
|
- matérialisation contrôlée des événements fees/rewards/admin lorsque la classification et les comptes le permettent ;
|
||||||
- matérialiser les événements `collect_protocol_fee`, `collect_fund_fee`, `collect_creator_fee`, `collect_fee` et assimilés,
|
- rattachement aux transactions, decoded events, pools et paires lorsque les données disponibles sont fiables ;
|
||||||
- matérialiser les événements `set_reward_params`, `initialize_reward`, `collect_reward`, `update_reward_infos` et assimilés,
|
- raccordement aux diagnostics locaux via `feeEventCount`, `rewardEventCount` et `poolAdminEventCount` ;
|
||||||
- matérialiser les événements `set_config`, `update_config`, `set_authority`, `set_fee_rate`, `pause`, `resume` et assimilés,
|
- ajout du profil `0.7.35_non_trade_fee_reward_admin` ;
|
||||||
- rattacher ces événements aux transactions, decoded events, pools, paires et wallets observés lorsque les comptes le permettent,
|
- invariant maintenu : aucun fee/reward/admin ne produit de trade, metric ou candle.
|
||||||
- documenter clairement que ces événements ne sont ni des trades ni des candles.
|
|
||||||
|
Limite connue : le corpus local `0.7.38` ne contient pas encore d’événements fee/reward/admin matérialisés ; les compteurs peuvent donc rester à zéro sans bloquer la validation.
|
||||||
|
|
||||||
### 6.068. Version `0.7.36` — Meteora : DBC / DAMM v1 / DAMM v2 / DLMM
|
### 6.068. Version `0.7.36` — Meteora : DBC / DAMM v1 / DAMM v2 / DLMM
|
||||||
Réalisé :
|
Réalisé :
|
||||||
@@ -915,26 +914,37 @@ Réalisé :
|
|||||||
### 6.069. Version `0.7.37` — Token metadata et catalogue local
|
### 6.069. Version `0.7.37` — Token metadata et catalogue local
|
||||||
Objectif : rendre le catalogue local exploitable et lisible avant d’ajouter davantage de launch surfaces.
|
Objectif : rendre le catalogue local exploitable et lisible avant d’ajouter davantage de launch surfaces.
|
||||||
|
|
||||||
Réalisé en `0.7.37-A` :
|
Réalisé :
|
||||||
|
|
||||||
- ajout du profil `0.7.37_token_metadata_catalog_enrichment` ;
|
- ajout du profil `0.7.37_token_metadata_catalog_enrichment` ;
|
||||||
- exposition des compteurs metadata/catalog dans les diagnostics et le rapport de validation ;
|
- exposition des compteurs metadata/catalog dans les diagnostics et le rapport de validation : `tokenCount`, `tokenMetadataMissingCount`, `tradableTokenMetadataMissingCount`, `quoteTokenMetadataMissingCount`, `pairSymbolFallbackCount`, `pairSymbolResolvedCount`, `wsolQuotePairCount`, `stableQuotePairCount` ;
|
||||||
- raccordement UI Demo Pipeline 2 au profil `0.7.37` ;
|
- raccordement UI Demo Pipeline 2 au profil `0.7.37` puis au profil `0.7.38` ;
|
||||||
- maintien volontaire du caractère non bloquant des metadata manquantes.
|
- maintien volontaire du caractère non bloquant des metadata manquantes ;
|
||||||
|
- backfill metadata des tokens déjà présents dans `k_sol_tokens` sans nécessiter un nouveau backfill transactionnel ;
|
||||||
|
- enrichissement depuis les sources disponibles : registre local, payloads Pump.fun, comptes SPL/Token-2022, Metaplex lorsque le service dispose d’un `HttpEndpointPool` ;
|
||||||
|
- rafraîchissement des `pair_symbol` après enrichissement des tokens ;
|
||||||
|
- commande UI disponible via `Demo Pipeline 2 > Replay local > Refresh missing token metadata` avec limite dédiée ;
|
||||||
|
- idempotence attendue : le backfill metadata met à jour tokens et symboles de paires sans recréer pools, paires, trades, candles ou origins ;
|
||||||
|
- registre local minimal consolidé : `SOL`, `WSOL`, `USDC`, `USDT`.
|
||||||
|
|
||||||
À faire :
|
Limite non bloquante : les diagnostics détaillés par origine de découverte restent une amélioration de confort ; l’étape `0.7.38` fournit déjà une liste priorisée exploitable via `tokenMetadataGapSamples`.
|
||||||
|
|
||||||
- ajouter ou consolider un registre local des mints connus et stables : `SOL`, `WSOL`, `USDC`, `USDT`, puis autres mints seulement si vérifiés ;
|
### 6.070. Version `0.7.38` — Priorisation des metadata manquantes
|
||||||
- améliorer le backfill metadata pour traiter les tokens déjà présents dans `k_sol_tokens` sans nécessiter un nouveau backfill transactionnel ;
|
Objectif : transformer les compteurs metadata/catalog de `0.7.37` en liste d’action priorisée sans rendre les metadata manquantes bloquantes.
|
||||||
- enrichir les tokens depuis les sources disponibles : registre local, payloads DEX, comptes Token-2022, Metaplex, transactions déjà persistées ;
|
|
||||||
- rafraîchir les `pair_symbol` après mise à jour des metadata de tokens ;
|
|
||||||
- exposer des diagnostics précis : `tokenMetadataMissingCount` par `dexCode`, `quoteAssetClass`, mint connu/inconnu et origine de découverte ;
|
|
||||||
- ajouter ou clarifier une commande UI dans `kb_demo_app` pour lancer le backfill metadata et rafraîchir le catalogue ;
|
|
||||||
- garantir l’idempotence : relancer l’enrichissement metadata ne doit pas recréer tokens, pools, paires, trades, candles ou origins ;
|
|
||||||
- conserver l’invariant de validation : le manque de metadata n’est pas un diagnostic bloquant tant que les trades/candles actionnables sont sains ;
|
|
||||||
- ajouter le profil de validation `0.7.37_token_metadata_catalog_enrichment`.
|
|
||||||
|
|
||||||
### 6.070. Version `0.7.38` — Launch surfaces : LaunchLab, LetsBonk, Bags, Moonshot/Moonit, Boop.fun, Believe
|
Réalisé :
|
||||||
|
|
||||||
|
- ajout du profil `0.7.38_token_metadata_gap_prioritization` ;
|
||||||
|
- exposition de `tokenMetadataGapSamples` dans les diagnostics locaux, la validation et les bindings Demo Pipeline 2 ;
|
||||||
|
- priorisation des tokens manquants par usage : `tradable_quote_missing_metadata`, `tradable_token_missing_metadata`, `quote_token_missing_metadata`, puis `catalog_token_missing_metadata` ;
|
||||||
|
- raccordement Demo Pipeline 2 au nouveau profil par défaut ;
|
||||||
|
- conservation de l’invariant : les metadata manquantes ne créent pas de blocking issue tant que les trades/candles actionnables restent sains ;
|
||||||
|
- validation locale confirmée avec `validationPassed = true`, `blockingIssueCount = 0`, `missingTradeEventCount = 0`, `decodedTradeCandidateWithoutTradeEventCount = 0` ;
|
||||||
|
- les samples permettent de sélectionner les prochains mints à enrichir via registre local, payloads DEX, Token-2022, Metaplex ou backfill HTTP.
|
||||||
|
|
||||||
|
Décision : `0.7.38` est clos. La clôture `0.7.38-B` conserve la logique de stable quotes limitée à `USDC`/`USDT` et ajoute un registre metadata local pour `JUP`, `RAY` et `BONK` sans les classer automatiquement comme quotes. La suite de développement commence à `0.7.39` avec les launch surfaces.
|
||||||
|
|
||||||
|
### 6.071. Version `0.7.39` — Launch surfaces : LaunchLab, LetsBonk, Bags, Moonshot/Moonit, Boop.fun, Believe
|
||||||
Objectif : détecter la première source de mint/lancement des tokens même lorsque le swap final se fait ailleurs.
|
Objectif : détecter la première source de mint/lancement des tokens même lorsque le swap final se fait ailleurs.
|
||||||
|
|
||||||
À faire :
|
À faire :
|
||||||
@@ -949,7 +959,7 @@ Objectif : détecter la première source de mint/lancement des tokens même lors
|
|||||||
- rattacher les launch origins aux pools et paires lorsque les comptes permettent un matching fiable,
|
- rattacher les launch origins aux pools et paires lorsque les comptes permettent un matching fiable,
|
||||||
- exposer les origins dans les diagnostics et l’UI d’inspection.
|
- exposer les origins dans les diagnostics et l’UI d’inspection.
|
||||||
|
|
||||||
### 6.071. Version `0.7.39` — Heaven : corpus, launch et AMM
|
### 6.072. Version `0.7.39` — Heaven : corpus, launch et AMM
|
||||||
Objectif : ajouter Heaven sans le classer trop tôt comme simple DEX ou simple launchpad.
|
Objectif : ajouter Heaven sans le classer trop tôt comme simple DEX ou simple launchpad.
|
||||||
|
|
||||||
À faire :
|
À faire :
|
||||||
@@ -961,7 +971,7 @@ Objectif : ajouter Heaven sans le classer trop tôt comme simple DEX ou simple l
|
|||||||
- documenter les limites si le corpus ne permet pas encore de matérialiser tous les événements,
|
- documenter les limites si le corpus ne permet pas encore de matérialiser tous les événements,
|
||||||
- vérifier que Heaven ne crée pas de candles invalides en cas d’événement de launch non pricé.
|
- vérifier que Heaven ne crée pas de candles invalides en cas d’événement de launch non pricé.
|
||||||
|
|
||||||
### 6.072. Version `0.7.40` — Orca / FluxBeam / DexLab : corpus et validation ciblée
|
### 6.073. Version `0.7.40` — Orca / FluxBeam / DexLab : corpus et validation ciblée
|
||||||
Objectif : consolider les connecteurs déjà présents à partir de corpus locaux vérifiables.
|
Objectif : consolider les connecteurs déjà présents à partir de corpus locaux vérifiables.
|
||||||
|
|
||||||
À faire :
|
À faire :
|
||||||
@@ -973,7 +983,7 @@ Objectif : consolider les connecteurs déjà présents à partir de corpus locau
|
|||||||
- marquer explicitement les variantes partiellement supportées ou heuristiques,
|
- marquer explicitement les variantes partiellement supportées ou heuristiques,
|
||||||
- rejouer les corpus plusieurs fois pour vérifier l’idempotence et l’absence de trades/candles invalides.
|
- rejouer les corpus plusieurs fois pour vérifier l’idempotence et l’absence de trades/candles invalides.
|
||||||
|
|
||||||
### 6.073. Version `0.7.41` — Raydium AMM v4 legacy : corpus et validation ciblée
|
### 6.074. Version `0.7.41` — Raydium AMM v4 legacy : corpus et validation ciblée
|
||||||
Objectif : traiter le vrai Raydium AMM v4 historique après les autres Raydium, afin de l’isoler de `raydium_cpmm`, `raydium_clmm` et des labels Raydium génériques.
|
Objectif : traiter le vrai Raydium AMM v4 historique après les autres Raydium, afin de l’isoler de `raydium_cpmm`, `raydium_clmm` et des labels Raydium génériques.
|
||||||
|
|
||||||
À faire :
|
À faire :
|
||||||
@@ -986,7 +996,7 @@ Objectif : traiter le vrai Raydium AMM v4 historique après les autres Raydium,
|
|||||||
- renommer/stabiliser les fonctions internes autour de `raydium_amm_v4` pour éviter l’ambiguïté avec `raydium_cpmm` et `raydium_clmm`,
|
- renommer/stabiliser les fonctions internes autour de `raydium_amm_v4` pour éviter l’ambiguïté avec `raydium_cpmm` et `raydium_clmm`,
|
||||||
- documenter les limites connues si le corpus AMM v4 reste faible.
|
- documenter les limites connues si le corpus AMM v4 reste faible.
|
||||||
|
|
||||||
### 6.074. Version `0.7.42` — Validation DEX v1 consolidée
|
### 6.075. Version `0.7.42` — Validation DEX v1 consolidée
|
||||||
Objectif : rejouer tous les DEX et launch surfaces supportés et valider les invariants du pipeline complet.
|
Objectif : rejouer tous les DEX et launch surfaces supportés et valider les invariants du pipeline complet.
|
||||||
|
|
||||||
À faire :
|
À faire :
|
||||||
@@ -999,7 +1009,7 @@ Objectif : rejouer tous les DEX et launch surfaces supportés et valider les inv
|
|||||||
- conserver une matrice de support par DEX, variante, instruction et type d’événement,
|
- conserver une matrice de support par DEX, variante, instruction et type d’événement,
|
||||||
- verrouiller les invariants avant d’ouvrir l’analyse `0.8.x`.
|
- verrouiller les invariants avant d’ouvrir l’analyse `0.8.x`.
|
||||||
|
|
||||||
### 6.075. Version `0.7.43` — `kb_demo_app` : overlays analytiques
|
### 6.076. Version `0.7.43` — `kb_demo_app` : overlays analytiques
|
||||||
Objectif : rendre visibles les signaux analytiques directement sur les graphes et vues de marché.
|
Objectif : rendre visibles les signaux analytiques directement sur les graphes et vues de marché.
|
||||||
|
|
||||||
À faire :
|
À faire :
|
||||||
@@ -1010,7 +1020,7 @@ Objectif : rendre visibles les signaux analytiques directement sur les graphes e
|
|||||||
- afficher un panneau latéral listant les signaux liés à une paire et à un timeframe,
|
- afficher un panneau latéral listant les signaux liés à une paire et à un timeframe,
|
||||||
- préparer l’extension future vers Ichimoku, Kumo, projections ABCD et égalités temps/prix sans les mélanger au pipeline de décodage DEX.
|
- préparer l’extension future vers Ichimoku, Kumo, projections ABCD et égalités temps/prix sans les mélanger au pipeline de décodage DEX.
|
||||||
|
|
||||||
### 6.076. Version `0.7.44` — `kb_demo_app` : vues consolidées token / pair / pool
|
### 6.077. Version `0.7.44` — `kb_demo_app` : vues consolidées token / pair / pool
|
||||||
Objectif : fournir une lecture métier plus confortable du modèle `0.7.x`.
|
Objectif : fournir une lecture métier plus confortable du modèle `0.7.x`.
|
||||||
|
|
||||||
À faire :
|
À faire :
|
||||||
@@ -1022,7 +1032,7 @@ Objectif : fournir une lecture métier plus confortable du modèle `0.7.x`.
|
|||||||
- préparer une navigation transversale entre objets techniques et objets métier,
|
- préparer une navigation transversale entre objets techniques et objets métier,
|
||||||
- rendre explicites les cas `tradeCount = null`, `lastPriceQuotePerBase = null`, tokens non enrichis et événements conservés uniquement pour analyse.
|
- rendre explicites les cas `tradeCount = null`, `lastPriceQuotePerBase = null`, tokens non enrichis et événements conservés uniquement pour analyse.
|
||||||
|
|
||||||
### 6.077. Version `0.7.45` — Finition UI `0.7.x`
|
### 6.078. Version `0.7.45` — Finition UI `0.7.x`
|
||||||
Objectif : stabiliser la couche desktop de validation avant l’ouverture de `0.8.x`.
|
Objectif : stabiliser la couche desktop de validation avant l’ouverture de `0.8.x`.
|
||||||
|
|
||||||
À faire :
|
À faire :
|
||||||
@@ -1033,7 +1043,7 @@ Objectif : stabiliser la couche desktop de validation avant l’ouverture de `0.
|
|||||||
- préparer une base UI suffisamment stable pour la future phase d’analyse et filtrage `0.8.x`,
|
- préparer une base UI suffisamment stable pour la future phase d’analyse et filtrage `0.8.x`,
|
||||||
- vérifier que les commandes Tauri restent de simples façades vers `kb_lib`.
|
- vérifier que les commandes Tauri restent de simples façades vers `kb_lib`.
|
||||||
|
|
||||||
### 6.078. Version `0.7.x` — Couverture DEX v1
|
### 6.079. Version `0.7.x` — Couverture DEX v1
|
||||||
Objectif : structurer les connecteurs DEX autour d’un pipeline complet de résolution, décodage, normalisation métier et classification des événements non-trade.
|
Objectif : structurer les connecteurs DEX autour d’un pipeline complet de résolution, décodage, normalisation métier et classification des événements non-trade.
|
||||||
|
|
||||||
Protocoles et surfaces cibles :
|
Protocoles et surfaces cibles :
|
||||||
@@ -1076,7 +1086,7 @@ Résultat attendu :
|
|||||||
- préparation d’une détection temps réel hybride et d’un backfill ciblé compatible avec les mêmes objets métier,
|
- préparation d’une détection temps réel hybride et d’un backfill ciblé compatible avec les mêmes objets métier,
|
||||||
- préparation d’agrégats DEX plus riches, de candles/OHLCV et d’une UI d’inspection du pipeline `0.7.x`.
|
- préparation d’agrégats DEX plus riches, de candles/OHLCV et d’une UI d’inspection du pipeline `0.7.x`.
|
||||||
|
|
||||||
### 6.079. Version `0.8.x` — Analyse et filtrage
|
### 6.080. Version `0.8.x` — Analyse et filtrage
|
||||||
Objectif : transformer les événements bruts en signaux exploitables.
|
Objectif : transformer les événements bruts en signaux exploitables.
|
||||||
|
|
||||||
À faire :
|
À faire :
|
||||||
@@ -1091,7 +1101,7 @@ Objectif : transformer les événements bruts en signaux exploitables.
|
|||||||
- outils de sélection manuelle de points ABC et projection d’un point D selon des règles temps/prix explicites,
|
- outils de sélection manuelle de points ABC et projection d’un point D selon des règles temps/prix explicites,
|
||||||
- séparation stricte entre signaux analytiques observés, projections hypothétiques et décisions de trading.
|
- séparation stricte entre signaux analytiques observés, projections hypothétiques et décisions de trading.
|
||||||
|
|
||||||
### 6.080. Version `1.x.y` — Wallets et swap préparatoire
|
### 6.081. Version `1.x.y` — Wallets et swap préparatoire
|
||||||
Objectif : préparer la couche d’action.
|
Objectif : préparer la couche d’action.
|
||||||
|
|
||||||
À faire :
|
À faire :
|
||||||
@@ -1102,7 +1112,7 @@ Objectif : préparer la couche d’action.
|
|||||||
- préparation d’ordres et de swaps,
|
- préparation d’ordres et de swaps,
|
||||||
- simulation et garde-fous.
|
- simulation et garde-fous.
|
||||||
|
|
||||||
### 6.081. Version `2.x.y` — Trading semi-automatisé
|
### 6.082. Version `2.x.y` — Trading semi-automatisé
|
||||||
Objectif : brancher l’analyse à l’action tout en gardant des garde-fous explicites.
|
Objectif : brancher l’analyse à l’action tout en gardant des garde-fous explicites.
|
||||||
|
|
||||||
À faire :
|
À faire :
|
||||||
@@ -1113,7 +1123,7 @@ Objectif : brancher l’analyse à l’action tout en gardant des garde-fous exp
|
|||||||
- confirmations explicites ou semi-automatiques,
|
- confirmations explicites ou semi-automatiques,
|
||||||
- journaux d’exécution.
|
- journaux d’exécution.
|
||||||
|
|
||||||
### 6.082. Version `3.x.y` — Yellowstone gRPC
|
### 6.083. Version `3.x.y` — Yellowstone gRPC
|
||||||
Objectif : ajouter le connecteur gRPC dédié.
|
Objectif : ajouter le connecteur gRPC dédié.
|
||||||
|
|
||||||
À faire :
|
À faire :
|
||||||
@@ -1243,17 +1253,21 @@ Le projet doit maintenir au minimum :
|
|||||||
|
|
||||||
## 12. Priorité immédiate
|
## 12. Priorité immédiate
|
||||||
|
|
||||||
La priorité immédiate reste de terminer `0.7.37_token_metadata_catalog_enrichment` par le backfill metadata effectif et le rafraîchissement des symboles de paires.
|
La priorité immédiate est `0.7.39_launch_surfaces` : détecter et rattacher les origines de lancement sans casser les invariants `0.7.36` à `0.7.38`.
|
||||||
|
|
||||||
Ordre de travail recommandé :
|
Préconditions validées avant `0.7.39` :
|
||||||
|
|
||||||
1. conserver la validation acquise `0.7.36` : Meteora consolidé, transactions failed traçables mais non actionnables, swaps sans amounts classés `non_actionable_trade`, aucun diagnostic bloquant masqué ;
|
1. validation `0.7.36` acquise : Meteora consolidé, transactions failed traçables mais non actionnables, swaps sans amounts classés `non_actionable_trade`, aucun diagnostic bloquant masqué ;
|
||||||
2. améliorer le catalogue local : metadata de tokens, symboles, noms, asset classes et `pair_symbol` lisibles ;
|
2. validation `0.7.37` acquise : compteurs metadata/catalog exposés, backfill metadata idempotent, `pair_symbol` rafraîchissables, metadata manquantes non bloquantes ;
|
||||||
3. ajouter un registre local des mints connus et stables : `SOL`, `WSOL`, `USDC`, `USDT`, puis autres mints seulement après vérification ;
|
3. validation `0.7.38` acquise : `tokenMetadataGapSamples` priorisés, Demo Pipeline 2 raccordé, `validationPassed = true`, `blockingIssueCount = 0`, registre local `WSOL`/`USDC`/`USDT`/`JUP`/`RAY`/`BONK` disponible ;
|
||||||
4. permettre un backfill metadata idempotent sur les tokens déjà présents sans relancer un backfill transactionnel complet ;
|
4. registre local minimal disponible : `SOL`, `WSOL`, `USDC`, `USDT` ;
|
||||||
5. exposer les compteurs de metadata manquantes par DEX, asset class, quote asset et origine de découverte ;
|
5. les diagnostics locaux restent l’outil de vérité pour décider si une surface peut passer de `planned` à `partial` ou `supported`.
|
||||||
6. ajouter ou clarifier la commande UI permettant de relancer le backfill metadata et le refresh du catalogue ;
|
|
||||||
7. vérifier que l’enrichissement metadata ne modifie pas les invariants DEX : pas de faux trades, pas de fausses candles, pas de recréation de pools/paires ;
|
Ordre de travail recommandé pour `0.7.39` :
|
||||||
8. déplacer ensuite les launch surfaces vers `0.7.38` : Raydium LaunchLab/Launchpad, LetsBonk/Bonk.fun, Boop.fun, Moonshot/Moonit, Believe et Bags ;
|
|
||||||
9. traiter Heaven en `0.7.39`, puis Orca/FluxBeam/DexLab, puis Raydium AMM v4 legacy ;
|
1. scanner `launch_origin.rs`, `pool_origin.rs`, `protocol_candidate_recording.rs`, `dex_support_matrix.rs`, `transaction_classification.rs`, `dex_decode.rs` et `dex_detect.rs` ;
|
||||||
10. effectuer une validation DEX v1 consolidée avant d’ouvrir réellement `0.8.x` pour l’analyse, les filtres, les patterns et les projections graphiques.
|
2. identifier les surfaces candidates déjà présentes dans la matrice : Raydium LaunchLab/Launchpad, LetsBonk/Bonk.fun, Bags, Moonshot/Moonit, Boop.fun et Believe ;
|
||||||
|
3. ne promouvoir une surface que si le corpus prouve le program id, les comptes/authorities et la migration vers le DEX effectif ;
|
||||||
|
4. distinguer explicitement `launch_origin`, `pool_origin`, `dex_effective` et `migration_target` ;
|
||||||
|
5. exposer les origins dans les diagnostics et l’UI d’inspection ;
|
||||||
|
6. conserver les garde-fous : pas de faux trade, pas de fausse candle, pas de program id inventé, pas de metadata manquante bloquante.
|
||||||
|
|||||||
@@ -166,7 +166,8 @@
|
|||||||
<div class="mb-3">
|
<div class="mb-3">
|
||||||
<label for="demoPipeline2ValidationProfileSelect" class="form-label">Validation profile</label>
|
<label for="demoPipeline2ValidationProfileSelect" class="form-label">Validation profile</label>
|
||||||
<select id="demoPipeline2ValidationProfileSelect" class="form-select">
|
<select id="demoPipeline2ValidationProfileSelect" class="form-select">
|
||||||
<option value="0.7.37_token_metadata_catalog_enrichment" selected>0.7.37 — token metadata/catalog enrichment</option>
|
<option value="0.7.38_token_metadata_gap_prioritization" selected>0.7.38 — token metadata gap prioritization</option>
|
||||||
|
<option value="0.7.37_token_metadata_catalog_enrichment">0.7.37 — token metadata/catalog enrichment</option>
|
||||||
<option value="0.7.36_meteora_family_consolidation">0.7.36 — Meteora family consolidation</option>
|
<option value="0.7.36_meteora_family_consolidation">0.7.36 — Meteora family consolidation</option>
|
||||||
<option value="0.7.35_non_trade_fee_reward_admin">0.7.35 — non-trade fee/reward admin</option>
|
<option value="0.7.35_non_trade_fee_reward_admin">0.7.35 — non-trade fee/reward admin</option>
|
||||||
<option value="0.7.34_non_trade_liquidity_lifecycle">0.7.34 — non-trade liquidity/lifecycle</option>
|
<option value="0.7.34_non_trade_liquidity_lifecycle">0.7.34 — non-trade liquidity/lifecycle</option>
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import type { DemoPipeline2LocalPairActionabilityDiagnosticSummary } from "./Dem
|
|||||||
import type { DemoPipeline2LocalPairDiagnosticSummary } from "./DemoPipeline2LocalPairDiagnosticSummary";
|
import type { DemoPipeline2LocalPairDiagnosticSummary } from "./DemoPipeline2LocalPairDiagnosticSummary";
|
||||||
import type { DemoPipeline2LocalPairGapDiagnosticSample } from "./DemoPipeline2LocalPairGapDiagnosticSample";
|
import type { DemoPipeline2LocalPairGapDiagnosticSample } from "./DemoPipeline2LocalPairGapDiagnosticSample";
|
||||||
import type { DemoPipeline2LocalPairTradingReadinessDiagnosticSummary } from "./DemoPipeline2LocalPairTradingReadinessDiagnosticSummary";
|
import type { DemoPipeline2LocalPairTradingReadinessDiagnosticSummary } from "./DemoPipeline2LocalPairTradingReadinessDiagnosticSummary";
|
||||||
|
import type { DemoPipeline2LocalTokenMetadataGapDiagnosticSample } from "./DemoPipeline2LocalTokenMetadataGapDiagnosticSample";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Local pipeline diagnostics summary for the UI.
|
* Local pipeline diagnostics summary for the UI.
|
||||||
@@ -60,6 +61,18 @@ liquidityEventCount: number,
|
|||||||
* Total persisted pool lifecycle events.
|
* Total persisted pool lifecycle events.
|
||||||
*/
|
*/
|
||||||
poolLifecycleEventCount: number,
|
poolLifecycleEventCount: number,
|
||||||
|
/**
|
||||||
|
* Total persisted fee events.
|
||||||
|
*/
|
||||||
|
feeEventCount: number,
|
||||||
|
/**
|
||||||
|
* Total persisted reward events.
|
||||||
|
*/
|
||||||
|
rewardEventCount: number,
|
||||||
|
/**
|
||||||
|
* Total persisted pool administration events.
|
||||||
|
*/
|
||||||
|
poolAdminEventCount: number,
|
||||||
/**
|
/**
|
||||||
* Whether the local persisted pipeline has no blocking diagnostic issue.
|
* Whether the local persisted pipeline has no blocking diagnostic issue.
|
||||||
*/
|
*/
|
||||||
@@ -125,6 +138,30 @@ tokenCount: number,
|
|||||||
* Total tokens missing symbol or name.
|
* Total tokens missing symbol or name.
|
||||||
*/
|
*/
|
||||||
tokenMetadataMissingCount: number,
|
tokenMetadataMissingCount: number,
|
||||||
|
/**
|
||||||
|
* Total tokens used by trade-materialized pairs that still miss symbol or name.
|
||||||
|
*/
|
||||||
|
tradableTokenMetadataMissingCount: number,
|
||||||
|
/**
|
||||||
|
* Total quote-side tokens used by pairs that still miss symbol or name.
|
||||||
|
*/
|
||||||
|
quoteTokenMetadataMissingCount: number,
|
||||||
|
/**
|
||||||
|
* Total pairs whose display symbol is missing or still falls back to raw mints.
|
||||||
|
*/
|
||||||
|
pairSymbolFallbackCount: number,
|
||||||
|
/**
|
||||||
|
* Total pairs whose display symbol is present and does not include raw mints.
|
||||||
|
*/
|
||||||
|
pairSymbolResolvedCount: number,
|
||||||
|
/**
|
||||||
|
* Total pairs whose quote token is WSOL.
|
||||||
|
*/
|
||||||
|
wsolQuotePairCount: number,
|
||||||
|
/**
|
||||||
|
* Total pairs whose quote token is a known stable quote.
|
||||||
|
*/
|
||||||
|
stableQuotePairCount: number,
|
||||||
/**
|
/**
|
||||||
* Total known pools.
|
* Total known pools.
|
||||||
*/
|
*/
|
||||||
@@ -209,6 +246,10 @@ eventClassificationSummaries: Array<DemoPipeline2LocalEventClassificationDiagnos
|
|||||||
* Missing trade events grouped by diagnostic reason.
|
* Missing trade events grouped by diagnostic reason.
|
||||||
*/
|
*/
|
||||||
missingTradeEventReasonSummaries: Array<DemoPipeline2LocalMissingTradeEventReasonSummary>,
|
missingTradeEventReasonSummaries: Array<DemoPipeline2LocalMissingTradeEventReasonSummary>,
|
||||||
|
/**
|
||||||
|
* Prioritized samples of tokens whose display metadata is still incomplete.
|
||||||
|
*/
|
||||||
|
tokenMetadataGapSamples: Array<DemoPipeline2LocalTokenMetadataGapDiagnosticSample>,
|
||||||
/**
|
/**
|
||||||
* Total pairs with only non-actionable missing trade events.
|
* Total pairs with only non-actionable missing trade events.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -50,6 +50,50 @@ liquidityEventCount: number,
|
|||||||
* Total persisted pool lifecycle events.
|
* Total persisted pool lifecycle events.
|
||||||
*/
|
*/
|
||||||
poolLifecycleEventCount: number,
|
poolLifecycleEventCount: number,
|
||||||
|
/**
|
||||||
|
* Total persisted fee events.
|
||||||
|
*/
|
||||||
|
feeEventCount: number,
|
||||||
|
/**
|
||||||
|
* Total persisted reward events.
|
||||||
|
*/
|
||||||
|
rewardEventCount: number,
|
||||||
|
/**
|
||||||
|
* Total persisted pool administration events.
|
||||||
|
*/
|
||||||
|
poolAdminEventCount: number,
|
||||||
|
/**
|
||||||
|
* Total known tokens.
|
||||||
|
*/
|
||||||
|
tokenCount: number,
|
||||||
|
/**
|
||||||
|
* Total tokens missing symbol or name.
|
||||||
|
*/
|
||||||
|
tokenMetadataMissingCount: number,
|
||||||
|
/**
|
||||||
|
* Total tokens used by trade-materialized pairs that still miss symbol or name.
|
||||||
|
*/
|
||||||
|
tradableTokenMetadataMissingCount: number,
|
||||||
|
/**
|
||||||
|
* Total quote-side tokens used by pairs that still miss symbol or name.
|
||||||
|
*/
|
||||||
|
quoteTokenMetadataMissingCount: number,
|
||||||
|
/**
|
||||||
|
* Total pairs whose display symbol is missing or still falls back to raw mints.
|
||||||
|
*/
|
||||||
|
pairSymbolFallbackCount: number,
|
||||||
|
/**
|
||||||
|
* Total pairs whose display symbol is present and does not include raw mints.
|
||||||
|
*/
|
||||||
|
pairSymbolResolvedCount: number,
|
||||||
|
/**
|
||||||
|
* Total pairs whose quote token is WSOL.
|
||||||
|
*/
|
||||||
|
wsolQuotePairCount: number,
|
||||||
|
/**
|
||||||
|
* Total pairs whose quote token is a known stable quote.
|
||||||
|
*/
|
||||||
|
stableQuotePairCount: number,
|
||||||
/**
|
/**
|
||||||
* Number of entries currently exposed by the DEX support matrix.
|
* Number of entries currently exposed by the DEX support matrix.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -0,0 +1,54 @@
|
|||||||
|
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Local token metadata gap sample for the UI.
|
||||||
|
*/
|
||||||
|
export type DemoPipeline2LocalTokenMetadataGapDiagnosticSample = {
|
||||||
|
/**
|
||||||
|
* Token id.
|
||||||
|
*/
|
||||||
|
tokenId: number,
|
||||||
|
/**
|
||||||
|
* Mint address.
|
||||||
|
*/
|
||||||
|
mint: string,
|
||||||
|
/**
|
||||||
|
* Current symbol, when present.
|
||||||
|
*/
|
||||||
|
symbol: string | null,
|
||||||
|
/**
|
||||||
|
* Current name, when present.
|
||||||
|
*/
|
||||||
|
name: string | null,
|
||||||
|
/**
|
||||||
|
* Current mint decimals, when known.
|
||||||
|
*/
|
||||||
|
decimals: number | null,
|
||||||
|
/**
|
||||||
|
* Token program id.
|
||||||
|
*/
|
||||||
|
tokenProgram: string,
|
||||||
|
/**
|
||||||
|
* Whether this token row is flagged as quote token.
|
||||||
|
*/
|
||||||
|
isQuoteToken: boolean,
|
||||||
|
/**
|
||||||
|
* Whether this token appears in at least one trade-materialized pair.
|
||||||
|
*/
|
||||||
|
usedByTradeMaterializedPair: boolean,
|
||||||
|
/**
|
||||||
|
* Whether this token appears on the quote side of at least one pair.
|
||||||
|
*/
|
||||||
|
usedAsQuoteToken: boolean,
|
||||||
|
/**
|
||||||
|
* Number of trade-materialized pairs using this token.
|
||||||
|
*/
|
||||||
|
tradeMaterializedPairCount: number,
|
||||||
|
/**
|
||||||
|
* Number of catalog pairs using this token.
|
||||||
|
*/
|
||||||
|
totalPairCount: number,
|
||||||
|
/**
|
||||||
|
* Prioritization bucket.
|
||||||
|
*/
|
||||||
|
priority: string, };
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "kb-demo-app",
|
"name": "kb-demo-app",
|
||||||
"private": true,
|
"private": true,
|
||||||
"version": "0.7.37",
|
"version": "0.7.38",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "vite",
|
"dev": "vite",
|
||||||
|
|||||||
@@ -451,6 +451,9 @@ pub(crate) struct DemoPipeline2LocalPipelineDiagnosticSummary {
|
|||||||
/// Missing trade events grouped by diagnostic reason.
|
/// Missing trade events grouped by diagnostic reason.
|
||||||
pub missing_trade_event_reason_summaries:
|
pub missing_trade_event_reason_summaries:
|
||||||
std::vec::Vec<DemoPipeline2LocalMissingTradeEventReasonSummary>,
|
std::vec::Vec<DemoPipeline2LocalMissingTradeEventReasonSummary>,
|
||||||
|
/// Prioritized samples of tokens whose display metadata is still incomplete.
|
||||||
|
pub token_metadata_gap_samples:
|
||||||
|
std::vec::Vec<DemoPipeline2LocalTokenMetadataGapDiagnosticSample>,
|
||||||
/// Total pairs with only non-actionable missing trade events.
|
/// Total pairs with only non-actionable missing trade events.
|
||||||
#[ts(type = "number")]
|
#[ts(type = "number")]
|
||||||
pub non_actionable_pair_count: i64,
|
pub non_actionable_pair_count: i64,
|
||||||
@@ -893,6 +896,44 @@ pub(crate) struct DemoPipeline2LocalPairGapDiagnosticSample {
|
|||||||
pub pair_candle_count: i64,
|
pub pair_candle_count: i64,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Local token metadata gap sample for the UI.
|
||||||
|
#[derive(Clone, Debug, serde::Serialize, TS)]
|
||||||
|
#[ts(
|
||||||
|
export,
|
||||||
|
export_to = "../frontend/ts/bindings/DemoPipeline2LocalTokenMetadataGapDiagnosticSample.ts"
|
||||||
|
)]
|
||||||
|
#[serde(rename_all = "camelCase")]
|
||||||
|
pub(crate) struct DemoPipeline2LocalTokenMetadataGapDiagnosticSample {
|
||||||
|
/// Token id.
|
||||||
|
#[ts(type = "number")]
|
||||||
|
pub token_id: i64,
|
||||||
|
/// Mint address.
|
||||||
|
pub mint: std::string::String,
|
||||||
|
/// Current symbol, when present.
|
||||||
|
pub symbol: std::option::Option<std::string::String>,
|
||||||
|
/// Current name, when present.
|
||||||
|
pub name: std::option::Option<std::string::String>,
|
||||||
|
/// Current mint decimals, when known.
|
||||||
|
#[ts(type = "number | null")]
|
||||||
|
pub decimals: std::option::Option<i64>,
|
||||||
|
/// Token program id.
|
||||||
|
pub token_program: std::string::String,
|
||||||
|
/// Whether this token row is flagged as quote token.
|
||||||
|
pub is_quote_token: bool,
|
||||||
|
/// Whether this token appears in at least one trade-materialized pair.
|
||||||
|
pub used_by_trade_materialized_pair: bool,
|
||||||
|
/// Whether this token appears on the quote side of at least one pair.
|
||||||
|
pub used_as_quote_token: bool,
|
||||||
|
/// Number of trade-materialized pairs using this token.
|
||||||
|
#[ts(type = "number")]
|
||||||
|
pub trade_materialized_pair_count: i64,
|
||||||
|
/// Number of catalog pairs using this token.
|
||||||
|
#[ts(type = "number")]
|
||||||
|
pub total_pair_count: i64,
|
||||||
|
/// Prioritization bucket.
|
||||||
|
pub priority: std::string::String,
|
||||||
|
}
|
||||||
|
|
||||||
/// One token item for the local catalog.
|
/// One token item for the local catalog.
|
||||||
#[derive(Clone, Debug, serde::Serialize, TS)]
|
#[derive(Clone, Debug, serde::Serialize, TS)]
|
||||||
#[ts(export, export_to = "../frontend/ts/bindings/DemoPipeline2TokenItem.ts")]
|
#[ts(export, export_to = "../frontend/ts/bindings/DemoPipeline2TokenItem.ts")]
|
||||||
@@ -1147,7 +1188,7 @@ pub(crate) async fn demo_pipeline2_validate_local_pipeline(
|
|||||||
let service = kb_lib::LocalPipelineValidationService::new(database.clone());
|
let service = kb_lib::LocalPipelineValidationService::new(database.clone());
|
||||||
let profile_code = match request {
|
let profile_code = match request {
|
||||||
Some(request) => request.profile_code,
|
Some(request) => request.profile_code,
|
||||||
None => "0.7.37_token_metadata_catalog_enrichment".to_string(),
|
None => "0.7.38_token_metadata_gap_prioritization".to_string(),
|
||||||
};
|
};
|
||||||
let run_result = match profile_code.as_str() {
|
let run_result = match profile_code.as_str() {
|
||||||
"0.7.27" | "0.7.27_dexes_non_regression" => {
|
"0.7.27" | "0.7.27_dexes_non_regression" => {
|
||||||
@@ -1183,6 +1224,9 @@ pub(crate) async fn demo_pipeline2_validate_local_pipeline(
|
|||||||
"0.7.37" | "0.7.37_token_metadata_catalog_enrichment" => {
|
"0.7.37" | "0.7.37_token_metadata_catalog_enrichment" => {
|
||||||
service.validate_v0_7_37_current_database().await
|
service.validate_v0_7_37_current_database().await
|
||||||
},
|
},
|
||||||
|
"0.7.38" | "0.7.38_token_metadata_gap_prioritization" => {
|
||||||
|
service.validate_v0_7_38_current_database().await
|
||||||
|
},
|
||||||
other => Err(kb_lib::Error::InvalidState(format!(
|
other => Err(kb_lib::Error::InvalidState(format!(
|
||||||
"unsupported local pipeline validation profile: {other}"
|
"unsupported local pipeline validation profile: {other}"
|
||||||
))),
|
))),
|
||||||
@@ -1733,6 +1777,10 @@ fn demo_pipeline2_map_local_diagnostics_summary(
|
|||||||
multi_trade_signature_pair_samples
|
multi_trade_signature_pair_samples
|
||||||
.push(demo_pipeline2_map_multi_trade_signature_pair_sample(sample));
|
.push(demo_pipeline2_map_multi_trade_signature_pair_sample(sample));
|
||||||
}
|
}
|
||||||
|
let mut token_metadata_gap_samples = std::vec::Vec::new();
|
||||||
|
for sample in summary.token_metadata_gap_samples {
|
||||||
|
token_metadata_gap_samples.push(demo_pipeline2_map_token_metadata_gap_sample(sample));
|
||||||
|
}
|
||||||
let mut pair_without_trade_samples = std::vec::Vec::new();
|
let mut pair_without_trade_samples = std::vec::Vec::new();
|
||||||
for sample in summary.pair_without_trade_samples {
|
for sample in summary.pair_without_trade_samples {
|
||||||
pair_without_trade_samples.push(demo_pipeline2_map_pair_gap_sample(sample));
|
pair_without_trade_samples.push(demo_pipeline2_map_pair_gap_sample(sample));
|
||||||
@@ -1805,6 +1853,7 @@ fn demo_pipeline2_map_local_diagnostics_summary(
|
|||||||
decoded_event_summaries,
|
decoded_event_summaries,
|
||||||
event_classification_summaries,
|
event_classification_summaries,
|
||||||
missing_trade_event_reason_summaries,
|
missing_trade_event_reason_summaries,
|
||||||
|
token_metadata_gap_samples,
|
||||||
non_actionable_pair_count: summary.non_actionable_pair_count,
|
non_actionable_pair_count: summary.non_actionable_pair_count,
|
||||||
non_actionable_pair_summaries,
|
non_actionable_pair_summaries,
|
||||||
missing_trade_event_samples,
|
missing_trade_event_samples,
|
||||||
@@ -2003,6 +2052,25 @@ fn demo_pipeline2_map_multi_trade_signature_pair_sample(
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn demo_pipeline2_map_token_metadata_gap_sample(
|
||||||
|
sample: kb_lib::LocalTokenMetadataGapDiagnosticSampleDto,
|
||||||
|
) -> DemoPipeline2LocalTokenMetadataGapDiagnosticSample {
|
||||||
|
return DemoPipeline2LocalTokenMetadataGapDiagnosticSample {
|
||||||
|
token_id: sample.token_id,
|
||||||
|
mint: sample.mint,
|
||||||
|
symbol: sample.symbol,
|
||||||
|
name: sample.name,
|
||||||
|
decimals: sample.decimals,
|
||||||
|
token_program: sample.token_program,
|
||||||
|
is_quote_token: sample.is_quote_token,
|
||||||
|
used_by_trade_materialized_pair: sample.used_by_trade_materialized_pair,
|
||||||
|
used_as_quote_token: sample.used_as_quote_token,
|
||||||
|
trade_materialized_pair_count: sample.trade_materialized_pair_count,
|
||||||
|
total_pair_count: sample.total_pair_count,
|
||||||
|
priority: sample.priority,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
fn demo_pipeline2_map_pair_gap_sample(
|
fn demo_pipeline2_map_pair_gap_sample(
|
||||||
sample: kb_lib::LocalPairGapDiagnosticSampleDto,
|
sample: kb_lib::LocalPairGapDiagnosticSampleDto,
|
||||||
) -> DemoPipeline2LocalPairGapDiagnosticSample {
|
) -> DemoPipeline2LocalPairGapDiagnosticSample {
|
||||||
|
|||||||
@@ -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.37",
|
"version": "0.7.38",
|
||||||
"identifier": "com.sasedev.kb-demo-app",
|
"identifier": "com.sasedev.kb-demo-app",
|
||||||
"build": {
|
"build": {
|
||||||
"beforeDevCommand": "npm run dev",
|
"beforeDevCommand": "npm run dev",
|
||||||
|
|||||||
@@ -143,6 +143,21 @@ pub const ZK_ELGAMAL_PROOF_PROGRAM_ID: &str = "ZkE1Gama1Proof1111111111111111111
|
|||||||
/// @see solana_sdk::pubkey::Pubkey = spl_token_interface::native_mint::ID
|
/// @see solana_sdk::pubkey::Pubkey = spl_token_interface::native_mint::ID
|
||||||
pub const WSOL_MINT_ID: &str = "So11111111111111111111111111111111111111112";
|
pub const WSOL_MINT_ID: &str = "So11111111111111111111111111111111111111112";
|
||||||
|
|
||||||
|
/// Canonical Solana USDC mint identifier.
|
||||||
|
pub const USDC_MINT_ID: &str = "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v";
|
||||||
|
|
||||||
|
/// Canonical Solana USDT mint identifier.
|
||||||
|
pub const USDT_MINT_ID: &str = "Es9vMFrzaCERmJfrF4H2FYD4KCoNkY11McCe8BenwNYB";
|
||||||
|
|
||||||
|
/// Canonical Jupiter governance token mint identifier.
|
||||||
|
pub const JUP_MINT_ID: &str = "JUPyiwrYJFskUPiHa7hkeR8VUtAeFoSYbKedZNsDvCN";
|
||||||
|
|
||||||
|
/// Canonical Raydium token mint identifier.
|
||||||
|
pub const RAY_MINT_ID: &str = "4k3Dyjzvzp8eMZWUXbBCjEvwSkkk59S5iCNLY3QrkX6R";
|
||||||
|
|
||||||
|
/// Canonical Bonk token mint identifier.
|
||||||
|
pub const BONK_MINT_ID: &str = "DezXAZ8z7PnrnRJjz3wXBoRgixCa6xjnB7YaB1pPB263";
|
||||||
|
|
||||||
/// DexLab Swap/Pool program id. ("DSwpgjMvXhtGn6BsbqmacdBZyfLj6jSWf3HJpdJtmg6N").
|
/// DexLab Swap/Pool program id. ("DSwpgjMvXhtGn6BsbqmacdBZyfLj6jSWf3HJpdJtmg6N").
|
||||||
pub const DEXLAB_PROGRAM_ID: &str = "DSwpgjMvXhtGn6BsbqmacdBZyfLj6jSWf3HJpdJtmg6N";
|
pub const DEXLAB_PROGRAM_ID: &str = "DSwpgjMvXhtGn6BsbqmacdBZyfLj6jSWf3HJpdJtmg6N";
|
||||||
|
|
||||||
|
|||||||
@@ -44,6 +44,7 @@ pub use dtos::LocalPairGapDiagnosticSampleDto;
|
|||||||
pub use dtos::LocalPairTradingReadinessDiagnosticSummaryDto;
|
pub use dtos::LocalPairTradingReadinessDiagnosticSummaryDto;
|
||||||
pub use dtos::LocalPipelineDiagnosticCountersDto;
|
pub use dtos::LocalPipelineDiagnosticCountersDto;
|
||||||
pub use dtos::LocalPipelineDiagnosticSummaryDto;
|
pub use dtos::LocalPipelineDiagnosticSummaryDto;
|
||||||
|
pub use dtos::LocalTokenMetadataGapDiagnosticSampleDto;
|
||||||
pub use dtos::ObservedTokenDto;
|
pub use dtos::ObservedTokenDto;
|
||||||
pub use dtos::OnchainObservationDto;
|
pub use dtos::OnchainObservationDto;
|
||||||
pub use dtos::PairAnalyticSignalDto;
|
pub use dtos::PairAnalyticSignalDto;
|
||||||
@@ -170,6 +171,7 @@ pub use queries::query_local_pair_without_candle_diagnostic_list_samples;
|
|||||||
pub use queries::query_local_pair_without_trade_diagnostic_list_samples;
|
pub use queries::query_local_pair_without_trade_diagnostic_list_samples;
|
||||||
pub use queries::query_local_pipeline_diagnostic_get_counters;
|
pub use queries::query_local_pipeline_diagnostic_get_counters;
|
||||||
pub use queries::query_local_pipeline_diagnostic_list_summaries;
|
pub use queries::query_local_pipeline_diagnostic_list_summaries;
|
||||||
|
pub use queries::query_local_token_metadata_gap_diagnostic_list_samples;
|
||||||
pub use queries::query_observed_tokens_get_by_mint;
|
pub use queries::query_observed_tokens_get_by_mint;
|
||||||
pub use queries::query_observed_tokens_list;
|
pub use queries::query_observed_tokens_list;
|
||||||
pub use queries::query_observed_tokens_upsert;
|
pub use queries::query_observed_tokens_upsert;
|
||||||
|
|||||||
@@ -58,6 +58,7 @@ pub(crate) use local_pipeline_diagnostics::LocalPairDiagnosticSummaryRow;
|
|||||||
pub(crate) use local_pipeline_diagnostics::LocalPairGapDiagnosticSampleRow;
|
pub(crate) use local_pipeline_diagnostics::LocalPairGapDiagnosticSampleRow;
|
||||||
pub(crate) use local_pipeline_diagnostics::LocalPairTradingReadinessDiagnosticSummaryRow;
|
pub(crate) use local_pipeline_diagnostics::LocalPairTradingReadinessDiagnosticSummaryRow;
|
||||||
pub(crate) use local_pipeline_diagnostics::LocalPipelineDiagnosticCountersRow;
|
pub(crate) use local_pipeline_diagnostics::LocalPipelineDiagnosticCountersRow;
|
||||||
|
pub(crate) use local_pipeline_diagnostics::LocalTokenMetadataGapDiagnosticSampleRow;
|
||||||
|
|
||||||
pub use analysis_signal::AnalysisSignalDto;
|
pub use analysis_signal::AnalysisSignalDto;
|
||||||
pub use chain_instruction::ChainInstructionDto;
|
pub use chain_instruction::ChainInstructionDto;
|
||||||
@@ -88,6 +89,7 @@ pub use local_pipeline_diagnostics::LocalPairGapDiagnosticSampleDto;
|
|||||||
pub use local_pipeline_diagnostics::LocalPairTradingReadinessDiagnosticSummaryDto;
|
pub use local_pipeline_diagnostics::LocalPairTradingReadinessDiagnosticSummaryDto;
|
||||||
pub use local_pipeline_diagnostics::LocalPipelineDiagnosticCountersDto;
|
pub use local_pipeline_diagnostics::LocalPipelineDiagnosticCountersDto;
|
||||||
pub use local_pipeline_diagnostics::LocalPipelineDiagnosticSummaryDto;
|
pub use local_pipeline_diagnostics::LocalPipelineDiagnosticSummaryDto;
|
||||||
|
pub use local_pipeline_diagnostics::LocalTokenMetadataGapDiagnosticSampleDto;
|
||||||
pub use observed_token::ObservedTokenDto;
|
pub use observed_token::ObservedTokenDto;
|
||||||
pub use onchain_observation::OnchainObservationDto;
|
pub use onchain_observation::OnchainObservationDto;
|
||||||
pub use pair::PairDto;
|
pub use pair::PairDto;
|
||||||
|
|||||||
@@ -138,6 +138,8 @@ pub struct LocalPipelineDiagnosticSummaryDto {
|
|||||||
/// Missing trade events grouped by diagnostic reason.
|
/// Missing trade events grouped by diagnostic reason.
|
||||||
pub missing_trade_event_reason_summaries:
|
pub missing_trade_event_reason_summaries:
|
||||||
std::vec::Vec<crate::LocalMissingTradeEventReasonSummaryDto>,
|
std::vec::Vec<crate::LocalMissingTradeEventReasonSummaryDto>,
|
||||||
|
/// Prioritized samples of tokens whose display metadata is still incomplete.
|
||||||
|
pub token_metadata_gap_samples: std::vec::Vec<crate::LocalTokenMetadataGapDiagnosticSampleDto>,
|
||||||
/// Total pairs with only non-actionable missing trade events.
|
/// Total pairs with only non-actionable missing trade events.
|
||||||
pub non_actionable_pair_count: i64,
|
pub non_actionable_pair_count: i64,
|
||||||
/// Pair summaries for non-actionable missing trade events.
|
/// Pair summaries for non-actionable missing trade events.
|
||||||
@@ -712,6 +714,35 @@ pub struct LocalPairGapDiagnosticSampleDto {
|
|||||||
pub pair_candle_count: i64,
|
pub pair_candle_count: i64,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Prioritized sample of an incomplete token metadata row.
|
||||||
|
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
|
||||||
|
pub struct LocalTokenMetadataGapDiagnosticSampleDto {
|
||||||
|
/// Token id.
|
||||||
|
pub token_id: i64,
|
||||||
|
/// Mint address.
|
||||||
|
pub mint: std::string::String,
|
||||||
|
/// Current symbol, when present.
|
||||||
|
pub symbol: std::option::Option<std::string::String>,
|
||||||
|
/// Current name, when present.
|
||||||
|
pub name: std::option::Option<std::string::String>,
|
||||||
|
/// Current mint decimals, when known.
|
||||||
|
pub decimals: std::option::Option<i64>,
|
||||||
|
/// Token program id.
|
||||||
|
pub token_program: std::string::String,
|
||||||
|
/// Whether this token row is flagged as quote token.
|
||||||
|
pub is_quote_token: bool,
|
||||||
|
/// Whether the token appears in at least one pair with materialized trades.
|
||||||
|
pub used_by_trade_materialized_pair: bool,
|
||||||
|
/// Whether the token appears on the quote side of at least one pair.
|
||||||
|
pub used_as_quote_token: bool,
|
||||||
|
/// Number of trade-materialized pairs using this token.
|
||||||
|
pub trade_materialized_pair_count: i64,
|
||||||
|
/// Number of catalog pairs using this token.
|
||||||
|
pub total_pair_count: i64,
|
||||||
|
/// Human-readable prioritization bucket.
|
||||||
|
pub priority: std::string::String,
|
||||||
|
}
|
||||||
|
|
||||||
/// SQL row for missing trade event reason summaries.
|
/// SQL row for missing trade event reason summaries.
|
||||||
#[derive(Debug, Clone, sqlx::FromRow)]
|
#[derive(Debug, Clone, sqlx::FromRow)]
|
||||||
pub(crate) struct LocalMissingTradeEventReasonSummaryRow {
|
pub(crate) struct LocalMissingTradeEventReasonSummaryRow {
|
||||||
@@ -803,3 +834,20 @@ pub(crate) struct LocalPairGapDiagnosticSampleRow {
|
|||||||
pub(crate) trade_event_count: i64,
|
pub(crate) trade_event_count: i64,
|
||||||
pub(crate) pair_candle_count: i64,
|
pub(crate) pair_candle_count: i64,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// SQL row for incomplete token metadata samples.
|
||||||
|
#[derive(Debug, Clone, sqlx::FromRow)]
|
||||||
|
pub(crate) struct LocalTokenMetadataGapDiagnosticSampleRow {
|
||||||
|
pub(crate) token_id: i64,
|
||||||
|
pub(crate) mint: std::string::String,
|
||||||
|
pub(crate) symbol: std::option::Option<std::string::String>,
|
||||||
|
pub(crate) name: std::option::Option<std::string::String>,
|
||||||
|
pub(crate) decimals: std::option::Option<i64>,
|
||||||
|
pub(crate) token_program: std::string::String,
|
||||||
|
pub(crate) is_quote_token: i64,
|
||||||
|
pub(crate) used_by_trade_materialized_pair: i64,
|
||||||
|
pub(crate) used_as_quote_token: i64,
|
||||||
|
pub(crate) trade_materialized_pair_count: i64,
|
||||||
|
pub(crate) total_pair_count: i64,
|
||||||
|
pub(crate) priority: std::string::String,
|
||||||
|
}
|
||||||
|
|||||||
@@ -102,6 +102,7 @@ pub use local_pipeline_diagnostics::query_local_pair_without_candle_diagnostic_l
|
|||||||
pub use local_pipeline_diagnostics::query_local_pair_without_trade_diagnostic_list_samples;
|
pub use local_pipeline_diagnostics::query_local_pair_without_trade_diagnostic_list_samples;
|
||||||
pub use local_pipeline_diagnostics::query_local_pipeline_diagnostic_get_counters;
|
pub use local_pipeline_diagnostics::query_local_pipeline_diagnostic_get_counters;
|
||||||
pub use local_pipeline_diagnostics::query_local_pipeline_diagnostic_list_summaries;
|
pub use local_pipeline_diagnostics::query_local_pipeline_diagnostic_list_summaries;
|
||||||
|
pub use local_pipeline_diagnostics::query_local_token_metadata_gap_diagnostic_list_samples;
|
||||||
pub use observed_token::query_observed_tokens_get_by_mint;
|
pub use observed_token::query_observed_tokens_get_by_mint;
|
||||||
pub use observed_token::query_observed_tokens_list;
|
pub use observed_token::query_observed_tokens_list;
|
||||||
pub use observed_token::query_observed_tokens_upsert;
|
pub use observed_token::query_observed_tokens_upsert;
|
||||||
|
|||||||
@@ -1678,6 +1678,104 @@ LIMIT ?
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Lists prioritized samples of tokens whose metadata is still incomplete.
|
||||||
|
pub async fn query_local_token_metadata_gap_diagnostic_list_samples(
|
||||||
|
database: &crate::Database,
|
||||||
|
limit: i64,
|
||||||
|
) -> Result<std::vec::Vec<crate::LocalTokenMetadataGapDiagnosticSampleDto>, crate::Error> {
|
||||||
|
match database.connection() {
|
||||||
|
crate::DatabaseConnection::Sqlite(pool) => {
|
||||||
|
let rows_result = sqlx::query_as::<
|
||||||
|
sqlx::Sqlite,
|
||||||
|
crate::db::dtos::LocalTokenMetadataGapDiagnosticSampleRow,
|
||||||
|
>(
|
||||||
|
r#"
|
||||||
|
WITH token_pair_usage AS (
|
||||||
|
SELECT
|
||||||
|
token.id AS token_id,
|
||||||
|
COUNT(DISTINCT pair.id) AS total_pair_count,
|
||||||
|
COUNT(DISTINCT CASE WHEN te.id IS NOT NULL THEN pair.id END) AS trade_materialized_pair_count,
|
||||||
|
COUNT(DISTINCT CASE WHEN pair.quote_token_id = token.id THEN pair.id END) AS quote_pair_count
|
||||||
|
FROM k_sol_tokens token
|
||||||
|
LEFT JOIN k_sol_pairs pair
|
||||||
|
ON pair.base_token_id = token.id
|
||||||
|
OR pair.quote_token_id = token.id
|
||||||
|
LEFT JOIN k_sol_trade_events te ON te.pair_id = pair.id
|
||||||
|
GROUP BY token.id
|
||||||
|
)
|
||||||
|
SELECT
|
||||||
|
token.id AS token_id,
|
||||||
|
token.mint AS mint,
|
||||||
|
token.symbol AS symbol,
|
||||||
|
token.name AS name,
|
||||||
|
token.decimals AS decimals,
|
||||||
|
token.token_program AS token_program,
|
||||||
|
token.is_quote_token AS is_quote_token,
|
||||||
|
CASE WHEN usage.trade_materialized_pair_count > 0 THEN 1 ELSE 0 END AS used_by_trade_materialized_pair,
|
||||||
|
CASE WHEN usage.quote_pair_count > 0 THEN 1 ELSE 0 END AS used_as_quote_token,
|
||||||
|
COALESCE(usage.trade_materialized_pair_count, 0) AS trade_materialized_pair_count,
|
||||||
|
COALESCE(usage.total_pair_count, 0) AS total_pair_count,
|
||||||
|
CASE
|
||||||
|
WHEN usage.trade_materialized_pair_count > 0 AND usage.quote_pair_count > 0 THEN 'tradable_quote_missing_metadata'
|
||||||
|
WHEN usage.trade_materialized_pair_count > 0 THEN 'tradable_token_missing_metadata'
|
||||||
|
WHEN usage.quote_pair_count > 0 THEN 'quote_token_missing_metadata'
|
||||||
|
ELSE 'catalog_token_missing_metadata'
|
||||||
|
END AS priority
|
||||||
|
FROM k_sol_tokens token
|
||||||
|
LEFT JOIN token_pair_usage usage ON usage.token_id = token.id
|
||||||
|
WHERE token.symbol IS NULL
|
||||||
|
OR TRIM(token.symbol) = ''
|
||||||
|
OR token.name IS NULL
|
||||||
|
OR TRIM(token.name) = ''
|
||||||
|
ORDER BY
|
||||||
|
CASE
|
||||||
|
WHEN usage.trade_materialized_pair_count > 0 AND usage.quote_pair_count > 0 THEN 1
|
||||||
|
WHEN usage.trade_materialized_pair_count > 0 THEN 2
|
||||||
|
WHEN usage.quote_pair_count > 0 THEN 3
|
||||||
|
ELSE 4
|
||||||
|
END,
|
||||||
|
usage.trade_materialized_pair_count DESC,
|
||||||
|
usage.total_pair_count DESC,
|
||||||
|
token.mint
|
||||||
|
LIMIT ?
|
||||||
|
"#,
|
||||||
|
)
|
||||||
|
.bind(limit)
|
||||||
|
.fetch_all(pool)
|
||||||
|
.await;
|
||||||
|
let rows = match rows_result {
|
||||||
|
Ok(rows) => rows,
|
||||||
|
Err(error) => {
|
||||||
|
return Err(crate::Error::Db(format!(
|
||||||
|
"cannot list token metadata gap diagnostic samples on sqlite: {}",
|
||||||
|
error
|
||||||
|
)));
|
||||||
|
},
|
||||||
|
};
|
||||||
|
let mut samples = std::vec::Vec::new();
|
||||||
|
for row in rows {
|
||||||
|
samples.push(crate::LocalTokenMetadataGapDiagnosticSampleDto {
|
||||||
|
token_id: row.token_id,
|
||||||
|
mint: row.mint,
|
||||||
|
symbol: row.symbol,
|
||||||
|
name: row.name,
|
||||||
|
decimals: row.decimals,
|
||||||
|
token_program: row.token_program,
|
||||||
|
is_quote_token: sqlite_i64_to_bool(row.is_quote_token),
|
||||||
|
used_by_trade_materialized_pair: sqlite_i64_to_bool(
|
||||||
|
row.used_by_trade_materialized_pair,
|
||||||
|
),
|
||||||
|
used_as_quote_token: sqlite_i64_to_bool(row.used_as_quote_token),
|
||||||
|
trade_materialized_pair_count: row.trade_materialized_pair_count,
|
||||||
|
total_pair_count: row.total_pair_count,
|
||||||
|
priority: row.priority,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return Ok(samples);
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Lists samples of pairs without trade events.
|
/// Lists samples of pairs without trade events.
|
||||||
pub async fn query_local_pair_without_trade_diagnostic_list_samples(
|
pub async fn query_local_pair_without_trade_diagnostic_list_samples(
|
||||||
database: &crate::Database,
|
database: &crate::Database,
|
||||||
|
|||||||
@@ -266,6 +266,16 @@ pub use constants::SYSVAR_STAKE_HISTORY_PROGRAM_ID;
|
|||||||
/// Vote program identifier. ("Vote111111111111111111111111111111111111111").
|
/// Vote program identifier. ("Vote111111111111111111111111111111111111111").
|
||||||
/// @see solana_sdk::pubkey::Pubkey = solana_sdk_ids::vote::ID
|
/// @see solana_sdk::pubkey::Pubkey = solana_sdk_ids::vote::ID
|
||||||
pub use constants::VOTE_PROGRAM_ID;
|
pub use constants::VOTE_PROGRAM_ID;
|
||||||
|
/// Canonical Bonk token mint identifier.
|
||||||
|
pub use constants::BONK_MINT_ID;
|
||||||
|
/// Canonical Jupiter governance token mint identifier.
|
||||||
|
pub use constants::JUP_MINT_ID;
|
||||||
|
/// Canonical Raydium token mint identifier.
|
||||||
|
pub use constants::RAY_MINT_ID;
|
||||||
|
/// Canonical Solana USDC mint identifier.
|
||||||
|
pub use constants::USDC_MINT_ID;
|
||||||
|
/// Canonical Solana USDT mint identifier.
|
||||||
|
pub use constants::USDT_MINT_ID;
|
||||||
/// Wrapped SOL mint identifier. ("So11111111111111111111111111111111111111112").
|
/// Wrapped SOL mint identifier. ("So11111111111111111111111111111111111111112").
|
||||||
/// @see solana_sdk::pubkey::Pubkey = spl_token_interface::native_mint::ID
|
/// @see solana_sdk::pubkey::Pubkey = spl_token_interface::native_mint::ID
|
||||||
pub use constants::WSOL_MINT_ID;
|
pub use constants::WSOL_MINT_ID;
|
||||||
@@ -375,6 +385,8 @@ pub use db::LocalPairTradingReadinessDiagnosticSummaryDto;
|
|||||||
pub use db::LocalPipelineDiagnosticCountersDto;
|
pub use db::LocalPipelineDiagnosticCountersDto;
|
||||||
/// Local pipeline diagnostics summary.
|
/// Local pipeline diagnostics summary.
|
||||||
pub use db::LocalPipelineDiagnosticSummaryDto;
|
pub use db::LocalPipelineDiagnosticSummaryDto;
|
||||||
|
/// Prioritized sample of an incomplete token metadata row.
|
||||||
|
pub use db::LocalTokenMetadataGapDiagnosticSampleDto;
|
||||||
/// Source family for one on-chain observation.
|
/// Source family for one on-chain observation.
|
||||||
pub use db::ObservationSourceKind;
|
pub use db::ObservationSourceKind;
|
||||||
/// Application-facing observed token DTO.
|
/// Application-facing observed token DTO.
|
||||||
@@ -613,6 +625,8 @@ pub use db::query_local_pair_without_trade_diagnostic_list_samples;
|
|||||||
pub use db::query_local_pipeline_diagnostic_get_counters;
|
pub use db::query_local_pipeline_diagnostic_get_counters;
|
||||||
/// Lists local DEX diagnostic summaries.
|
/// Lists local DEX diagnostic summaries.
|
||||||
pub use db::query_local_pipeline_diagnostic_list_summaries;
|
pub use db::query_local_pipeline_diagnostic_list_summaries;
|
||||||
|
/// Lists prioritized token metadata gap diagnostic samples.
|
||||||
|
pub use db::query_local_token_metadata_gap_diagnostic_list_samples;
|
||||||
/// Reads one observed token by mint.
|
/// Reads one observed token by mint.
|
||||||
pub use db::query_observed_tokens_get_by_mint;
|
pub use db::query_observed_tokens_get_by_mint;
|
||||||
/// Lists observed tokens ordered by newest first.
|
/// Lists observed tokens ordered by newest first.
|
||||||
|
|||||||
@@ -75,6 +75,16 @@ impl LocalPipelineDiagnosticsService {
|
|||||||
Ok(summaries) => summaries,
|
Ok(summaries) => summaries,
|
||||||
Err(error) => return Err(error),
|
Err(error) => return Err(error),
|
||||||
};
|
};
|
||||||
|
let token_metadata_gap_samples_result =
|
||||||
|
crate::query_local_token_metadata_gap_diagnostic_list_samples(
|
||||||
|
self.database.as_ref(),
|
||||||
|
sample_limit,
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
let token_metadata_gap_samples = match token_metadata_gap_samples_result {
|
||||||
|
Ok(samples) => samples,
|
||||||
|
Err(error) => return Err(error),
|
||||||
|
};
|
||||||
let non_actionable_pair_summaries_result =
|
let non_actionable_pair_summaries_result =
|
||||||
crate::query_local_non_actionable_pair_diagnostic_list_summaries(
|
crate::query_local_non_actionable_pair_diagnostic_list_summaries(
|
||||||
self.database.as_ref(),
|
self.database.as_ref(),
|
||||||
@@ -206,6 +216,7 @@ impl LocalPipelineDiagnosticsService {
|
|||||||
decoded_event_summaries,
|
decoded_event_summaries,
|
||||||
event_classification_summaries,
|
event_classification_summaries,
|
||||||
missing_trade_event_reason_summaries,
|
missing_trade_event_reason_summaries,
|
||||||
|
token_metadata_gap_samples,
|
||||||
non_actionable_pair_count: counters.non_actionable_pair_count,
|
non_actionable_pair_count: counters.non_actionable_pair_count,
|
||||||
non_actionable_pair_summaries,
|
non_actionable_pair_summaries,
|
||||||
missing_trade_event_samples,
|
missing_trade_event_samples,
|
||||||
|
|||||||
@@ -266,6 +266,17 @@ impl LocalPipelineValidationConfig {
|
|||||||
config.profile_code = "0.7.37_token_metadata_catalog_enrichment".to_string();
|
config.profile_code = "0.7.37_token_metadata_catalog_enrichment".to_string();
|
||||||
return config;
|
return config;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Builds the `0.7.38` token metadata gap prioritization validation config.
|
||||||
|
///
|
||||||
|
/// This profile keeps the `0.7.37` metadata counters and exposes
|
||||||
|
/// prioritized token metadata gap samples so the next backfill targets are
|
||||||
|
/// visible without making missing metadata a blocking validation issue.
|
||||||
|
pub fn v0_7_38_token_metadata_gap_prioritization() -> Self {
|
||||||
|
let mut config = Self::v0_7_37_token_metadata_catalog_enrichment();
|
||||||
|
config.profile_code = "0.7.38_token_metadata_gap_prioritization".to_string();
|
||||||
|
return config;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A single local pipeline validation issue.
|
/// A single local pipeline validation issue.
|
||||||
@@ -485,6 +496,15 @@ impl LocalPipelineValidationService {
|
|||||||
crate::LocalPipelineValidationConfig::v0_7_37_token_metadata_catalog_enrichment();
|
crate::LocalPipelineValidationConfig::v0_7_37_token_metadata_catalog_enrichment();
|
||||||
return self.validate_current_database(&config).await;
|
return self.validate_current_database(&config).await;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Diagnoses the current database with the `0.7.38` metadata-gap profile.
|
||||||
|
pub async fn validate_v0_7_38_current_database(
|
||||||
|
&self,
|
||||||
|
) -> Result<crate::LocalPipelineValidationRunDto, crate::Error> {
|
||||||
|
let config =
|
||||||
|
crate::LocalPipelineValidationConfig::v0_7_38_token_metadata_gap_prioritization();
|
||||||
|
return self.validate_current_database(&config).await;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Validates a diagnostics summary without performing database access.
|
/// Validates a diagnostics summary without performing database access.
|
||||||
@@ -606,7 +626,8 @@ pub fn validate_local_pipeline_diagnostics_summary(
|
|||||||
== "0.7.34_non_trade_liquidity_lifecycle"
|
== "0.7.34_non_trade_liquidity_lifecycle"
|
||||||
|| config.profile_code == "0.7.35_non_trade_fee_reward_admin"
|
|| config.profile_code == "0.7.35_non_trade_fee_reward_admin"
|
||||||
|| config.profile_code == "0.7.36_meteora_family_consolidation"
|
|| config.profile_code == "0.7.36_meteora_family_consolidation"
|
||||||
|| config.profile_code == "0.7.37_token_metadata_catalog_enrichment";
|
|| config.profile_code == "0.7.37_token_metadata_catalog_enrichment"
|
||||||
|
|| config.profile_code == "0.7.38_token_metadata_gap_prioritization";
|
||||||
if config.require_all_expected_dexes || missing_expected_dex_is_warning {
|
if config.require_all_expected_dexes || missing_expected_dex_is_warning {
|
||||||
for expected_dex_code in &expected_dex_codes {
|
for expected_dex_code in &expected_dex_codes {
|
||||||
if !observed_dex_codes.contains(expected_dex_code) {
|
if !observed_dex_codes.contains(expected_dex_code) {
|
||||||
@@ -1040,6 +1061,7 @@ mod tests {
|
|||||||
decoded_event_summaries: vec![],
|
decoded_event_summaries: vec![],
|
||||||
event_classification_summaries: vec![],
|
event_classification_summaries: vec![],
|
||||||
missing_trade_event_reason_summaries: vec![],
|
missing_trade_event_reason_summaries: vec![],
|
||||||
|
token_metadata_gap_samples: vec![],
|
||||||
non_actionable_pair_summaries: vec![],
|
non_actionable_pair_summaries: vec![],
|
||||||
missing_trade_event_samples: vec![],
|
missing_trade_event_samples: vec![],
|
||||||
duplicate_decoded_event_trade_samples: vec![],
|
duplicate_decoded_event_trade_samples: vec![],
|
||||||
@@ -1300,6 +1322,39 @@ mod tests {
|
|||||||
assert_eq!(report.stable_quote_pair_count, 2);
|
assert_eq!(report.stable_quote_pair_count, 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn validation_accepts_0_7_38_metadata_gap_samples() {
|
||||||
|
let mut summary = make_0_7_28_summary_with_meteora();
|
||||||
|
summary.token_count = 108;
|
||||||
|
summary.token_metadata_missing_count = 102;
|
||||||
|
summary.tradable_token_metadata_missing_count = 6;
|
||||||
|
summary.quote_token_metadata_missing_count = 5;
|
||||||
|
summary
|
||||||
|
.token_metadata_gap_samples
|
||||||
|
.push(crate::LocalTokenMetadataGapDiagnosticSampleDto {
|
||||||
|
token_id: 42,
|
||||||
|
mint: "MissingMetaMint111".to_string(),
|
||||||
|
symbol: None,
|
||||||
|
name: None,
|
||||||
|
decimals: Some(6),
|
||||||
|
token_program: crate::SPL_TOKEN_PROGRAM_ID.to_string(),
|
||||||
|
is_quote_token: false,
|
||||||
|
used_by_trade_materialized_pair: true,
|
||||||
|
used_as_quote_token: false,
|
||||||
|
trade_materialized_pair_count: 2,
|
||||||
|
total_pair_count: 3,
|
||||||
|
priority: "tradable_token_missing_metadata".to_string(),
|
||||||
|
});
|
||||||
|
let config =
|
||||||
|
crate::LocalPipelineValidationConfig::v0_7_38_token_metadata_gap_prioritization();
|
||||||
|
let report = crate::validate_local_pipeline_diagnostics_summary(&summary, &config);
|
||||||
|
assert!(report.validation_passed);
|
||||||
|
assert_eq!(report.validation_profile_code, "0.7.38_token_metadata_gap_prioritization");
|
||||||
|
assert_eq!(report.blocking_issue_count, 0);
|
||||||
|
assert_eq!(report.token_metadata_missing_count, 102);
|
||||||
|
assert_eq!(summary.token_metadata_gap_samples.len(), 1);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn validation_rejects_0_7_33_pair_trading_readiness_mismatch() {
|
fn validation_rejects_0_7_33_pair_trading_readiness_mismatch() {
|
||||||
let mut summary = make_0_7_28_summary_with_meteora();
|
let mut summary = make_0_7_28_summary_with_meteora();
|
||||||
|
|||||||
@@ -13,6 +13,67 @@ const WRAPPED_SOL_SYMBOL: &str = "WSOL";
|
|||||||
const WRAPPED_SOL_NAME: &str = "Wrapped SOL";
|
const WRAPPED_SOL_NAME: &str = "Wrapped SOL";
|
||||||
const METAPLEX_TOKEN_METADATA_PROGRAM_ID: &str = "metaqbxxUerdq28cj1RbAWkYQm3ybzjb6a8bt518x1s";
|
const METAPLEX_TOKEN_METADATA_PROGRAM_ID: &str = "metaqbxxUerdq28cj1RbAWkYQm3ybzjb6a8bt518x1s";
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy)]
|
||||||
|
struct KnownLocalTokenMetadata {
|
||||||
|
mint: &'static str,
|
||||||
|
symbol: &'static str,
|
||||||
|
name: &'static str,
|
||||||
|
decimals: u8,
|
||||||
|
token_program: &'static str,
|
||||||
|
is_quote_token: std::option::Option<bool>,
|
||||||
|
}
|
||||||
|
|
||||||
|
const KNOWN_LOCAL_TOKEN_METADATA: &[KnownLocalTokenMetadata] = &[
|
||||||
|
KnownLocalTokenMetadata {
|
||||||
|
mint: crate::WSOL_MINT_ID,
|
||||||
|
symbol: WRAPPED_SOL_SYMBOL,
|
||||||
|
name: WRAPPED_SOL_NAME,
|
||||||
|
decimals: 9,
|
||||||
|
token_program: crate::SPL_TOKEN_PROGRAM_ID,
|
||||||
|
is_quote_token: Some(true),
|
||||||
|
},
|
||||||
|
KnownLocalTokenMetadata {
|
||||||
|
mint: crate::USDC_MINT_ID,
|
||||||
|
symbol: "USDC",
|
||||||
|
name: "USD Coin",
|
||||||
|
decimals: 6,
|
||||||
|
token_program: crate::SPL_TOKEN_PROGRAM_ID,
|
||||||
|
is_quote_token: Some(true),
|
||||||
|
},
|
||||||
|
KnownLocalTokenMetadata {
|
||||||
|
mint: crate::USDT_MINT_ID,
|
||||||
|
symbol: "USDT",
|
||||||
|
name: "Tether USD",
|
||||||
|
decimals: 6,
|
||||||
|
token_program: crate::SPL_TOKEN_PROGRAM_ID,
|
||||||
|
is_quote_token: Some(true),
|
||||||
|
},
|
||||||
|
KnownLocalTokenMetadata {
|
||||||
|
mint: crate::JUP_MINT_ID,
|
||||||
|
symbol: "JUP",
|
||||||
|
name: "Jupiter",
|
||||||
|
decimals: 6,
|
||||||
|
token_program: crate::SPL_TOKEN_PROGRAM_ID,
|
||||||
|
is_quote_token: None,
|
||||||
|
},
|
||||||
|
KnownLocalTokenMetadata {
|
||||||
|
mint: crate::RAY_MINT_ID,
|
||||||
|
symbol: "RAY",
|
||||||
|
name: "Raydium",
|
||||||
|
decimals: 6,
|
||||||
|
token_program: crate::SPL_TOKEN_PROGRAM_ID,
|
||||||
|
is_quote_token: None,
|
||||||
|
},
|
||||||
|
KnownLocalTokenMetadata {
|
||||||
|
mint: crate::BONK_MINT_ID,
|
||||||
|
symbol: "BONK",
|
||||||
|
name: "Bonk",
|
||||||
|
decimals: 5,
|
||||||
|
token_program: crate::SPL_TOKEN_PROGRAM_ID,
|
||||||
|
is_quote_token: None,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
/// Summary produced by a token metadata backfill pass.
|
/// Summary produced by a token metadata backfill pass.
|
||||||
#[derive(Debug, Clone, Default, serde::Serialize, serde::Deserialize)]
|
#[derive(Debug, Clone, Default, serde::Serialize, serde::Deserialize)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
@@ -321,15 +382,16 @@ fn resolve_local_token_metadata(mint: &str) -> std::option::Option<ResolvedToken
|
|||||||
is_quote_token: Some(true),
|
is_quote_token: Some(true),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
let wsol_mint = crate::WSOL_MINT_ID.to_string();
|
for known_token in KNOWN_LOCAL_TOKEN_METADATA {
|
||||||
if mint == wsol_mint {
|
if mint == known_token.mint {
|
||||||
return Some(ResolvedTokenMetadata {
|
return Some(ResolvedTokenMetadata {
|
||||||
symbol: Some(WRAPPED_SOL_SYMBOL.to_string()),
|
symbol: Some(known_token.symbol.to_string()),
|
||||||
name: Some(WRAPPED_SOL_NAME.to_string()),
|
name: Some(known_token.name.to_string()),
|
||||||
decimals: Some(9),
|
decimals: Some(known_token.decimals),
|
||||||
token_program: Some(crate::SPL_TOKEN_PROGRAM_ID.to_string()),
|
token_program: Some(known_token.token_program.to_string()),
|
||||||
is_quote_token: Some(true),
|
is_quote_token: known_token.is_quote_token,
|
||||||
});
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
@@ -820,6 +882,26 @@ mod tests {
|
|||||||
assert_eq!(metadata.is_quote_token, Some(true));
|
assert_eq!(metadata.is_quote_token, Some(true));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn local_metadata_resolves_known_ecosystem_tokens_without_forcing_quote_flag() {
|
||||||
|
let cases = [
|
||||||
|
(crate::JUP_MINT_ID, "JUP", "Jupiter", 6_u8),
|
||||||
|
(crate::RAY_MINT_ID, "RAY", "Raydium", 6_u8),
|
||||||
|
(crate::BONK_MINT_ID, "BONK", "Bonk", 5_u8),
|
||||||
|
];
|
||||||
|
for (mint, expected_symbol, expected_name, expected_decimals) in cases {
|
||||||
|
let metadata_option = super::resolve_local_token_metadata(mint);
|
||||||
|
let metadata = match metadata_option {
|
||||||
|
Some(metadata) => metadata,
|
||||||
|
None => panic!("known ecosystem token metadata must resolve"),
|
||||||
|
};
|
||||||
|
assert_eq!(metadata.symbol.as_deref(), Some(expected_symbol));
|
||||||
|
assert_eq!(metadata.name.as_deref(), Some(expected_name));
|
||||||
|
assert_eq!(metadata.decimals, Some(expected_decimals));
|
||||||
|
assert_eq!(metadata.is_quote_token, None);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn pump_fun_payload_metadata_extracts_name_and_symbol() {
|
fn pump_fun_payload_metadata_extracts_name_and_symbol() {
|
||||||
let payload = serde_json::json!({
|
let payload = serde_json::json!({
|
||||||
@@ -932,6 +1014,144 @@ mod tests {
|
|||||||
assert!(fetched.is_quote_token);
|
assert!(fetched.is_quote_token);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn local_backfill_updates_stable_quotes_without_http() {
|
||||||
|
let database = make_database().await;
|
||||||
|
let usdc = crate::TokenDto::new(
|
||||||
|
crate::USDC_MINT_ID.to_string(),
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
crate::SPL_TOKEN_PROGRAM_ID.to_string(),
|
||||||
|
false,
|
||||||
|
);
|
||||||
|
let usdt = crate::TokenDto::new(
|
||||||
|
crate::USDT_MINT_ID.to_string(),
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
crate::SPL_TOKEN_PROGRAM_ID.to_string(),
|
||||||
|
false,
|
||||||
|
);
|
||||||
|
let usdc_upsert_result = crate::query_tokens_upsert(database.as_ref(), &usdc).await;
|
||||||
|
if let Err(error) = usdc_upsert_result {
|
||||||
|
panic!("usdc token upsert must succeed: {}", error);
|
||||||
|
}
|
||||||
|
let usdt_upsert_result = crate::query_tokens_upsert(database.as_ref(), &usdt).await;
|
||||||
|
if let Err(error) = usdt_upsert_result {
|
||||||
|
panic!("usdt token upsert must succeed: {}", error);
|
||||||
|
}
|
||||||
|
let service = crate::TokenMetadataBackfillService::new_local(database.clone());
|
||||||
|
let result = service.backfill_missing_token_metadata(Some(10)).await;
|
||||||
|
let result = match result {
|
||||||
|
Ok(result) => result,
|
||||||
|
Err(error) => panic!("metadata backfill must succeed: {}", error),
|
||||||
|
};
|
||||||
|
assert_eq!(result.updated_token_count, 2);
|
||||||
|
|
||||||
|
let usdc_result =
|
||||||
|
crate::query_tokens_get_by_mint(database.as_ref(), crate::USDC_MINT_ID).await;
|
||||||
|
let usdc_option = match usdc_result {
|
||||||
|
Ok(usdc_option) => usdc_option,
|
||||||
|
Err(error) => panic!("usdc token fetch must succeed: {}", error),
|
||||||
|
};
|
||||||
|
let usdc = match usdc_option {
|
||||||
|
Some(usdc) => usdc,
|
||||||
|
None => panic!("usdc token must exist"),
|
||||||
|
};
|
||||||
|
assert_eq!(usdc.symbol.as_deref(), Some("USDC"));
|
||||||
|
assert_eq!(usdc.name.as_deref(), Some("USD Coin"));
|
||||||
|
assert_eq!(usdc.decimals, Some(6));
|
||||||
|
assert!(usdc.is_quote_token);
|
||||||
|
|
||||||
|
let usdt_result =
|
||||||
|
crate::query_tokens_get_by_mint(database.as_ref(), crate::USDT_MINT_ID).await;
|
||||||
|
let usdt_option = match usdt_result {
|
||||||
|
Ok(usdt_option) => usdt_option,
|
||||||
|
Err(error) => panic!("usdt token fetch must succeed: {}", error),
|
||||||
|
};
|
||||||
|
let usdt = match usdt_option {
|
||||||
|
Some(usdt) => usdt,
|
||||||
|
None => panic!("usdt token must exist"),
|
||||||
|
};
|
||||||
|
assert_eq!(usdt.symbol.as_deref(), Some("USDT"));
|
||||||
|
assert_eq!(usdt.name.as_deref(), Some("Tether USD"));
|
||||||
|
assert_eq!(usdt.decimals, Some(6));
|
||||||
|
assert!(usdt.is_quote_token);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn local_backfill_updates_known_ecosystem_tokens_without_http() {
|
||||||
|
let database = make_database().await;
|
||||||
|
let token_mints = [crate::JUP_MINT_ID, crate::RAY_MINT_ID, crate::BONK_MINT_ID];
|
||||||
|
for mint in token_mints {
|
||||||
|
let token = crate::TokenDto::new(
|
||||||
|
mint.to_string(),
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
crate::SPL_TOKEN_PROGRAM_ID.to_string(),
|
||||||
|
false,
|
||||||
|
);
|
||||||
|
let upsert_result = crate::query_tokens_upsert(database.as_ref(), &token).await;
|
||||||
|
if let Err(error) = upsert_result {
|
||||||
|
panic!("known token upsert must succeed: {}", error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let service = crate::TokenMetadataBackfillService::new_local(database.clone());
|
||||||
|
let result = service.backfill_missing_token_metadata(Some(10)).await;
|
||||||
|
let result = match result {
|
||||||
|
Ok(result) => result,
|
||||||
|
Err(error) => panic!("metadata backfill must succeed: {}", error),
|
||||||
|
};
|
||||||
|
assert_eq!(result.updated_token_count, 3);
|
||||||
|
|
||||||
|
let jup_result =
|
||||||
|
crate::query_tokens_get_by_mint(database.as_ref(), crate::JUP_MINT_ID).await;
|
||||||
|
let jup_option = match jup_result {
|
||||||
|
Ok(jup_option) => jup_option,
|
||||||
|
Err(error) => panic!("jup token fetch must succeed: {}", error),
|
||||||
|
};
|
||||||
|
let jup = match jup_option {
|
||||||
|
Some(jup) => jup,
|
||||||
|
None => panic!("jup token must exist"),
|
||||||
|
};
|
||||||
|
assert_eq!(jup.symbol.as_deref(), Some("JUP"));
|
||||||
|
assert_eq!(jup.name.as_deref(), Some("Jupiter"));
|
||||||
|
assert_eq!(jup.decimals, Some(6));
|
||||||
|
assert!(!jup.is_quote_token);
|
||||||
|
|
||||||
|
let ray_result =
|
||||||
|
crate::query_tokens_get_by_mint(database.as_ref(), crate::RAY_MINT_ID).await;
|
||||||
|
let ray_option = match ray_result {
|
||||||
|
Ok(ray_option) => ray_option,
|
||||||
|
Err(error) => panic!("ray token fetch must succeed: {}", error),
|
||||||
|
};
|
||||||
|
let ray = match ray_option {
|
||||||
|
Some(ray) => ray,
|
||||||
|
None => panic!("ray token must exist"),
|
||||||
|
};
|
||||||
|
assert_eq!(ray.symbol.as_deref(), Some("RAY"));
|
||||||
|
assert_eq!(ray.name.as_deref(), Some("Raydium"));
|
||||||
|
assert_eq!(ray.decimals, Some(6));
|
||||||
|
assert!(!ray.is_quote_token);
|
||||||
|
|
||||||
|
let bonk_result =
|
||||||
|
crate::query_tokens_get_by_mint(database.as_ref(), crate::BONK_MINT_ID).await;
|
||||||
|
let bonk_option = match bonk_result {
|
||||||
|
Ok(bonk_option) => bonk_option,
|
||||||
|
Err(error) => panic!("bonk token fetch must succeed: {}", error),
|
||||||
|
};
|
||||||
|
let bonk = match bonk_option {
|
||||||
|
Some(bonk) => bonk,
|
||||||
|
None => panic!("bonk token must exist"),
|
||||||
|
};
|
||||||
|
assert_eq!(bonk.symbol.as_deref(), Some("BONK"));
|
||||||
|
assert_eq!(bonk.name.as_deref(), Some("Bonk"));
|
||||||
|
assert_eq!(bonk.decimals, Some(5));
|
||||||
|
assert!(!bonk.is_quote_token);
|
||||||
|
}
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn local_backfill_does_not_overwrite_existing_display_metadata() {
|
async fn local_backfill_does_not_overwrite_existing_display_metadata() {
|
||||||
let database = make_database().await;
|
let database = make_database().await;
|
||||||
|
|||||||
Reference in New Issue
Block a user