0.7.41
This commit is contained in:
@@ -71,3 +71,4 @@
|
||||
0.7.38 - Priorisation des metadata manquantes : ajout du profil `0.7.38_token_metadata_gap_prioritization`, samples `tokenMetadataGapSamples`, priorités tradable/quote/catalog, raccordement UI Demo Pipeline 2 et maintien du caractère non bloquant des metadata incomplètes.
|
||||
0.7.39 - Réorientation DEX-first : distinction explicite des rôles `dex_effective`, `aggregator_router`, `launch_surface` et `to_verify` dans la matrice DEX, suppression de l’alias ambigu `raydium`, ajout de `metaDAO` et `Printr` comme surfaces à vérifier sans `program_id`, profil `0.7.39_dex_first_effective_swap_surfaces`, validation locale avec `blockingIssueCount = 0`, `actionableMissingTradeEventCount = 0` et `missingTradeEventCount = 0`.
|
||||
0.7.40 - Ajout de Demo3 pour la découverte on-chain de corpus DEX par `dex_code` / `program_id` via `getSignaturesForAddress` + `getTransaction`, extraction générique des mints observés, deltas SPL Token, comptes pool/state candidats, vaults candidats et comptes programme, ajout du backfill par signature dans Demo Pipeline 2, validation pratique sur Raydium AMM v4 avec instructions internes `675kPX...` persistées, et report de Demo4 après la première consolidation Raydium AMM v4.
|
||||
0.7.41 - Raydium AMM v4 swap decoder v1 : ajout du décodeur `raydium_amm_v4.swap` sur inner instructions `675kPX...`, extraction pool/state, authority, vaults, mints, routeSource et montants exploitables, matérialisation trades/candles sur transactions OK, profil `0.7.41_raydium_amm_v4_swap_decoder`, matrice AMM v4 passée en `supported`, et validation locale avec `blockingIssueCount = 0`, `warningCount = 0`, `actionableMissingTradeEventCount = 0`, `missingTradeEventCount = 0` et 58 trades AMM v4 matérialisés.
|
||||
|
||||
@@ -8,7 +8,7 @@ members = [
|
||||
]
|
||||
|
||||
[workspace.package]
|
||||
version = "0.7.40"
|
||||
version = "0.7.41"
|
||||
edition = "2024"
|
||||
license = "MIT"
|
||||
repository = "https://git.sasedev.com/Sasedev/khadhroony-bobobot"
|
||||
|
||||
21
README.md
21
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.
|
||||
|
||||
Le README précédent décrivait surtout l’état `0.3.1`. Ce fichier reflète l’état de clôture `0.7.40` et la réorientation de travail `0.7.41 Raydium AMM v4` : le socle transport HTTP/WS, la résolution transactionnelle, le modèle SQLite, plusieurs connecteurs DEX, les candles, les signaux analytiques, la validation locale, la matrice DEX commune et les diagnostics de metadata prioritaires existent déjà. La prochaine phase exploite les corpus on-chain obtenus via Demo3 et Demo Pipeline 2 pour consolider les DEX effectifs, en commençant par `raydium_amm_v4`, avant de revenir aux launch surfaces.
|
||||
Le README précédent décrivait surtout l’état `0.3.1`. Ce fichier reflète l’état de clôture `0.7.41` : 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, les diagnostics de metadata prioritaires, Demo3, le backfill par signature et le décodeur `raydium_amm_v4.swap` v1 existent déjà. La prochaine phase consolide la famille Raydium complète (`raydium_cpmm`, `raydium_clmm`, `raydium_amm_v4`, router/stable/launch surfaces différées) avant de poursuivre les autres DEX effectifs.
|
||||
|
||||
## 1. Objectif
|
||||
|
||||
@@ -127,7 +127,7 @@ Depuis `0.7.33`, les diagnostics ajoutent une classification `pairTradingReadine
|
||||
| `pump_swap` | AMM / swap | haute | conserver les invariants trade/candle et étendre les événements non-trade prouvés. |
|
||||
| `raydium_cpmm` | AMM | haute | vérifier corpus swap/liquidité/admin et maintenir la matérialisation trade/candle. |
|
||||
| `raydium_clmm` | CLMM | haute | vérifier corpus swap/liquidité/position et maintenir la matérialisation trade/candle. |
|
||||
| `raydium_amm_v4` | AMM legacy | haute | rechercher des paires/pools réels pour `675kPX9MHTjS2zt1qfr1NYHuzeLXfQM9H24wFSUt1Mp8`, valider `initialize2` et identifier les swaps. |
|
||||
| `raydium_amm_v4` | AMM legacy | haute | support v1 validé : décodage des inner swaps `675kPX...`, matérialisation trades/candles, pools/paires et payloads de montants exploitables ; prochaine étape : consolidation Raydium famille. |
|
||||
| `raydium_stable_swap` | AMM legacy | moyenne | vérifier l’usage réel de `5quBtoiQqxF9Jv6KYKctB59NT3gtJD2Y65kdnB1Uev3h` et ne l’activer qu’avec corpus. |
|
||||
| `meteora_dlmm` | DLMM | haute | verrouiller swaps, positions, liquidité et lifecycle. |
|
||||
| `meteora_damm_v1` | AMM legacy | haute | conserver le skip sans amounts exploitables et rechercher un corpus swap/liquidité exploitable. |
|
||||
@@ -238,19 +238,18 @@ Les phases `0.7.38`, `0.7.39` et `0.7.40` sont considérées comme closes lorsqu
|
||||
- `0.7.39_dex_first_effective_swap_surfaces` : matrice DEX-first, suppression de l’alias `raydium`, ajout de `metaDAO` et `Printr` en `to_verify`, invariants locaux maintenus ;
|
||||
- `0.7.40` : Demo3 découvre on-chain des signatures, mints, deltas et comptes candidats par DEX/program id, et Demo Pipeline 2 peut backfiller une signature précise.
|
||||
|
||||
La prochaine étape est maintenant `0.7.41_raydium_amm_v4_swap_decoder_v1`.
|
||||
La prochaine étape est maintenant `0.7.42_raydium_family_consolidation`.
|
||||
|
||||
Objectifs immédiats :
|
||||
|
||||
- utiliser le corpus Raydium AMM v4 obtenu via Demo3 et backfill signature ;
|
||||
- décoder les inner instructions `675kPX9MHTjS2zt1qfr1NYHuzeLXfQM9H24wFSUt1Mp8`, y compris lorsqu’elles sont appelées par Jupiter ou un autre routeur top-level ;
|
||||
- extraire les comptes pool/state, authority, vaults et comptes utilisateur lorsque le layout observé est compatible ;
|
||||
- dériver les montants à partir des deltas SPL Token ou transferts instruction-scoped ;
|
||||
- produire `tradeCandidate=true` seulement si les mints et montants sont exploitables ;
|
||||
- conserver les routes ambiguës, failed transactions et swaps sans montants fiables comme non actionnables ;
|
||||
- ne créer aucun trade, metric ou candle sans payload exploitable.
|
||||
- verrouiller ensemble `raydium_cpmm`, `raydium_clmm` et `raydium_amm_v4` comme surfaces Raydium effectives déjà observées ;
|
||||
- vérifier que `raydium_amm_v4` reste limité aux swaps avec mints/montants exploitables et que les transactions failed ne produisent aucun trade/candle ;
|
||||
- consolider les diagnostics Raydium : decoded events, trades, candles, pools, pairs, route sources, comptes pool/state et vaults ;
|
||||
- garder `raydium_router` comme `aggregator_router` non matérialisé en DEX direct ;
|
||||
- garder `raydium_stable_swap`, `raydium_launchlab` et `raydium_launchpad` hors matérialisation tant qu’un corpus dédié ne les justifie pas ;
|
||||
- préparer la suite Meteora/Orca/FluxBeam/DexLab sans réintroduire d’alias ambigu `raydium`.
|
||||
|
||||
`Demo4` est volontairement reportée à une version ultérieure. Pour la suite immédiate, Demo3 et Demo Pipeline 2 suffisent à produire le corpus nécessaire à la consolidation Raydium AMM v4.
|
||||
`Demo4` reste volontairement reportée à une version ultérieure. Pour la suite immédiate, Demo3 et Demo Pipeline 2 suffisent à produire le corpus nécessaire aux consolidations DEX.
|
||||
|
||||
Les launch surfaces restent importantes, mais elles sont reportées après la consolidation des DEX effectifs. Elles ne doivent pas générer de faux trades/candles ni de `program_id` fictif.
|
||||
|
||||
|
||||
58
ROADMAP.md
58
ROADMAP.md
@@ -981,21 +981,23 @@ Décision : `0.7.40` est clos. `Demo3` et Demo Pipeline 2 suffisent pour constit
|
||||
### 6.073. Version `0.7.41` — Raydium AMM v4 swap decoder v1
|
||||
Objectif : ajouter un premier décodeur fiable pour les swaps Raydium AMM v4 observés dans le corpus constitué avec Demo3 et Demo Pipeline 2.
|
||||
|
||||
À faire :
|
||||
Réalisé :
|
||||
|
||||
- créer ou compléter le décodeur `raydium_amm_v4` pour les instructions de swap AMM v4 ;
|
||||
- traiter explicitement les inner instructions dont `program_id = 675kPX9MHTjS2zt1qfr1NYHuzeLXfQM9H24wFSUt1Mp8`, notamment lorsqu’elles sont appelées via Jupiter ou un autre routeur top-level ;
|
||||
- conserver dans le payload décodé les informations de routage utiles : `routeSource`, `topLevelProgram`, `innerInstruction`, `instructionIndex`, `innerInstructionIndex` ;
|
||||
- extraire les comptes selon le layout observé lorsque le format est compatible : token program, pool/state candidat, authority, vault A, vault B, comptes intermédiaires, comptes utilisateur ;
|
||||
- utiliser les deltas SPL Token et/ou les transferts inner instruction-scoped pour dériver les montants `base_amount_raw` et `quote_amount_raw` ;
|
||||
- marquer `eventActionability = trade_candidate` seulement si les montants et les mints sont exploitables ;
|
||||
- classer en `non_actionable_trade` les swaps ou routes dont les montants sont absents, ambigus, multi-hop non isolables ou issus de transactions failed ;
|
||||
- matérialiser `trade_events`, metrics et candles uniquement pour les transactions OK avec payload de montants exploitable ;
|
||||
- matérialiser ou mettre à jour pools/paires/listings uniquement lorsque les comptes permettent un rattachement fiable ;
|
||||
- ajouter des tests de non-régression sur les signatures et patterns Raydium AMM v4 observés localement ;
|
||||
- maintenir `blockingIssueCount = 0`, `actionableMissingTradeEventCount = 0`, `missingTradeEventCount = 0` après replay/validation.
|
||||
- ajout du décodeur `raydium_amm_v4.swap` pour les instructions de swap AMM v4 ;
|
||||
- prise en charge des inner instructions dont `program_id = 675kPX9MHTjS2zt1qfr1NYHuzeLXfQM9H24wFSUt1Mp8`, notamment lorsqu’elles sont appelées via Jupiter ou un autre routeur top-level ;
|
||||
- conservation dans le payload décodé des informations de routage utiles : `routeSource`, programme parent, `innerInstruction`, `instructionIndex`, `innerInstructionIndex` et comptes complets ;
|
||||
- extraction des comptes selon les layouts observés : token program, pool/state, authority, vault A, vault B, comptes intermédiaires et comptes utilisateur lorsque disponibles ;
|
||||
- dérivation des mints et montants via deltas SPL Token et transferts instruction-scoped, avec refus des cas sans payload exploitable ;
|
||||
- production de `eventActionability = trade_candidate` uniquement lorsque les mints et montants sont exploitables ;
|
||||
- matérialisation des pools, paires, listings, `trade_events`, metrics et candles uniquement pour les transactions OK ;
|
||||
- maintien des transactions failed comme traçables mais sans `trade_events`, metrics ni candles ;
|
||||
- ajout du profil `0.7.41_raydium_amm_v4_swap_decoder` dans la validation locale et dans Demo Pipeline 2 ;
|
||||
- mise à jour de la matrice DEX : `raydium_amm_v4` passe en `observed = true`, `status = supported`, `confidence = high`, sans `skipReason` ;
|
||||
- validation locale confirmée avec `validationPassed = true`, `blockingIssueCount = 0`, `warningCount = 0`, `actionableMissingTradeEventCount = 0`, `missingTradeEventCount = 0`, `decodedTradeCandidateWithoutAmountPayloadCount = 0` et `invalidTradeEventCount = 0`.
|
||||
|
||||
Corpus de travail initial : comptes candidats tels que `48yaEzz1JSHoghWYg3RGE31srN66ubfPgm4Wc5556YTG`, `SJmR8rJgzzCi4sPjGnrNsqY4akQb3jn5nsxZBhyEifC`, `EMFHrMra2GepQK9KtkQ9C65zxqdDCpQM2uKySPyNEFDA`, `58oQChx4yWmvKdwLLZzBi4ChoCc2fqCUWBkwMihLYQo2`, et les signatures associées issues de Demo3.
|
||||
Résultat de corpus validé : `raydium_amm_v4` produit 58 decoded events, 58 trade candidates, 58 trade events, 11 pools/paires et 147 candles sur la base de test Raydium AMM v4. Les routes Jupiter ou routeurs top-level restent annotées comme sources de route, tandis que le decoded event métier est attribué au DEX effectif `raydium_amm_v4`.
|
||||
|
||||
Décision : `0.7.41` est clos. La suite immédiate est `0.7.42_raydium_family_consolidation` afin de verrouiller ensemble `raydium_cpmm`, `raydium_clmm`, `raydium_amm_v4` et les surfaces Raydium non encore matérialisées.
|
||||
|
||||
### 6.074. Version `0.7.42` — Raydium effectif : famille Raydium consolidée
|
||||
Objectif : consolider la famille Raydium après le premier décodeur AMM v4.
|
||||
@@ -1270,30 +1272,30 @@ Le projet doit maintenir au minimum :
|
||||
|
||||
## 12. Priorité immédiate
|
||||
|
||||
La priorité immédiate après `0.7.40` est `0.7.41_raydium_amm_v4_swap_decoder_v1`. `Demo3` et le backfill par signature donnent maintenant assez de corpus on-chain pour travailler sur Raydium AMM v4 sans inventer de layout ni promouvoir de faux pool.
|
||||
La priorité immédiate après `0.7.41` est `0.7.42_raydium_family_consolidation`. `raydium_amm_v4.swap` v1 est maintenant validé sur corpus réel, avec trades/candles matérialisés et aucun warning de validation. La suite doit verrouiller la famille Raydium complète avant de passer aux autres DEX effectifs.
|
||||
|
||||
Préconditions validées avant `0.7.41` :
|
||||
Préconditions validées avant `0.7.42` :
|
||||
|
||||
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. validation `0.7.37` acquise : compteurs metadata/catalog exposés, backfill metadata idempotent, `pair_symbol` rafraîchissables, metadata manquantes non bloquantes ;
|
||||
3. validation `0.7.38` acquise : `tokenMetadataGapSamples` priorisés, Demo Pipeline 2 raccordé, `validationPassed = true`, `blockingIssueCount = 0`, registre local `WSOL`/`USDC`/`USDT`/`JUP`/`RAY`/`BONK` disponible ;
|
||||
4. validation `0.7.39` acquise : matrice DEX-first, suppression de l’alias `raydium`, `metaDAO` et `Printr` en `to_verify`, aucun `program_id` fictif ;
|
||||
5. validation `0.7.40` acquise : `Demo3` découvre on-chain des signatures, mints, deltas et comptes candidats ; Demo Pipeline 2 peut backfiller une signature précise ; les instructions Raydium AMM v4 sont persistées en base ;
|
||||
6. aucune transaction failed ne doit alimenter `trade_events`, metrics ou candles ;
|
||||
7. aucun decoded event AMM v4 ne doit être promu `trade_candidate` sans montants exploitables.
|
||||
6. validation `0.7.41` acquise : `raydium_amm_v4.swap` décode les inner instructions `675kPX...`, produit des trades/candles lorsque les montants sont exploitables, et conserve les transactions failed sans matérialisation marché ;
|
||||
7. aucune transaction failed ne doit alimenter `trade_events`, metrics ou candles ;
|
||||
8. aucun decoded event ne doit être promu `trade_candidate` sans montants exploitables.
|
||||
|
||||
Ordre de travail recommandé pour `0.7.41+` :
|
||||
Ordre de travail recommandé pour `0.7.42+` :
|
||||
|
||||
1. implémenter `raydium_amm_v4.swap` v1 à partir du corpus local : instructions internes `675kPX9MHTjS2zt1qfr1NYHuzeLXfQM9H24wFSUt1Mp8`, comptes, `data_json`, deltas SPL Token et routes Jupiter ;
|
||||
2. distinguer `routeSource` / routeur top-level et DEX effectif : une transaction Jupiter peut contenir un leg Raydium AMM v4, mais le decoded event métier doit rester attribué au DEX effectif ;
|
||||
3. matérialiser trades/candles uniquement pour transactions OK et montants fiables ;
|
||||
4. conserver les swaps ambigus ou multi-hop non isolables en `non_actionable_trade` ;
|
||||
5. consolider ensuite la famille Raydium (`cpmm`, `clmm`, `amm_v4`, `stable_swap`, router et launch surfaces reportées) ;
|
||||
6. reprendre Meteora, Orca, FluxBeam, DexLab, metaDAO et Printr avec la même approche corpus-first ;
|
||||
7. décaler `Demo4` après la première consolidation Raydium AMM v4, car la découverte on-chain locale suffit pour la suite immédiate ;
|
||||
8. ajouter ensuite `Demo10` pour le watcher WebSocket live DEX avec start/stop, subscribe/unsubscribe et écriture en base via le pipeline existant ;
|
||||
9. reprendre seulement après cela les launch surfaces spécialisées ;
|
||||
10. passer ensuite à la couche wallet : création de wallet/keypair, inspection et transfert de fonds vers un autre account.
|
||||
1. consolider ensemble `raydium_cpmm`, `raydium_clmm` et `raydium_amm_v4` comme surfaces Raydium effectives supportées ;
|
||||
2. ajouter des diagnostics Raydium par surface : decoded events, trades, candles, route sources, pools, pairs, comptes pool/state, vaults et mints ;
|
||||
3. vérifier que `raydium_router` reste un `aggregator_router` non matérialisé comme DEX direct ;
|
||||
4. garder `raydium_stable_swap`, `raydium_launchlab` et `raydium_launchpad` en surfaces non matérialisées tant qu’un corpus dédié ne prouve pas leur exploitation ;
|
||||
5. reprendre Meteora, Orca, FluxBeam, DexLab, metaDAO et Printr avec la même approche corpus-first ;
|
||||
6. décaler `Demo4` après la consolidation Raydium, car la découverte on-chain locale suffit pour la suite immédiate ;
|
||||
7. ajouter ensuite `Demo10` pour le watcher WebSocket live DEX avec start/stop, subscribe/unsubscribe et écriture en base via le pipeline existant ;
|
||||
8. reprendre seulement après cela les launch surfaces spécialisées ;
|
||||
9. passer ensuite à la couche wallet : création de wallet/keypair, inspection et transfert de fonds vers un autre account.
|
||||
|
||||
Garde-fous constants :
|
||||
|
||||
|
||||
@@ -179,8 +179,9 @@
|
||||
<div class="mb-3">
|
||||
<label for="demoPipeline2ValidationProfileSelect" class="form-label">Validation profile</label>
|
||||
<select id="demoPipeline2ValidationProfileSelect" class="form-select">
|
||||
<option value="0.7.40_raydium_effective_surfaces" selected>0.7.340 — Raydium effective surfaces</option>
|
||||
<option value="0.7.39_dex_first_effective_swap_surfaces" selected>0.7.39 — DEX-first effective swap surfaces</option>
|
||||
<option value="0.7.41_raydium_amm_v4_swap_decoder" selected>0.7.41 — Raydium AMM v4 swap decoder</option>
|
||||
<option value="0.7.40_raydium_effective_surfaces">0.7.40 — Raydium effective surfaces</option>
|
||||
<option value="0.7.39_dex_first_effective_swap_surfaces">0.7.39 — DEX-first effective swap surfaces</option>
|
||||
<option value="0.7.38_token_metadata_gap_prioritization">0.7.38 — token metadata gap prioritization</option>
|
||||
<option value="0.7.37_token_metadata_catalog_enrichment">0.7.37 — token metadata/catalog enrichment</option>
|
||||
<option value="0.7.36_meteora_family_consolidation">0.7.36 — Meteora family consolidation</option>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "kb-demo-app",
|
||||
"private": true,
|
||||
"version": "0.7.40",
|
||||
"version": "0.7.41",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
|
||||
@@ -1324,7 +1324,10 @@ pub(crate) async fn demo_pipeline2_validate_local_pipeline(
|
||||
service.validate_v0_7_39_current_database().await
|
||||
},
|
||||
"0.7.40" | "0.7.40_raydium_effective_surfaces" => {
|
||||
service.validate_v0_7_39_current_database().await
|
||||
service.validate_v0_7_40_current_database().await
|
||||
},
|
||||
"0.7.41" | "0.7.41_raydium_amm_v4_swap_decoder" => {
|
||||
service.validate_v0_7_41_current_database().await
|
||||
},
|
||||
other => Err(kb_lib::Error::InvalidState(format!(
|
||||
"unsupported local pipeline validation profile: {other}"
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"$schema": "https://schema.tauri.app/config/2",
|
||||
"productName": "kb-demo-app",
|
||||
"version": "0.7.40",
|
||||
"version": "0.7.41",
|
||||
"identifier": "com.sasedev.kb-demo-app",
|
||||
"build": {
|
||||
"beforeDevCommand": "npm run dev",
|
||||
|
||||
@@ -55,6 +55,7 @@ pub use pump_swap::PumpSwapTradeDecoded;
|
||||
pub use raydium_amm_v4::RaydiumAmmV4DecodedEvent;
|
||||
pub use raydium_amm_v4::RaydiumAmmV4Decoder;
|
||||
pub use raydium_amm_v4::RaydiumAmmV4Initialize2PoolDecoded;
|
||||
pub use raydium_amm_v4::RaydiumAmmV4SwapDecoded;
|
||||
pub use raydium_clmm::RaydiumClmmDecodedEvent;
|
||||
pub use raydium_clmm::RaydiumClmmSwapV2Decoded;
|
||||
pub use raydium_clmm::decode_raydium_clmm_instruction;
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -562,6 +562,24 @@ impl DexDecodeService {
|
||||
)
|
||||
.await;
|
||||
},
|
||||
crate::RaydiumAmmV4DecodedEvent::Swap(event) => {
|
||||
return self
|
||||
.materialize_named_dex_event(
|
||||
transaction,
|
||||
event.transaction_id,
|
||||
event.instruction_id,
|
||||
"raydium_amm_v4",
|
||||
event.program_id.clone(),
|
||||
"raydium_amm_v4.swap",
|
||||
Some(event.pool_account.clone()),
|
||||
None,
|
||||
Some(event.token_a_mint.clone()),
|
||||
Some(event.token_b_mint.clone()),
|
||||
None,
|
||||
event.payload_json.clone(),
|
||||
)
|
||||
.await;
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -88,6 +88,9 @@ impl DexDetectService {
|
||||
crate::dex_detection_route::DexDetectionRoute::RaydiumAmmV4Initialize2Pool => {
|
||||
self.detect_raydium_initialize2_pool(&transaction, decoded_event).await
|
||||
},
|
||||
crate::dex_detection_route::DexDetectionRoute::RaydiumAmmV4Trade => {
|
||||
self.detect_raydium_amm_v4_trade(&transaction, decoded_event).await
|
||||
},
|
||||
crate::dex_detection_route::DexDetectionRoute::RaydiumCpmmTrade => {
|
||||
self.detect_raydium_cpmm_trade(&transaction, decoded_event).await
|
||||
},
|
||||
@@ -522,6 +525,60 @@ impl DexDetectService {
|
||||
.await;
|
||||
}
|
||||
|
||||
async fn detect_raydium_amm_v4_trade(
|
||||
&self,
|
||||
transaction: &crate::ChainTransactionDto,
|
||||
decoded_event: &crate::DexDecodedEventDto,
|
||||
) -> Result<crate::DexPoolDetectionResult, crate::Error> {
|
||||
let dex_id_result =
|
||||
crate::dex_catalog::ensure_known_dex(self.database.as_ref(), "raydium_amm_v4").await;
|
||||
let dex_id = match dex_id_result {
|
||||
Ok(dex_id) => dex_id,
|
||||
Err(error) => return Err(error),
|
||||
};
|
||||
let payload_value_result = parse_payload_json(decoded_event.payload_json.as_str());
|
||||
let payload_value = match payload_value_result {
|
||||
Ok(payload_value) => payload_value,
|
||||
Err(error) => return Err(error),
|
||||
};
|
||||
let base_vault_address = extract_payload_string_field(&payload_value, "baseVault");
|
||||
let quote_vault_address = extract_payload_string_field(&payload_value, "quoteVault");
|
||||
let input_result =
|
||||
crate::dex_pool_materialization::DexPoolMaterializationInput::from_decoded_event(
|
||||
decoded_event,
|
||||
dex_id,
|
||||
crate::PoolKind::Amm,
|
||||
crate::PoolStatus::Active,
|
||||
crate::dex_pool_materialization::DexPoolTokenOrder::AlreadyBaseQuote,
|
||||
base_vault_address,
|
||||
quote_vault_address,
|
||||
transaction.source_endpoint_name.clone(),
|
||||
);
|
||||
let input = match input_result {
|
||||
Ok(input) => input,
|
||||
Err(error) => return Err(error),
|
||||
};
|
||||
let detection_result =
|
||||
crate::dex_pool_materialization::materialize_dex_pool(self.database.as_ref(), &input)
|
||||
.await;
|
||||
let detection_result = match detection_result {
|
||||
Ok(detection_result) => detection_result,
|
||||
Err(error) => return Err(error),
|
||||
};
|
||||
let signal_result = self
|
||||
.record_pool_detection_signals(
|
||||
transaction,
|
||||
"signal.dex.raydium_amm_v4",
|
||||
&detection_result,
|
||||
payload_value,
|
||||
)
|
||||
.await;
|
||||
if let Err(error) = signal_result {
|
||||
return Err(error);
|
||||
}
|
||||
return Ok(detection_result);
|
||||
}
|
||||
|
||||
async fn detect_raydium_clmm_trade(
|
||||
&self,
|
||||
transaction: &crate::ChainTransactionDto,
|
||||
|
||||
@@ -7,6 +7,8 @@
|
||||
pub(crate) enum DexDetectionRoute {
|
||||
/// Raydium AMM v4 initialize2 pool route.
|
||||
RaydiumAmmV4Initialize2Pool,
|
||||
/// Raydium AMM v4 trade route.
|
||||
RaydiumAmmV4Trade,
|
||||
/// Raydium CPMM trade route.
|
||||
RaydiumCpmmTrade,
|
||||
/// Raydium CLMM trade route.
|
||||
@@ -45,6 +47,9 @@ pub(crate) fn dex_detection_route(
|
||||
crate::dex_detection_route::DexDetectionRoute::RaydiumAmmV4Initialize2Pool,
|
||||
);
|
||||
},
|
||||
("raydium_amm_v4", "raydium_amm_v4.swap") => {
|
||||
return Some(crate::dex_detection_route::DexDetectionRoute::RaydiumAmmV4Trade);
|
||||
},
|
||||
("raydium_cpmm", "raydium_cpmm.swap_base_input") => {
|
||||
return Some(crate::dex_detection_route::DexDetectionRoute::RaydiumCpmmTrade);
|
||||
},
|
||||
|
||||
@@ -235,16 +235,16 @@ const DEX_SUPPORT_MATRIX_ENTRIES: &[DexSupportMatrixEntry] = &[
|
||||
program_id: Some(crate::RAYDIUM_AMM_V4_PROGRAM_ID),
|
||||
router_program_id: None,
|
||||
program_id_status: "known",
|
||||
observed: false,
|
||||
observed: true,
|
||||
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"),
|
||||
status: "supported",
|
||||
confidence: "high",
|
||||
skip_reason: None,
|
||||
catalog_enabled: true,
|
||||
},
|
||||
DexSupportMatrixEntry {
|
||||
@@ -949,6 +949,22 @@ mod tests {
|
||||
assert_eq!(raydium_entry.code, "raydium_amm_v4");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn matrix_marks_raydium_amm_v4_supported_after_0_7_41() {
|
||||
let entry = match crate::dex_support_matrix_entry_by_code("raydium_amm_v4") {
|
||||
Some(entry) => entry,
|
||||
None => panic!("expected raydium_amm_v4 matrix entry"),
|
||||
};
|
||||
assert!(entry.observed);
|
||||
assert!(entry.decoded);
|
||||
assert!(entry.materialized);
|
||||
assert!(entry.trade_candidate);
|
||||
assert!(entry.candle_candidate);
|
||||
assert_eq!(entry.status, "supported");
|
||||
assert_eq!(entry.confidence, "high");
|
||||
assert_eq!(entry.skip_reason, None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn matrix_marks_partial_meteora_damm_v1_correctly() {
|
||||
let entry = match crate::dex_support_matrix_entry_by_code("meteora_damm_v1") {
|
||||
|
||||
@@ -923,6 +923,8 @@ pub use dex::RaydiumAmmV4DecodedEvent;
|
||||
pub use dex::RaydiumAmmV4Decoder;
|
||||
/// Decoded Raydium AmmV4 initialize2 pool event.
|
||||
pub use dex::RaydiumAmmV4Initialize2PoolDecoded;
|
||||
/// Decoded Raydium AMM v4 swap event.
|
||||
pub use dex::RaydiumAmmV4SwapDecoded;
|
||||
/// Decoded Raydium CLMM event.
|
||||
pub use dex::RaydiumClmmDecodedEvent;
|
||||
/// Decoded Raydium CLMM swap_v2 instruction.
|
||||
|
||||
@@ -310,6 +310,20 @@ impl LocalPipelineValidationConfig {
|
||||
return config;
|
||||
}
|
||||
|
||||
/// Builds the `0.7.41` Raydium AMM v4 swap decoder validation config.
|
||||
///
|
||||
/// This profile keeps the Raydium surface diagnostics and labels validation
|
||||
/// runs produced after the AMM v4 swap decoder is active. Missing AMM v4
|
||||
/// remains a warning so empty or unrelated local corpora stay inspectable.
|
||||
pub fn v0_7_41_raydium_amm_v4_swap_decoder() -> Self {
|
||||
let mut config = Self::v0_7_40_raydium_effective_surfaces();
|
||||
config.profile_code = "0.7.41_raydium_amm_v4_swap_decoder".to_string();
|
||||
config.expected_dex_codes = vec!["raydium_amm_v4".to_string()];
|
||||
config.require_all_expected_dexes = false;
|
||||
config.allow_unexpected_dexes = true;
|
||||
return config;
|
||||
}
|
||||
|
||||
/// Builds the legacy `0.7.39` launch-surface validation alias.
|
||||
///
|
||||
/// The implementation now delegates to the DEX-first profile so callers that
|
||||
@@ -551,7 +565,8 @@ impl LocalPipelineValidationService {
|
||||
pub async fn validate_v0_7_39_current_database(
|
||||
&self,
|
||||
) -> Result<crate::LocalPipelineValidationRunDto, crate::Error> {
|
||||
let config = crate::LocalPipelineValidationConfig::v0_7_39_dex_first_effective_swap_surfaces();
|
||||
let config =
|
||||
crate::LocalPipelineValidationConfig::v0_7_39_dex_first_effective_swap_surfaces();
|
||||
return self.validate_current_database(&config).await;
|
||||
}
|
||||
|
||||
@@ -562,6 +577,14 @@ impl LocalPipelineValidationService {
|
||||
let config = crate::LocalPipelineValidationConfig::v0_7_40_raydium_effective_surfaces();
|
||||
return self.validate_current_database(&config).await;
|
||||
}
|
||||
|
||||
/// Diagnoses the current database with the `0.7.41` Raydium AMM v4 profile.
|
||||
pub async fn validate_v0_7_41_current_database(
|
||||
&self,
|
||||
) -> Result<crate::LocalPipelineValidationRunDto, crate::Error> {
|
||||
let config = crate::LocalPipelineValidationConfig::v0_7_41_raydium_amm_v4_swap_decoder();
|
||||
return self.validate_current_database(&config).await;
|
||||
}
|
||||
}
|
||||
|
||||
/// Validates a diagnostics summary without performing database access.
|
||||
@@ -687,7 +710,8 @@ pub fn validate_local_pipeline_diagnostics_summary(
|
||||
|| config.profile_code == "0.7.38_token_metadata_gap_prioritization"
|
||||
|| config.profile_code == "0.7.39_dex_first_effective_swap_surfaces"
|
||||
|| config.profile_code == "0.7.39_launch_surface_origin_baseline"
|
||||
|| config.profile_code == "0.7.40_raydium_effective_surfaces";
|
||||
|| config.profile_code == "0.7.40_raydium_effective_surfaces"
|
||||
|| config.profile_code == "0.7.41_raydium_amm_v4_swap_decoder";
|
||||
if config.require_all_expected_dexes || missing_expected_dex_is_warning {
|
||||
for expected_dex_code in &expected_dex_codes {
|
||||
if !observed_dex_codes.contains(expected_dex_code) {
|
||||
@@ -1112,27 +1136,25 @@ mod tests {
|
||||
pair_candle_count: 131,
|
||||
},
|
||||
],
|
||||
raydium_surface_summaries: vec![
|
||||
crate::LocalRaydiumSurfaceDiagnosticSummaryDto {
|
||||
dex_code: "raydium_clmm".to_string(),
|
||||
display_name: "Raydium CLMM".to_string(),
|
||||
surface_role: "dex_effective".to_string(),
|
||||
program_id: Some(crate::RAYDIUM_CLMM_PROGRAM_ID.to_string()),
|
||||
program_id_status: "known".to_string(),
|
||||
status: "supported".to_string(),
|
||||
catalog_enabled: true,
|
||||
instruction_count: 106,
|
||||
transaction_count: 101,
|
||||
decoded_event_count: 106,
|
||||
trade_event_count: 101,
|
||||
pair_candle_count: 131,
|
||||
latest_slot: Some(1),
|
||||
latest_signature: Some("raydium_clmm_fixture".to_string()),
|
||||
observed_in_current_corpus: true,
|
||||
decoded_in_current_corpus: true,
|
||||
trade_materialized_in_current_corpus: true,
|
||||
},
|
||||
],
|
||||
raydium_surface_summaries: vec![crate::LocalRaydiumSurfaceDiagnosticSummaryDto {
|
||||
dex_code: "raydium_clmm".to_string(),
|
||||
display_name: "Raydium CLMM".to_string(),
|
||||
surface_role: "dex_effective".to_string(),
|
||||
program_id: Some(crate::RAYDIUM_CLMM_PROGRAM_ID.to_string()),
|
||||
program_id_status: "known".to_string(),
|
||||
status: "supported".to_string(),
|
||||
catalog_enabled: true,
|
||||
instruction_count: 106,
|
||||
transaction_count: 101,
|
||||
decoded_event_count: 106,
|
||||
trade_event_count: 101,
|
||||
pair_candle_count: 131,
|
||||
latest_slot: Some(1),
|
||||
latest_signature: Some("raydium_clmm_fixture".to_string()),
|
||||
observed_in_current_corpus: true,
|
||||
decoded_in_current_corpus: true,
|
||||
trade_materialized_in_current_corpus: true,
|
||||
}],
|
||||
pair_summaries: vec![],
|
||||
pair_actionability_summaries: vec![
|
||||
crate::LocalPairActionabilityDiagnosticSummaryDto {
|
||||
@@ -1490,7 +1512,8 @@ mod tests {
|
||||
#[test]
|
||||
fn validation_accepts_0_7_39_dex_first_effective_swap_surfaces() {
|
||||
let summary = make_0_7_28_summary_with_meteora();
|
||||
let config = crate::LocalPipelineValidationConfig::v0_7_39_dex_first_effective_swap_surfaces();
|
||||
let config =
|
||||
crate::LocalPipelineValidationConfig::v0_7_39_dex_first_effective_swap_surfaces();
|
||||
let report = crate::validate_local_pipeline_diagnostics_summary(&summary, &config);
|
||||
assert!(report.validation_passed);
|
||||
assert_eq!(report.validation_profile_code, "0.7.39_dex_first_effective_swap_surfaces");
|
||||
@@ -1509,6 +1532,18 @@ mod tests {
|
||||
assert!(report.expected_dex_codes.contains(&"raydium_amm_v4".to_string()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn validation_accepts_0_7_41_raydium_amm_v4_swap_decoder_profile() {
|
||||
let summary = make_0_7_28_summary_with_meteora();
|
||||
let config = crate::LocalPipelineValidationConfig::v0_7_41_raydium_amm_v4_swap_decoder();
|
||||
let report = crate::validate_local_pipeline_diagnostics_summary(&summary, &config);
|
||||
assert!(report.validation_passed);
|
||||
assert_eq!(report.validation_profile_code, "0.7.41_raydium_amm_v4_swap_decoder");
|
||||
assert_eq!(report.blocking_issue_count, 0);
|
||||
assert!(report.warning_count >= 1);
|
||||
assert!(report.expected_dex_codes.contains(&"raydium_amm_v4".to_string()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn validation_rejects_0_7_33_pair_trading_readiness_mismatch() {
|
||||
let mut summary = make_0_7_28_summary_with_meteora();
|
||||
|
||||
@@ -96,7 +96,8 @@ pub(crate) async fn resolve_trade_amounts(
|
||||
return Err(error);
|
||||
}
|
||||
}
|
||||
if (input.decoded_event.event_kind.starts_with("raydium_cpmm.")
|
||||
if (input.decoded_event.event_kind.starts_with("raydium_amm_v4.")
|
||||
|| input.decoded_event.event_kind.starts_with("raydium_cpmm.")
|
||||
|| input.decoded_event.event_kind.starts_with("raydium_clmm."))
|
||||
&& (base_amount_raw.is_none()
|
||||
|| quote_amount_raw.is_none()
|
||||
@@ -114,6 +115,21 @@ pub(crate) async fn resolve_trade_amounts(
|
||||
return Err(error);
|
||||
}
|
||||
}
|
||||
if input.decoded_event.event_kind.starts_with("raydium_amm_v4.")
|
||||
&& (base_amount_raw.is_none() || quote_amount_raw.is_none())
|
||||
{
|
||||
let resolution_result = crate::trade_amount_resolution::apply_vault_balance_delta_fallback(
|
||||
input,
|
||||
input.base_vault_address,
|
||||
input.quote_vault_address,
|
||||
&mut base_amount_raw,
|
||||
&mut quote_amount_raw,
|
||||
&mut price_quote_per_base,
|
||||
);
|
||||
if let Err(error) = resolution_result {
|
||||
return Err(error);
|
||||
}
|
||||
}
|
||||
if input.decoded_event.event_kind.starts_with("raydium_cpmm.")
|
||||
&& (base_amount_raw.is_none() || quote_amount_raw.is_none())
|
||||
{
|
||||
@@ -204,6 +220,17 @@ pub(crate) async fn resolve_trade_amounts(
|
||||
return Err(error);
|
||||
}
|
||||
}
|
||||
if input.decoded_event.event_kind.starts_with("raydium_amm_v4.") {
|
||||
let vault_side = crate::trade_amount_resolution::infer_trade_side_from_vault_balance_deltas(
|
||||
input.transaction.meta_json.as_deref(),
|
||||
input.transaction.transaction_json.as_str(),
|
||||
input.base_vault_address,
|
||||
input.quote_vault_address,
|
||||
);
|
||||
if vault_side.is_some() {
|
||||
resolved_trade_side = vault_side;
|
||||
}
|
||||
}
|
||||
if input.decoded_event.event_kind.starts_with("meteora_dlmm.") {
|
||||
let vault_side = crate::trade_amount_resolution::infer_trade_side_from_vault_balance_deltas(
|
||||
input.transaction.meta_json.as_deref(),
|
||||
|
||||
Reference in New Issue
Block a user