Files
khadhroony-bobobot/docs/prompts/PROMPT_REPRISE_khadhroony-bobobot_0.7.52-raydium-stable.md
2026-06-09 10:13:03 +02:00

627 lines
21 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!-- file: docs/prompts/PROMPT_REPRISE_khadhroony-bobobot_0.7.52-raydium-stable.md -->
# Prompt de reprise — `khadhroony-bobobot` — `0.7.52 raydium_stable_swap`
Reprise du projet `khadhroony-bobobot` après clôture de `0.7.51 raydium_amm_v4`.
## Archive de départ
Utiliser la dernière archive complète du workspace intégrant les deltas validés jusquà :
```text
0.7.51-raydium-amm-v4-final
```
Joindre aussi les docs et SQL de validation à jour :
```text
README.md
ROADMAP.md
CHANGELOG.md
docs/DEX_DECODER_MATRIX.md
docs/DEX_EVENT_COVERAGE_MATRIX.md
docs/DB_EVENT_MODEL_REVIEW.md
docs/reports/RAYDIUM_AMM_V4_EVENT_COVERAGE_REPORT.md
docs/reports/RAYDIUM_POOL_V4_DECISION_NOTE.md
docs/VALIDATION_STATUS_0_7_51_FINAL.md
validation_sql/SQL_VALIDATION_RAYDIUM_AMM_V4_0_7_51.sql
```
## État validé avant reprise
`0.7.51` a clôturé `raydium_amm_v4`.
Validation locale finale rapportée :
```text
cargo test -p kb_lib
405 passed / 0 failed
cargo clippy -p kb_lib --all-targets -- -D warnings
OK
```
Dernier replay local `0.7.51` :
```text
195 replayed
0 decode skipped
195 ledger upserts
70 unsafe ledger rows
168 trades
7 liquidity
15 lifecycle
0 tokenAccount
668 candle upserts
instructionObservations = 2599
resetDeleted = 1578
catalog = 61 tokens / 65 pools / 65 pairs
```
Points de clôture AMM v4 à préserver :
```text
raydium_amm_v4.swap legacy = vide
decoded without coverage entry = vide
instruction_observations > 1 octet = vide
non-swap -> trade = vide
failed tx -> trade = vide
unexplained successful non-materialized events = vide
multi-target materialization = vide
pre_initialize lifecycle audit = 7 / 7
migrate_to_open_book = orderbook only
simulate_info = decoded-only
raydium_pool_v4 = audit-only / pas de decoder autonome
```
## Décision de reprise
Ouvrir une nouvelle tranche :
```text
0.7.52 raydium_stable_swap
```
Code local canonique :
```text
raydium_stable_swap
```
Program id canonique à utiliser comme hypothèse de départ :
```text
5quBtoiQqxF9Jv6KYKctB59NT3gtJD2Y65kdnB1Uev3h
```
Important : upstream Git/IDL/Solscan est un indice, pas une preuve métier. Le program id doit être confirmé par corpus local via `k_sol_instruction_observations`, decoded events, coverage entries et absence de fallback upstream.
## Nouvelle base de travail
Démarrer `0.7.52` sur une base SQLite vide dédiée.
Avant le replay de validation complet, construire volontairement un corpus initial :
```text
1. Demo3 program_id = 5quBtoiQqxF9Jv6KYKctB59NT3gtJD2Y65kdnB1Uev3h
2. Solscan non filtré + essais instruction=<DISCRIMINATOR>
3. backfill Demo2 de signatures contenant des instructions stable swap variées
4. backfill de pools stable swap quand Demo3/Solscan fournit un AMM/pool account fiable
```
Ne pas interpréter labsence de résultat Solscan comme absence on-chain définitive.
## Note Solscan importante
Pour `raydium_stable_swap`, il semble que Solscan ne dispose pas dun Program IDL exploitable sur :
```text
https://solscan.io/account/5quBtoiQqxF9Jv6KYKctB59NT3gtJD2Y65kdnB1Uev3h#programIdl
```
Donc le filtrage Solscan par instruction peut ne pas fonctionner avec les discriminants 8 octets Carbon/Pinax.
Il faut tester deux approches :
```text
1. Liens exploratoires courts : instruction=00, 01, 02, ...
2. Liens discriminants upstream 8 octets : instruction=<DISCRIMINATOR_HEX>
```
Solscan est une aide de découverte uniquement. La preuve métier reste locale : signatures backfillées, decoded events, instruction observations et coverage DB.
## Sources Git/IDL à utiliser systématiquement
Sources globales :
```text
https://github.com/sevenlabs-hq/carbon/tree/main/decoders
https://github.com/0xfnzero/solana-streamer
https://github.com/0xfnzero/sol-parser-sdk/tree/main/idl
https://github.com/0xfnzero/sol-parser-sdk/tree/main/idls
https://github.com/pinax-network/substreams-solana-idls/tree/main/src
https://github.com/hodlwarden/solana-tx-parser/tree/main/src
https://docs.vybenetwork.com/docs/available-dexs-amms
```
Sources spécifiques `raydium_stable_swap` à vérifier en priorité :
```text
https://github.com/sevenlabs-hq/carbon/tree/main/decoders/raydium-stable-swap-decoder
https://github.com/pinax-network/substreams-solana-idls/tree/main/src/raydium/stable
https://github.com/pinax-network/substreams-solana-idls/tree/main/src/raydium/stable/idl.json
https://github.com/pinax-network/substreams-solana-idls/tree/main/src/raydium/stable/instructions.rs
https://github.com/pinax-network/substreams-solana-idls/tree/main/src/raydium/stable/events.rs
https://github.com/pinax-network/substreams-solana-idls/tree/main/src/raydium/stable/accounts.rs
```
## Solscan — liens exploratoires
Base non filtrée :
```text
https://solscan.io/account/5quBtoiQqxF9Jv6KYKctB59NT3gtJD2Y65kdnB1Uev3h?hide_spam=false&hide_failed=false&show_related=true&sort=desc
```
Base non filtrée sans related :
```text
https://solscan.io/account/5quBtoiQqxF9Jv6KYKctB59NT3gtJD2Y65kdnB1Uev3h?hide_spam=false&hide_failed=false&show_related=false&sort=desc
```
### Essais courts `instruction=00..11`
Ces liens sont exploratoires. Ils ne prouvent pas que le program utilise des discriminants 1 octet ; ils servent seulement à tester le comportement Solscan quand aucun IDL nest présent.
```text
instruction=00
https://solscan.io/account/5quBtoiQqxF9Jv6KYKctB59NT3gtJD2Y65kdnB1Uev3h?instruction=00&hide_spam=false&hide_failed=false&show_related=false&sort=desc
instruction=01
https://solscan.io/account/5quBtoiQqxF9Jv6KYKctB59NT3gtJD2Y65kdnB1Uev3h?instruction=01&hide_spam=false&hide_failed=false&show_related=false&sort=desc
instruction=02
https://solscan.io/account/5quBtoiQqxF9Jv6KYKctB59NT3gtJD2Y65kdnB1Uev3h?instruction=02&hide_spam=false&hide_failed=false&show_related=false&sort=desc
instruction=03
https://solscan.io/account/5quBtoiQqxF9Jv6KYKctB59NT3gtJD2Y65kdnB1Uev3h?instruction=03&hide_spam=false&hide_failed=false&show_related=false&sort=desc
instruction=04
https://solscan.io/account/5quBtoiQqxF9Jv6KYKctB59NT3gtJD2Y65kdnB1Uev3h?instruction=04&hide_spam=false&hide_failed=false&show_related=false&sort=desc
instruction=05
https://solscan.io/account/5quBtoiQqxF9Jv6KYKctB59NT3gtJD2Y65kdnB1Uev3h?instruction=05&hide_spam=false&hide_failed=false&show_related=false&sort=desc
instruction=06
https://solscan.io/account/5quBtoiQqxF9Jv6KYKctB59NT3gtJD2Y65kdnB1Uev3h?instruction=06&hide_spam=false&hide_failed=false&show_related=false&sort=desc
instruction=07
https://solscan.io/account/5quBtoiQqxF9Jv6KYKctB59NT3gtJD2Y65kdnB1Uev3h?instruction=07&hide_spam=false&hide_failed=false&show_related=false&sort=desc
instruction=08
https://solscan.io/account/5quBtoiQqxF9Jv6KYKctB59NT3gtJD2Y65kdnB1Uev3h?instruction=08&hide_spam=false&hide_failed=false&show_related=false&sort=desc
instruction=09
https://solscan.io/account/5quBtoiQqxF9Jv6KYKctB59NT3gtJD2Y65kdnB1Uev3h?instruction=09&hide_spam=false&hide_failed=false&show_related=false&sort=desc
instruction=0a
https://solscan.io/account/5quBtoiQqxF9Jv6KYKctB59NT3gtJD2Y65kdnB1Uev3h?instruction=0a&hide_spam=false&hide_failed=false&show_related=false&sort=desc
instruction=0b
https://solscan.io/account/5quBtoiQqxF9Jv6KYKctB59NT3gtJD2Y65kdnB1Uev3h?instruction=0b&hide_spam=false&hide_failed=false&show_related=false&sort=desc
instruction=0c
https://solscan.io/account/5quBtoiQqxF9Jv6KYKctB59NT3gtJD2Y65kdnB1Uev3h?instruction=0c&hide_spam=false&hide_failed=false&show_related=false&sort=desc
instruction=0d
https://solscan.io/account/5quBtoiQqxF9Jv6KYKctB59NT3gtJD2Y65kdnB1Uev3h?instruction=0d&hide_spam=false&hide_failed=false&show_related=false&sort=desc
instruction=0e
https://solscan.io/account/5quBtoiQqxF9Jv6KYKctB59NT3gtJD2Y65kdnB1Uev3h?instruction=0e&hide_spam=false&hide_failed=false&show_related=false&sort=desc
instruction=0f
https://solscan.io/account/5quBtoiQqxF9Jv6KYKctB59NT3gtJD2Y65kdnB1Uev3h?instruction=0f&hide_spam=false&hide_failed=false&show_related=false&sort=desc
instruction=10
https://solscan.io/account/5quBtoiQqxF9Jv6KYKctB59NT3gtJD2Y65kdnB1Uev3h?instruction=10&hide_spam=false&hide_failed=false&show_related=false&sort=desc
instruction=11
https://solscan.io/account/5quBtoiQqxF9Jv6KYKctB59NT3gtJD2Y65kdnB1Uev3h?instruction=11&hide_spam=false&hide_failed=false&show_related=false&sort=desc
```
### Essais discriminants 8 octets Carbon/Pinax
À tester aussi, mais ne pas bloquer si Solscan ne filtre rien.
```text
initialize / afaf6d1f0d989bed
https://solscan.io/account/5quBtoiQqxF9Jv6KYKctB59NT3gtJD2Y65kdnB1Uev3h?instruction=afaf6d1f0d989bed&hide_spam=false&hide_failed=false&show_related=false&sort=desc
pre_initialize / ff5c572dc6acec02
https://solscan.io/account/5quBtoiQqxF9Jv6KYKctB59NT3gtJD2Y65kdnB1Uev3h?instruction=ff5c572dc6acec02&hide_spam=false&hide_failed=false&show_related=false&sort=desc
deposit / f223c68952e1f2b6
https://solscan.io/account/5quBtoiQqxF9Jv6KYKctB59NT3gtJD2Y65kdnB1Uev3h?instruction=f223c68952e1f2b6&hide_spam=false&hide_failed=false&show_related=false&sort=desc
withdraw / b712469c946da122
https://solscan.io/account/5quBtoiQqxF9Jv6KYKctB59NT3gtJD2Y65kdnB1Uev3h?instruction=b712469c946da122&hide_spam=false&hide_failed=false&show_related=false&sort=desc
swap_base_in / 2aec48a2f2182754
https://solscan.io/account/5quBtoiQqxF9Jv6KYKctB59NT3gtJD2Y65kdnB1Uev3h?instruction=2aec48a2f2182754&hide_spam=false&hide_failed=false&show_related=false&sort=desc
swap_base_out / a3d29bd0af92d596
https://solscan.io/account/5quBtoiQqxF9Jv6KYKctB59NT3gtJD2Y65kdnB1Uev3h?instruction=a3d29bd0af92d596&hide_spam=false&hide_failed=false&show_related=false&sort=desc
```
## Instructions/discriminants de départ à couvrir
À partir de Carbon stable swap, à vérifier contre Pinax :
```text
initialize afaf6d1f0d989bed pool_create / k_sol_pool_lifecycle_events
pre_initialize ff5c572dc6acec02 pool_create deprecated/partial / k_sol_pool_lifecycle_events si pool context suffisant
deposit f223c68952e1f2b6 liquidity_add / k_sol_liquidity_events
withdraw b712469c946da122 liquidity_remove / k_sol_liquidity_events
swap_base_in 2aec48a2f2182754 swap / k_sol_trade_events
swap_base_out a3d29bd0af92d596 swap / k_sol_trade_events
```
Si Pinax expose des discriminants numériques ou une ABI non Anchor, ne pas forcer les discriminants 8 octets. Le decoder local doit suivre le layout prouvé par corpus local.
## Objectif `0.7.52` — `raydium_stable_swap`
Reprendre Raydium Stable Swap au même niveau de couverture que CPMM/CLMM/AMM v4 :
```text
initialize / pre_initialize
pool lifecycle / pool_create
deposit / withdraw
swap_base_in / swap_base_out
fees / admin/config si présents dans IDL/events/accounts
OpenBook/Serum side effects documentés si présents
side effects SPL Token / Token-2022 documentés mais non promus comme raydium_stable_swap.* directs
fallback instruction_audit nettoyé quand une entrée locale spécialisée couvre linstruction
coverage entries synchronisées et rafraîchies
decoded-only explicitement expliqué quand la matérialisation métier est impossible
```
## Règles fixes
```text
Rust 2024
pas de mod.rs
fichiers Rust avec // file: ...
pas de anyhow
pas de thiserror
pas de ? / unwrap / expect dans kb_lib applicatif
match / if let Err / let Err = ... else
rustdoc sur API publique
re-exports db.rs puis lib.rs si DB modifiée
```
## Invariants métier
```text
non-trade event = jamais trade/candle
failed transaction = audit-only / jamais matérialisée métier
upstream Git/IDL/Solscan = indice, pas preuve métier
program id upstream non promu sans corpus local
side effects SPL Token / Token-2022 restent transversaux sauf preuve multi-DEX et décision DB
instruction_audit et upstream_git.instruction_match doivent être nettoyés quand une entrée locale spécialisée couvre le discriminant
observed_count ne doit pas obligatoirement égaler materialized_count
règle de clôture : observed_count = materialized_count + decoded_only_explained_count + failed_count
```
## Workflow conseillé
1. Créer une nouvelle base SQLite dédiée `0.7.52`.
2. Inventorier Carbon + Pinax pour `raydium_stable_swap`.
3. Vérifier explicitement le program id `5quBtoiQqxF9Jv6KYKctB59NT3gtJD2Y65kdnB1Uev3h`.
4. Vérifier si les discriminants sont 8 octets Anchor-like, 1 octet, ou autre layout.
5. Synchroniser `k_sol_dex_event_coverage_entries` avec `decoder_code = raydium_stable_swap`.
6. Utiliser Solscan seulement comme aide exploratoire ; si le filtre instruction échoue, utiliser Demo3 program_id + signatures récentes/non filtrées.
7. Backfill Demo2 signature/pool sur corpus varié.
8. Replay local avec :
```text
skipDexDecode = no
forceDexDecode = yes
deferInstructionObservations = yes
```
9. Vérifier :
```text
coverage listed/observed/materialized
residual instruction_audit
residual upstream_git.instruction_match
decoded without coverage entry
failed tx materialization = 0
non-trade trade_count = 0
single-target materialization
trade/candle only for swap events validés
decoded-only explanations
```
## SQL de contrôle minimal `0.7.52`
Coverage stable swap :
```sql
SELECT
entry_name,
entry_kind,
event_family,
expected_db_target,
proof_status,
local_event_kind,
discriminator_hex,
observed_count,
materialized_count,
trade_count
FROM k_sol_dex_event_coverage_entries
WHERE decoder_code = 'raydium_stable_swap'
ORDER BY entry_kind, entry_name, discriminator_hex;
```
Instruction observations :
```sql
SELECT
instruction_name,
discriminator_hex,
COUNT(*) AS observed_count,
COUNT(DISTINCT signature) AS tx_count
FROM k_sol_instruction_observations
WHERE decoder_code = 'raydium_stable_swap'
GROUP BY instruction_name, discriminator_hex
ORDER BY observed_count DESC, instruction_name, discriminator_hex;
```
Residual audit :
```sql
SELECT
json_extract(payload_json, '$.discriminatorHex') AS discriminator_hex,
COUNT(*) AS audit_count,
COUNT(DISTINCT transaction_id) AS tx_count
FROM k_sol_dex_decoded_events
WHERE protocol_name = 'raydium_stable_swap'
AND event_kind = 'raydium_stable_swap.instruction_audit'
GROUP BY discriminator_hex
ORDER BY audit_count DESC, discriminator_hex;
```
Fallback upstream :
```sql
SELECT
json_extract(ug.payload_json, '$.upstreamDecoderCode') AS upstream_decoder_code,
json_extract(ug.payload_json, '$.upstreamEntryName') AS entry_name,
json_extract(ug.payload_json, '$.upstreamDiscriminatorHex') AS discriminator_hex,
json_extract(ug.payload_json, '$.upstreamSourceRepo') AS source_repo,
COUNT(*) AS fallback_count,
COUNT(DISTINCT ug.transaction_id) AS tx_count
FROM k_sol_dex_decoded_events ug
JOIN k_sol_dex_event_coverage_entries ce
ON ce.decoder_code = json_extract(ug.payload_json, '$.upstreamDecoderCode')
AND ce.entry_name = json_extract(ug.payload_json, '$.upstreamEntryName')
AND ce.discriminator_hex = json_extract(ug.payload_json, '$.upstreamDiscriminatorHex')
AND ce.local_event_kind IS NOT NULL
AND ce.local_event_kind <> ''
WHERE ug.protocol_name = 'upstream_git'
AND ug.event_kind = 'upstream_git.instruction_match'
AND json_extract(ug.payload_json, '$.upstreamDecoderCode') = 'raydium_stable_swap'
GROUP BY upstream_decoder_code, entry_name, discriminator_hex, source_repo
ORDER BY fallback_count DESC, entry_name;
```
Non-swap safety :
```sql
SELECT
de.event_kind,
ce.event_family,
COUNT(*) AS decoded_count,
COUNT(te.id) AS trade_count
FROM k_sol_dex_decoded_events de
LEFT JOIN k_sol_dex_event_coverage_entries ce
ON ce.decoder_code = 'raydium_stable_swap'
AND ce.local_event_kind = de.event_kind
LEFT JOIN k_sol_trade_events te
ON te.decoded_event_id = de.id
WHERE de.protocol_name = 'raydium_stable_swap'
GROUP BY de.event_kind, ce.event_family
HAVING ce.event_family <> 'swap'
AND COUNT(te.id) > 0
ORDER BY trade_count DESC, de.event_kind;
```
Failed tx safety :
```sql
SELECT
de.event_kind,
COUNT(*) AS decoded_failed_count,
COUNT(te.id) AS trade_count
FROM k_sol_dex_decoded_events de
JOIN k_sol_chain_transactions tx
ON tx.id = de.transaction_id
LEFT JOIN k_sol_trade_events te
ON te.decoded_event_id = de.id
WHERE de.protocol_name = 'raydium_stable_swap'
AND tx.err_json IS NOT NULL
AND tx.err_json <> ''
AND tx.err_json <> 'null'
GROUP BY de.event_kind
HAVING COUNT(te.id) > 0
ORDER BY trade_count DESC, de.event_kind;
```
Decoded without coverage :
```sql
SELECT
de.event_kind,
COUNT(*) AS decoded_count
FROM k_sol_dex_decoded_events de
LEFT JOIN k_sol_dex_event_coverage_entries ce
ON ce.decoder_code = 'raydium_stable_swap'
AND ce.local_event_kind = de.event_kind
WHERE de.protocol_name = 'raydium_stable_swap'
AND ce.id IS NULL
GROUP BY de.event_kind
ORDER BY decoded_count DESC, de.event_kind;
```
Multi-target materialization :
```sql
SELECT
de.event_kind,
COUNT(DISTINCT de.id) AS decoded_count,
COUNT(DISTINCT te.id) AS trade_count,
COUNT(DISTINCT le.id) AS liquidity_count,
COUNT(DISTINCT pe.id) AS lifecycle_count,
COUNT(DISTINCT fe.id) AS fee_count,
COUNT(DISTINCT ae.id) AS admin_count,
COUNT(DISTINCT oe.id) AS orderbook_count,
(
CASE WHEN COUNT(DISTINCT te.id) > 0 THEN 1 ELSE 0 END
+ CASE WHEN COUNT(DISTINCT le.id) > 0 THEN 1 ELSE 0 END
+ CASE WHEN COUNT(DISTINCT pe.id) > 0 THEN 1 ELSE 0 END
+ CASE WHEN COUNT(DISTINCT fe.id) > 0 THEN 1 ELSE 0 END
+ CASE WHEN COUNT(DISTINCT ae.id) > 0 THEN 1 ELSE 0 END
+ CASE WHEN COUNT(DISTINCT oe.id) > 0 THEN 1 ELSE 0 END
) AS materialized_target_count
FROM k_sol_dex_decoded_events de
LEFT JOIN k_sol_trade_events te
ON te.decoded_event_id = de.id
LEFT JOIN k_sol_liquidity_events le
ON le.decoded_event_id = de.id
LEFT JOIN k_sol_pool_lifecycle_events pe
ON pe.decoded_event_id = de.id
LEFT JOIN k_sol_fee_events fe
ON fe.decoded_event_id = de.id
LEFT JOIN k_sol_pool_admin_events ae
ON ae.decoded_event_id = de.id
LEFT JOIN k_sol_orderbook_events oe
ON oe.decoded_event_id = de.id
WHERE de.protocol_name = 'raydium_stable_swap'
GROUP BY de.event_kind
HAVING materialized_target_count > 1
ORDER BY materialized_target_count DESC, de.event_kind;
```
Unexplained successful non-materialized events :
```sql
SELECT
de.event_kind,
COUNT(*) AS unexplained_count
FROM k_sol_dex_decoded_events de
JOIN k_sol_chain_transactions tx
ON tx.id = de.transaction_id
LEFT JOIN k_sol_trade_events te
ON te.decoded_event_id = de.id
LEFT JOIN k_sol_liquidity_events le
ON le.decoded_event_id = de.id
LEFT JOIN k_sol_pool_lifecycle_events pe
ON pe.decoded_event_id = de.id
LEFT JOIN k_sol_fee_events fe
ON fe.decoded_event_id = de.id
LEFT JOIN k_sol_pool_admin_events ae
ON ae.decoded_event_id = de.id
LEFT JOIN k_sol_orderbook_events oe
ON oe.decoded_event_id = de.id
LEFT JOIN k_sol_token_account_events tae
ON tae.decoded_event_id = de.id
WHERE de.protocol_name = 'raydium_stable_swap'
AND (
tx.err_json IS NULL
OR tx.err_json = ''
OR tx.err_json = 'null'
)
AND te.id IS NULL
AND le.id IS NULL
AND pe.id IS NULL
AND fe.id IS NULL
AND ae.id IS NULL
AND oe.id IS NULL
AND tae.id IS NULL
AND COALESCE(TRIM(json_extract(de.payload_json, '$.skipTradeReason')), '') = ''
AND COALESCE(TRIM(json_extract(de.payload_json, '$.skipLiquidityReason')), '') = ''
AND COALESCE(TRIM(json_extract(de.payload_json, '$.skipLifecycleReason')), '') = ''
AND COALESCE(TRIM(json_extract(de.payload_json, '$.skipCatalogReason')), '') = ''
GROUP BY de.event_kind
ORDER BY unexplained_count DESC, de.event_kind;
```
Materialization summary :
```sql
SELECT
de.event_kind,
COUNT(DISTINCT de.id) AS decoded_count,
COUNT(DISTINCT te.id) AS trade_count,
COUNT(DISTINCT le.id) AS liquidity_count,
COUNT(DISTINCT pe.id) AS lifecycle_count,
COUNT(DISTINCT fe.id) AS fee_count,
COUNT(DISTINCT ae.id) AS admin_count,
COUNT(DISTINCT oe.id) AS orderbook_count
FROM k_sol_dex_decoded_events de
LEFT JOIN k_sol_trade_events te
ON te.decoded_event_id = de.id
LEFT JOIN k_sol_liquidity_events le
ON le.decoded_event_id = de.id
LEFT JOIN k_sol_pool_lifecycle_events pe
ON pe.decoded_event_id = de.id
LEFT JOIN k_sol_fee_events fe
ON fe.decoded_event_id = de.id
LEFT JOIN k_sol_pool_admin_events ae
ON ae.decoded_event_id = de.id
LEFT JOIN k_sol_orderbook_events oe
ON oe.decoded_event_id = de.id
WHERE de.protocol_name = 'raydium_stable_swap'
GROUP BY de.event_kind
ORDER BY de.event_kind;
```
## Livrables attendus
```text
archive delta fichiers modifiés/ajoutés
README.md / ROADMAP.md / CHANGELOG.md mis à jour
docs/DEX_DECODER_MATRIX.md
docs/DEX_EVENT_COVERAGE_MATRIX.md
docs/DB_EVENT_MODEL_REVIEW.md
docs/reports/RAYDIUM_STABLE_SWAP_EVENT_COVERAGE_REPORT.md
validation_sql/SQL_VALIDATION_RAYDIUM_STABLE_SWAP_0_7_52.sql
```
Validation finale locale :
```bash
cargo fmt
cargo test -p kb_lib
cargo clippy -p kb_lib --all-targets -- -D warnings
```
## Critères de clôture `0.7.52`
```text
tous les discriminants stable swap connus sont listés en coverage
tous les discriminants stable swap connus sont observés localement ou explicitement marqués mapped_unverified
instruction_audit résiduel vide pour les discriminants couverts
fallback upstream_git.instruction_match résiduel vide pour les discriminants couverts
decoded without coverage vide
non-swap -> trade vide
failed tx -> trade vide
multi-target materialization vide
successful decoded-only events expliqués par skip*Reason
trade/candle uniquement pour swaps avec montants fiables
deposit/withdraw uniquement vers liquidity
initialize/pre_initialize uniquement vers lifecycle
simulate/transport éventuel reste decoded-only sauf preuve métier
```