This commit is contained in:
2026-06-14 14:25:09 +02:00
parent 38f42da970
commit 3b908b318e
100 changed files with 5873 additions and 225 deletions

View File

@@ -84,4 +84,5 @@
0.7.50 - Raydium Launchpad event coverage bootstrap : normalisation locale canonique vers `raydium_launchpad`, ajout de `RAYDIUM_LAUNCHPAD_PROGRAM_ID`, synchronisation des entrées Carbon Launchpad dans le registre upstream, fallback audit/mapped decoder pour discriminants Launchpad, enrichissement audit Anchor self-CPI, maintien conservatoire en `decoded_events_only`, rapport Launchpad et SQL de validation dédiés. 0.7.50 - Raydium Launchpad event coverage bootstrap : normalisation locale canonique vers `raydium_launchpad`, ajout de `RAYDIUM_LAUNCHPAD_PROGRAM_ID`, synchronisation des entrées Carbon Launchpad dans le registre upstream, fallback audit/mapped decoder pour discriminants Launchpad, enrichissement audit Anchor self-CPI, maintien conservatoire en `decoded_events_only`, rapport Launchpad et SQL de validation dédiés.
0.7.50-pre-r2 - Clôture CPMM/CLMM post-Launchpad : ajout des entrées Carbon `cpi_event` pour `raydium_cpmm` et `raydium_clmm`, ajout de `raydium_clmm.update_dynamic_fee_config`, normalisation des Program-data events CLMM, ajout de la table `k_sol_token_account_events` et de la matérialisation `create_support_mint_associated`, reclassement des familles ambiguës (`cpi_transport`, `liquidity_calculation`, `liquidity_change`, `position_open`, `pool_create`, `admin_config`, `account_create`, `idl_management`), codage du discriminant CPMM `40f4bc78a7e9690a` comme `raydium_cpmm.anchor_idl_instruction` decoded-only après inspection Solscan, et contexte de secours pour matérialisation liquidity CLMM via événements frères quand possible. 0.7.50-pre-r2 - Clôture CPMM/CLMM post-Launchpad : ajout des entrées Carbon `cpi_event` pour `raydium_cpmm` et `raydium_clmm`, ajout de `raydium_clmm.update_dynamic_fee_config`, normalisation des Program-data events CLMM, ajout de la table `k_sol_token_account_events` et de la matérialisation `create_support_mint_associated`, reclassement des familles ambiguës (`cpi_transport`, `liquidity_calculation`, `liquidity_change`, `position_open`, `pool_create`, `admin_config`, `account_create`, `idl_management`), codage du discriminant CPMM `40f4bc78a7e9690a` comme `raydium_cpmm.anchor_idl_instruction` decoded-only après inspection Solscan, et contexte de secours pour matérialisation liquidity CLMM via événements frères quand possible.
0.7.51 - Raydium AMM v4 event coverage clôturé : decoder maximal local pour tous les discriminants officiels AMM v4 `00..11`, spécialisation des swaps `swap_base_in/out` et `swap_base_in/out_v2`, suppression durable du `raydium_amm_v4.swap` legacy, index AMM v4 en discriminant 1 octet, matérialisation validée des swaps, liquidity, lifecycle, fees, admin/config et side effects orderbook, `pre_initialize` conservé comme lifecycle audit deprecated/partial, `simulate_info` decoded-only, reset replay renforcé par `protocol_name`, validation des invariants failed/non-swap/single-target/unexplained gaps et maintien de `raydium_pool_v4` en audit conditionnel sans decoder autonome. 0.7.51 - Raydium AMM v4 event coverage clôturé : decoder maximal local pour tous les discriminants officiels AMM v4 `00..11`, spécialisation des swaps `swap_base_in/out` et `swap_base_in/out_v2`, suppression durable du `raydium_amm_v4.swap` legacy, index AMM v4 en discriminant 1 octet, matérialisation validée des swaps, liquidity, lifecycle, fees, admin/config et side effects orderbook, `pre_initialize` conservé comme lifecycle audit deprecated/partial, `simulate_info` decoded-only, reset replay renforcé par `protocol_name`, validation des invariants failed/non-swap/single-target/unexplained gaps et maintien de `raydium_pool_v4` en audit conditionnel sans decoder autonome.
0.7.52 - Raydium Stable Swap event coverage clôturé : decoder legacy 1 octet pour la surface locale `00..0d`, matérialisation lifecycle/liquidity/admin/fee/orderbook selon contexte, swaps `swap_base_in/out` matérialisés uniquement depuis deltas de vaults exacts (`stable_swap_vault_balance_delta`), conservation des bornes dinstruction comme audit-only, failed transactions decoded-only avec skip reasons, validation locale 407 tests et clippy `-D warnings` OK. 0.7.52 - Raydium Stable Swap event coverage clôturé : decoder legacy 1 octet pour la surface locale `00..0d`, matérialisation lifecycle/liquidity/admin/fee/orderbook selon contexte, swaps `swap_base_in/out` matérialisés uniquement depuis deltas de vaults exacts (`stable_swap_vault_balance_delta`), conservation des bornes dinstruction comme audit-only, failed transactions decoded-only avec skip reasons, validation locale 407 tests et clippy `-D warnings` OK.
0.7.53 - Clôture PumpSwap : décodage transaction/log complet, matérialisation `buy/sell/buy_exact_quote_in` depuis sources exactes, events Anchor audit-only, tests synthétiques IDL, validation globale coverage SQL et non-régression Raydium.

View File

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

View File

@@ -2,6 +2,65 @@
# khadhroony-bobobot # khadhroony-bobobot
## État final validé `0.7.53` — `pump_swap`
La tranche `0.7.53 pump_swap` est clôturée côté décodage transaction/log et matérialisation métier. Elle ferme le program id unique `pAMMBay6oceH9fJKBRHGP5D4bD4sWpmSwMn52FMfXEA` sans rouvrir Raydium.
Points verrouillés :
- `pump_swap.buy` et `pump_swap.sell` restent matérialisés uniquement depuis montants exacts transaction/vault/balance-delta ;
- `pump_swap.buy_exact_quote_in` est matérialisable seulement lorsqu'un `BuyEvent` Anchor exact est présent (`amountSource=pump_swap_anchor_buy_event`) ; les cas `instruction_bounds_only` restent decoded-only avec raison explicite ;
- les events Anchor `*_event` sont décodés comme events autonomes audit-only, sauf exception explicitement matérialisable (`claim_token_incentives_event` si un corpus réussi apparaît) ;
- les transactions failed restent traçables mais non actionnables ;
- les non-trades PumpSwap alimentent uniquement les tables métier prévues (`liquidity`, `lifecycle`, `fee`, `reward`, `admin`) et ne créent jamais de trade/candle ;
- les tests synthétiques verrouillent les instructions/events IDL non observés dans le corpus local ;
- la surveillance globale distingue maintenant les vrais gaps locaux, le backlog `upstream_git` et les observations non attribuées.
Validation locale finale rapportée :
```text
cargo test -p kb_lib -> 421 passed / 0 failed
cargo clippy -p kb_lib --all-targets -- -D warnings -> OK
```
Dernier replay élargi rapporté après backfill pool :
```text
1189 replayed
0 decode skipped
1189 ledger upserts
967 unsafe ledger rows
928 trades
13 liquidity
8 lifecycle
0 tokenAccount
3700 candle upserts
instructionObservations = 25724
resetDeleted = 5622
catalog = 76 tokens / 80 pools / 80 pairs
```
Checks de fermeture :
- `pump_swap` decoded without coverage : vide ;
- fallback `upstream_git` PumpSwap couvert localement : vide ;
- successful trade candidates PumpSwap sans trade et sans raison explicite : vide ;
- failed transaction avec business trade : vide ;
- non-swap matérialisé en trade : vide ;
- multi-target materialization : vide ;
- Raydium AMM v4 / CLMM / CPMM targeted observation gaps : vide après normalisation.
Livrables `0.7.53` :
- `docs/reports/PUMP_SWAP_EVENT_COVERAGE_REPORT.md` ;
- `docs/reports/DEX_COVERAGE_GLOBAL_WATCHLIST_0_7_53.md` ;
- `validation_sql/SQL_VALIDATION_PUMP_SWAP_0_7_53.sql` ;
- `validation_sql/SQL_VALIDATION_DEX_COVERAGE_GLOBAL_0_7_53.sql` ;
- `idls/` comme corpus local d'IDL Solscan à comparer aux sources Git.
La suite immédiate est `pump_fees` / `pump_fun` selon priorité de backlog observé. Les petits gaps Meteora sont volontairement reportés aux tranches Meteora futures.
## État final validé `0.7.51` — `raydium_amm_v4` ## État final validé `0.7.51` — `raydium_amm_v4`
La tranche `0.7.51 raydium_amm_v4` est clôturable côté `kb_lib` après validation locale du decoder maximal AMM v4. La tranche `0.7.51 raydium_amm_v4` est clôturable côté `kb_lib` après validation locale du decoder maximal AMM v4.
@@ -118,9 +177,10 @@ Sources prioritaires :
| Source | Usage attendu | | Source | Usage attendu |
|---|---| |---|---|
| `idls/` | Corpus local dIDL téléchargés depuis Solscan ; source de savoir locale à comparer aux sources Git avant promotion métier. |
| `https://github.com/sevenlabs-hq/carbon/tree/main/decoders` | Source principale de discriminants, instructions et events multi-protocoles. | | `https://github.com/sevenlabs-hq/carbon/tree/main/decoders` | Source principale de discriminants, instructions et events multi-protocoles. |
| `https://github.com/0xfnzero/solana-streamer` | Source complémentaire pour PumpFun, PumpSwap, Bonk et Raydium CPMM. | | `https://github.com/0xfnzero/solana-streamer` | Source complémentaire pour PumpFun, PumpSwap, Bonk et Raydium CPMM. |
| `https://github.com/0xfnzero/sol-parser-sdk/tree/main/idl` | IDL complémentaires pour programmes Solana supportés par parser SDK. | | `https://github.com/0xfnzero/sol-parser-sdk/tree/main/idls` | IDL complémentaires pour programmes Solana supportés par parser SDK. |
| `https://github.com/pinax-network/substreams-solana-idls/tree/main/src` | IDL et layouts additionnels à comparer au registre local. | | `https://github.com/pinax-network/substreams-solana-idls/tree/main/src` | IDL et layouts additionnels à comparer au registre local. |
| `https://github.com/hodlwarden/solana-tx-parser/tree/main/src` | Décodage transactionnel complémentaire et conventions de mapping. | | `https://github.com/hodlwarden/solana-tx-parser/tree/main/src` | Décodage transactionnel complémentaire et conventions de mapping. |
| `https://github.com/openbook-dex/openbook-v2` | Source officielle OpenBook v2 : programme, IDL et logs. | | `https://github.com/openbook-dex/openbook-v2` | Source officielle OpenBook v2 : programme, IDL et logs. |
@@ -427,17 +487,14 @@ Si une requête DB est ajoutée ou modifiée, mettre à jour les re-exports dans
## 8. Priorité immédiate ## 8. Priorité immédiate
La priorité immédiate après le point de reprise `0.7.43-E5C` est : La priorité immédiate après la clôture `0.7.53` est la suivante :
1. `0.7.48` : `raydium_cpmm` — clôturé côté event coverage ; 1. `0.7.53` est clos pour `pump_swap` : ne rouvrir que pour correction de bug, pas pour ajout fonctionnel IDL déjà couvert ;
2. `0.7.49` : `raydium_clmm` — clôturé côté instructions observées, matérialisation non-trade prouvée et nettoyage fallback ; 2. maintenir les checks globaux de surveillance dans `validation_sql/SQL_VALIDATION_DEX_COVERAGE_GLOBAL_0_7_53.sql` après chaque gros backfill ;
3. `0.7.50-pre-r2` : `raydium_launchpad` clos + re-vérification `raydium_cpmm` / `raydium_clmm` ; 3. traiter ensuite le backlog observé, en priorité `pump_fees`, puis `pump_fun`, puis `jupiter_swap` si lobjectif devient lanalyse des routes/agrégateurs ;
4. `0.7.51` : `raydium_amm_v4` ; 4. reporter volontairement les corrections Meteora restantes (`meteora_dlmm.swap`, `meteora_damm_v2.swap`, `meteora_damm_v2.instruction_audit`) aux tranches Meteora dédiées ;
5. `0.7.52` : `raydium_stable_swap` — clôturé ; 5. ne pas rouvrir `raydium_amm_v4`, `raydium_clmm` ou `raydium_cpmm` tant que les requêtes Raydium normalisées restent vides ;
6. `0.7.53` : `raydium_pool_v4` audit / program-id decision seulement si program id distinct et corpus exploitable ; 6. garder `raydium_launchpad` et `raydium_stable_swap` en surveillance : les entrées non observées restent `upstream_git_mapped_unverified`, pas des régressions.
7. `0.7.54` : `pump_swap` ;
8. `0.7.55` : `pump_fun` ;
9. `0.7.56+` : Meteora, Phoenix/OpenBook, Orca puis validation progressive des autres DEX/surfaces issus du registre upstream Git.
Garde-fous constants : Garde-fous constants :
@@ -547,8 +604,12 @@ La suite fonctionnelle reprend par Raydium avant Meteora :
3. `0.7.50-pre-r2``raydium_launchpad` + clôture CPMM/CLMM ; 3. `0.7.50-pre-r2``raydium_launchpad` + clôture CPMM/CLMM ;
4. `0.7.51``raydium_amm_v4` ; 4. `0.7.51``raydium_amm_v4` ;
5. `0.7.52``raydium_stable_swap` — clôturé ; 5. `0.7.52``raydium_stable_swap` — clôturé ;
6. `0.7.53``raydium_pool_v4` audit conditionnel, sans promotion automatique ; 6. `0.7.53``pump_swap` — clôturé ;
7. `0.7.54+`Pump, Meteora, Phoenix/OpenBook, Orca puis les autres DEX/surfaces. 7. `0.7.54``pump_fees` ;
8. `0.7.55``pump_fun` ;
9. `0.7.56+` — Meteora, routers/agrégateurs, Phoenix/OpenBook, Orca puis les autres DEX/surfaces.
`raydium_pool_v4.json` reste repoussé en audit conditionnel tardif, pas une tranche bloquante.
## Note 0.7.48 — Raydium CPMM event coverage ## Note 0.7.48 — Raydium CPMM event coverage

View File

@@ -2,6 +2,30 @@
# khadhroony-bobobot — Roadmap # khadhroony-bobobot — Roadmap
## État courant — clôture `0.7.53 pump_swap`
`0.7.53` est clos pour `pump_swap`. La version ferme le décodage transaction/log de PumpSwap, la matérialisation `buy`, `sell` et `buy_exact_quote_in` depuis sources exactes, les events Anchor audit-only, les tests synthétiques IDL, et la surveillance SQL globale. Les futures interventions PumpSwap doivent être des corrections de bugs ou des adaptations à un changement externe prouvé, pas lajout dentrées IDL déjà connues.
Décisions de clôture :
- `pump_swap.buy_exact_quote_in` est matérialisé uniquement avec `amountSource=pump_swap_anchor_buy_event`; les rows `instruction_bounds_only` restent decoded-only ;
- les events Anchor `buy_event`, `sell_event`, `deposit_event`, `withdraw_event`, `create_pool_event`, etc. restent audit-only pour éviter le double-count avec les instructions locales ;
- `claim_token_incentives_event` est testé et prêt à matérialiser `reward` si un corpus réussi apparaît ; les signatures observées côté instruction étaient failed et ne doivent pas produire de reward ;
- `sync_user_volume_accumulator_event` reste `implemented_idl_unobserved` : plus de 60/70 signatures supplémentaires ont confirmé linstruction sans faire apparaître levent ;
- Raydium AMM v4 / CLMM / CPMM ne présentent plus de gap ciblé après normalisation des observations ;
- les gaps Meteora sont explicitement différés.
### Phasage immédiat après `0.7.53`
| Priorité | Tranche | Surface | Raison |
|---:|---|---|---|
| 1 | `0.7.54` | `pump_fees` | Backlog observé dominant (`get_fees` très fréquent) ; aucun trade/candle direct attendu. |
| 2 | `0.7.55` | `pump_fun` | Launch/bonding/migration et creator fees observés en fallback upstream. |
| 3 | `0.7.56+` | `meteora_*` | Corriger les gaps locaux Meteora reportés volontairement. |
| 4 | ultérieur | `jupiter_swap` / agrégateurs | Routes et comptes auxiliaires à traiter sans double-count des DEX effectifs. |
## 0.7.47-1FE5 — Décision de planification : ne plus viser “tous les events en une session” ## 0.7.47-1FE5 — Décision de planification : ne plus viser “tous les events en une session”
La phase `0.7.47` a montré que lobjectif “réimplémenter tous les décodeurs Carbon et toutes les sources en un seul bloc” est trop large. Le plan est donc redécoupé en **un DEX/version par tranche**, avec une matrice documentaire dédiée : `docs/DEX_DECODER_MATRIX.md`. La phase `0.7.47` a montré que lobjectif “réimplémenter tous les décodeurs Carbon et toutes les sources en un seul bloc” est trop large. Le plan est donc redécoupé en **un DEX/version par tranche**, avec une matrice documentaire dédiée : `docs/DEX_DECODER_MATRIX.md`.
@@ -19,37 +43,111 @@ Règles de planification :
| Source | Usage | | Source | Usage |
|---|---| |---|---|
| `idls/` | Corpus local dIDL Solscan téléchargés et versionnés dans le workspace ; source locale obligatoire à comparer aux liens Git avant décision de decoder. |
| `https://github.com/sevenlabs-hq/carbon/tree/main/decoders` | Source principale des decoders multi-protocoles. | | `https://github.com/sevenlabs-hq/carbon/tree/main/decoders` | Source principale des decoders multi-protocoles. |
| `https://github.com/0xfnzero/solana-streamer` | Source complémentaire PumpFun/PumpSwap/Bonk/Raydium CPMM. | | `https://github.com/0xfnzero/solana-streamer` | Source complémentaire PumpFun/PumpSwap/Bonk/Raydium CPMM. |
| `https://github.com/0xfnzero/sol-parser-sdk/tree/main/idl` | IDL complémentaires. | | `https://github.com/0xfnzero/sol-parser-sdk/tree/main/idls` | IDL complémentaires. |
| `https://github.com/pinax-network/substreams-solana-idls/tree/main/src` | IDL et layouts additionnels. | | `https://github.com/pinax-network/substreams-solana-idls/tree/main/src` | IDL et layouts additionnels. |
| `https://github.com/hodlwarden/solana-tx-parser/tree/main/src` | Parsers transactionnels complémentaires. | | `https://github.com/hodlwarden/solana-tx-parser/tree/main/src` | Parsers transactionnels complémentaires. |
| `https://github.com/openbook-dex/openbook-v2` | Source officielle OpenBook v2. | | `https://github.com/openbook-dex/openbook-v2` | Source officielle OpenBook v2. |
| `https://github.com/all-in-one-blockchain/phoenix-onchain-mm` | Source Phoenix/MM complémentaire. | | `https://github.com/all-in-one-blockchain/phoenix-onchain-mm` | Source Phoenix/MM complémentaire. |
| `https://docs.vybenetwork.com/docs/available-dexs-amms` | Source externe de découverte DEX/AMM, non vérifiante. | | `https://docs.vybenetwork.com/docs/available-dexs-amms` | Source externe de découverte DEX/AMM, non vérifiante. |
### Plan révisé `0.7.48` à `0.7.63+` ### Plan révisé `0.7.53+` — une version par `program_id`
| Version cible | Scope | Objectif de clôture | Règle de planification validée après `0.7.52` : **une version cible = un `program_id`**.
|---|---|---|
| `0.7.48` | `raydium_cpmm` | Clôturé : instructions/events CPMM, lifecycle, fees, admin/config, deposit/withdraw, `lp_change_event`, invariants trade/candle. |
| `0.7.49` | `raydium_clmm` | Clôturé : 33 instructions observées/décodées, orderbook CLMM, liquidity/fee/reward/admin/lifecycle, fallbacks upstream nettoyés, 11 Program-data events préparés mais non observés. |
| `0.7.50` | `raydium_launchpad` | Bootstrap ouvert : surface LaunchLab/Launchpad, discriminants Carbon/IDL, fallback audit, SQL de validation, aucune matérialisation métier sans corpus. |
| `0.7.51` | `raydium_amm_v4` | Clôturé : decoder maximal `00..11`, swaps spécialisés, lifecycle/liquidity/fees/admin/orderbook, `pre_initialize` audit, `simulate_info` decoded-only, cleanup legacy/fallback. |
| `0.7.52` | `raydium_stable_swap` | Clôturé : surface legacy `00..0d`, swaps via deltas vault exacts, failed tx decoded-only, invariants trade/candle propres. |
| `0.7.53` | `raydium_pool_v4` | Audit / program-id decision seulement : confirmer program id, rôle exact et corpus avant toute promotion métier. |
| `0.7.54` | `pump_swap` | Couvrir `buy/sell` et tous les events auxiliaires disponibles : fees, cashback, volume accumulator, admin/config. |
| `0.7.55` | `pump_fun` | Traiter launch/bonding/migration ; séparer création token, buy/sell bonding, migration vers DEX effectif. |
| `0.7.56` | `meteora_dbc` | Couverture DBC : bonding curve, swap, migration, launch attribution, fees/admin, non-trade. |
| `0.7.57` | `meteora_dlmm` | Audit final de parité avec sources Git/IDL ; fermer ou documenter les audits résiduels. |
| `0.7.58` | `meteora_damm_v1` | Parité upstream complète ; résoudre les cas non matérialisés faute de pool/pair quand possible. |
| `0.7.59` | `meteora_damm_v2` | Couverture DAMM v2 complète : create, swap, liquidity, fees/admin/config ; décider trade actionability. |
| `0.7.60` | `phoenix_v1` | Finir tous les events Git disponibles en audit ; préparer mais ne pas activer trade materialization. |
| `0.7.61` | `openbook_v2` | Finir layouts logs/events ; définir conditions futures de trade/candle sans les activer par défaut. |
| `0.7.62` | `orca_whirlpools` | Reprendre Whirlpools depuis IDL/source : swaps, pools, positions, liquidity, fees/rewards. |
| `0.7.63+` | Launch surfaces / DEX candidats / validation consolidée | Moonshot/Moonit, Boop, Heaven, Bags, LetsBonk, FluxBeam, DexLab, Lifinity, Stabble, BonkSwap, GooseFX, Obric, SolFi puis base neuve multi-DEX. |
Ce plan remplace les anciens regroupements larges `0.7.50+` qui mélangeaient plusieurs DEX dans une même version. Exceptions : les comptes non-programmes (`platform_config`, token authority, comptes de configuration, comptes de pool, accounts de programme) ne créent pas de version decoder autonome. Ils restent des sources de contexte ou denrichissement. `SOLSCAN_ACCOUNT_SOURCES` reste un inventaire de découverte, pas une preuve de support local.
| Version cible | Decoder / surface | Program id | Famille | Objectif de clôture |
|---|---|---|---|---|
| `0.7.53` | `pump_swap` | `pAMMBay6oceH9fJKBRHGP5D4bD4sWpmSwMn52FMfXEA` | Pump / AMM | **Clos** : `buy/sell/buy_exact_quote_in` matérialisés seulement depuis sources exactes ; events Anchor audit-only ; tests synthétiques IDL ; SQL global. |
| `0.7.54` | `pump_fees` | `pfeeUxB6jkeY1Hxd7CsFCAjcbHA9rWtchMGdZ6VojVZ` | Pump / fee | Couvrir fee accounting/claim/config observés ; aucun trade/candle direct. |
| `0.7.55` | `pump_fun` | `6EF8rrecthR5Dkzon8Nwu78hRvfCKubJ14M5uBEwF6P` | Pump / launch-bonding | Couvrir create, buy/sell bonding, migration/graduate, config/update ; séparer bonding curve et DEX effectif. |
| `0.7.56` | `meteora_dbc` | `dbcij3LWUppWqq96dh6gJWwBifmcGfLSB5D4DuSMaqN` | Meteora / DBC | Compléter launch/bonding, swaps exploitables, migration, fees/admin/config. |
| `0.7.57` | `meteora_dlmm` | `LBUZKhRxPF3XUpBCjp4YzTKgLccjZhTSDM9YuVaPwxo` | Meteora / DLMM | Parité upstream finale : swaps, bins, positions, liquidity, fees/rewards/admin. |
| `0.7.58` | `meteora_damm_v1` | `Eo7WjKq67rjJQSZxS6z3YkapzY3eMj6Xy8X5EQVn5UaB` | Meteora / DAMM v1 | Parité upstream finale : pools, swaps, liquidity, lock, fees/admin. |
| `0.7.59` | `meteora_damm_v2` | `cpamdpZCGKUy5JxQXB4dcpGPiikHawvSWAd6mEn1sGG` | Meteora / DAMM v2 | Couverture complète : create/custom pools, swaps, liquidity, dynamic config, fees/admin. |
| `0.7.60` | `meteora_vault` | `24Uqj9JCLxUeoC3hGfh5W3s9FM9uCHDS2SG3LYwBpyTi` | Meteora / vault | Vault deposit/withdraw/fee/accounting ; pas de candle directe. |
| `0.7.61` | `system_program` | `11111111111111111111111111111111` | Système Solana | Create/assign/transfer account ; side effects de contexte, pas de trade. |
| `0.7.62` | `spl_token` | `TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA` | SPL Token | Transfer, mint, burn, close account, sync native ; base transversale pour deltas. |
| `0.7.63` | `spl_token_2022` | `TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb` | Token-2022 | Transfers/extensions Token-2022, mint/burn/close, comptes et side effects. |
| `0.7.64` | `associated_token_account` | `ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL` | Système token | Création ATA, rattachement wallet/token/pool. |
| `0.7.65` | `compute_budget` | `ComputeBudget111111111111111111111111111111` | Contexte tx | Budget/prioritization fee ; utile scoring/MEV, pas de trade. |
| `0.7.66` | `memo` | `MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr` | Contexte tx | Mémo transactionnel et attribution éventuelle. |
| `0.7.67` | `address_lookup_table` | `AddressLookupTab1e1111111111111111111111111` | Contexte tx | Résolution/diagnostic ALT si nécessaire. |
| `0.7.68` | `mpl_token_metadata` | `metaqbxxUerdq28cj1RbAWkYQm3ybzjb6a8bt518x1s` | Metadata | Enrichissement token/NFT/mint metadata. |
| `0.7.69` | `mpl_core` | `CoREENxT6tW1HoK8ypY1SxRMZTcVPm7R94rH4PZNhX7d` | Metadata / asset | Contexte asset si présent dans corpus. |
| `0.7.70` | `bubblegum` | `BGUMAp9Gq7iTEuizy4pqaxsTyUCBK68MDfK752saRPUY` | Compressed assets | Audit/contexte assets compressés, pas DEX. |
| `0.7.71` | `raydium_routing` | `routeUGWgWzqBWFcrCfv8tritsqukccJPu3q5GPP3xS` | Router | Route/legs Raydium ; éviter le double-count avec DEX effectifs. |
| `0.7.72` | `jupiter_swap_v6` | `JUP6LkbZbjS1jKKwapdHNy74zcZ3tLUZoi5QNyVTaV4` | Aggregator | Route attribution, legs, no duplicate trade/candle. |
| `0.7.73` | `jupiter_swap_v4` | `JUP4Fb2cqiRUcaTHdrPC8h2gNsA2ETXiPDD33WcGuJB` | Legacy aggregator | Audit/route only si corpus encore utile. |
| `0.7.74` | `dflow_aggregator_v4` | `DF1ow4tspfHX9JwWJsAb9epbkA8hmpSEAtxXy1V27QBH` | Aggregator | Route/intent/orderflow ; pas de double matérialisation. |
| `0.7.75` | `okx_dex` | `6m2CDdhRgxpH4WjvdzxAYbGxwdGUz5MziiL5jek2kBma` | Aggregator/router | Route attribution ; trades seulement si source exacte non doublonnée. |
| `0.7.76` | `onchain_labs_dex_v2` | `proVF4pMXVaYqmy4NjniPh4pqKNfMmsihgd4wdkCX3u` | Router/DEX candidat | Corpus dabord ; classifier route vs DEX effectif. |
| `0.7.77` | `titan_router` | `T1TANpTeScyeqVzzgNViGDNrkQ6qHz9KrSBS4aNXvGT` | Router | Audit route-only sauf preuve de trade direct non doublonné. |
| `0.7.78` | `sanctum_router` | `stkitrT1Uoy18Dk1fTrgPw8W6MVzoCfYoAFT4MLsmhq` | Router | Route/liquid staking context ; pas de candle DEX directe. |
| `0.7.79` | `orca_whirlpools` | `whirLbMiicVdio4qvUfM5KAg6Ct8VwpYzGff3uctyCc` | DEX avec IDL/source | Swaps, pools, positions, liquidity, fees/rewards. |
| `0.7.80` | `stabble_stable_swap` | `swapNyd8XiQwJ6ianp9snpu4brUqFxadzvHebnAXjJZ` | DEX avec IDL/source | Stable swap ; deltas exacts, liquidity/admin. |
| `0.7.81` | `stabble_weighted_swap` | `swapFpHZwjELNnjvThjajtiVmkz3yPQEHjLtka2fwHW` | DEX avec IDL/source | Weighted swap, deltas exacts, liquidity/admin. |
| `0.7.82` | `stabble_clmm` | `6dMXqGZ3ga2dikrYS9ovDXgHGh5RUsb2RTUj6hrQXhk6` | DEX avec IDL/source | CLMM Stabble si corpus utile. |
| `0.7.83` | `bonkswap` | `BSwp6bEBihVLdqJRKGgzjcGLHkcTuzmSo1TQkHepzH8p` | DEX avec IDL/source | Swap/liquidity/non-trade. |
| `0.7.84` | `boop_fun` | `boop8hVGQGqehUK2iVEMEnMrL5RbjywRzHKBmBE7ry4` | Launch/DEX candidat | Launch/swap/migration selon corpus. |
| `0.7.85` | `byreal_clmm` | `REALQqNEomY6cQGZJUGwywTBD2UmDT32rZcNnfxQ5N2` | DEX avec IDL/source | CLMM ; corpus puis matérialisation contrôlée. |
| `0.7.86` | `fusionamm` | `fUSioN9YKKSa3CUC2YUc4tPkHJ5Y6XW1yz8y6F7qWz9` | DEX avec IDL/source | AMM ; swaps/liquidity si corpus. |
| `0.7.87` | `goosefx_v1` | `GAMMA7meSFWaBXF25oSUgmGRwaW6sCMFLmBNiMSdbHVT` | DEX avec IDL/source | DEX/AMM selon corpus. |
| `0.7.88` | `goosefx_v2` | `GFXsSL5sSaDfNFQUYsHekbWBW1TsFdjDYzACh62tEHxn` | DEX avec IDL/source | DEX/AMM selon corpus. |
| `0.7.89` | `guac_swap` | `Gswppe6ERWKpUTXvRPfXdzHhiCyJvLadVvXGfdpBqcE1` | DEX avec IDL/source | Swap/liquidity si corpus. |
| `0.7.90` | `hylo_exchange` | `HYEXCHtHkBagdStcJCp3xbbb9B7sdMdWXFNj6mdsG4hn` | DEX/source à classifier | Classer DEX/lending/stable selon IDL/corpus. |
| `0.7.91` | `printr` | `T8HsGYv7sMk3kTnyaRqZrbRPuntYzdh12evXBkprint` | Launch/DEX candidat | Corpus dabord ; surface launch/swap à confirmer. |
| `0.7.92` | `moonit` | `MoonCVVNZFSYkqNXP6bxHLPL6QQJiMagDL3qcqUQTrG` | Launch/DEX candidat | Launch/migration/swap si prouvé. |
| `0.7.93` | `metadao_amm_v0_5` | `AMMJdEiCCa8mdugg6JPF7gFirmmxisTfDJoSNSUi5zDJ` | DEX avec source | AMM futarchy ; corpus et price semantics. |
| `0.7.94` | `metadao_bid_wall` | `WALL8ucBuUyL46QYxwYJjidaFYhdvxUFrgvBxPshERx` | Order/bid-wall | Order/bid-wall context ; pas de candle directe sans fill exact. |
| `0.7.95` | `metadao_launchpad` | `moontUzsdepotRGe5xsfip7vLPTJnVuafqdUWexVnPM` | Launch surface | Launch/ICO surface ; pas DEX effectif par défaut. |
| `0.7.96` | `vertigo` | `vrTGoBuy5rYSxAfV3jaRJWHH6nN9WK4NRExGxsk1bCJ` | DEX/source | Swap/launch selon corpus. |
| `0.7.97` | `virtuals` | `5U3EU2ubXtK84QcRjWVmYt9RaDyA8gKxdUrPFXmZyaki` | Launch/DEX candidat | Launch/AMM à confirmer. |
| `0.7.98` | `wavebreak` | `waveQX2yP3H1pVU8djGvEHmYg8uamQ84AuyGtpsrXTF` | DEX/source | Corpus et rôle exact. |
| `0.7.99` | `woofi` | `WooFif76YGRNjk1pA8wCsN67aQsD9f9iLsz4NcJ1AVb` | DEX/router | Swap/route selon corpus. |
| `0.7.100` | `pancake_swap` | `HpNfyc2Saw7RKkQd8nEL4khUcuPhQ7WwY1B2qjx8jxFq` | DEX/source | DEX Solana à confirmer par corpus. |
| `0.7.101` | `gavel` | `srAMMzfVHVAtgSJc8iH6CfKzuWuUTzLHVCE81QU1rgi` | Source upstream | Corpus dabord ; rôle exact à classer. |
| `0.7.102` | `heaven` | `HEAVENoP2qxoeuF8Dj2oT1GHEnu49U5mJYkdeC8BAX2o` | Launch/DEX candidat | Launch/DEX selon corpus. |
| `0.7.103` | `lifinity_v2` | `2wT8Yq49kHgDzXuPxZSaeLaH1qbmGXtEyPy64bL7aD3c` | DEX legacy/actif | Support selon corpus. |
| `0.7.104` | `moonshot` | `MoonCVVNZFSYkqNXP6bxHLPL6QQJiMagDL3qcqUQTrG` | Source upstream/Solscan | À dédupliquer avec `moonit` si même program id. |
| `0.7.105` | `openbook_v2` | `opnb2LAfJYbRMAHHvqjCwQxanZn7ReEHp1k81EohpZb` | Orderbook | Audit/orderbook complet ; trade/candle seulement si fills exacts. |
| `0.7.106` | `phoenix_v1` | `PhoeNiXZ8ByJGLkxNfZRnkUfjvmuYqLR89jjFHGqdXY` | Orderbook | Audit-only complet avant toute matérialisation fill. |
| `0.7.107` | `alphaq` | `ALPHAQmeA7bjrVuccPsYPiCvsi428SNwte66Srvs4pHA` | Solscan/no IDL | Probe par Demo3 + corpus ; décider support ou abandon. |
| `0.7.108` | `aquifer` | `AQU1FRd7papthgdrwPTTq5JacJh8YtwEXaBfKU3bTz45` | Solscan/no IDL | Probe uniquement. |
| `0.7.109` | `bisonfi` | `BiSoNHVpsVZW2F7rx2eQ59yQwKxzU5NvBcmKshCSUypi` | Solscan/no IDL | Probe uniquement. |
| `0.7.110` | `dexlab` | `DSwpgjMvXhtGn6BsbqmacdBZyfLj6jSWf3HJpdJtmg6N` | Solscan/no IDL | Vérifier support partiel / corpus. |
| `0.7.111` | `fluxbeam` | `FLUXubRmkEi2q6K3Y9kBPg9248ggaZVsoSFhtJHSrm1X` | Solscan/no IDL | Vérifier support partiel / corpus. |
| `0.7.112` | `goonfi` | `goonERTdGsjnkZqWuVjs73BZ3Pb9qoCUdBUL17BnS5j` | Solscan/no IDL | Probe. |
| `0.7.113` | `goonfi_v2` | `goonuddtQRrWqqn5nFyczVKaie28f3kDkHWkHtURSLE` | Solscan/no IDL | Probe ; adresse à revérifier si erreur de taille. |
| `0.7.114` | `humidifi` | `9H6tua7jkLhdm3w8BvgpTn5LZNU7g4ZynDmCiNN3q6Rp` | Solscan/no IDL | Probe. |
| `0.7.115` | `obric_v2` | `obriQD1zbpyLz95G5n7nJe6a4DPjpFwa5XYPoNm113y` | Solscan/no IDL | Probe. |
| `0.7.116` | `ondo_global_market` | `XzTT4XB8m7sLD2xi6snefSasaswsKCxx5Tifjondogm` | Solscan IDL | Rôle marché/tokenized assets à confirmer. |
| `0.7.117` | `scorch` | `SCoRcH8c2dpjvcJD6FiPbCSQyQgu3PcUAWj2Xxx3mqn` | Solscan/no IDL | Probe. |
| `0.7.118` | `solfi` | `SoLFiHG9TfgtdUXUjWAxi3LtvYuFyDLVhBWxdMZxyCe` | Solscan/no IDL | Probe. |
| `0.7.119` | `solfi_v2` | `SV2EYYJyRz2YhfXwXnhNAevDEui5Q6yrfyo13WtupPF` | Solscan/no IDL | Probe. |
| `0.7.120` | `zerofi` | `ZERor4xhbUycZ6gb9ntrhqscUcZmAbQDjEAtCf4hbZY` | Solscan/no IDL | Probe. |
| `0.7.121` | `zora` | `zoRabwLGd5zXaV7Gxacppw8tcceXEiTrSKyNLSaSTUc` | Solscan/no IDL | Probe, pas de promotion sans corpus. |
| `0.7.122` | `1dex` | `DEXYosS6oEGvk8uCDayvwEZz4qEyDJRf9nFgYCaqPMTm` | Solscan/no IDL | Probe. |
| `0.7.123` | `aldrin_amm` | `AMM55ShdkoGRB5jVYPjWziwk8m5MpwyDgsMWHaMSQWH6` | Legacy/no IDL | Historique/probe. |
| `0.7.124` | `aldrin_amm_v2` | `CURVGoZn8zycx6FXwwevgBTB2gVvdbGTEpvMJDbgs2t4` | Legacy/no IDL | Historique/probe. |
| `0.7.125` | `crema_finance` | `CLMM9tUoggJu2wagPkkqs9eFG4BWhVBZWkP1qv3Sp7tR` | Legacy/no IDL | Historique/probe. |
| `0.7.126` | `cropper_finance` | `CTMAxxk34HjKWxQ3QLZK1HpaLXmBveao3ESePXbiyfzh` | Legacy/no IDL | Historique/probe. |
| `0.7.127` | `cropper_whirlpool` | `H8W3ctz92svYg6mkn1UtGfu2aQr2fnUFHM1RhScEtQDt` | Legacy/no IDL | Historique/probe. |
| `0.7.128` | `mercurial_stable_swap` | `MERLuDFBMmsHnsBPZw2sDQZHvXFMwp8EdjudcU2HKky` | Legacy/no IDL | Historique stable swap ; deltas exacts si support. |
| `0.7.129` | `saber_stable_swap` | `SSwpkEEcbUqx4vtoEByFjSkhKdCT862DNVb52nZg1UZ` | Legacy/no IDL | Historique stable swap. |
| `0.7.130` | `saros_amm` | `SSwapUtytfBdBn1b9NUGG6foMVPtcWgpRU32HToDUZr` | Legacy/no IDL | Historique/probe. |
| `0.7.131` | `step_finance_swap` | `SSwpMgqNDsyV7mAgN9ady4bDVu5ySjmmXejXvy2vLt1` | Legacy/no IDL | Historique/probe. |
| `0.7.132` | `stepn_dooar_swap` | `Dooar9JkhdZ7J3LHN3A7YCuoGRUggXhQaG4kijfLGU2j` | Legacy/no IDL | Historique/probe. |
| `0.7.133` | `raydium_amm_v2_legacy` | `RVKd61ztZW9GUwhRbbLoYVRE5Xf1B2tVscKqwZqXgEr` | Raydium legacy/no IDL | Historique Raydium ; corpus dabord. |
| `0.7.134` | `raydium_amm_v3_legacy` | `27haf8L6oxUeXrHrgEgsexjSY5hbVUWEmvv9Nyxg8vQv` | Raydium legacy/no IDL | Historique Raydium ; ne pas confondre avec CLMM moderne. |
| `0.7.135` | `raydium_pool_v4_json_audit` | aucun `program_id` prouvé par le fichier seul | Audit source annexe | Vérifier `sol-parser-sdk/idls/raydium_pool_v4.json` après les surfaces documentées ; patch AMM v4 si amélioration, sinon clôture no-op. |
| `0.7.136` | cleanup `SOLSCAN_ACCOUNT_SOURCES` | n/a | Nettoyage registry/constants | Retirer doublons/promotions ; les programmes validés deviennent constantes/support matrix, les comptes non-programmes restent contexte ou sont supprimés. |
| `0.7.137` | base neuve multi-programmes | n/a | Validation consolidée | Replay consolidé, coverage global, zéro faux trade/candle, diagnostics bloquants à zéro. |
Ce plan remplace les regroupements larges qui mélangeaient plusieurs DEX ou plusieurs `program_id` dans une seule tranche. `raydium_pool_v4.json` est explicitement repoussé vers la fin : il ne bloque plus `0.7.53`.
@@ -1315,60 +1413,132 @@ Objectif : reprendre Raydium Stable comme tranche Raydium dédiée après AMM v4
Réalisé : decoder legacy 1 octet, surface locale `00..0d`, matérialisation lifecycle/liquidity/admin/fee/orderbook selon contexte, swaps `swap_base_in/out` matérialisés uniquement depuis deltas vault exacts (`stable_swap_vault_balance_delta`), transactions failed decoded-only, invariants trade/candle propres. Réalisé : decoder legacy 1 octet, surface locale `00..0d`, matérialisation lifecycle/liquidity/admin/fee/orderbook selon contexte, swaps `swap_base_in/out` matérialisés uniquement depuis deltas vault exacts (`stable_swap_vault_balance_delta`), transactions failed decoded-only, invariants trade/candle propres.
### 6.085. Version `0.7.53` `raydium_pool_v4` audit / program-id decision ### 6.085. Versions `0.7.53` à `0.7.137` — phasage par `program_id`
Objectif : auditer `raydium_pool_v4.json` comme source IDL annexe, sans promotion métier automatique. Objectif : reprendre la suite après `0.7.52 raydium_stable_swap` avec une règle stricte : **une version = un `program_id`**.
À faire : confirmer s'il correspond à un program id distinct, confirmer son rôle exact par rapport à `raydium_amm_v4`, chercher un corpus exploitable, puis décider seulement ensuite si une surface dédiée est nécessaire. Les comptes non-programmes ne créent pas de tranche decoder autonome. `SOLSCAN_ACCOUNT_SOURCES` reste un inventaire de découverte et sera nettoyé après validation des surfaces.
### 6.086. Version `0.7.54` — `pump_swap` event coverage #### Bloc Pump
Objectif : compléter `pump_swap` au-delà de `buy/sell`.
À faire : couvrir fees, cashback, volume accumulator, admin/config et autres events upstream disponibles, tout en maintenant linvariant non-trade = zéro trade/candle. | Version | Decoder / surface | Program id | Objectif |
|---:|---|---|---|
| `0.7.53` | `pump_swap` | `pAMMBay6oceH9fJKBRHGP5D4bD4sWpmSwMn52FMfXEA` | Clos : `buy/sell/buy_exact_quote_in` depuis sources exactes, non-trades spécialisés, events Anchor audit-only. |
| `0.7.54` | `pump_fees` | `pfeeUxB6jkeY1Hxd7CsFCAjcbHA9rWtchMGdZ6VojVZ` | Couvrir fee accounting/claim/config ; aucun trade/candle direct. |
| `0.7.55` | `pump_fun` | `6EF8rrecthR5Dkzon8Nwu78hRvfCKubJ14M5uBEwF6P` | Couvrir launch/bonding/migration : create, buy/sell bonding, update/config, graduate/migrate. |
### 6.087. Version `0.7.55` — `pump_fun` launch/bonding/migration #### Bloc Meteora
Objectif : séparer launch/bonding de DEX effectif et valider migration vers PumpSwap ou autre surface tradable.
À faire : traiter create, buy/sell bonding, update/config, mint/burn éventuels, migration/graduate et rattachement au pool tradable. | Version | Decoder / surface | Program id | Objectif |
|---:|---|---|---|
| `0.7.56` | `meteora_dbc` | `dbcij3LWUppWqq96dh6gJWwBifmcGfLSB5D4DuSMaqN` | Compléter toutes les instructions/events DBC : launch/bonding, swap exploitable, migration, fees/admin/config. |
| `0.7.57` | `meteora_dlmm` | `LBUZKhRxPF3XUpBCjp4YzTKgLccjZhTSDM9YuVaPwxo` | Parité upstream finale : swaps, bins, positions, liquidity, fees/rewards/admin. |
| `0.7.58` | `meteora_damm_v1` | `Eo7WjKq67rjJQSZxS6z3YkapzY3eMj6Xy8X5EQVn5UaB` | Parité upstream finale : pools, swaps, liquidity, lock, fees/admin. |
| `0.7.59` | `meteora_damm_v2` | `cpamdpZCGKUy5JxQXB4dcpGPiikHawvSWAd6mEn1sGG` | Couverture complète : create/custom pools, swaps, liquidity, dynamic config, fees/admin. |
| `0.7.60` | `meteora_vault` | `24Uqj9JCLxUeoC3hGfh5W3s9FM9uCHDS2SG3LYwBpyTi` | Vault deposit/withdraw/fee/accounting ; pas de candle directe. |
### 6.088. Version `0.7.56` — `meteora_dbc` séparé #### Bloc programmes système / contexte transactionnel
Objectif : reprendre Meteora après les tranches Raydium et Pump, en séparant bonding/launch, swap effectif, migration et attribution dorigine.
À faire : vérifier swaps exploitables, migration, lifecycle, mint/burn éventuels, launch attribution, fees/admin, sans candle artificielle sur events non pricés. | Version | Decoder / surface | Program id | Objectif |
|---:|---|---|---|
| `0.7.61` | `system_program` | `11111111111111111111111111111111` | Create/assign/transfer account ; side effects de contexte, pas de trade. |
| `0.7.62` | `spl_token` | `TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA` | Transfer, mint, burn, close account, sync native ; base transversale pour deltas. |
| `0.7.63` | `spl_token_2022` | `TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb` | Transfers/extensions Token-2022, mint/burn/close, comptes et side effects. |
| `0.7.64` | `associated_token_account` | `ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL` | Création ATA, rattachement wallet/token/pool. |
| `0.7.65` | `compute_budget` | `ComputeBudget111111111111111111111111111111` | Budget/prioritization fee ; utile scoring/MEV, pas de trade. |
| `0.7.66` | `memo` | `MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr` | Mémo transactionnel et attribution éventuelle. |
| `0.7.67` | `address_lookup_table` | `AddressLookupTab1e1111111111111111111111111` | Résolution/diagnostic ALT si nécessaire. |
| `0.7.68` | `mpl_token_metadata` | `metaqbxxUerdq28cj1RbAWkYQm3ybzjb6a8bt518x1s` | Enrichissement token/NFT/mint metadata. |
| `0.7.69` | `mpl_core` | `CoREENxT6tW1HoK8ypY1SxRMZTcVPm7R94rH4PZNhX7d` | Contexte asset si présent dans corpus. |
| `0.7.70` | `bubblegum` | `BGUMAp9Gq7iTEuizy4pqaxsTyUCBK68MDfK752saRPUY` | Audit/contexte assets compressés, pas DEX. |
### 6.089. Version `0.7.57` — `meteora_dlmm` parité upstream finale #### Bloc routers / aggregators
Objectif : comparer la couverture locale DLMM déjà avancée avec toutes les sources Git/IDL et documenter ou fermer les audits résiduels.
À faire : revalider swaps, liquidity, positions, lifecycle, fees/rewards/admin, et garder les discriminants non mappés en audit documenté. | Version | Decoder / surface | Program id | Objectif |
|---:|---|---|---|
| `0.7.71` | `raydium_routing` | `routeUGWgWzqBWFcrCfv8tritsqukccJPu3q5GPP3xS` | Route/legs Raydium ; éviter le double-count avec AMM/CLMM/CPMM/Stable. |
| `0.7.72` | `jupiter_swap_v6` | `JUP6LkbZbjS1jKKwapdHNy74zcZ3tLUZoi5QNyVTaV4` | Route attribution, legs, no duplicate trade/candle. |
| `0.7.73` | `jupiter_swap_v4` | `JUP4Fb2cqiRUcaTHdrPC8h2gNsA2ETXiPDD33WcGuJB` | Audit/route only si corpus encore utile. |
| `0.7.74` | `dflow_aggregator_v4` | `DF1ow4tspfHX9JwWJsAb9epbkA8hmpSEAtxXy1V27QBH` | Route/intent/orderflow ; pas de double matérialisation. |
| `0.7.75` | `okx_dex` | `6m2CDdhRgxpH4WjvdzxAYbGxwdGUz5MziiL5jek2kBma` | Route attribution ; trades seulement si source exacte non doublonnée. |
| `0.7.76` | `onchain_labs_dex_v2` | `proVF4pMXVaYqmy4NjniPh4pqKNfMmsihgd4wdkCX3u` | Corpus dabord ; classifier route vs DEX effectif. |
| `0.7.77` | `titan_router` | `T1TANpTeScyeqVzzgNViGDNrkQ6qHz9KrSBS4aNXvGT` | Audit route-only sauf preuve de trade direct non doublonné. |
| `0.7.78` | `sanctum_router` | `stkitrT1Uoy18Dk1fTrgPw8W6MVzoCfYoAFT4MLsmhq` | Route/liquid staking context ; pas de candle DEX directe. |
### 6.090. Version `0.7.58` — `meteora_damm_v1` parité upstream finale #### Bloc DEX avec IDL/code/source exploitable
Objectif : compléter la tranche DAMM v1 déjà engagée, résoudre les surfaces non observées et améliorer le rattachement pool/pair quand possible.
À faire : vérifier toutes les instructions upstream restantes, matérialiser uniquement les events prouvés et documenter les cas sans pool/pair local. | Version | Decoder / surface | Program id | Objectif |
|---:|---|---|---|
| `0.7.79` | `orca_whirlpools` | `whirLbMiicVdio4qvUfM5KAg6Ct8VwpYzGff3uctyCc` | Swaps, pools, positions, liquidity, fees/rewards. |
| `0.7.80` | `stabble_stable_swap` | `swapNyd8XiQwJ6ianp9snpu4brUqFxadzvHebnAXjJZ` | Stable swap ; deltas exacts, liquidity/admin. |
| `0.7.81` | `stabble_weighted_swap` | `swapFpHZwjELNnjvThjajtiVmkz3yPQEHjLtka2fwHW` | Weighted swap, deltas exacts, liquidity/admin. |
| `0.7.82` | `stabble_clmm` | `6dMXqGZ3ga2dikrYS9ovDXgHGh5RUsb2RTUj6hrQXhk6` | CLMM Stabble si corpus utile. |
| `0.7.83` | `bonkswap` | `BSwp6bEBihVLdqJRKGgzjcGLHkcTuzmSo1TQkHepzH8p` | Swap/liquidity/non-trade. |
| `0.7.84` | `boop_fun` | `boop8hVGQGqehUK2iVEMEnMrL5RbjywRzHKBmBE7ry4` | Launch/swap/migration selon corpus. |
| `0.7.85` | `byreal_clmm` | `REALQqNEomY6cQGZJUGwywTBD2UmDT32rZcNnfxQ5N2` | CLMM ; corpus puis matérialisation contrôlée. |
| `0.7.86` | `fusionamm` | `fUSioN9YKKSa3CUC2YUc4tPkHJ5Y6XW1yz8y6F7qWz9` | AMM ; swaps/liquidity si corpus. |
| `0.7.87` | `goosefx_v1` | `GAMMA7meSFWaBXF25oSUgmGRwaW6sCMFLmBNiMSdbHVT` | DEX/AMM selon corpus. |
| `0.7.88` | `goosefx_v2` | `GFXsSL5sSaDfNFQUYsHekbWBW1TsFdjDYzACh62tEHxn` | DEX/AMM selon corpus. |
| `0.7.89` | `guac_swap` | `Gswppe6ERWKpUTXvRPfXdzHhiCyJvLadVvXGfdpBqcE1` | Swap/liquidity si corpus. |
| `0.7.90` | `hylo_exchange` | `HYEXCHtHkBagdStcJCp3xbbb9B7sdMdWXFNj6mdsG4hn` | Classer DEX/lending/stable selon IDL/corpus. |
| `0.7.91` | `printr` | `T8HsGYv7sMk3kTnyaRqZrbRPuntYzdh12evXBkprint` | Corpus dabord ; surface launch/swap à confirmer. |
| `0.7.92` | `moonit` | `MoonCVVNZFSYkqNXP6bxHLPL6QQJiMagDL3qcqUQTrG` | Launch/migration/swap si prouvé. |
| `0.7.93` | `metadao_amm_v0_5` | `AMMJdEiCCa8mdugg6JPF7gFirmmxisTfDJoSNSUi5zDJ` | AMM futarchy ; corpus et price semantics. |
| `0.7.94` | `metadao_bid_wall` | `WALL8ucBuUyL46QYxwYJjidaFYhdvxUFrgvBxPshERx` | Order/bid-wall context ; pas de candle directe sans fill exact. |
| `0.7.95` | `metadao_launchpad` | `moontUzsdepotRGe5xsfip7vLPTJnVuafqdUWexVnPM` | Launch/ICO surface ; pas DEX effectif par défaut. |
| `0.7.96` | `vertigo` | `vrTGoBuy5rYSxAfV3jaRJWHH6nN9WK4NRExGxsk1bCJ` | Swap/launch selon corpus. |
| `0.7.97` | `virtuals` | `5U3EU2ubXtK84QcRjWVmYt9RaDyA8gKxdUrPFXmZyaki` | Launch/AMM à confirmer. |
| `0.7.98` | `wavebreak` | `waveQX2yP3H1pVU8djGvEHmYg8uamQ84AuyGtpsrXTF` | Corpus et rôle exact. |
| `0.7.99` | `woofi` | `WooFif76YGRNjk1pA8wCsN67aQsD9f9iLsz4NcJ1AVb` | Swap/route selon corpus. |
| `0.7.100` | `pancake_swap` | `HpNfyc2Saw7RKkQd8nEL4khUcuPhQ7WwY1B2qjx8jxFq` | DEX Solana à confirmer par corpus. |
| `0.7.101` | `gavel` | `srAMMzfVHVAtgSJc8iH6CfKzuWuUTzLHVCE81QU1rgi` | Corpus dabord ; rôle exact à classer. |
| `0.7.102` | `heaven` | `HEAVENoP2qxoeuF8Dj2oT1GHEnu49U5mJYkdeC8BAX2o` | Launch/DEX selon corpus. |
| `0.7.103` | `lifinity_v2` | `2wT8Yq49kHgDzXuPxZSaeLaH1qbmGXtEyPy64bL7aD3c` | DEX legacy/actif à confirmer. |
| `0.7.104` | `moonshot` | `MoonCVVNZFSYkqNXP6bxHLPL6QQJiMagDL3qcqUQTrG` | À dédupliquer avec `moonit` si même program id. |
| `0.7.105` | `openbook_v2` | `opnb2LAfJYbRMAHHvqjCwQxanZn7ReEHp1k81EohpZb` | Audit/orderbook complet ; trade/candle seulement si fills exacts. |
| `0.7.106` | `phoenix_v1` | `PhoeNiXZ8ByJGLkxNfZRnkUfjvmuYqLR89jjFHGqdXY` | Audit-only complet avant toute matérialisation fill. |
### 6.091. Version `0.7.59` — `meteora_damm_v2` séparé #### Bloc probes / DEX sans IDL fiable / historiques
Objectif : reprendre DAMM v2 comme DEX effectif séparé après disponibilité du ledger de coverage.
À faire : consolider create_pool, swaps exploitables, configs dynamiques, liquidity, fees/admin, lifecycle ; conserver les swaps sans payload montant/prix fiable comme `non_actionable_trade`. | Version | Decoder / surface | Program id | Objectif |
|---:|---|---|---|
| `0.7.107` | `alphaq` | `ALPHAQmeA7bjrVuccPsYPiCvsi428SNwte66Srvs4pHA` | Demo3 + corpus ; décider support ou abandon. |
| `0.7.108` | `aquifer` | `AQU1FRd7papthgdrwPTTq5JacJh8YtwEXaBfKU3bTz45` | Probe uniquement. |
| `0.7.109` | `bisonfi` | `BiSoNHVpsVZW2F7rx2eQ59yQwKxzU5NvBcmKshCSUypi` | Probe uniquement. |
| `0.7.110` | `dexlab` | `DSwpgjMvXhtGn6BsbqmacdBZyfLj6jSWf3HJpdJtmg6N` | Vérifier support partiel / corpus. |
| `0.7.111` | `fluxbeam` | `FLUXubRmkEi2q6K3Y9kBPg9248ggaZVsoSFhtJHSrm1X` | Vérifier support partiel / corpus. |
| `0.7.112` | `goonfi` | `goonERTdGsjnkZqWuVjs73BZ3Pb9qoCUdBUL17BnS5j` | Probe. |
| `0.7.113` | `goonfi_v2` | `goonuddtQRrWqqn5nFyczVKaie28f3kDkHWkHtURSLE` | Probe ; adresse à revérifier si erreur de taille. |
| `0.7.114` | `humidifi` | `9H6tua7jkLhdm3w8BvgpTn5LZNU7g4ZynDmCiNN3q6Rp` | Probe. |
| `0.7.115` | `obric_v2` | `obriQD1zbpyLz95G5n7nJe6a4DPjpFwa5XYPoNm113y` | Probe. |
| `0.7.116` | `ondo_global_market` | `XzTT4XB8m7sLD2xi6snefSasaswsKCxx5Tifjondogm` | Rôle marché/tokenized assets à confirmer. |
| `0.7.117` | `scorch` | `SCoRcH8c2dpjvcJD6FiPbCSQyQgu3PcUAWj2Xxx3mqn` | Probe. |
| `0.7.118` | `solfi` | `SoLFiHG9TfgtdUXUjWAxi3LtvYuFyDLVhBWxdMZxyCe` | Probe. |
| `0.7.119` | `solfi_v2` | `SV2EYYJyRz2YhfXwXnhNAevDEui5Q6yrfyo13WtupPF` | Probe. |
| `0.7.120` | `zerofi` | `ZERor4xhbUycZ6gb9ntrhqscUcZmAbQDjEAtCf4hbZY` | Probe. |
| `0.7.121` | `zora` | `zoRabwLGd5zXaV7Gxacppw8tcceXEiTrSKyNLSaSTUc` | Probe, pas de promotion sans corpus. |
| `0.7.122` | `1dex` | `DEXYosS6oEGvk8uCDayvwEZz4qEyDJRf9nFgYCaqPMTm` | Probe. |
| `0.7.123` | `aldrin_amm` | `AMM55ShdkoGRB5jVYPjWziwk8m5MpwyDgsMWHaMSQWH6` | Historique/probe. |
| `0.7.124` | `aldrin_amm_v2` | `CURVGoZn8zycx6FXwwevgBTB2gVvdbGTEpvMJDbgs2t4` | Historique/probe. |
| `0.7.125` | `crema_finance` | `CLMM9tUoggJu2wagPkkqs9eFG4BWhVBZWkP1qv3Sp7tR` | Historique/probe. |
| `0.7.126` | `cropper_finance` | `CTMAxxk34HjKWxQ3QLZK1HpaLXmBveao3ESePXbiyfzh` | Historique/probe. |
| `0.7.127` | `cropper_whirlpool` | `H8W3ctz92svYg6mkn1UtGfu2aQr2fnUFHM1RhScEtQDt` | Historique/probe. |
| `0.7.128` | `mercurial_stable_swap` | `MERLuDFBMmsHnsBPZw2sDQZHvXFMwp8EdjudcU2HKky` | Historique stable swap ; deltas exacts si support. |
| `0.7.129` | `saber_stable_swap` | `SSwpkEEcbUqx4vtoEByFjSkhKdCT862DNVb52nZg1UZ` | Historique stable swap. |
| `0.7.130` | `saros_amm` | `SSwapUtytfBdBn1b9NUGG6foMVPtcWgpRU32HToDUZr` | Historique/probe. |
| `0.7.131` | `step_finance_swap` | `SSwpMgqNDsyV7mAgN9ady4bDVu5ySjmmXejXvy2vLt1` | Historique/probe. |
| `0.7.132` | `stepn_dooar_swap` | `Dooar9JkhdZ7J3LHN3A7YCuoGRUggXhQaG4kijfLGU2j` | Historique/probe. |
| `0.7.133` | `raydium_amm_v2_legacy` | `RVKd61ztZW9GUwhRbbLoYVRE5Xf1B2tVscKqwZqXgEr` | Historique Raydium ; corpus dabord. |
| `0.7.134` | `raydium_amm_v3_legacy` | `27haf8L6oxUeXrHrgEgsexjSY5hbVUWEmvv9Nyxg8vQv` | Historique Raydium ; ne pas confondre avec CLMM moderne. |
| `0.7.135` | `raydium_pool_v4_json_audit` | aucun `program_id` prouvé par le fichier seul | Vérifier `sol-parser-sdk/idls/raydium_pool_v4.json` après les surfaces documentées ; patch AMM v4 si amélioration, sinon clôture no-op. |
### 6.092. Version `0.7.60` — `phoenix_v1` audit-only complet #### Nettoyage / consolidation
Objectif : finir tous les events Git disponibles en audit, sans activer de trade/candle.
À faire : couvrir `Fill`, `FillSummary`, `Fee`, `Evict`, `ExpiredOrder` et autres logs/events disponibles ; préparer le futur modèle orderbook sans matérialisation marché par défaut. | Version | Scope | Objectif |
|---:|---|---|
| `0.7.136` | cleanup `SOLSCAN_ACCOUNT_SOURCES` | Retirer les doublons/promotions : les vrais programmes validés deviennent constantes + support matrix ; les comptes non-programmes restent contexte ou sont supprimés. |
| `0.7.137` | base neuve multi-programmes | Replay consolidé, coverage global, zéro faux trade/candle, diagnostics bloquants à zéro. |
### 6.093. Version `0.7.61` — `openbook_v2` audit-only complet Garde-fous constants : pas de faux trade, pas de fausse candle, pas de `program_id` fictif, pas de promotion sans corpus transactionnel local, pas de double matérialisation router/leg, pas de logique métier DEX profonde dans `kb_demo_app`.
Objectif : finir les layouts logs/events OpenBook v2 et définir les conditions futures de matérialisation orderbook/trade.
À faire : vérifier fills, settle, consume events, open orders create/close, maker/taker, lots/decimals et sens économique avant toute promotion.
### 6.094. Version `0.7.62` — `orca_whirlpools` event coverage
Objectif : reprendre Whirlpools depuis IDL/source avec corpus dédié.
À faire : swaps, pools, positions, liquidity, fees/rewards, tick arrays, mint/burn/Token-2022 si applicable.
### 6.095. Version `0.7.63+` — Launch surfaces, DEX historiques/candidats et validation consolidée
Objectif : traiter les surfaces restantes puis rejouer une base neuve multi-DEX.
À faire : Moonshot/Moonit, Boop, Heaven, Bags, LetsBonk, FluxBeam, DexLab, Lifinity, Stabble, BonkSwap, GooseFX, Obric, SolFi et autres entrées Vybe/registry ; rapport coverage par DEX/event, zéro faux trade/candle, corpus documentés, matrices cohérentes, diagnostics bloquants à zéro.
### 6.091. Version `0.8.x` — Analyse et filtrage ### 6.091. 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.
@@ -1561,23 +1731,16 @@ Ordre de travail recommandé pour la suite :
2. `0.7.45` : `meteora_dlmm` — clos ; 2. `0.7.45` : `meteora_dlmm` — clos ;
3. `0.7.46` : `meteora_damm_v1` — clos côté corpus local ; 3. `0.7.46` : `meteora_damm_v1` — clos côté corpus local ;
4. `0.7.47` : Upstream Git Registry / DEX discovery preparation — acquis ; 4. `0.7.47` : Upstream Git Registry / DEX discovery preparation — acquis ;
5. `0.7.48-pre` : event coverage + DB model checkpoint — clos après table, sync upstream, refresh counts, diagnostics et profil validation ; 5. `0.7.48-pre` : event coverage + DB model checkpoint — clos ;
6. `0.7.48` : `raydium_cpmm` — clos ; 6. `0.7.48` : `raydium_cpmm` — clos ;
7. `0.7.49` : `raydium_clmm` — clos ; 7. `0.7.49` : `raydium_clmm` — clos ;
8. `0.7.50-pre-r2` : `raydium_launchpad` clos + re-vérification CPMM/CLMM ; 8. `0.7.50-pre-r2` : `raydium_launchpad` clos + re-vérification CPMM/CLMM ;
9. `0.7.51` : `raydium_amm_v4` ; 9. `0.7.51` : `raydium_amm_v4` — clos ;
10. `0.7.52` : `raydium_stable_swap` — clôturé ; 10. `0.7.52` : `raydium_stable_swap` — clos ;
11. `0.7.53` : `raydium_pool_v4` audit conditionnel ; 11. `0.7.53` : `pump_swap` / `pAMMBay6oceH9fJKBRHGP5D4bD4sWpmSwMn52FMfXEA` — clos ;
12. `0.7.54` : `pump_swap` ; 12. `0.7.54` : `pump_fees` / `pfeeUxB6jkeY1Hxd7CsFCAjcbHA9rWtchMGdZ6VojVZ` ;
13. `0.7.55` : `pump_fun` ; 13. `0.7.55` : `pump_fun` / `6EF8rrecthR5Dkzon8Nwu78hRvfCKubJ14M5uBEwF6P` ;
14. `0.7.56` : `meteora_dbc` ; 14. `0.7.56+` : appliquer le phasage strict “une version = un `program_id`” défini en section `6.085`.
15. `0.7.57` : `meteora_dlmm` parité upstream finale ;
16. `0.7.58` : `meteora_damm_v1` parité upstream finale ;
17. `0.7.59` : `meteora_damm_v2` ;
18. `0.7.60` : `phoenix_v1` audit-only complet ;
19. `0.7.61` : `openbook_v2` audit-only complet ;
20. `0.7.62` : `orca_whirlpools` ;
21. `0.7.63+` : launch surfaces, DEX candidats/historiques et validation consolidée.
Garde-fous constants : Garde-fous constants :
@@ -1649,7 +1812,7 @@ La tranche CPMM reconnaît désormais tous les discriminants instruction-level l
`0.7.48` est clôturable côté `raydium_cpmm`. Le decoder couvre les instructions/events CPMM listés par Carbon/fnzero/Raydium CP-Swap, avec matérialisation locale validée pour trades, liquidity, lifecycle, fees et admin/config. `swap_event` reste audit-only pour éviter les doublons avec `swap_base_input` / `swap_base_output`. Les side effects SPL Token / Token-2022 observés via Solscan (`burn`, `transfer`, `transferChecked`, `closeAccount`) restent hors decoder CPMM direct et alimenteront une réflexion transversale future. `0.7.48` est clôturable côté `raydium_cpmm`. Le decoder couvre les instructions/events CPMM listés par Carbon/fnzero/Raydium CP-Swap, avec matérialisation locale validée pour trades, liquidity, lifecycle, fees et admin/config. `swap_event` reste audit-only pour éviter les doublons avec `swap_base_input` / `swap_base_output`. Les side effects SPL Token / Token-2022 observés via Solscan (`burn`, `transfer`, `transferChecked`, `closeAccount`) restent hors decoder CPMM direct et alimenteront une réflexion transversale future.
La suite après `0.7.49 raydium_clmm` reprend en `0.7.50-pre-r2` par la clôture Launchpad et la re-vérification CPMM/CLMM, puis `0.7.51 raydium_amm_v4`, `0.7.52 raydium_stable_swap` et `0.7.53 raydium_pool_v4` uniquement comme audit conditionnel, en gardant la même discipline : sources Git/IDL + Solscan pour accélérer la découverte, mais corpus local obligatoire avant toute promotion métier. La suite après `0.7.52 raydium_stable_swap` a été clôturée par `0.7.53 pump_swap`, puis continue avec le phasage strict “une version = un `program_id`”. `raydium_pool_v4.json` reste repoussé vers la fin comme audit conditionnel : source Git/IDL utile seulement si elle apporte une amélioration concrète à `raydium_amm_v4` ou prouve un nouveau scope par corpus local.
## Clôture `0.7.51` — Raydium AMM v4 ## Clôture `0.7.51` — Raydium AMM v4
@@ -1674,7 +1837,7 @@ Décision `raydium_pool_v4` : ne pas ouvrir de decoder autonome dans cette tranc
Le rapport de décision est `docs/reports/RAYDIUM_POOL_V4_DECISION_NOTE.md`. Le rapport de décision est `docs/reports/RAYDIUM_POOL_V4_DECISION_NOTE.md`.
La tranche `0.7.52 raydium_stable_swap` est clôturée ; la suite reprend sur les surfaces restantes ou les audits conditionnels selon le corpus disponible. La tranche `0.7.52 raydium_stable_swap` est clôturée et `0.7.53 pump_swap` est désormais clôturé également. L'audit `raydium_pool_v4.json` reste repoussé vers la fin du phasage, avant le nettoyage/consolidation, afin de ne pas bloquer les surfaces Pump/Meteora/système/routers/DEX documentés.
### Addendum final — `0.7.52 raydium_stable_swap` ### Addendum final — `0.7.52 raydium_stable_swap`
@@ -1723,3 +1886,11 @@ SQL de validation : `validation_sql/SQL_VALIDATION_RAYDIUM_STABLE_SWAP_0_7_52.sq
Rapport : `docs/reports/RAYDIUM_STABLE_SWAP_EVENT_COVERAGE_REPORT.md`. Rapport : `docs/reports/RAYDIUM_STABLE_SWAP_EVENT_COVERAGE_REPORT.md`.
### Addendum — `0.7.53 pump_swap`
La reprise après `0.7.52 raydium_stable_swap` cible uniquement le program id `pAMMBay6oceH9fJKBRHGP5D4bD4sWpmSwMn52FMfXEA`. Le decoder local `pump_swap` couvre désormais les discriminants dinstructions upstream connus : trades matérialisables (`buy`, `sell`), swap decoded-only provisoire (`buy_exact_quote_in` tant que les montants exacts ne sont pas prouvés), liquidity (`deposit`, `withdraw`), pool/config (`create_pool`, `create_config`, `update_fee_config`), creator/protocol fee paths, cashback/token incentives et volume accumulator. Les events Program-data associés sont conservés comme entrées de coverage explicites ; ils ne doivent pas créer de doublon trade si linstruction locale matérialise déjà le swap effectif.
Delta post-replay : `toggle_cashback_enabled` est admin-only, `migrate_pool_coin_creator` est admin/config et lindex `k_sol_instruction_observations` nomme les discriminants PumpSwap observés. Les trois discriminants dabord inconnus (`01214eb921432c5c`, `fbe0ab92a01a71e9`, `cfbdb247a77a44b4`) sont maintenant nommés : `transfer_creator_fees_to_pump_v2` et `update_buyback_config` sont confirmés par le raw Solscan IDL ; `set_reserved_fee_recipient` reste une entrée observée dans les logs locaux, probablement une instruction ancienne/supprimée ou non exposée dans ce raw.
Critères de fermeture : corpus Demo3/backfill/replay dédié, coverage `pump_swap` synchronisée, zéro fallback `upstream_git.instruction_match` pour les instructions couvertes localement, failed tx sans trade/candle, non-swap sans trade/candle, et SQL `validation_sql/SQL_VALIDATION_PUMP_SWAP_0_7_53.sql` vide sur les requêtes danomalies.

View File

@@ -1,4 +1,10 @@
# DEX Decoder Matrix — `khadhroony-bobobot` `0.7.50-pre-r2` # DEX Decoder Matrix — `khadhroony-bobobot` `0.7.53 final`
## Note `0.7.53 final` — PumpSwap clôturé et sources IDL locales
Le répertoire `idls/` devient une source locale de savoir en plus des liens Git. Les IDL Solscan locales doivent être comparées au registre upstream et au corpus avant promotion métier. `pump_swap` est clôturé côté transaction/log decoder : instructions IDL, events Anchor audit-only, `buy_exact_quote_in` via `BuyEvent` exact, tests synthétiques et SQL global. Raydium AMM v4 / CLMM / CPMM ne nécessitent pas de correction dans cette clôture ; les gaps Meteora sont reportés.
`sol-parser-sdk/idls/raydium_pool_v4.json` a été vérifié comme source annexe distincte des fichiers `idls/raydium_*.json` présents localement : il expose notamment `swapBaseIn` et des comptes OpenBook/Serum, mais aucun fichier local ne porte ce nom ni ne correspond à son empreinte/format. Il reste donc un audit conditionnel, pas une source bloquante pour `0.7.53`.
Cette matrice complète `kb_lib/src/dex_support_matrix.rs`. Elle documente **ce qui est fait**, **ce qui reste à faire**, et **le niveau de preuve attendu** par DEX/version. Cette matrice complète `kb_lib/src/dex_support_matrix.rs`. Elle documente **ce qui est fait**, **ce qui reste à faire**, et **le niveau de preuve attendu** par DEX/version.
@@ -17,7 +23,7 @@ Cette matrice complète `kb_lib/src/dex_support_matrix.rs`. Elle documente **ce
- Carbon decoders : `https://github.com/sevenlabs-hq/carbon/tree/main/decoders` - Carbon decoders : `https://github.com/sevenlabs-hq/carbon/tree/main/decoders`
- Solana Streamer : `https://github.com/0xfnzero/solana-streamer` - Solana Streamer : `https://github.com/0xfnzero/solana-streamer`
- Sol Parser SDK IDLs : `https://github.com/0xfnzero/sol-parser-sdk/tree/main/idl` - Sol Parser SDK IDLs : `https://github.com/0xfnzero/sol-parser-sdk/tree/main/idls`
- Pinax Substreams Solana IDLs : `https://github.com/pinax-network/substreams-solana-idls/tree/main/src` - Pinax Substreams Solana IDLs : `https://github.com/pinax-network/substreams-solana-idls/tree/main/src`
- HODL Warden Solana Tx Parser : `https://github.com/hodlwarden/solana-tx-parser/tree/main/src` - HODL Warden Solana Tx Parser : `https://github.com/hodlwarden/solana-tx-parser/tree/main/src`
- OpenBook v2 : `https://github.com/openbook-dex/openbook-v2` - OpenBook v2 : `https://github.com/openbook-dex/openbook-v2`
@@ -33,8 +39,8 @@ Cette matrice complète `kb_lib/src/dex_support_matrix.rs`. Elle documente **ce
| 3 | `raydium_launchpad` | `bootstrap / 0.7.50` | Surface canonique normalisée, 1 entrée programme + 26 discriminants Carbon/IDL listés, fallback audit/mapped decoder, SQL dédié. | Créer DB neuve, backfill par discriminant, replay forcé, promouvoir seulement après corpus local. | | 3 | `raydium_launchpad` | `bootstrap / 0.7.50` | Surface canonique normalisée, 1 entrée programme + 26 discriminants Carbon/IDL listés, fallback audit/mapped decoder, SQL dédié. | Créer DB neuve, backfill par discriminant, replay forcé, promouvoir seulement après corpus local. |
| 4 | `raydium_amm_v4` | `supported / 0.7.51 closed` | Decoder maximal AMM v4 `00..11`, swaps spécialisés, lifecycle/liquidity/fees/admin/orderbook validés. | Stable Swap clôturé ensuite en `0.7.52`; surveiller les surfaces restantes. | | 4 | `raydium_amm_v4` | `supported / 0.7.51 closed` | Decoder maximal AMM v4 `00..11`, swaps spécialisés, lifecycle/liquidity/fees/admin/orderbook validés. | Stable Swap clôturé ensuite en `0.7.52`; surveiller les surfaces restantes. |
| 5 | `raydium_stable_swap` | `supported / 0.7.52 closed` | Decoder legacy 1 octet, surface `00..0d`, swaps matérialisés depuis deltas vault exacts. | Surveiller seulement de nouveaux discriminants ou `swap_event` observé. | | 5 | `raydium_stable_swap` | `supported / 0.7.52 closed` | Decoder legacy 1 octet, surface `00..0d`, swaps matérialisés depuis deltas vault exacts. | Surveiller seulement de nouveaux discriminants ou `swap_event` observé. |
| 6 | `raydium_pool_v4` | `to_verify / 0.7.53 conditional audit` | IDL annexe mentionnée par fnzero, non présente dans l'archive locale, pas de program id/rôle confirmé ici. | Ne pas promouvoir tant que program id distinct, rôle exact et corpus exploitable ne sont pas confirmés. | | 6 | `raydium_pool_v4` | `to_verify / late-phase conditional audit` | IDL annexe mentionnée par fnzero, non présente dans l'archive locale, pas de program id/rôle confirmé ici. | Ne pas promouvoir tant que program id distinct, rôle exact et corpus exploitable ne sont pas confirmés. |
| 7 | `pump_swap` | `supported / 0.7.54 planned` | `buy`/`sell` décodés et matérialisés ; trade/candle OK. | Ajouter tous les events Carbon/Solana Streamer : cashback, fee, volume accumulator, admin/config ; conserver les non-trades hors candles. | | 7 | `pump_swap` | `supported / 0.7.53 closed` | `buy`, `sell` + `buy_exact_quote_in` matérialisable via `BuyEvent` exact ; instructions non-trade spécialisées : liquidity, fee/creator fee, admin/config, cashback/token incentives, volume accumulator ; events Anchor autonomes audit-only. | Trades/candles uniquement depuis montants exacts ; failed tx decoded-only ; `instruction_bounds_only` reste decoded-only ; tests synthétiques IDL et SQL global ajoutés. |
| 8 | `pump_fun` | `partial / 0.7.55 launch_surface` | Création/token launch partiellement décodée ; intégrée au pipeline de listings. | Traiter tous les events Pump.fun disponibles : buy/sell/migrate/create/update ; séparer bonding/launch de DEX effectif ; valider migration vers PumpSwap. | | 8 | `pump_fun` | `partial / 0.7.55 launch_surface` | Création/token launch partiellement décodée ; intégrée au pipeline de listings. | Traiter tous les events Pump.fun disponibles : buy/sell/migrate/create/update ; séparer bonding/launch de DEX effectif ; valider migration vers PumpSwap. |
| 9 | `meteora_dbc` | `partial / 0.7.56 planned` | Swaps/instruction audits observés ; Demo3 donne du corpus. | Couverture complète DBC : launch/bonding curve, swap, migration, config/admin, fees ; matérialiser seulement ce qui est prouvé. | | 9 | `meteora_dbc` | `partial / 0.7.56 planned` | Swaps/instruction audits observés ; Demo3 donne du corpus. | Couverture complète DBC : launch/bonding curve, swap, migration, config/admin, fees ; matérialiser seulement ce qui est prouvé. |
| 10 | `meteora_dlmm` | `supported / 0.7.57 parity` | Couverture avancée validée en `0.7.45` : swaps, liquidity, positions, lifecycle, fees ; non-trade matérialisé. | Résoudre les audits résiduels non mappés ; comparer Carbon/IDL pour events rewards/admin restants ; revalidation base neuve. | | 10 | `meteora_dlmm` | `supported / 0.7.57 parity` | Couverture avancée validée en `0.7.45` : swaps, liquidity, positions, lifecycle, fees ; non-trade matérialisé. | Résoudre les audits résiduels non mappés ; comparer Carbon/IDL pour events rewards/admin restants ; revalidation base neuve. |
@@ -273,3 +279,9 @@ La tranche a été validée sur base SQLite dédiée : tous les discriminants `0
|---|---|---|---|---| |---|---|---|---|---|
| `raydium_stable_swap` | `5quBtoiQqxF9Jv6KYKctB59NT3gtJD2Y65kdnB1Uev3h` | supported / closed | legacy 1 octet | Surface locale `00..0d` couverte ; swaps `swap_base_in/out` matérialisés uniquement depuis deltas vault exacts ; instruction bounds et failed tx restent decoded-only. | | `raydium_stable_swap` | `5quBtoiQqxF9Jv6KYKctB59NT3gtJD2Y65kdnB1Uev3h` | supported / closed | legacy 1 octet | Surface locale `00..0d` couverte ; swaps `swap_base_in/out` matérialisés uniquement depuis deltas vault exacts ; instruction bounds et failed tx restent decoded-only. |
## 0.7.53 — PumpSwap
| Decoder | Program id | Statut | Source discriminants | Couverture locale | Règles métier |
|---|---|---:|---|---|---|
| `pump_swap` | `pAMMBay6oceH9fJKBRHGP5D4bD4sWpmSwMn52FMfXEA` | supported / 0.7.53 closed | upstream registry + `idls/pump_swap.pAMMBay6oceH9fJKBRHGP5D4bD4sWpmSwMn52FMfXEA.json` + corpus Demo3/replay | instructions : `buy`, `sell`, `buy_exact_quote_in`, `deposit`, `withdraw`, `create_pool`, `create_config`, `update_fee_config`, creator-fee, cashback, token incentives, volume accumulator, admin/config ; events Anchor autonomes audit-only | `buy_exact_quote_in` trade uniquement avec `pump_swap_anchor_buy_event`; aucun non-swap en trade/candle ; failed tx decoded-only ; `buy_event`/`sell_event` Program-data audit-only ; `transfer_creator_fees_to_pump_v2` et `update_buyback_config` confirmés par Solscan IDL ; `set_reserved_fee_recipient` conservé sur preuve log locale |

View File

@@ -1,4 +1,4 @@
# DEX Event Coverage Matrix — `khadhroony-bobobot` `0.7.50-pre-r2` # DEX Event Coverage Matrix — `khadhroony-bobobot` `0.7.53 final`
Cette matrice complète `docs/DEX_DECODER_MATRIX.md` avec une lecture par familles d'événements. Elle ne remplace pas la preuve locale : une entrée Git/IDL reste un indice tant qu'elle n'est pas observée dans le corpus local puis validée par replay et SQL. Cette matrice complète `docs/DEX_DECODER_MATRIX.md` avec une lecture par familles d'événements. Elle ne remplace pas la preuve locale : une entrée Git/IDL reste un indice tant qu'elle n'est pas observée dans le corpus local puis validée par replay et SQL.
@@ -214,3 +214,38 @@ Status: **closed on local corpus**.
Stable Swap swaps are not materialized from instruction min/max bounds. `swap_base_in/out` require `amountSource=stable_swap_vault_balance_delta`; `stable_swap_instruction_bounds_only` remains decoded-only and, in the final corpus, appears only on failed transactions. Stable Swap swaps are not materialized from instruction min/max bounds. `swap_base_in/out` require `amountSource=stable_swap_vault_balance_delta`; `stable_swap_instruction_bounds_only` remains decoded-only and, in the final corpus, appears only on failed transactions.
## 0.7.53 — `pump_swap`
Program id unique : `pAMMBay6oceH9fJKBRHGP5D4bD4sWpmSwMn52FMfXEA`.
| Entry | Discriminator | Family | Expected DB target | Local event kind | Status |
|---|---:|---|---|---|---|
| `buy` | `66063d1201daebea` | `swap` | `k_sol_trade_events` | `pump_swap.buy` | local specialized decoder ; materialize only from exact transfer/vault deltas |
| `sell` | `33e685a4017f83ad` | `swap` | `k_sol_trade_events` | `pump_swap.sell` | local specialized decoder ; materialize only from exact transfer/vault deltas |
| `buy_exact_quote_in` | `c62e1552b4d9e870` | `swap` | `k_sol_trade_events` only when exact Anchor `BuyEvent` is present ; otherwise decoded-only | `pump_swap.buy_exact_quote_in` | local specialized decoder ; `amountSource=pump_swap_anchor_buy_event` materializes trades ; `instruction_bounds_only` rows keep explicit `skipTradeReason` |
| `create_pool` | `e992d18ecf6840bc` | `pool_create` | `k_sol_pool_lifecycle_events` or decoded-only | `pump_swap.create_pool` | local specialized instruction ; non-trade |
| `deposit` | `f223c68952e1f2b6` | `liquidity_add` | `k_sol_liquidity_events` or decoded-only | `pump_swap.deposit` | local specialized instruction ; non-trade |
| `withdraw` | `b712469c946da122` | `liquidity_remove` | `k_sol_liquidity_events` or decoded-only | `pump_swap.withdraw` | local specialized instruction ; non-trade |
| `collect_coin_creator_fee` | `a039592ab58b2b42` | `fee` | `k_sol_fee_events` or decoded-only | `pump_swap.collect_coin_creator_fee` | local specialized instruction ; non-trade |
| `transfer_creator_fees_to_pump` | `8b348655e4e56cf1` | `fee` | `k_sol_fee_events` or decoded-only | `pump_swap.transfer_creator_fees_to_pump` | local specialized instruction ; non-trade |
| `transfer_creator_fees_to_pump_v2` | `01214eb921432c5c` | `fee` | `k_sol_fee_events` or decoded-only | `pump_swap.transfer_creator_fees_to_pump_v2` | Solscan IDL proof + local log proof: `transfer_creator_fees_to_pump_v2` / `Instruction: TransferCreatorFeesToPumpV2` ; non-trade |
| `claim_cashback` | `253a237ebe35e4c5` | `reward` | `k_sol_reward_events` or decoded-only | `pump_swap.claim_cashback` | local specialized instruction ; non-trade |
| `claim_token_incentives` | `1004471ccc01281b` | `reward` | `k_sol_reward_events` or decoded-only | `pump_swap.claim_token_incentives` | local specialized instruction ; non-trade |
| `init_user_volume_accumulator` | `5e06ca73ff60e8b7` | `reward` | `k_sol_reward_events` or decoded-only | `pump_swap.init_user_volume_accumulator` | local specialized instruction ; non-trade |
| `sync_user_volume_accumulator` | `561fc057a3574fee` | `reward` | `k_sol_reward_events` or decoded-only | `pump_swap.sync_user_volume_accumulator` | local specialized instruction ; non-trade |
| `close_user_volume_accumulator` | `f945a4da9667548a` | `reward` | `k_sol_reward_events` or decoded-only | `pump_swap.close_user_volume_accumulator` | local specialized instruction ; non-trade |
| `update_buyback_config` | `fbe0ab92a01a71e9` | `admin_config` | `k_sol_pool_admin_events` or decoded-only | `pump_swap.update_buyback_config` | Solscan IDL proof + local log proof: `update_buyback_config` / `Instruction: UpdateBuybackConfig` ; non-trade |
| `set_reserved_fee_recipient` | `cfbdb247a77a44b4` | `admin_config` | `k_sol_pool_admin_events` or decoded-only | `pump_swap.set_reserved_fee_recipient` | local log proof only: `Instruction: SetReservedFeeRecipient` ; absent from checked Solscan IDL raw ; non-trade |
| admin/config group | see report | `admin_config` | `k_sol_pool_admin_events` or decoded-only | `pump_swap.*` | local specialized instruction ; non-trade ; includes `toggle_cashback_enabled` and `migrate_pool_coin_creator` as admin-only |
| `buy_event` / `sell_event` | `67f4521f2cf57777` / `3e2f370aa503dc2a` | `swap_event_audit` | decoded-only | not local-instruction mapped | Program-data event discriminators listed; not used to duplicate local instruction trades |
| auxiliary Program-data events | see report | fee/reward/admin/liquidity | decoded-only until payload-specific materializer exists | not local-instruction mapped | explicit upstream status required after replay |
### Fermeture `0.7.53`
- `pump_swap` ne présente plus de decoded event local sans coverage dans le corpus de clôture.
- `buy_exact_quote_in` est matérialisé seulement quand le `BuyEvent` Anchor donne les montants exacts ; les bornes dinstruction seules restent non actionnables.
- Les events Anchor `*_event` sont décodés en audit-only pour éviter les doublons, sauf exception matérialisable explicitement testée.
- Les gaps globaux restants sont classés comme backlog upstream (`pump_fees`, `pump_fun`, `jupiter_swap`, agrégateurs), gaps Meteora reportés, ou observations non attribuées.
- Les checks Raydium AMM v4 / CLMM / CPMM normalisés sont vides ; aucune correction Raydium nest incluse dans cette clôture.

View File

@@ -0,0 +1,150 @@
<!-- file: docs/prompts/PROMPT_0_7_53_PUMP_SWAP.md -->
# Prompt de reprise — `0.7.53 pump_swap`
Nous reprenons le workspace Rust/Tauri `khadhroony-bobobot` après le commit final `0.7.52 raydium_stable_swap`.
## Objectif de tranche
Version cible : `0.7.53`
Surface cible unique : `pump_swap`
Program id cible unique :
```text
pAMMBay6oceH9fJKBRHGP5D4bD4sWpmSwMn52FMfXEA
```
Règle de phasage validée : **une version = un `program_id`**.
`raydium_pool_v4.json` est explicitement repoussé vers la fin du phasage. Il ne doit pas bloquer `0.7.53`.
## Contexte validé avant reprise
Raydium est clos sur les surfaces suivantes :
- `raydium_cpmm``CPMMoo8L3F4NbTegBCKVNunggL7H1ZpdTHKxQB5qKP1C` ;
- `raydium_clmm``CAMMCzo5YL8w4VFF8KVHrK22GGUsp5VTaW7grrKgrWqK` ;
- `raydium_launchpad``LanMV9sAd7wArD4vJFi2qDdfnVhFxYSUg6eADduJ3uj` ;
- `raydium_amm_v4``675kPX9MHTjS2zt1qfr1NYHuzeLXfQM9H24wFSUt1Mp8` ;
- `raydium_stable_swap``5quBtoiQqxF9Jv6KYKctB59NT3gtJD2Y65kdnB1Uev3h`.
`0.7.52 raydium_stable_swap` est clos avec :
- `cargo test -p kb_lib` : `407 passed`, `0 failed` ;
- `cargo clippy -p kb_lib --all-targets -- -D warnings` : OK ;
- swaps matérialisés uniquement depuis `amountSource=stable_swap_vault_balance_delta` ;
- failed transactions conservées decoded-only ;
- aucun successful swap non expliqué.
## Sources à utiliser pour `pump_swap`
Sources obligatoires à vérifier avant de patcher :
- `kb_lib/src/constants.rs` et `SOLSCAN_ACCOUNT_SOURCES` ;
- sources upstream Git déjà intégrées dans le registre : Carbon, fnzero, Pinax, HODL Warden si disponibles ;
- IDL Solscan du programme `pAMMBay6oceH9fJKBRHGP5D4bD4sWpmSwMn52FMfXEA` si disponible ;
- corpus local Demo3 + backfill signature/pool ;
- `k_sol_dex_event_coverage_entries` après replay.
Les sources Git/IDL/Solscan sont des indices. La preuve métier exige corpus local, replay et SQL.
## Tâches attendues
1. Inspecter le support local existant de `pump_swap`.
2. Lister toutes les instructions/events/discriminators disponibles depuis les sources upstream/IDL.
3. Comparer avec les events localement couverts.
4. Compléter le decoder `pump_swap` pour couvrir au minimum :
- `buy` ;
- `sell` ;
- fees / creator fees / protocol fees si présents ;
- admin/config si présents ;
- events auxiliaires tels que cashback, volume accumulator ou équivalents si présents dans les sources.
5. Matérialiser en `k_sol_trade_events` et `k_sol_pair_candles` uniquement si les montants exacts et le sens économique sont prouvés.
6. Conserver les failed transactions comme decoded-only avec `failed_transaction`.
7. Matérialiser les non-trade uniquement vers les tables adaptées : liquidity, fee, admin, lifecycle, reward, orderbook ou decoded-only selon le cas.
8. Nettoyer les fallbacks `upstream_git.instruction_match` uniquement quand un decoder local spécialisé couvre vraiment lentrée.
9. Mettre à jour la coverage DB et la matrice documentaire.
10. Ajouter ou mettre à jour le SQL de validation dédié : `validation_sql/SQL_VALIDATION_PUMP_SWAP_0_7_53.sql`.
## Invariants obligatoires
- Aucun non-swap ne doit créer de trade/candle.
- Aucune transaction failed ne doit créer de trade/candle.
- Aucun trade/candle ne doit être créé depuis des bornes dinstruction ou des montants incomplets.
- Aucun router/aggregator ne doit créer un doublon si le DEX effectif matérialise déjà le trade.
- Aucun `program_id` ne doit être inventé.
- Aucun compte non-programme de `SOLSCAN_ACCOUNT_SOURCES` ne doit être promu en decoder autonome.
- `kb_demo_app` ne doit pas contenir de logique métier DEX profonde.
## Contraintes de code
Respecter les contraintes du projet :
- Rust 2024 ;
- aucun fichier `mod.rs` ;
- pas de `pub mod` ; utiliser `mod` + `pub use` ;
- pas de `anyhow`, pas de `thiserror` ;
- pas de `?`, `unwrap`, `expect` dans le code applicatif `kb_lib` ;
- gestion derreurs explicite via `match`, `if let Err`, `let Err = ... else` ;
- rustdoc publique ;
- `#![deny(unreachable_pub)]` et `#![warn(missing_docs)]` ;
- tracing obligatoire ;
- tests offline ;
- si une requête DB est ajoutée ou modifiée, mettre à jour les re-exports dans `kb_lib/src/db.rs`, puis dans `kb_lib/src/lib.rs`.
## Validation attendue
Commandes locales à exécuter après patch :
```bash
cargo fmt
cargo test -p kb_lib
cargo clippy -p kb_lib --all-targets -- -D warnings
```
Replay attendu sur une base dédiée `0.7.53 pump_swap` :
```text
skipDexDecode=no
forceDexDecode=yes
deferInstructionObservations=yes
```
SQL de clôture attendu :
- coverage par entrée `pump_swap` ;
- instruction observations ;
- absence daudit résiduel local non expliqué ;
- absence de fallback upstream pour les entrées couvertes localement ;
- failed tx -> zéro trade ;
- non-swap -> zéro trade ;
- decoded without coverage -> vide ;
- successful non-materialized inexpliqués -> vide ;
- multi-target materialization -> vide ;
- résumé matérialisation par event_kind.
## Livrables documentaires
Mettre à jour au minimum :
- `ROADMAP.md` ;
- `CHANGELOG.md` ;
- `docs/DEX_DECODER_MATRIX.md` ;
- `docs/DEX_EVENT_COVERAGE_MATRIX.md` ;
- `docs/reports/PUMP_SWAP_EVENT_COVERAGE_REPORT.md` ;
- `validation_sql/SQL_VALIDATION_PUMP_SWAP_0_7_53.sql`.
## Décision attendue en fin de tranche
`0.7.53 pump_swap` est clôturable uniquement si :
```text
- tous les events/instructions disponibles ont un statut explicite ;
- les swaps réussis exploitables produisent trades/candles ;
- les failed transactions restent decoded-only ;
- les non-trade sont matérialisés dans les bonnes tables ou restent decoded-only expliqués ;
- la coverage DB ne contient pas de gap local inexpliqué ;
- cargo test et clippy sont OK.
```

View File

@@ -0,0 +1,272 @@
<!-- file: docs/prompts/PROMPT_0_7_54_PUMP_FUN.md -->
# Prompt de reprise — khadhroony-bobobot 0.7.54 — pump_fun
Tu reprends le workspace Rust/Tauri `khadhroony-bobobot` après clôture documentaire et fonctionnelle de `0.7.53 pump_swap`.
## 1. Fichiers à utiliser comme base
Je fournis larchive la plus récente du workspace, intégrant normalement les deltas de clôture `0.7.53`.
À considérer comme sources locales de savoir :
- le code Rust du workspace ;
- les fichiers Markdown (`README.md`, `ROADMAP.md`, `CHANGELOG.md`, `docs/**`) ;
- les SQL de validation dans `validation_sql/**` ;
- le répertoire `idls/**`, ajouté au projet, contenant des IDL JSON téléchargés depuis Solscan ;
- les liens Git/upstream déjà documentés dans les docs ;
- les résultats SQL/logs que je colle dans la conversation.
Ne pas supposer que les docs sont parfaites : les vérifier contre le code et contre les IDL locales.
## 2. État validé avant cette version
La version `0.7.53` a fermé `pump_swap` côté transaction/log decoder :
- `cargo test -p kb_lib` : `421 passed`;
- `cargo clippy -p kb_lib --all-targets -- -D warnings` : OK ;
- `pump_swap.buy`, `pump_swap.sell`, `pump_swap.buy_exact_quote_in` matérialisent correctement les trades ;
- `buy_exact_quote_in` utilise `pump_swap_anchor_buy_event` quand lAnchor `BuyEvent` exact est disponible ;
- les `*_event` Anchor PumpSwap sont décodés en audit-only sauf exception métier explicite ;
- `claim_token_incentives_event` est prêt à matérialiser `k_sol_reward_events` si un event réussi apparaît ;
- `pump_swap` ne présente plus de decoded event sans coverage, ni fallback upstream résiduel, ni trade candidate réussi non matérialisé ;
- les surfaces Raydium déjà travaillées (`raydium_amm_v4`, `raydium_clmm`, `raydium_cpmm`) ne doivent pas être rouvertes sauf bug prouvé ;
- les petits gaps Meteora sont volontairement différés.
Ne pas modifier PumpSwap ou Raydium sauf preuve SQL/code claire dune régression.
## 3. Objectif de la version 0.7.54
Objectif principal : ouvrir et avancer une tranche `pump_fun`.
Cette tranche doit traiter la surface launch/mint Pump.fun, qui est logiquement prioritaire avant `pump_fees`.
Le backlog global montre des entrées `pump_fun` encore en fallback upstream, notamment :
- `pump_fun.collect_creator_fee` / discriminator `1416567bc61cdb84` ;
- `pump_fun.migrate_bonding_curve_creator` / `577c34bf3426d6e8` ;
- `pump_fun.distribute_creator_fees` / `a572670079cef751` ;
- `pump_fun.admin_set_creator` / `4519ab8e39ef0d04` ;
- `pump_fun.extend_account` / `ea66c2cb96483ee5` ;
- `pump_fun.set_creator` / `fe94ff70cf8eaaa5` ;
- `pump_fun.migrate` / `9beae792ec9ea21e` ;
- `pump_fun.claim_cashback` / `253a237ebe35e4c5` ;
- `pump_fun.sell` / `33e685a4017f83ad` ;
- autres discriminators `pump_fun` observés dans `k_sol_instruction_observations`.
Lobjectif nest pas seulement de faire disparaître un fallback : il faut couvrir proprement la surface `pump_fun` :
- registry/coverage ;
- decoder local ou statut local explicitement decoded-only/audit-only ;
- matérialisation métier si les données sont fiables ;
- tests unitaires ;
- SQL de validation ;
- documentation.
## 4. Périmètre
### Inclus
- `pump_fun` launch/mint/bonding-curve surface ;
- instructions/events liés à création/migration/configuration/creator fees/cashback si présents dans IDL ou corpus ;
- intégration coverage ;
- matérialisation vers tables métier seulement si fiable.
### Hors périmètre sauf nécessité démontrée
- `pump_swap`, déjà fermé en `0.7.53` ;
- `raydium_*`, sauf régression prouvée ;
- `meteora_*`, volontairement différé ;
- `jupiter_swap`, `dflow_aggregator_v4`, `onchain_labs_dex_v2`, `orca_whirlpools`, backlog futur ;
- `pump_fees`, qui doit venir après `pump_fun`, probablement en `0.7.55`.
## 5. Méthode obligatoire
### 5.1 Nouvelle base SQLite
Créer une nouvelle DB dédiée à `0.7.54`, ne pas travailler sur lancienne DB de validation PumpSwap.
Démarrage attendu :
- catalog initial propre ou connu ;
- backfills ciblés ;
- replay forcé ;
- validation SQL.
### 5.2 Backfill de corpus
Construire un corpus local à partir de :
1. filtres Solscan.io quand disponibles ;
2. Demo3 discovery quand nécessaire ;
3. batch backfill de groupes de signatures ;
4. program/signature backfill si pertinent ;
5. signatures issues des requêtes SQL `sample_signature`.
Utiliser dabord les signatures fortes observées dans le backlog :
- `pump_fun.collect_creator_fee`;
- `pump_fun.migrate_bonding_curve_creator`;
- `pump_fun.distribute_creator_fees`;
- `pump_fun.admin_set_creator`;
- `pump_fun.extend_account`;
- `pump_fun.set_creator`;
- `pump_fun.migrate`;
- `pump_fun.claim_cashback`;
- puis autres discriminators observés.
Après chaque groupe de backfill :
- replay local avec `skipDexDecode=no`;
- utiliser `forceDexDecode=yes` quand le decoder/coverage change ;
- `deferInstructionObservations=yes` ;
- rafraîchir catalog ;
- relancer les SQL de surveillance.
### 5.3 Sources
Utiliser en priorité :
- `idls/**` local ;
- code existant ;
- docs existantes ;
- `upstream_registry_generated.rs` ;
- sources Git déjà référencées dans les docs ;
- Solscan signatures/logs ;
- payloads locaux DB.
Si une IDL locale contredit une source Git, signaler la divergence et ne pas inventer.
## 6. Contraintes de code Rust
Respecter strictement les conventions du projet :
- Rust 2024 ;
- pas de `?` ;
- pas de `unwrap()` / `expect()` en code applicatif ;
- pas de `anyhow` / `thiserror` ;
- `match` / `if let Err` explicites ;
- async-first ;
- `tracing` obligatoire ;
- pas de `mod.rs` ;
- pas de `pub mod` ; utiliser `mod` + `pub use` ;
- imports limités, types appelés de façon qualifiée quand cest la convention locale ;
- tests offline ;
- ne pas casser `#![deny(unreachable_pub)]` et `#![warn(missing_docs)]`.
Si des requêtes DB sont ajoutées ou déplacées, penser aux re-exports :
- `kb_lib/src/db.rs` ;
- `kb_lib/src/lib.rs`.
## 7. Matérialisation attendue
Pour `pump_fun`, ne pas forcer une matérialisation si les montants/acteurs/comptes ne sont pas prouvés.
Classer explicitement chaque entrée :
- `k_sol_launch_events` pour les événements de launch/mint/migration ;
- `k_sol_fee_events` si linstruction représente réellement des frais exploitables ;
- `k_sol_pool_admin_events` si cest de la configuration/admin/creator ;
- `k_sol_reward_events` uniquement si cest réellement une récompense/incitation/cashback fiable ;
- `k_sol_dex_decoded_events_only` si audit-only ou données insuffisantes ;
- `k_sol_trade_events` seulement si cest un swap/trade fiable ;
- `skip*Reason` explicite quand non matérialisable.
Ne jamais matérialiser une transaction failed comme business event.
## 8. SQL de validation à utiliser
Utiliser et adapter :
- `validation_sql/SQL_VALIDATION_DEX_COVERAGE_GLOBAL_0_7_53.sql`;
- les SQL de validation PumpSwap comme modèle ;
- une nouvelle validation dédiée à `pump_fun`, par exemple :
- `validation_sql/SQL_VALIDATION_PUMP_FUN_0_7_54.sql`.
Requêtes minimales attendues :
1. coverage `pump_fun` ;
2. instruction observations `pump_fun` ;
3. decoded events `pump_fun` sans coverage ;
4. fallback upstream `pump_fun` résiduel ;
5. successful non-materialized events sans skip reason ;
6. failed tx materialization safety ;
7. multi-target materialization safety ;
8. materialization summary ;
9. comparaison entre `k_sol_instruction_observations` et `k_sol_dex_event_coverage_entries` ;
10. global watchlist après replay.
## 9. Invariants de validation
Après correction, viser :
- pas de `pump_fun` decoded event local sans coverage ;
- pas de fallback `upstream_git` résiduel pour les entrées `pump_fun` couvertes localement ;
- pas de materialized business event sur failed transaction ;
- pas de multi-target incohérent ;
- tous les events/instructions observés ont :
- un decoder local,
- ou un statut audit-only,
- ou un skip reason explicite,
- ou une justification documentée sils restent upstream-only.
## 10. Documentation à mettre à jour
À la fin de la tranche, mettre à jour :
- `CHANGELOG.md` : une ligne ;
- `README.md` : état courant, nouvelle version, validation ;
- `ROADMAP.md` : phasage et clôture/état de `pump_fun`, puis annonce de `pump_fees` en version suivante ;
- `docs/DEX_DECODER_MATRIX.md` ;
- `docs/DEX_EVENT_COVERAGE_MATRIX.md` ;
- éventuellement un rapport :
- `docs/reports/PUMP_FUN_EVENT_COVERAGE_REPORT.md`.
Ne pas gonfler les docs inutilement : noter les faits validés, les limites, et les prochaines tranches.
## 11. Format de livraison attendu
Ne pas fournir une archive complète du workspace sauf demande explicite.
Fournir un delta zip contenant uniquement les fichiers modifiés/ajoutés.
Nom recommandé :
`khadhroony-bobobot-v0.7.54-pump_fun-delta-N-files.zip`
Chaque réponse de livraison doit inclure :
- résumé des changements ;
- liste exacte des fichiers modifiés ;
- commandes à lancer :
- `cargo fmt`
- `cargo test -p kb_lib`
- `cargo clippy -p kb_lib --all-targets -- -D warnings`
- replay recommandé ;
- SQL à exécuter ;
- ce qui est attendu dans les résultats.
## 12. Priorités si plusieurs gaps apparaissent
Ordre de priorité :
1. `pump_fun` ;
2. `pump_fees` seulement si strictement nécessaire pour comprendre un flux `pump_fun` ;
3. ne pas traiter Meteora dans cette version ;
4. ne pas rouvrir Raydium sauf régression démontrée ;
5. ne pas ouvrir Jupiter/dFlow/onchain_labs dans cette tranche sauf pour les classer en backlog.
## 13. Première tâche demandée
Commencer par analyser larchive fournie :
1. identifier les fichiers existants liés à `pump_fun`, `pump_fees`, upstream registry, coverage, materialization ;
2. chercher dans `idls/**` sil existe une IDL Solscan liée à `pump_fun` ;
3. produire un état des lieux court :
- entrées upstream disponibles ;
- entrées observées dans SQL/logs fournis ;
- fichiers à modifier ;
- hypothèse de classification par entry ;
- SQL initial de backfill/validation ;
4. proposer puis produire le premier delta minimal.

View File

@@ -0,0 +1,128 @@
# DEX coverage global watchlist — `0.7.53`
## Objet
Ce rapport accompagne la clôture `0.7.53 pump_swap`. Il sépare les anomalies bloquantes des files de travail futures.
`pump_swap` est clos côté transaction/log decoder et matérialisation métier. Les lignes restantes de surveillance globale ne sont pas des erreurs PumpSwap.
## Résultat de clôture PumpSwap
Validation rapportée :
```text
cargo test -p kb_lib -> 421 passed / 0 failed
cargo clippy -p kb_lib --all-targets -- -D warnings -> OK
```
Replay élargi rapporté :
```text
1189 replayed
0 decode skipped
1189 ledger upserts
967 unsafe ledger rows
928 trades
13 liquidity
8 lifecycle
0 tokenAccount
3700 candle upserts
instructionObservations = 25724
resetDeleted = 5622
catalog = 76 tokens / 80 pools / 80 pairs
```
Checks PumpSwap attendus vides :
- decoded events sans coverage ;
- fallback `upstream_git` couvert localement ;
- successful trade candidates sans trade et sans raison explicite ;
- failed transaction matérialisée en business trade ;
- non-swap matérialisé en trade ;
- multi-target materialization.
`buy_exact_quote_in` :
```text
pump_swap_anchor_buy_event -> 168 decoded / 167 trades / 1 failed tx
instruction_bounds_only -> decoded-only, sans trade
```
## Non-régression Raydium
Les checks ciblés Raydium AMM v4 / CLMM / CPMM normalisés sont vides. Il n'y a pas de correction Raydium à inclure dans `0.7.53`.
Lecture correcte :
- `raydium_amm_v4.swap_base_in_v2` : observé et matérialisé dans le corpus courant ;
- `raydium_clmm.swap_v2` : observé et matérialisé dans le corpus courant ;
- `raydium_cpmm.swap_base_input` : observé et matérialisé dans le corpus courant ;
- les entrées `upstream_git_mapped_unverified` avec `observed_count=0` sont des entrées registry non observées, pas des régressions.
## Gaps locaux reportés
La requête globale `local decoded events without coverage` retourne des gaps Meteora connus et reportés :
```text
meteora_dlmm.swap
meteora_damm_v2.instruction_audit
meteora_damm_v2.swap
```
Décision : ne pas les corriger dans `0.7.53`. Ils seront repris dans les tranches Meteora futures.
## Backlog upstream observé
La file `upstream_git.instruction_match` indique les prochaines surfaces à prioriser. Au moment de la clôture :
| Priorité | Surface | Indice principal | Décision |
|---:|---|---|---|
| 1 | `pump_fees` | `get_fees` très fréquent | Prochaine tranche recommandée ; aucun trade/candle direct attendu. |
| 2 | `pump_fun` | creator fees, migrate, set/admin creator | Tranche launch/bonding séparée. |
| 3 | `jupiter_swap` | `route_v2`, `create_token_account`, `route` | Agrégateur/router ; éviter double-count DEX effectifs. |
| 4 | `dflow_aggregator_v4`, `onchain_labs_dex_v2` | swaps/route helpers | Surfaces secondaires après Pump/Meteora. |
| 5 | `orca_whirlpools` | faibles occurrences | À reprendre plus tard avec corpus dédié. |
## Observations non attribuées
Les observations avec `decoder_code` vide ne doivent pas être traitées comme des discriminants Anchor confirmés. Elles exigent d'abord :
1. inspection de `sample_signature` ;
2. extraction du `program_id` effectif ;
3. comparaison aux IDL locales `idls/` et aux sources Git ;
4. ajout d'un decoder ou d'un statut explicite seulement si le programme est identifié.
## Source IDL locale
Le répertoire `idls/` est désormais une source locale de savoir. Les IDL téléchargées depuis Solscan doivent être utilisées en plus des liens Git, avec la règle suivante : une IDL donne une surface possible, mais la matérialisation métier exige corpus, replay et SQL.
## Vérification `raydium_pool_v4.json`
Fichier Git vérifié : `0xfnzero/sol-parser-sdk/idls/raydium_pool_v4.json`.
Constats :
- la page GitHub annonce `3400` lignes / environ `101 KB` ;
- le fichier expose notamment `swapBaseIn`, `addLiquidity` et des comptes OpenBook/Serum (`OpenBookMarket`, `OpenBookBids`, `OpenBookAsks`, etc.) ;
- la chaîne du program id AMM v4 `675kPX9MHTjS2zt1qfr1NYHuzeLXfQM9H24wFSUt1Mp8` n'apparaît pas dans la page GitHub consultée ;
- aucun fichier local `idls/raydium_*.json` ne porte le nom `raydium_pool_v4` ;
- les fichiers Raydium locaux présents sont `raydium_clmm`, `raydium_cpmm`, `raydium_launchlab` et `raydium_lock`.
Table locale :
| Fichier local | Name IDL | Address IDL | Taille | SHA-256 |
|---|---|---|---:|---|
| `idls/raydium_clmm.CAMMCzo5YL8w4VFF8KVHrK22GGUsp5VTaW7grrKgrWqK.json` | `raydium_clmm` | `CAMMCzo5YL8w4VFF8KVHrK22GGUsp5VTaW7grrKgrWqK` | 86240 | `d3f265adc9e4d7b1a28641444800a60bcdeb921ef136855fe8dea4532afa18bd` |
| `idls/raydium_cpmm.CPMMoo8L3F4NbTegBCKVNunggL7H1ZpdTHKxQB5qKP1C.json` | `raydium_cp_swap` | `CPMMoo8L3F4NbTegBCKVNunggL7H1ZpdTHKxQB5qKP1C` | 32066 | `5b709dfe7b0df67c9e29655307ae877244d3bb8022056ee01c754f0e36264d9c` |
| `idls/raydium_launchlab.LanMV9sAd7wArD4vJFi2qDdfnVhFxYSUg6eADduJ3uj.json` | `raydium_launchpad` | `LanMV9sAd7wArD4vJFi2qDdfnVhFxYSUg6eADduJ3uj` | 68052 | `c1465a10f38912413f55f04a68536c989992add1b3d48cba86dca97212a482da` |
| `idls/raydium_lock.LockrWmn6K5twhz3y9w1dQERbmgSaRkfnTeTKbpofwE.json` | `raydium_liquidity_locking` | absent | 10621 | `da45fd5eaf726e5e2ead7280c04588ebd4224d203304cdc8217b012b8130e1f1` |
Décision : `raydium_pool_v4.json` ne correspond à aucun fichier JSON local actuel. Il reste une source annexe d'audit AMM v4/strategy/wrapper à traiter plus tard, sans bloquer `0.7.53`.
## SQL associé
Voir :
```text
validation_sql/SQL_VALIDATION_DEX_COVERAGE_GLOBAL_0_7_53.sql
```

View File

@@ -0,0 +1,166 @@
# PumpSwap event coverage report — `0.7.53`
## Scope
- Version cible : `0.7.53`.
- Surface unique : `pump_swap`.
- Program id unique : `pAMMBay6oceH9fJKBRHGP5D4bD4sWpmSwMn52FMfXEA`.
- Phasage : une version = un `program_id`.
- `raydium_pool_v4.json` reste repoussé vers la fin du phasage et ne bloque pas cette tranche.
## Sources vérifiées / à vérifier pendant la fermeture
- `kb_lib/src/constants.rs`, notamment `PUMP_SWAP_PROGRAM_ID` et `SOLSCAN_ACCOUNT_SOURCES`.
- Registre upstream local généré : `kb_lib/src/upstream_registry_generated.rs`.
- Sources Git/IDL externes disponibles : Pump public docs / IDL, Carbon, fnzero, Pinax, HODL Warden si présents dans le registre.
- IDL locale : `idls/pump_swap.pAMMBay6oceH9fJKBRHGP5D4bD4sWpmSwMn52FMfXEA.json`, téléchargée depuis Solscan et versionnée dans le workspace.
- Solscan IDL du program id PumpSwap si disponible.
- Corpus local Demo3 + backfills signature/pool.
- `k_sol_dex_event_coverage_entries` après replay forcé.
Les sources Git/IDL/Solscan sont des indices. La fermeture métier exige corpus local, replay et SQL.
## Couverture locale ajoutée
Le decoder local `kb_lib/src/dex/pump_swap.rs` ne se limite plus à `buy`/`sell`. Il reconnaît maintenant les discriminants dinstruction suivants et produit un event spécialisé `pump_swap.<entry_name>`.
| Entry | Kind | Discriminator | Target attendu | Matérialisation |
|---|---|---:|---|---|
| `admin_set_coin_creator` | instruction | `f228759149606968` | admin/config | decoded specialized non-trade |
| `admin_update_token_incentives` | instruction | `d10b7357d5177ccc` | reward/admin | decoded specialized non-trade |
| `buy` | instruction | `66063d1201daebea` | trade | trade/candle seulement depuis montants exacts |
| `buy_exact_quote_in` | instruction | `c62e1552b4d9e870` | trade conditionnel | matérialisé seulement si un `BuyEvent` Anchor exact est présent (`amountSource=pump_swap_anchor_buy_event`) ; sinon decoded-only `instruction_bounds_only` avec `skipTradeReason` |
| `claim_cashback` | instruction | `253a237ebe35e4c5` | reward | decoded specialized non-trade |
| `claim_token_incentives` | instruction | `1004471ccc01281b` | reward | decoded specialized non-trade |
| `close_user_volume_accumulator` | instruction | `f945a4da9667548a` | reward | decoded specialized non-trade |
| `collect_coin_creator_fee` | instruction | `a039592ab58b2b42` | fee | decoded specialized non-trade |
| `create_config` | instruction | `c9cff3724b6f2fbd` | admin/config | decoded specialized non-trade |
| `create_pool` | instruction | `e992d18ecf6840bc` | pool lifecycle | decoded specialized non-trade |
| `deposit` | instruction | `f223c68952e1f2b6` | liquidity add | decoded specialized non-trade |
| `disable` | instruction | `b9adbb5ad80feee9` | admin/config | decoded specialized non-trade |
| `extend_account` | instruction | `ea66c2cb96483ee5` | admin/config | decoded specialized non-trade |
| `init_user_volume_accumulator` | instruction | `5e06ca73ff60e8b7` | reward | decoded specialized non-trade |
| `migrate_pool_coin_creator` | instruction | `d0089f044aaf103a` | admin/migration | decoded specialized non-trade |
| `sell` | instruction | `33e685a4017f83ad` | trade | trade/candle seulement depuis montants exacts |
| `set_coin_creator` | instruction | `d295802dbc3a4eaf` | admin/config | decoded specialized non-trade |
| `set_reserved_fee_recipients` | instruction | `6faca2e87259d58e` | admin/config | decoded specialized non-trade |
| `set_reserved_fee_recipient` | instruction | `cfbdb247a77a44b4` | admin/config | local log proof only: `Instruction: SetReservedFeeRecipient` ; absent from checked Solscan IDL raw ; decoded specialized non-trade |
| `sync_user_volume_accumulator` | instruction | `561fc057a3574fee` | reward | decoded specialized non-trade |
| `toggle_cashback_enabled` | instruction | `7367e0ffbd5956c3` | admin/config | decoded specialized non-trade |
| `toggle_mayhem_mode` | instruction | `01096fd0641fffa3` | admin/config | decoded specialized non-trade |
| `transfer_creator_fees_to_pump` | instruction | `8b348655e4e56cf1` | fee | decoded specialized non-trade |
| `transfer_creator_fees_to_pump_v2` | instruction | `01214eb921432c5c` | fee | Solscan IDL proof + local log proof: `transfer_creator_fees_to_pump_v2` / `Instruction: TransferCreatorFeesToPumpV2` ; decoded specialized non-trade |
| `update_admin` | instruction | `a1b028d53cb8b3e4` | admin/config | decoded specialized non-trade |
| `update_buyback_config` | instruction | `fbe0ab92a01a71e9` | admin/config | Solscan IDL proof + local log proof: `update_buyback_config` / `Instruction: UpdateBuybackConfig` ; decoded specialized non-trade |
| `update_fee_config` | instruction | `68b867f258976b14` | admin/config | decoded specialized non-trade |
| `withdraw` | instruction | `b712469c946da122` | liquidity remove | decoded specialized non-trade |
## Upstream Program-data events à statut explicite
Ces events sont listés dans le registre upstream. Ils doivent apparaître avec un statut explicite dans la coverage DB après sync/replay. Ils ne doivent pas créer de doublons trade/candle tant que linstruction locale spécialisée couvre déjà le DEX effectif.
| Event | Discriminator | Statut `0.7.53` |
|---|---:|---|
| `admin_set_coin_creator_event` | `2ddc5d181961ac68` | upstream listed ; decoded-only/status explicite après replay |
| `admin_update_token_incentives_event` | `93fa6c78f71d43de` | upstream listed ; decoded-only/status explicite après replay |
| `buy_event` | `67f4521f2cf57777` | upstream listed ; audit/decoded-only to avoid duplicate trade |
| `claim_cashback_event` | `e2d6f62107f293e5` | upstream listed ; decoded-only/status explicite après replay |
| `claim_token_incentives_event` | `4facf631cd5bcee8` | upstream listed ; decoded-only/status explicite après replay |
| `close_user_volume_accumulator_event` | `929fbdac925838f4` | upstream listed ; decoded-only/status explicite après replay |
| `collect_coin_creator_fee_event` | `e8f5c2eeeada3a59` | upstream listed ; decoded-only/status explicite après replay |
| `create_config_event` | `6b34598137e25116` | upstream listed ; decoded-only/status explicite après replay |
| `create_pool_event` | `b1310cd2a076a774` | upstream listed ; decoded-only/status explicite après replay |
| `deposit_event` | `78f83d531f8e6b90` | upstream listed ; decoded-only/status explicite après replay |
| `disable_event` | `6bfdc14ce4ca1b68` | upstream listed ; decoded-only/status explicite après replay |
| `extend_account_event` | `6161d7905d92167c` | upstream listed ; decoded-only/status explicite après replay |
| `init_user_volume_accumulator_event` | `86240d48e86582d8` | upstream listed ; decoded-only/status explicite après replay |
| `migrate_pool_coin_creator_event` | `aadd52c793a5f72e` | upstream listed ; decoded-only/status explicite après replay |
| `reserved_fee_recipients_event` | `2bbcfa12dd4bbb5f` | upstream listed ; decoded-only/status explicite après replay |
| `sell_event` | `3e2f370aa503dc2a` | upstream listed ; audit/decoded-only to avoid duplicate trade |
| `set_bonding_curve_coin_creator_event` | `f2e7eb664163bdd3` | upstream listed ; decoded-only/status explicite après replay |
| `set_metaplex_coin_creator_event` | `966bc77b7ccf66e4` | upstream listed ; decoded-only/status explicite après replay |
| `sync_user_volume_accumulator_event` | `c57aa77c74515bff` | upstream listed ; decoded-only/status explicite après replay |
| `update_admin_event` | `e198ab57f63f42ea` | upstream listed ; decoded-only/status explicite après replay |
| `update_fee_config_event` | `5a1741233ef4bcd0` | upstream listed ; decoded-only/status explicite après replay |
| `withdraw_event` | `1609851aa02c47c0` | upstream listed ; decoded-only/status explicite après replay |
## Matérialisation
- `pump_swap.buy`, `pump_swap.sell` et `pump_swap.buy_exact_quote_in` peuvent alimenter `k_sol_trade_events`, mais seulement depuis des montants exacts.
- `pump_swap.buy_exact_quote_in` est matérialisé quand le log Anchor `BuyEvent` fournit `baseAmountOutRaw` et `userQuoteAmountInRaw` pour `ixName=buy_exact_quote_in`. Les rows `instruction_bounds_only` restent decoded-only avec `skipTradeReason` explicite.
- Les bornes dinstruction (`maxQuoteAmountIn`, `spendableQuoteAmountIn`, `minQuoteAmountOut`, `minBaseAmountOut`) ne sont pas considérées comme des montants exacts suffisants pour créer un trade/candle.
- La matérialisation doit passer par les résolutions exactes existantes : token transfer deltas, vault/account balance deltas ou autre source damount explicitement prouvée.
- Les transactions failed restent decoded-only via lactionability `failed_transaction`.
- Les non-trades restent hors `k_sol_trade_events` et hors `k_sol_pair_candles`.
## SQL de fermeture
Le fichier dédié est :
```text
validation_sql/SQL_VALIDATION_PUMP_SWAP_0_7_53.sql
```
Les requêtes danomalies attendues vides couvrent :
- fallback upstream résiduel sur instruction couverte localement ;
- failed tx avec trade ;
- non-swap avec trade ;
- decoded without coverage ;
- successful non-materialized inexpliqué ;
- multi-target materialization ;
- successful swap exploitable sans trade et sans raison explicite.
## Delta post-replay `0.7.53-pump_swap-delta-1`
Le replay local a révélé trois écarts corrigés par ce delta :
- `pump_swap.toggle_cashback_enabled` était classé à la fois `reward` et `admin`; il devient admin-only pour respecter linvariant single-target.
- `pump_swap.buy_exact_quote_in` réussi non matérialisé reçoit maintenant un `skipTradeReason` explicite et reste decoded-only tant que les montants exacts ne sont pas prouvés.
- `k_sol_instruction_observations.instruction_name` reçoit un mapping PumpSwap par discriminant pour que la requête de coverage locale ne sorte plus des noms vides. Le discriminant `e445a52e51cb9a1d` reste marqué comme transport technique Anchor self-CPI. Les trois discriminants initialement inconnus ne doivent plus sortir en `observed_unknown_*` : deux sont confirmés par le raw Solscan IDL et un reste conservé comme local-log-only.
`pump_swap.migrate_pool_coin_creator` est également forcé en admin/config, pas lifecycle, car il modifie lattribution coin creator et ne représente pas une migration de pool échangeable.
## Delta post-replay `0.7.53-pump_swap-delta-2`
- Les trois discriminants initialement inconnus (`01214eb921432c5c`, `fbe0ab92a01a71e9`, `cfbdb247a77a44b4`) ont été provisoirement couverts decoded-only pour supprimer les gaps locaux sans inventer de métier.
- `toggle_cashback_enabled` est corrigé aussi dans la famille coverage (`admin_config`) afin de refléter la matérialisation admin-only.
## Delta post-replay `0.7.53-pump_swap-delta-4`
- Le corpus local donne maintenant le nom métier via logs Anchor : `TransferCreatorFeesToPumpV2`, `UpdateBuybackConfig`, `SetReservedFeeRecipient`.
- Ces trois discriminants deviennent des instructions locales spécialisées : `pump_swap.transfer_creator_fees_to_pump_v2`, `pump_swap.update_buyback_config`, `pump_swap.set_reserved_fee_recipient`.
- Recomparaison raw Solscan IDL : `transfer_creator_fees_to_pump_v2` et `update_buyback_config` sont présents dans lIDL. `set_reserved_fee_recipient` nest pas listé dans ce raw ; il est gardé sur preuve log locale, possiblement instruction historique/supprimée ou non exposée par cette version IDL.
- `transfer_creator_fees_to_pump_v2` est fee/non-trade ; `update_buyback_config` et `set_reserved_fee_recipient` sont admin/config non-trade.
## Clôture finale `0.7.53`
La tranche est clôturée après les deltas de consolidation :
- `buy_exact_quote_in` route désormais vers la matérialisation pool/pair/trade quand `amountSource=pump_swap_anchor_buy_event`;
- les montants du `BuyEvent` sont normalisés par rapport à lordre local de la paire pour éviter les inversions base/quote ;
- les events Anchor PumpSwap sont décodés comme events autonomes audit-only ;
- `claim_token_incentives_event` possède un test synthétique de matérialisabilité reward si un corpus réussi apparaît ;
- `sync_user_volume_accumulator_event` reste implémenté mais non observé malgré un backfill élargi sur linstruction ;
- les tests synthétiques couvrent les instructions/events IDL non observés localement ;
- `cargo test -p kb_lib` a été validé à `421 passed / 0 failed` et clippy est OK côté utilisateur.
Résultats de validation rapportés après corpus élargi :
```text
pump_swap decoded without coverage = vide
pump_swap upstream fallback couvert localement = vide
successful trade candidates sans trade = vide
failed tx avec business trade = vide
non-swap matérialisé en trade = vide
multi-target materialization = vide
buy_exact_quote_in / pump_swap_anchor_buy_event = 168 decoded / 167 trades / 1 failed tx
```
Les fichiers de surveillance à conserver sont :
- `validation_sql/SQL_VALIDATION_PUMP_SWAP_0_7_53.sql` ;
- `validation_sql/SQL_VALIDATION_DEX_COVERAGE_GLOBAL_0_7_53.sql` ;
- `docs/reports/DEX_COVERAGE_GLOBAL_WATCHLIST_0_7_53.md`.

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1 @@
{"version":"0.0.0","name":"add_decimals","instructions":[{"name":"initializeWrapper","accounts":[{"name":"wrapper","isMut":true,"isSigner":false},{"name":"wrapperUnderlyingTokens","isMut":false,"isSigner":false},{"name":"underlyingMint","isMut":false,"isSigner":false},{"name":"wrapperMint","isMut":false,"isSigner":false},{"name":"payer","isMut":false,"isSigner":true},{"name":"rent","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[{"name":"nonce","type":"u8"}]},{"name":"deposit","accounts":[{"name":"wrapper","isMut":false,"isSigner":false},{"name":"wrapperMint","isMut":true,"isSigner":false},{"name":"wrapperUnderlyingTokens","isMut":true,"isSigner":false},{"name":"owner","isMut":false,"isSigner":true},{"name":"userUnderlyingTokens","isMut":true,"isSigner":false},{"name":"userWrappedTokens","isMut":true,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false}],"args":[{"name":"depositAmount","type":"u64"}]},{"name":"withdraw","accounts":[{"name":"wrapper","isMut":false,"isSigner":false},{"name":"wrapperMint","isMut":true,"isSigner":false},{"name":"wrapperUnderlyingTokens","isMut":true,"isSigner":false},{"name":"owner","isMut":false,"isSigner":true},{"name":"userUnderlyingTokens","isMut":true,"isSigner":false},{"name":"userWrappedTokens","isMut":true,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false}],"args":[{"name":"maxBurnAmount","type":"u64"}]}],"accounts":[{"name":"WrappedToken","type":{"kind":"struct","fields":[{"name":"decimals","type":"u8"},{"name":"multiplier","type":"u64"},{"name":"wrapperUnderlyingMint","type":"publicKey"},{"name":"wrapperUnderlyingTokens","type":"publicKey"},{"name":"wrapperMint","type":"publicKey"}]}}],"events":[{"name":"InitEvent","fields":[{"name":"payer","type":"publicKey","index":false},{"name":"decimals","type":"u8","index":false},{"name":"multiplier","type":"u64","index":false},{"name":"wrapperUnderlyingMint","type":"publicKey","index":false},{"name":"wrapperUnderlyingTokens","type":"publicKey","index":false},{"name":"wrapperMint","type":"publicKey","index":false}]},{"name":"DepositEvent","fields":[{"name":"owner","type":"publicKey","index":false},{"name":"underlyingMint","type":"publicKey","index":false},{"name":"wrappedMint","type":"publicKey","index":false},{"name":"depositAmount","type":"u64","index":false},{"name":"mintAmount","type":"u64","index":false}]},{"name":"WithdrawEvent","fields":[{"name":"owner","type":"publicKey","index":false},{"name":"underlyingMint","type":"publicKey","index":false},{"name":"wrappedMint","type":"publicKey","index":false},{"name":"withdrawAmount","type":"u64","index":false},{"name":"burnAmount","type":"u64","index":false},{"name":"dustAmount","type":"u64","index":false}]}],"errors":[{"code":300,"name":"InitNonEmptyAccount","msg":"Wrapper underlying tokens account must be empty."},{"code":301,"name":"InitWrapperSupplyNonZero","msg":"Supply of the wrapper mint is non-zero"},{"code":302,"name":"InitWrapperUnderlyingOwnerMismatch","msg":"Owner of the wrapper underlying tokens account must be the wrapper"},{"code":303,"name":"InitWrapperUnderlyingMintMismatch","msg":"Underlying mint does not match underlying tokens account mint"},{"code":304,"name":"InitMintAuthorityMismatch","msg":"Mint authority mismatch"},{"code":305,"name":"InitMultiplierOverflow","msg":"Initial decimals too high"},{"code":306,"name":"InitWrapperDecimalsTooLow","msg":"The number of target decimals must be greater than or equal to the underlying asset's decimals."},{"code":307,"name":"MintAmountOverflow","msg":"Mint amount overflow. This error happens when the token cannot support this many decimals added to the token."},{"code":308,"name":"InvalidBurnAmount","msg":"Failed to convert burn amount from withdraw amount."},{"code":309,"name":"InvalidWithdrawAmount","msg":"Failed to convert withdraw amount from wrapped amount."},{"code":310,"name":"InsufficientUnderlyingBalance","msg":"User does not have enough underlying tokens"},{"code":311,"name":"InsufficientWrappedBalance","msg":"User does not have enough wrapped tokens"},{"code":312,"name":"ZeroAmount","msg":"Cannot send zero tokens"},{"code":313,"name":"UnknownAction","msg":"Unknown router action"}]}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

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

View File

@@ -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.52", "version": "0.7.53",
"identifier": "com.sasedev.kb-demo-app", "identifier": "com.sasedev.kb-demo-app",
"build": { "build": {
"beforeDevCommand": "npm run dev", "beforeDevCommand": "npm run dev",

View File

@@ -66,6 +66,7 @@ pub use pump_fun::PumpFunDecodedEvent;
pub use pump_fun::PumpFunDecoder; pub use pump_fun::PumpFunDecoder;
pub use pump_fun::PumpFunTradeDecoded; pub use pump_fun::PumpFunTradeDecoded;
pub use pump_swap::PumpSwapDecodedEvent; pub use pump_swap::PumpSwapDecodedEvent;
pub use pump_swap::PumpSwapInstructionDecoded;
pub use pump_swap::PumpSwapDecoder; pub use pump_swap::PumpSwapDecoder;
pub use pump_swap::PumpSwapTradeDecoded; pub use pump_swap::PumpSwapTradeDecoded;
pub use raydium_amm_v4::RaydiumAmmV4DecodedEvent; pub use raydium_amm_v4::RaydiumAmmV4DecodedEvent;

File diff suppressed because it is too large Load Diff

View File

@@ -82,7 +82,8 @@ impl DexDecodeService {
} }
let append_result = append_persisted_events_result( let append_result = append_persisted_events_result(
&mut persisted, &mut persisted,
self.decode_and_persist_raydium_stable_swap_events(&transaction, &instructions).await, self.decode_and_persist_raydium_stable_swap_events(&transaction, &instructions)
.await,
); );
if let Err(error) = append_result { if let Err(error) = append_result {
return Err(error); return Err(error);
@@ -1679,6 +1680,17 @@ impl DexDecodeService {
) )
.await; .await;
}, },
crate::PumpSwapDecodedEvent::BuyExactQuoteInTrade(event) => {
return self
.persist_pump_swap_trade_event(
transaction,
event,
"pump_swap.buy_exact_quote_in",
"signal.dex.pump_swap.buy_exact_quote_in",
"dex.pump_swap.buy_exact_quote_in",
)
.await;
},
crate::PumpSwapDecodedEvent::SellTrade(event) => { crate::PumpSwapDecodedEvent::SellTrade(event) => {
return self return self
.persist_pump_swap_trade_event( .persist_pump_swap_trade_event(
@@ -1690,9 +1702,35 @@ impl DexDecodeService {
) )
.await; .await;
}, },
crate::PumpSwapDecodedEvent::Instruction(event) => {
return self.persist_pump_swap_instruction_event(transaction, event).await;
},
} }
} }
async fn persist_pump_swap_instruction_event(
&self,
transaction: &crate::ChainTransactionDto,
event: &crate::PumpSwapInstructionDecoded,
) -> Result<crate::DexDecodedEventDto, crate::Error> {
return self
.materialize_named_dex_event(
transaction,
event.transaction_id,
event.instruction_id,
"pump_swap",
event.program_id.clone(),
event.event_kind.as_str(),
event.pool_account.clone(),
None,
event.token_a_mint.clone(),
event.token_b_mint.clone(),
event.lp_mint.clone(),
event.payload_json.clone(),
)
.await;
}
async fn persist_pump_swap_trade_event( async fn persist_pump_swap_trade_event(
&self, &self,
transaction: &crate::ChainTransactionDto, transaction: &crate::ChainTransactionDto,
@@ -1781,18 +1819,16 @@ impl DexDecodeService {
transaction: &crate::ChainTransactionDto, transaction: &crate::ChainTransactionDto,
instructions: &[crate::ChainInstructionDto], instructions: &[crate::ChainInstructionDto],
) -> Result<std::vec::Vec<crate::DexDecodedEventDto>, crate::Error> { ) -> Result<std::vec::Vec<crate::DexDecodedEventDto>, crate::Error> {
let decoded_result = self let decoded_result =
.raydium_stable_swap_decoder self.raydium_stable_swap_decoder.decode_transaction(transaction, instructions);
.decode_transaction(transaction, instructions);
let decoded_events = match decoded_result { let decoded_events = match decoded_result {
Ok(decoded_events) => decoded_events, Ok(decoded_events) => decoded_events,
Err(error) => return Err(error), Err(error) => return Err(error),
}; };
let mut persisted = std::vec::Vec::new(); let mut persisted = std::vec::Vec::new();
for decoded_event in &decoded_events { for decoded_event in &decoded_events {
let persist_result = self let persist_result =
.persist_raydium_stable_swap_event(transaction, decoded_event) self.persist_raydium_stable_swap_event(transaction, decoded_event).await;
.await;
let persisted_event = match persist_result { let persisted_event = match persist_result {
Ok(persisted_event) => persisted_event, Ok(persisted_event) => persisted_event,
Err(error) => return Err(error), Err(error) => return Err(error),
@@ -3686,7 +3722,10 @@ fn insert_raydium_mapped_amounts(
); );
} }
if let Some(open_time) = read_u64_le_from_bytes(data, 2) { if let Some(open_time) = read_u64_le_from_bytes(data, 2) {
object.insert("openTime".to_string(), serde_json::Value::String(open_time.to_string())); object.insert(
"openTime".to_string(),
serde_json::Value::String(open_time.to_string()),
);
} }
}, },
RaydiumMappedNonTradeAmountLayout::AmmV4Initialize2 => { RaydiumMappedNonTradeAmountLayout::AmmV4Initialize2 => {
@@ -3697,7 +3736,10 @@ fn insert_raydium_mapped_amounts(
); );
} }
if let Some(open_time) = read_u64_le_from_bytes(data, 2) { if let Some(open_time) = read_u64_le_from_bytes(data, 2) {
object.insert("openTime".to_string(), serde_json::Value::String(open_time.to_string())); object.insert(
"openTime".to_string(),
serde_json::Value::String(open_time.to_string()),
);
} }
if let Some(init_pc_amount) = read_u64_le_from_bytes(data, 10) { if let Some(init_pc_amount) = read_u64_le_from_bytes(data, 10) {
object.insert( object.insert(
@@ -3762,7 +3804,10 @@ fn insert_raydium_mapped_amounts(
); );
} }
if let Some(base_side) = read_u64_le_from_bytes(data, 17) { if let Some(base_side) = read_u64_le_from_bytes(data, 17) {
object.insert("baseSide".to_string(), serde_json::Value::String(base_side.to_string())); object.insert(
"baseSide".to_string(),
serde_json::Value::String(base_side.to_string()),
);
} }
if let Some(other_amount_min) = read_u64_le_from_bytes(data, 25) { if let Some(other_amount_min) = read_u64_le_from_bytes(data, 25) {
object.insert( object.insert(
@@ -3773,8 +3818,14 @@ fn insert_raydium_mapped_amounts(
}, },
RaydiumMappedNonTradeAmountLayout::AmmV4Withdraw => { RaydiumMappedNonTradeAmountLayout::AmmV4Withdraw => {
if let Some(lp_amount) = read_u64_le_from_bytes(data, 1) { if let Some(lp_amount) = read_u64_le_from_bytes(data, 1) {
object.insert("lpAmountRaw".to_string(), serde_json::Value::String(lp_amount.to_string())); object.insert(
object.insert("liquidity".to_string(), serde_json::Value::String(lp_amount.to_string())); "lpAmountRaw".to_string(),
serde_json::Value::String(lp_amount.to_string()),
);
object.insert(
"liquidity".to_string(),
serde_json::Value::String(lp_amount.to_string()),
);
} }
if let Some(min_coin_amount) = read_u64_le_from_bytes(data, 9) { if let Some(min_coin_amount) = read_u64_le_from_bytes(data, 9) {
object.insert( object.insert(
@@ -3797,7 +3848,10 @@ fn insert_raydium_mapped_amounts(
); );
} }
if let Some(value) = read_u64_le_from_bytes(data, 2) { if let Some(value) = read_u64_le_from_bytes(data, 2) {
object.insert("configValue".to_string(), serde_json::Value::String(value.to_string())); object.insert(
"configValue".to_string(),
serde_json::Value::String(value.to_string()),
);
} }
if let Some(last_order_denominator) = read_u64_le_from_bytes(data, 10) { if let Some(last_order_denominator) = read_u64_le_from_bytes(data, 10) {
object.insert( object.insert(
@@ -3808,7 +3862,8 @@ fn insert_raydium_mapped_amounts(
}, },
RaydiumMappedNonTradeAmountLayout::AmmV4WithdrawSrm => { RaydiumMappedNonTradeAmountLayout::AmmV4WithdrawSrm => {
if let Some(amount) = read_u64_le_from_bytes(data, 1) { if let Some(amount) = read_u64_le_from_bytes(data, 1) {
object.insert("amountRaw".to_string(), serde_json::Value::String(amount.to_string())); object
.insert("amountRaw".to_string(), serde_json::Value::String(amount.to_string()));
} }
}, },
RaydiumMappedNonTradeAmountLayout::AmmV4PreInitialize => { RaydiumMappedNonTradeAmountLayout::AmmV4PreInitialize => {
@@ -3833,10 +3888,16 @@ fn insert_raydium_mapped_amounts(
); );
} }
if let Some(amount_in) = read_u64_le_from_bytes(data, 2) { if let Some(amount_in) = read_u64_le_from_bytes(data, 2) {
object.insert("amountIn".to_string(), serde_json::Value::String(amount_in.to_string())); object.insert(
"amountIn".to_string(),
serde_json::Value::String(amount_in.to_string()),
);
} }
if let Some(amount_out) = read_u64_le_from_bytes(data, 10) { if let Some(amount_out) = read_u64_le_from_bytes(data, 10) {
object.insert("amountOutOrMinimumAmountOut".to_string(), serde_json::Value::String(amount_out.to_string())); object.insert(
"amountOutOrMinimumAmountOut".to_string(),
serde_json::Value::String(amount_out.to_string()),
);
} }
}, },
RaydiumMappedNonTradeAmountLayout::AmmV4AdminCancelOrders => { RaydiumMappedNonTradeAmountLayout::AmmV4AdminCancelOrders => {
@@ -4078,7 +4139,8 @@ fn build_meteora_instruction_audit_payload(
}; };
let data_base58 = parse_instruction_data_base58(instruction.data_json.as_deref()); let data_base58 = parse_instruction_data_base58(instruction.data_json.as_deref());
let data_bytes = instruction_data_bytes_from_base58(data_base58.as_deref()); let data_bytes = instruction_data_bytes_from_base58(data_base58.as_deref());
let discriminator_hex = raydium_instruction_discriminator_hex(protocol_name, data_bytes.as_deref(), 0); let discriminator_hex =
raydium_instruction_discriminator_hex(protocol_name, data_bytes.as_deref(), 0);
let anchor_self_cpi_log = let anchor_self_cpi_log =
discriminator_hex.as_deref() == Some(METEORA_ANCHOR_SELF_CPI_LOG_SELECTOR_HEX); discriminator_hex.as_deref() == Some(METEORA_ANCHOR_SELF_CPI_LOG_SELECTOR_HEX);
let anchor_event_discriminator_hex = if anchor_self_cpi_log { let anchor_event_discriminator_hex = if anchor_self_cpi_log {
@@ -4617,7 +4679,8 @@ fn build_raydium_instruction_audit_payload(
}; };
let data_base58 = parse_instruction_data_base58(instruction.data_json.as_deref()); let data_base58 = parse_instruction_data_base58(instruction.data_json.as_deref());
let data_bytes = instruction_data_bytes_from_base58(data_base58.as_deref()); let data_bytes = instruction_data_bytes_from_base58(data_base58.as_deref());
let discriminator_hex = raydium_instruction_discriminator_hex(protocol_name, data_bytes.as_deref(), 0); let discriminator_hex =
raydium_instruction_discriminator_hex(protocol_name, data_bytes.as_deref(), 0);
let anchor_self_cpi_log = let anchor_self_cpi_log =
discriminator_hex.as_deref() == Some(METEORA_ANCHOR_SELF_CPI_LOG_SELECTOR_HEX); discriminator_hex.as_deref() == Some(METEORA_ANCHOR_SELF_CPI_LOG_SELECTOR_HEX);
let anchor_event_discriminator_hex = if anchor_self_cpi_log { let anchor_event_discriminator_hex = if anchor_self_cpi_log {
@@ -5266,6 +5329,8 @@ fn prepare_pump_swap_trade_payload_for_classification(
}; };
if event.pool_account.is_some() && event.token_a_mint.is_some() && event.token_b_mint.is_some() if event.pool_account.is_some() && event.token_a_mint.is_some() && event.token_b_mint.is_some()
{ {
object.insert("tradeCandidate".to_string(), serde_json::Value::Bool(true));
object.insert("candleCandidate".to_string(), serde_json::Value::Bool(true));
return serde_json::Value::Object(object); return serde_json::Value::Object(object);
} }
object.insert("tradeCandidate".to_string(), serde_json::Value::Bool(false)); object.insert("tradeCandidate".to_string(), serde_json::Value::Bool(false));

View File

@@ -127,6 +127,14 @@ pub(crate) fn dex_detection_route(
} }
return Some(crate::dex_detection_route::DexDetectionRoute::PumpSwapTrade); return Some(crate::dex_detection_route::DexDetectionRoute::PumpSwapTrade);
}, },
("pump_swap", "pump_swap.buy_exact_quote_in") => {
if crate::dex_detection_route::is_incomplete_pump_swap_decoded_event(decoded_event) {
return Some(
crate::dex_detection_route::DexDetectionRoute::SkipIncompletePumpSwapTrade,
);
}
return Some(crate::dex_detection_route::DexDetectionRoute::PumpSwapTrade);
},
("pump_swap", "pump_swap.sell") => { ("pump_swap", "pump_swap.sell") => {
if crate::dex_detection_route::is_incomplete_pump_swap_decoded_event(decoded_event) { if crate::dex_detection_route::is_incomplete_pump_swap_decoded_event(decoded_event) {
return Some( return Some(
@@ -301,6 +309,47 @@ mod tests {
)); ));
} }
#[test]
fn pump_swap_buy_exact_quote_in_routes_to_pool_detection_when_complete() {
let event = make_decoded_event(
"pump_swap",
"pump_swap.buy_exact_quote_in",
Some("PumpSwapPool111"),
Some("TokenA111"),
Some("TokenB111"),
);
let route_option = crate::dex_detection_route::dex_detection_route(&event);
let route = match route_option {
Some(route) => route,
None => panic!("route must be selected"),
};
assert_eq!(route, crate::dex_detection_route::DexDetectionRoute::PumpSwapTrade);
assert!(crate::dex_detection_route::dex_detection_route_requires_full_pool_context(
route
));
}
#[test]
fn pump_swap_buy_exact_quote_in_incomplete_route_is_skipped() {
let event = make_decoded_event(
"pump_swap",
"pump_swap.buy_exact_quote_in",
Some("PumpSwapPool111"),
Some("TokenA111"),
None,
);
let route_option = crate::dex_detection_route::dex_detection_route(&event);
let route = match route_option {
Some(route) => route,
None => panic!("route must be selected"),
};
assert_eq!(
route,
crate::dex_detection_route::DexDetectionRoute::SkipIncompletePumpSwapTrade
);
}
#[test] #[test]
fn pump_fun_create_token_route_does_not_require_full_pool_context() { fn pump_fun_create_token_route_does_not_require_full_pool_context() {
let event = make_decoded_event( let event = make_decoded_event(

View File

@@ -329,6 +329,12 @@ pub fn is_dex_trade_event_kind(event_kind: &str) -> bool {
if event_kind.ends_with(".sell") { if event_kind.ends_with(".sell") {
return true; return true;
} }
if event_kind.contains(".buy_exact") {
return true;
}
if event_kind.contains(".sell_exact") {
return true;
}
if event_kind.ends_with(".swap") { if event_kind.ends_with(".swap") {
return true; return true;
} }
@@ -484,6 +490,12 @@ pub fn is_dex_fee_event_kind(event_kind: &str) -> bool {
if event_kind.contains("collect_creator_fee") { if event_kind.contains("collect_creator_fee") {
return true; return true;
} }
if event_kind.contains("collect_coin_creator_fee") {
return true;
}
if event_kind.contains("transfer_creator_fees") {
return true;
}
if event_kind.contains("collect_protocol_fee") { if event_kind.contains("collect_protocol_fee") {
return true; return true;
} }
@@ -519,6 +531,18 @@ pub fn is_dex_reward_event_kind(event_kind: &str) -> bool {
if event_kind.contains("emission") { if event_kind.contains("emission") {
return true; return true;
} }
if event_kind.contains("incentive") {
return true;
}
if event_kind.contains("toggle_cashback_enabled") {
return false;
}
if event_kind.contains("cashback") {
return true;
}
if event_kind.contains("volume_accumulator") {
return true;
}
return false; return false;
} }
@@ -657,6 +681,9 @@ pub fn is_dex_migration_event_kind(event_kind: &str) -> bool {
if event_kind.contains(".migrate_to_open_book") { if event_kind.contains(".migrate_to_open_book") {
return false; return false;
} }
if event_kind.contains(".migrate_pool_coin_creator") {
return false;
}
if event_kind.contains(".migrate") { if event_kind.contains(".migrate") {
return true; return true;
} }
@@ -759,6 +786,9 @@ pub fn is_dex_admin_event_kind(event_kind: &str) -> bool {
if event_kind.contains(".migrate_to_open_book") { if event_kind.contains(".migrate_to_open_book") {
return false; return false;
} }
if event_kind.contains(".migrate_pool_coin_creator") {
return true;
}
if event_kind.contains(".close_platform_global_access") { if event_kind.contains(".close_platform_global_access") {
return true; return true;
} }
@@ -789,6 +819,15 @@ pub fn is_dex_admin_event_kind(event_kind: &str) -> bool {
if event_kind.contains("set_") { if event_kind.contains("set_") {
return true; return true;
} }
if event_kind.contains("toggle_") {
return true;
}
if event_kind.contains(".disable") {
return true;
}
if event_kind.contains(".extend_account") {
return true;
}
if event_kind.contains("update_") { if event_kind.contains("update_") {
return true; return true;
} }
@@ -1154,6 +1193,19 @@ mod tests {
super::classify_dex_event_category_code("raydium_clmm.set_reward_params"), super::classify_dex_event_category_code("raydium_clmm.set_reward_params"),
"reward" "reward"
); );
assert_eq!(
super::classify_dex_event_category_code("pump_swap.toggle_cashback_enabled"),
"admin"
);
assert!(!super::is_dex_reward_event_kind("pump_swap.toggle_cashback_enabled"));
assert!(super::is_dex_admin_event_kind("pump_swap.toggle_cashback_enabled"));
assert_eq!(
super::classify_dex_event_category_code("pump_swap.migrate_pool_coin_creator"),
"admin"
);
assert!(!super::is_dex_pool_lifecycle_event_kind(
"pump_swap.migrate_pool_coin_creator"
));
assert_eq!( assert_eq!(
super::classify_dex_event_category_code("raydium_clmm.increase_liquidity_v2"), super::classify_dex_event_category_code("raydium_clmm.increase_liquidity_v2"),
"liquidity" "liquidity"

View File

@@ -89,6 +89,16 @@ impl DexEventCoverageService {
Ok(sync_counts) => sync_counts, Ok(sync_counts) => sync_counts,
Err(error) => return Err(error), Err(error) => return Err(error),
}; };
let cleanup_result =
self.cleanup_deprecated_pump_swap_observed_unknown_rows(&decoder_code).await;
if let Err(error) = cleanup_result {
return Err(error);
}
let duplicate_cleanup_result =
self.cleanup_duplicate_pump_swap_logical_coverage_rows(&decoder_code).await;
if let Err(error) = duplicate_cleanup_result {
return Err(error);
}
let refreshed_entry_count = match &decoder_code { let refreshed_entry_count = match &decoder_code {
Some(decoder_code) => { Some(decoder_code) => {
let refresh_result = let refresh_result =
@@ -139,6 +149,16 @@ impl DexEventCoverageService {
Ok(sync_counts) => sync_counts, Ok(sync_counts) => sync_counts,
Err(error) => return Err(error), Err(error) => return Err(error),
}; };
let cleanup_result =
self.cleanup_deprecated_pump_swap_observed_unknown_rows(&decoder_code).await;
if let Err(error) = cleanup_result {
return Err(error);
}
let duplicate_cleanup_result =
self.cleanup_duplicate_pump_swap_logical_coverage_rows(&decoder_code).await;
if let Err(error) = duplicate_cleanup_result {
return Err(error);
}
let refreshed_entry_count = match &decoder_code { let refreshed_entry_count = match &decoder_code {
Some(decoder_code) => { Some(decoder_code) => {
let refresh_result = let refresh_result =
@@ -178,6 +198,86 @@ impl DexEventCoverageService {
summaries, summaries,
}); });
} }
async fn cleanup_deprecated_pump_swap_observed_unknown_rows(
&self,
decoder_code: &std::option::Option<std::string::String>,
) -> Result<u64, crate::Error> {
if let Some(decoder_code) = decoder_code {
if decoder_code != "pump_swap" {
return Ok(0);
}
}
match self.database.connection() {
crate::DatabaseConnection::Sqlite(pool) => {
let query_result = sqlx::query(
r#"
DELETE FROM k_sol_dex_event_coverage_entries
WHERE decoder_code = 'pump_swap'
AND (
entry_name LIKE 'observed_unknown_%'
OR local_event_kind LIKE 'pump_swap.observed_unknown_%'
)
"#,
)
.execute(pool)
.await;
match query_result {
Ok(query_result) => return Ok(query_result.rows_affected()),
Err(error) => {
return Err(crate::Error::Db(format!(
"cannot delete deprecated PumpSwap observed_unknown coverage rows on sqlite: {}",
error
)));
},
}
},
}
}
async fn cleanup_duplicate_pump_swap_logical_coverage_rows(
&self,
decoder_code: &std::option::Option<std::string::String>,
) -> Result<u64, crate::Error> {
if let Some(decoder_code) = decoder_code {
if decoder_code != "pump_swap" {
return Ok(0);
}
}
match self.database.connection() {
crate::DatabaseConnection::Sqlite(pool) => {
let query_result = sqlx::query(
r#"
DELETE FROM k_sol_dex_event_coverage_entries
WHERE decoder_code = 'pump_swap'
AND id NOT IN (
SELECT MIN(id)
FROM k_sol_dex_event_coverage_entries
WHERE decoder_code = 'pump_swap'
GROUP BY
decoder_code,
COALESCE(program_id, ''),
entry_kind,
entry_name,
COALESCE(discriminator_hex, ''),
COALESCE(local_event_kind, '')
)
"#,
)
.execute(pool)
.await;
match query_result {
Ok(query_result) => return Ok(query_result.rows_affected()),
Err(error) => {
return Err(crate::Error::Db(format!(
"cannot delete duplicate PumpSwap logical coverage rows on sqlite: {}",
error
)));
},
}
},
}
}
} }
fn build_coverage_entry_from_upstream( fn build_coverage_entry_from_upstream(
@@ -215,6 +315,9 @@ fn infer_expected_db_target_for_entry(
event_family: std::option::Option<&str>, event_family: std::option::Option<&str>,
entry_kind: &str, entry_kind: &str,
) -> std::option::Option<std::string::String> { ) -> std::option::Option<std::string::String> {
if decoder_code == "pump_swap" {
return infer_pump_swap_expected_db_target(entry_name, entry_kind);
}
if decoder_code == "raydium_cpmm" if decoder_code == "raydium_cpmm"
&& (entry_name == "swap_event" || entry_name == "anchor_idl_instruction") && (entry_name == "swap_event" || entry_name == "anchor_idl_instruction")
{ {
@@ -421,11 +524,201 @@ fn infer_expected_db_target(
return Some(target.to_string()); return Some(target.to_string());
} }
fn infer_pump_swap_expected_db_target(
entry_name: &str,
entry_kind: &str,
) -> std::option::Option<std::string::String> {
if entry_kind == crate::ENTRY_KIND_PROGRAM {
return None;
}
if entry_name == "buy" || entry_name == "sell" || entry_name == "buy_exact_quote_in" {
return Some(crate::DexEventCoverageEntryDto::DB_TARGET_TRADE_EVENTS.to_string());
}
if entry_name.starts_with("observed_unknown_") {
return Some(crate::DexEventCoverageEntryDto::DB_TARGET_DECODED_EVENTS_ONLY.to_string());
}
if entry_name.ends_with("_event") && entry_name != "claim_token_incentives_event" {
return Some(crate::DexEventCoverageEntryDto::DB_TARGET_DECODED_EVENTS_ONLY.to_string());
}
if entry_name == "deposit"
|| entry_name == "deposit_event"
|| entry_name == "withdraw"
|| entry_name == "withdraw_event"
{
return Some(crate::DexEventCoverageEntryDto::DB_TARGET_LIQUIDITY_EVENTS.to_string());
}
if entry_name == "create_pool" || entry_name == "create_pool_event" {
return Some(crate::DexEventCoverageEntryDto::DB_TARGET_POOL_LIFECYCLE_EVENTS.to_string());
}
if entry_name == "collect_coin_creator_fee"
|| entry_name == "collect_coin_creator_fee_event"
|| entry_name == "transfer_creator_fees_to_pump"
|| entry_name == "transfer_creator_fees_to_pump_v2"
{
return Some(crate::DexEventCoverageEntryDto::DB_TARGET_FEE_EVENTS.to_string());
}
if entry_name == "claim_cashback"
|| entry_name == "claim_cashback_event"
|| entry_name == "claim_token_incentives"
|| entry_name == "claim_token_incentives_event"
|| entry_name == "admin_update_token_incentives"
|| entry_name == "admin_update_token_incentives_event"
|| entry_name == "init_user_volume_accumulator"
|| entry_name == "init_user_volume_accumulator_event"
|| entry_name == "sync_user_volume_accumulator"
|| entry_name == "sync_user_volume_accumulator_event"
|| entry_name == "close_user_volume_accumulator"
|| entry_name == "close_user_volume_accumulator_event"
{
return Some(crate::DexEventCoverageEntryDto::DB_TARGET_REWARD_EVENTS.to_string());
}
if entry_name == "admin_set_coin_creator"
|| entry_name == "admin_set_coin_creator_event"
|| entry_name == "create_config"
|| entry_name == "create_config_event"
|| entry_name == "disable"
|| entry_name == "disable_event"
|| entry_name == "extend_account"
|| entry_name == "extend_account_event"
|| entry_name == "migrate_pool_coin_creator"
|| entry_name == "migrate_pool_coin_creator_event"
|| entry_name == "reserved_fee_recipients_event"
|| entry_name == "set_bonding_curve_coin_creator_event"
|| entry_name == "set_coin_creator"
|| entry_name == "set_metaplex_coin_creator_event"
|| entry_name == "set_reserved_fee_recipient"
|| entry_name == "set_reserved_fee_recipients"
|| entry_name == "toggle_cashback_enabled"
|| entry_name == "toggle_mayhem_mode"
|| entry_name == "update_admin"
|| entry_name == "update_buyback_config"
|| entry_name == "update_admin_event"
|| entry_name == "update_fee_config"
|| entry_name == "update_fee_config_event"
{
return Some(crate::DexEventCoverageEntryDto::DB_TARGET_POOL_ADMIN_EVENTS.to_string());
}
return Some(crate::DexEventCoverageEntryDto::DB_TARGET_DECODED_EVENTS_ONLY.to_string());
}
fn infer_pump_swap_event_family(
entry_name: &str,
entry_kind: &str,
) -> std::option::Option<std::string::String> {
if entry_kind == crate::ENTRY_KIND_PROGRAM {
return None;
}
if entry_name == "buy" || entry_name == "sell" || entry_name == "buy_exact_quote_in" {
return Some("swap".to_string());
}
if entry_name == "buy_event" || entry_name == "sell_event" {
return Some("swap_event_audit".to_string());
}
if entry_name == "deposit" || entry_name == "deposit_event" {
return Some("liquidity_add".to_string());
}
if entry_name == "withdraw" || entry_name == "withdraw_event" {
return Some("liquidity_remove".to_string());
}
if entry_name == "create_pool" || entry_name == "create_pool_event" {
return Some("pool_create".to_string());
}
if entry_name.starts_with("observed_unknown_") {
return Some("observed_unknown_instruction".to_string());
}
if entry_name == "toggle_cashback_enabled"
|| entry_name == "set_reserved_fee_recipient"
|| entry_name == "set_reserved_fee_recipients"
|| entry_name == "reserved_fee_recipients_event"
{
return Some("admin_config".to_string());
}
if entry_name.contains("creator_fee") || entry_name.contains("fee_recipient") {
return Some("fee".to_string());
}
if entry_name.contains("cashback")
|| entry_name.contains("incentive")
|| entry_name.contains("volume_accumulator")
{
return Some("reward".to_string());
}
if entry_name.contains("config")
|| entry_name.contains("admin")
|| entry_name.contains("disable")
|| entry_name.contains("toggle")
|| entry_name.contains("coin_creator")
|| entry_name.contains("extend_account")
{
return Some("admin_config".to_string());
}
return infer_event_family(entry_name, entry_kind);
}
fn pump_swap_local_event_kind(entry_name: &str) -> std::option::Option<std::string::String> {
if entry_name.ends_with("_event") {
return Some(format!("pump_swap.{}", entry_name));
}
match entry_name {
"admin_set_coin_creator" => return Some("pump_swap.admin_set_coin_creator".to_string()),
"admin_update_token_incentives" => {
return Some("pump_swap.admin_update_token_incentives".to_string());
},
"buy" => return Some("pump_swap.buy".to_string()),
"buy_exact_quote_in" => return Some("pump_swap.buy_exact_quote_in".to_string()),
"claim_cashback" => return Some("pump_swap.claim_cashback".to_string()),
"claim_token_incentives" => return Some("pump_swap.claim_token_incentives".to_string()),
"close_user_volume_accumulator" => {
return Some("pump_swap.close_user_volume_accumulator".to_string());
},
"collect_coin_creator_fee" => {
return Some("pump_swap.collect_coin_creator_fee".to_string());
},
"create_config" => return Some("pump_swap.create_config".to_string()),
"create_pool" => return Some("pump_swap.create_pool".to_string()),
"deposit" => return Some("pump_swap.deposit".to_string()),
"disable" => return Some("pump_swap.disable".to_string()),
"extend_account" => return Some("pump_swap.extend_account".to_string()),
"init_user_volume_accumulator" => {
return Some("pump_swap.init_user_volume_accumulator".to_string());
},
"migrate_pool_coin_creator" => {
return Some("pump_swap.migrate_pool_coin_creator".to_string());
},
"sell" => return Some("pump_swap.sell".to_string()),
"set_coin_creator" => return Some("pump_swap.set_coin_creator".to_string()),
"set_reserved_fee_recipients" => {
return Some("pump_swap.set_reserved_fee_recipients".to_string());
},
"sync_user_volume_accumulator" => {
return Some("pump_swap.sync_user_volume_accumulator".to_string());
},
"toggle_cashback_enabled" => return Some("pump_swap.toggle_cashback_enabled".to_string()),
"toggle_mayhem_mode" => return Some("pump_swap.toggle_mayhem_mode".to_string()),
"transfer_creator_fees_to_pump" => {
return Some("pump_swap.transfer_creator_fees_to_pump".to_string());
},
"transfer_creator_fees_to_pump_v2" => {
return Some("pump_swap.transfer_creator_fees_to_pump_v2".to_string());
},
"update_admin" => return Some("pump_swap.update_admin".to_string()),
"update_buyback_config" => return Some("pump_swap.update_buyback_config".to_string()),
"update_fee_config" => return Some("pump_swap.update_fee_config".to_string()),
"withdraw" => return Some("pump_swap.withdraw".to_string()),
"set_reserved_fee_recipient" => {
return Some("pump_swap.set_reserved_fee_recipient".to_string());
},
_ => return None,
}
}
fn infer_event_family_for_entry( fn infer_event_family_for_entry(
decoder_code: &str, decoder_code: &str,
entry_name: &str, entry_name: &str,
entry_kind: &str, entry_kind: &str,
) -> std::option::Option<std::string::String> { ) -> std::option::Option<std::string::String> {
if decoder_code == "pump_swap" {
return infer_pump_swap_event_family(entry_name, entry_kind);
}
if decoder_code == "raydium_launchpad" { if decoder_code == "raydium_launchpad" {
return infer_raydium_launchpad_event_family(entry_name, entry_kind); return infer_raydium_launchpad_event_family(entry_name, entry_kind);
} }
@@ -488,7 +781,6 @@ fn infer_raydium_cpmm_event_family(
} }
} }
fn infer_raydium_stable_swap_event_family( fn infer_raydium_stable_swap_event_family(
entry_name: &str, entry_name: &str,
entry_kind: &str, entry_kind: &str,
@@ -789,7 +1081,6 @@ fn raydium_amm_v4_local_event_kind(entry_name: &str) -> std::option::Option<std:
} }
} }
fn raydium_stable_swap_local_event_kind( fn raydium_stable_swap_local_event_kind(
entry_name: &str, entry_name: &str,
) -> std::option::Option<std::string::String> { ) -> std::option::Option<std::string::String> {
@@ -819,6 +1110,9 @@ pub(crate) fn known_local_event_kind(
decoder_code: &str, decoder_code: &str,
entry_name: &str, entry_name: &str,
) -> std::option::Option<std::string::String> { ) -> std::option::Option<std::string::String> {
if decoder_code == "pump_swap" {
return pump_swap_local_event_kind(entry_name);
}
if decoder_code == "raydium_amm_v4" { if decoder_code == "raydium_amm_v4" {
return raydium_amm_v4_local_event_kind(entry_name); return raydium_amm_v4_local_event_kind(entry_name);
} }

View File

@@ -319,6 +319,63 @@ fn resolve_instruction_name(
}; };
return Some(format!("raydium_launchpad.{}", layout.instruction_name)); return Some(format!("raydium_launchpad.{}", layout.instruction_name));
} }
if program_id == crate::PUMP_SWAP_PROGRAM_ID || decoder_code == Some("pump_swap") {
let name = match discriminator_hex {
"e445a52e51cb9a1d" => "anchor_self_cpi_log",
"f228759149606968" => "admin_set_coin_creator",
"d10b7357d5177ccc" => "admin_update_token_incentives",
"66063d1201daebea" => "buy",
"c62e1552b4d9e870" => "buy_exact_quote_in",
"253a237ebe35e4c5" => "claim_cashback",
"1004471ccc01281b" => "claim_token_incentives",
"f945a4da9667548a" => "close_user_volume_accumulator",
"a039592ab58b2b42" => "collect_coin_creator_fee",
"c9cff3724b6f2fbd" => "create_config",
"e992d18ecf6840bc" => "create_pool",
"f223c68952e1f2b6" => "deposit",
"b9adbb5ad80feee9" => "disable",
"ea66c2cb96483ee5" => "extend_account",
"5e06ca73ff60e8b7" => "init_user_volume_accumulator",
"d0089f044aaf103a" => "migrate_pool_coin_creator",
"33e685a4017f83ad" => "sell",
"d295802dbc3a4eaf" => "set_coin_creator",
"6faca2e87259d58e" => "set_reserved_fee_recipients",
"561fc057a3574fee" => "sync_user_volume_accumulator",
"7367e0ffbd5956c3" => "toggle_cashback_enabled",
"01096fd0641fffa3" => "toggle_mayhem_mode",
"8b348655e4e56cf1" => "transfer_creator_fees_to_pump",
"01214eb921432c5c" => "transfer_creator_fees_to_pump_v2",
"a1b028d53cb8b3e4" => "update_admin",
"fbe0ab92a01a71e9" => "update_buyback_config",
"68b867f258976b14" => "update_fee_config",
"b712469c946da122" => "withdraw",
"cfbdb247a77a44b4" => "set_reserved_fee_recipient",
"2ddc5d181961ac68" => "admin_set_coin_creator_event",
"93fa6c78f71d43de" => "admin_update_token_incentives_event",
"67f4521f2cf57777" => "buy_event",
"e2d6f62107f293e5" => "claim_cashback_event",
"4facf631cd5bcee8" => "claim_token_incentives_event",
"929fbdac925838f4" => "close_user_volume_accumulator_event",
"e8f5c2eeeada3a59" => "collect_coin_creator_fee_event",
"6b34598137e25116" => "create_config_event",
"b1310cd2a076a774" => "create_pool_event",
"78f83d531f8e6b90" => "deposit_event",
"6bfdc14ce4ca1b68" => "disable_event",
"6161d7905d92167c" => "extend_account_event",
"86240d48e86582d8" => "init_user_volume_accumulator_event",
"aadd52c793a5f72e" => "migrate_pool_coin_creator_event",
"2bbcfa12dd4bbb5f" => "reserved_fee_recipients_event",
"3e2f370aa503dc2a" => "sell_event",
"f2e7eb664163bdd3" => "set_bonding_curve_coin_creator_event",
"966bc77b7ccf66e4" => "set_metaplex_coin_creator_event",
"c57aa77c74515bff" => "sync_user_volume_accumulator_event",
"e198ab57f63f42ea" => "update_admin_event",
"5a1741233ef4bcd0" => "update_fee_config_event",
"1609851aa02c47c0" => "withdraw_event",
_ => return None,
};
return Some(name.to_string());
}
return None; return None;
} }

View File

@@ -1183,6 +1183,8 @@ pub use dex::PumpFunTradeDecoded;
pub use dex::PumpSwapDecodedEvent; pub use dex::PumpSwapDecodedEvent;
/// PumpSwap decoder. /// PumpSwap decoder.
pub use dex::PumpSwapDecoder; pub use dex::PumpSwapDecoder;
/// Decoded PumpSwap non-trade instruction event.
pub use dex::PumpSwapInstructionDecoded;
/// Decoded PumpSwap trade event. /// Decoded PumpSwap trade event.
pub use dex::PumpSwapTradeDecoded; pub use dex::PumpSwapTradeDecoded;
/// Decoded Raydium AmmV4 event. /// Decoded Raydium AmmV4 event.

View File

@@ -109,6 +109,9 @@ impl NonTradeEventMaterializationService {
continue; continue;
}, },
}; };
if is_anchor_event_audit_only(&payload) {
continue;
}
if crate::is_dex_pool_lifecycle_event_kind(decoded_event.event_kind.as_str()) { if crate::is_dex_pool_lifecycle_event_kind(decoded_event.event_kind.as_str()) {
let cleanup_result = let cleanup_result =
self.delete_stale_pool_admin_event_for_lifecycle(decoded_event).await; self.delete_stale_pool_admin_event_for_lifecycle(decoded_event).await;
@@ -1935,6 +1938,18 @@ fn extract_first_bool(
return None; return None;
} }
fn is_anchor_event_audit_only(payload: &serde_json::Value) -> bool {
if let Some(object) = payload.as_object() {
let flag = object.get("anchorEventAuditOnly");
if let Some(flag) = flag {
if flag.as_bool() == Some(true) {
return true;
}
}
}
return false;
}
fn transaction_has_effective_error(transaction: &crate::ChainTransactionDto) -> bool { fn transaction_has_effective_error(transaction: &crate::ChainTransactionDto) -> bool {
let err_json = match transaction.err_json.as_ref() { let err_json = match transaction.err_json.as_ref() {
Some(err_json) => err_json.trim(), Some(err_json) => err_json.trim(),

View File

@@ -47,9 +47,10 @@ impl TradeAggregationService {
Err(error) => return Err(error), Err(error) => return Err(error),
}; };
let transaction = transaction_context.transaction; let transaction = transaction_context.transaction;
if transaction.err_json.is_some() { if crate::trade_aggregation::transaction_has_effective_error(&transaction) {
tracing::debug!( tracing::debug!(
signature = %transaction.signature, signature = %transaction.signature,
err_json = ?transaction.err_json,
"skipping trade aggregation for failed transaction" "skipping trade aggregation for failed transaction"
); );
return Ok(std::vec::Vec::new()); return Ok(std::vec::Vec::new());
@@ -199,6 +200,21 @@ impl TradeAggregationService {
} }
} }
fn transaction_has_effective_error(transaction: &crate::ChainTransactionDto) -> bool {
let err_json = match transaction.err_json.as_ref() {
Some(err_json) => err_json.trim(),
None => return false,
};
if err_json.is_empty() {
return false;
}
if err_json == "null" {
return false;
}
return true;
}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
async fn make_database() -> std::sync::Arc<crate::Database> { async fn make_database() -> std::sync::Arc<crate::Database> {
@@ -416,6 +432,51 @@ mod tests {
assert_eq!(pair_metric.trade_count, 1); assert_eq!(pair_metric.trade_count, 1);
} }
#[test]
fn transaction_null_err_json_is_not_effective_error() {
let transaction = crate::ChainTransactionDto::new(
"sig-null-err".to_string(),
Some(1),
None,
Some("test".to_string()),
Some("0".to_string()),
Some("null".to_string()),
None,
serde_json::json!({"meta":{"err":null}}).to_string(),
);
assert!(!super::transaction_has_effective_error(&transaction));
}
#[test]
fn transaction_empty_err_json_is_not_effective_error() {
let transaction = crate::ChainTransactionDto::new(
"sig-empty-err".to_string(),
Some(1),
None,
Some("test".to_string()),
Some("0".to_string()),
Some("".to_string()),
None,
serde_json::json!({"meta":{"err":null}}).to_string(),
);
assert!(!super::transaction_has_effective_error(&transaction));
}
#[test]
fn transaction_non_null_err_json_is_effective_error() {
let transaction = crate::ChainTransactionDto::new(
"sig-real-err".to_string(),
Some(1),
None,
Some("test".to_string()),
Some("0".to_string()),
Some(serde_json::json!({"InstructionError":[0,{"Custom":1}]}).to_string()),
None,
serde_json::json!({"meta":{"err":{"InstructionError":[0,{"Custom":1}]}}}).to_string(),
);
assert!(super::transaction_has_effective_error(&transaction));
}
#[tokio::test] #[tokio::test]
async fn record_transaction_by_signature_skips_failed_transaction() { async fn record_transaction_by_signature_skips_failed_transaction() {
let database = make_database().await; let database = make_database().await;

View File

@@ -138,6 +138,14 @@ pub(crate) async fn resolve_trade_amounts(
return Err(error); return Err(error);
} }
} }
if input.decoded_event.event_kind == "pump_swap.buy_exact_quote_in" {
crate::trade_amount_resolution::normalize_pump_swap_anchor_buy_exact_quote_in_amounts(
input,
&mut base_amount_raw,
&mut quote_amount_raw,
&mut resolved_trade_side,
);
}
if input.decoded_event.event_kind.starts_with("raydium_launchpad.") if input.decoded_event.event_kind.starts_with("raydium_launchpad.")
&& (base_amount_raw.is_none() && (base_amount_raw.is_none()
|| quote_amount_raw.is_none() || quote_amount_raw.is_none()
@@ -442,7 +450,12 @@ async fn apply_pump_swap_amount_fallbacks(
None => payload_pool_quote_token_account.as_deref(), None => payload_pool_quote_token_account.as_deref(),
}; };
let (input_vault_address, output_vault_address, input_token_account, output_token_account) = let (input_vault_address, output_vault_address, input_token_account, output_token_account) =
if input.decoded_event.event_kind.ends_with(".buy") { if input.decoded_event.event_kind.ends_with(".buy")
|| input
.decoded_event
.event_kind
.ends_with(".buy_exact_quote_in")
{
( (
effective_quote_vault_address, effective_quote_vault_address,
effective_base_vault_address, effective_base_vault_address,
@@ -1401,6 +1414,74 @@ async fn load_decoded_instruction(
return Ok(instruction_option); return Ok(instruction_option);
} }
fn normalize_pump_swap_anchor_buy_exact_quote_in_amounts(
input: &crate::trade_amount_resolution::TradeAmountResolutionInput<'_>,
base_amount_raw: &mut std::option::Option<std::string::String>,
quote_amount_raw: &mut std::option::Option<std::string::String>,
resolved_trade_side: &mut std::option::Option<crate::SwapTradeSide>,
) {
let amount_source = crate::trade_amount_resolution::extract_string_by_candidate_keys(
input.payload,
&["amountSource", "amount_source"],
);
if amount_source.as_deref() != Some("pump_swap_anchor_buy_event") {
return;
}
let token_a_mint = crate::trade_amount_resolution::extract_string_by_candidate_keys(
input.payload,
&["tokenAMint", "token_a_mint"],
);
let token_b_mint = crate::trade_amount_resolution::extract_string_by_candidate_keys(
input.payload,
&["tokenBMint", "token_b_mint"],
);
let token_a_mint = match token_a_mint.as_deref() {
Some(token_a_mint) => token_a_mint,
None => return,
};
let token_b_mint = match token_b_mint.as_deref() {
Some(token_b_mint) => token_b_mint,
None => return,
};
let pair_base_mint = match input.base_token_mint {
Some(pair_base_mint) => pair_base_mint,
None => return,
};
let pair_quote_mint = match input.quote_token_mint {
Some(pair_quote_mint) => pair_quote_mint,
None => return,
};
let anchor_base_amount_raw = crate::trade_amount_resolution::extract_amount_string(
input.payload,
&["baseAmountRaw", "baseAmountOutRaw", "base_amount_out_raw"],
);
let anchor_quote_amount_raw = crate::trade_amount_resolution::extract_amount_string(
input.payload,
&["quoteAmountRaw", "userQuoteAmountInRaw", "user_quote_amount_in_raw"],
);
let anchor_base_amount_raw = match anchor_base_amount_raw {
Some(anchor_base_amount_raw) => anchor_base_amount_raw,
None => return,
};
let anchor_quote_amount_raw = match anchor_quote_amount_raw {
Some(anchor_quote_amount_raw) => anchor_quote_amount_raw,
None => return,
};
if pair_base_mint == token_a_mint && pair_quote_mint == token_b_mint {
*base_amount_raw = Some(anchor_base_amount_raw);
*quote_amount_raw = Some(anchor_quote_amount_raw);
*resolved_trade_side = Some(crate::SwapTradeSide::BuyBase);
return;
}
if pair_base_mint == token_b_mint && pair_quote_mint == token_a_mint {
*base_amount_raw = Some(anchor_quote_amount_raw);
*quote_amount_raw = Some(anchor_base_amount_raw);
*resolved_trade_side = Some(crate::SwapTradeSide::SellBase);
return;
}
}
fn extract_amount_string( fn extract_amount_string(
payload: &serde_json::Value, payload: &serde_json::Value,
candidate_keys: &[&str], candidate_keys: &[&str],

View File

@@ -23,10 +23,10 @@ pub(crate) fn extract_trade_side(
Some("SELL") => return crate::SwapTradeSide::SellBase, Some("SELL") => return crate::SwapTradeSide::SellBase,
_ => {}, _ => {},
} }
if event_kind.ends_with(".buy") { if event_kind.ends_with(".buy") || event_kind.contains(".buy_exact") {
return crate::SwapTradeSide::BuyBase; return crate::SwapTradeSide::BuyBase;
} }
if event_kind.ends_with(".sell") { if event_kind.ends_with(".sell") || event_kind.contains(".sell_exact") {
return crate::SwapTradeSide::SellBase; return crate::SwapTradeSide::SellBase;
} }
return crate::SwapTradeSide::Unknown; return crate::SwapTradeSide::Unknown;
@@ -109,6 +109,13 @@ mod tests {
assert_eq!(side, crate::SwapTradeSide::SellBase); assert_eq!(side, crate::SwapTradeSide::SellBase);
} }
#[test]
fn buy_exact_suffix_is_resolved_as_buy_base() {
let payload = serde_json::json!({});
let side = super::extract_trade_side("pump_swap.buy_exact_quote_in", &payload);
assert_eq!(side, crate::SwapTradeSide::BuyBase);
}
#[test] #[test]
fn unknown_side_is_returned_when_no_hint_exists() { fn unknown_side_is_returned_when_no_hint_exists() {
let payload = serde_json::json!({}); let payload = serde_json::json!({});

View File

@@ -14,9 +14,8 @@ const UPSTREAM_GIT_ALIAS_PROGRAM_NOTES: &str = "upstream Git decoder name kept a
const RAYDIUM_IDL_SOURCE_REPO: &str = "raydium-io/raydium-idl"; const RAYDIUM_IDL_SOURCE_REPO: &str = "raydium-io/raydium-idl";
const MANUAL_SOLSCAN_SOURCE_REPO: &str = "manual-solscan"; const MANUAL_SOLSCAN_SOURCE_REPO: &str = "manual-solscan";
const RAYDIUM_IDL_DISCRIMINATOR_NOTES: &str = "entry name and discriminator extracted from Raydium official IDL snapshot; not corpus-verified; no trade/candle/materialization proof"; const RAYDIUM_IDL_DISCRIMINATOR_NOTES: &str = "entry name and discriminator extracted from Raydium official IDL snapshot; not corpus-verified; no trade/candle/materialization proof";
const MANUAL_SOLSCAN_DISCRIMINATOR_NOTES: &str = "entry name and discriminator derived from local corpus plus manual Solscan transaction-log inspection; no trade/candle/materialization proof"; const MANUAL_SOLSCAN_DISCRIMINATOR_NOTES: &str = "entry name and discriminator derived from manual Solscan program IDL or Solscan transaction-log inspection; no trade/candle/materialization proof";
const fn manual_solscan_discriminator_entry( const fn manual_solscan_discriminator_entry(
decoder_code: &'static str, decoder_code: &'static str,
@@ -10735,6 +10734,39 @@ pub(crate) const UPSTREAM_REGISTRY_ENTRIES: &[crate::UpstreamRegistryEntry] = &[
8, 8,
"decoders/pump-swap-decoder/src/instructions/withdraw.rs", "decoders/pump-swap-decoder/src/instructions/withdraw.rs",
), ),
manual_solscan_discriminator_entry(
"pump_swap",
Some(crate::PUMP_SWAP_PROGRAM_ID),
"pump",
"amm",
crate::ENTRY_KIND_INSTRUCTION,
"transfer_creator_fees_to_pump_v2",
"01214eb921432c5c",
8,
"solscan.io/account/pAMMBay6oceH9fJKBRHGP5D4bD4sWpmSwMn52FMfXEA#programIdl:transfer_creator_fees_to_pump_v2",
),
manual_solscan_discriminator_entry(
"pump_swap",
Some(crate::PUMP_SWAP_PROGRAM_ID),
"pump",
"amm",
crate::ENTRY_KIND_INSTRUCTION,
"update_buyback_config",
"fbe0ab92a01a71e9",
8,
"solscan.io/account/pAMMBay6oceH9fJKBRHGP5D4bD4sWpmSwMn52FMfXEA#programIdl:update_buyback_config",
),
manual_solscan_discriminator_entry(
"pump_swap",
Some(crate::PUMP_SWAP_PROGRAM_ID),
"pump",
"amm",
crate::ENTRY_KIND_INSTRUCTION,
"set_reserved_fee_recipient",
"cfbdb247a77a44b4",
8,
"validation_sql/SQL_VALIDATION_PUMP_SWAP_0_7_53.sql#local_log_instruction_names",
),
upstream_git_discriminator_entry( upstream_git_discriminator_entry(
"pump_swap", "pump_swap",
Some(crate::PUMP_SWAP_PROGRAM_ID), Some(crate::PUMP_SWAP_PROGRAM_ID),

View File

@@ -0,0 +1,274 @@
-- file: validation_sql/SQL_VALIDATION_DEX_COVERAGE_GLOBAL_0_7_53.sql
-- Global DEX coverage watchlist for the 0.7.53 PumpSwap closure.
-- Run after a forced local replay when the goal is to detect:
-- - decoded local events without coverage entries;
-- - upstream_git fallbacks that still need a local decoder or an explicit deferral;
-- - instruction observations not covered by k_sol_dex_event_coverage_entries;
-- - unattributed discriminators that need program-id classification;
-- - PumpSwap/Raydium non-regression after future backfills.
-- 01. Local decoded events without coverage.
-- Expected after 0.7.53 closure: no pump_swap or validated Raydium rows.
-- Known deferred rows at closure time: small Meteora gaps, intentionally not fixed in 0.7.53.
SELECT
de.protocol_name,
de.event_kind,
COUNT(*) AS decoded_count,
COUNT(DISTINCT de.transaction_id) AS tx_count,
MIN(tx.signature) AS sample_signature
FROM k_sol_dex_decoded_events de
LEFT JOIN k_sol_chain_transactions tx
ON tx.id = de.transaction_id
LEFT JOIN k_sol_dex_event_coverage_entries ce
ON ce.decoder_code = de.protocol_name
AND ce.local_event_kind = de.event_kind
WHERE de.protocol_name <> 'upstream_git'
AND ce.id IS NULL
GROUP BY
de.protocol_name,
de.event_kind
ORDER BY
decoded_count DESC,
de.protocol_name,
de.event_kind;
-- 02. Upstream fallback backlog to promote or defer.
-- This is a roadmap queue, not a local decoder error by itself.
SELECT
json_extract(de.payload_json, '$.upstreamDecoderCode') AS upstream_decoder_code,
json_extract(de.payload_json, '$.upstreamEntryName') AS upstream_entry_name,
json_extract(de.payload_json, '$.upstreamDiscriminatorHex') AS upstream_discriminator_hex,
COUNT(*) AS decoded_count,
COUNT(DISTINCT de.transaction_id) AS tx_count,
MIN(tx.signature) AS sample_signature
FROM k_sol_dex_decoded_events de
LEFT JOIN k_sol_chain_transactions tx
ON tx.id = de.transaction_id
WHERE de.protocol_name = 'upstream_git'
AND de.event_kind = 'upstream_git.instruction_match'
GROUP BY
upstream_decoder_code,
upstream_entry_name,
upstream_discriminator_hex
ORDER BY
decoded_count DESC,
upstream_decoder_code,
upstream_entry_name;
-- 03. Instruction observations with decoder_code but no coverage, normalized.
-- Handles both instruction_name='swap_base_input' and instruction_name='raydium_cpmm.swap_base_input'.
WITH normalized_io AS (
SELECT
io.decoder_code,
io.instruction_name,
CASE
WHEN io.decoder_code IS NOT NULL
AND TRIM(io.decoder_code) <> ''
AND io.instruction_name LIKE (io.decoder_code || '.%')
THEN SUBSTR(io.instruction_name, LENGTH(io.decoder_code) + 2)
ELSE io.instruction_name
END AS normalized_entry_name,
io.discriminator_hex,
io.signature
FROM k_sol_instruction_observations io
WHERE io.discriminator_hex IS NOT NULL
AND io.discriminator_hex <> ''
AND io.discriminator_hex <> 'e445a52e51cb9a1d'
AND io.decoder_code IS NOT NULL
AND TRIM(io.decoder_code) <> ''
)
SELECT
nio.decoder_code,
nio.instruction_name,
nio.normalized_entry_name,
nio.discriminator_hex,
COUNT(*) AS observed_count,
COUNT(DISTINCT nio.signature) AS tx_count,
MIN(nio.signature) AS sample_signature
FROM normalized_io nio
LEFT JOIN k_sol_dex_event_coverage_entries ce
ON ce.decoder_code = nio.decoder_code
AND COALESCE(ce.discriminator_hex, '') = COALESCE(nio.discriminator_hex, '')
AND (
ce.entry_name = nio.instruction_name
OR ce.entry_name = nio.normalized_entry_name
OR ce.local_event_kind = nio.instruction_name
OR ce.local_event_kind = (nio.decoder_code || '.' || nio.normalized_entry_name)
)
WHERE ce.id IS NULL
GROUP BY
nio.decoder_code,
nio.instruction_name,
nio.normalized_entry_name,
nio.discriminator_hex
ORDER BY
observed_count DESC,
nio.decoder_code,
nio.discriminator_hex;
-- 04. Unattributed instruction observations.
-- Treat as classification backlog: discriminator alone is not enough; inspect sample_signature/program_id first.
SELECT
io.discriminator_hex,
COUNT(*) AS observed_count,
COUNT(DISTINCT io.signature) AS tx_count,
MIN(io.signature) AS sample_signature
FROM k_sol_instruction_observations io
WHERE io.discriminator_hex IS NOT NULL
AND io.discriminator_hex <> ''
AND io.discriminator_hex <> 'e445a52e51cb9a1d'
AND (
io.decoder_code IS NULL
OR TRIM(io.decoder_code) = ''
)
GROUP BY
io.discriminator_hex
ORDER BY
observed_count DESC,
io.discriminator_hex;
-- 05. PumpSwap local closure check.
-- Expected result: empty.
WITH normalized_io AS (
SELECT
io.decoder_code,
io.instruction_name,
CASE
WHEN io.instruction_name LIKE 'pump_swap.%'
THEN SUBSTR(io.instruction_name, LENGTH('pump_swap') + 2)
ELSE io.instruction_name
END AS normalized_entry_name,
io.discriminator_hex,
io.signature
FROM k_sol_instruction_observations io
WHERE io.decoder_code = 'pump_swap'
AND io.discriminator_hex IS NOT NULL
AND io.discriminator_hex <> ''
AND io.discriminator_hex <> 'e445a52e51cb9a1d'
)
SELECT
nio.decoder_code,
nio.instruction_name,
nio.normalized_entry_name,
nio.discriminator_hex,
COUNT(*) AS observed_count,
COUNT(DISTINCT nio.signature) AS tx_count,
MIN(nio.signature) AS sample_signature
FROM normalized_io nio
LEFT JOIN k_sol_dex_event_coverage_entries ce
ON ce.decoder_code = 'pump_swap'
AND COALESCE(ce.discriminator_hex, '') = COALESCE(nio.discriminator_hex, '')
AND (
ce.entry_name = nio.instruction_name
OR ce.entry_name = nio.normalized_entry_name
OR ce.local_event_kind = nio.instruction_name
OR ce.local_event_kind = ('pump_swap.' || nio.normalized_entry_name)
)
WHERE ce.id IS NULL
GROUP BY
nio.decoder_code,
nio.instruction_name,
nio.normalized_entry_name,
nio.discriminator_hex
ORDER BY observed_count DESC;
-- 06. Raydium AMM v4 / CLMM / CPMM targeted non-regression.
-- Expected result after 0.7.53 closure: empty.
WITH normalized_io AS (
SELECT
io.decoder_code,
io.instruction_name,
CASE
WHEN io.decoder_code IS NOT NULL
AND TRIM(io.decoder_code) <> ''
AND io.instruction_name LIKE (io.decoder_code || '.%')
THEN SUBSTR(io.instruction_name, LENGTH(io.decoder_code) + 2)
ELSE io.instruction_name
END AS normalized_entry_name,
io.discriminator_hex,
io.signature
FROM k_sol_instruction_observations io
WHERE io.decoder_code IN (
'raydium_amm_v4',
'raydium_clmm',
'raydium_cpmm'
)
AND io.discriminator_hex IS NOT NULL
AND io.discriminator_hex <> ''
)
SELECT
nio.decoder_code,
nio.instruction_name,
nio.normalized_entry_name,
nio.discriminator_hex,
COUNT(*) AS observed_count,
COUNT(DISTINCT nio.signature) AS tx_count,
MIN(nio.signature) AS sample_signature
FROM normalized_io nio
LEFT JOIN k_sol_dex_event_coverage_entries ce
ON ce.decoder_code = nio.decoder_code
AND COALESCE(ce.discriminator_hex, '') = COALESCE(nio.discriminator_hex, '')
AND (
ce.entry_name = nio.instruction_name
OR ce.entry_name = nio.normalized_entry_name
OR ce.local_event_kind = nio.instruction_name
OR ce.local_event_kind = (nio.decoder_code || '.' || nio.normalized_entry_name)
)
WHERE ce.id IS NULL
GROUP BY
nio.decoder_code,
nio.instruction_name,
nio.normalized_entry_name,
nio.discriminator_hex
ORDER BY
observed_count DESC,
nio.decoder_code,
nio.discriminator_hex;
-- 07. Raydium swap/trade coverage overview.
-- This is informational. Entries with observed_count=0 are unobserved registry coverage, not failures.
SELECT
decoder_code,
entry_kind,
entry_name,
event_family,
local_event_kind,
discriminator_hex,
expected_db_target,
proof_status,
observed_count,
materialized_count,
trade_count
FROM k_sol_dex_event_coverage_entries
WHERE decoder_code IN (
'raydium_amm_v4',
'raydium_clmm',
'raydium_cpmm',
'raydium_stable_swap',
'raydium_launchpad'
)
AND (
event_family IN ('swap', 'swap_event_audit')
OR expected_db_target = 'k_sol_trade_events'
)
ORDER BY
decoder_code,
entry_kind,
entry_name,
discriminator_hex;
-- 08. Upstream backlog by decoder_code.
SELECT
json_extract(de.payload_json, '$.upstreamDecoderCode') AS upstream_decoder_code,
COUNT(*) AS decoded_count,
COUNT(DISTINCT de.transaction_id) AS tx_count,
COUNT(DISTINCT json_extract(de.payload_json, '$.upstreamEntryName')) AS distinct_entry_count,
MIN(tx.signature) AS sample_signature
FROM k_sol_dex_decoded_events de
LEFT JOIN k_sol_chain_transactions tx
ON tx.id = de.transaction_id
WHERE de.protocol_name = 'upstream_git'
AND de.event_kind = 'upstream_git.instruction_match'
GROUP BY upstream_decoder_code
ORDER BY decoded_count DESC, upstream_decoder_code;

View File

@@ -0,0 +1,325 @@
-- file: validation_sql/SQL_VALIDATION_PUMP_SWAP_0_7_53.sql
-- 0.7.53 pump_swap validation checklist.
-- Run on a dedicated fresh SQLite database after corpus construction and replay with:
-- skipDexDecode=no, forceDexDecode=yes, deferInstructionObservations=yes.
-- 01. Coverage pump_swap.
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 = 'pump_swap'
ORDER BY entry_kind, entry_name, discriminator_hex;
-- 02. Instruction observations.
SELECT
instruction_name,
discriminator_hex,
COUNT(*) AS observed_count,
COUNT(DISTINCT signature) AS tx_count
FROM k_sol_instruction_observations
WHERE decoder_code = 'pump_swap'
GROUP BY instruction_name, discriminator_hex
ORDER BY observed_count DESC, instruction_name, discriminator_hex;
-- 03. Residual local instruction audit.
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 = 'pump_swap'
AND event_kind = 'pump_swap.instruction_audit'
GROUP BY discriminator_hex
ORDER BY audit_count DESC, discriminator_hex;
-- 04. Residual upstream fallback for covered local entries.
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') = 'pump_swap'
GROUP BY upstream_decoder_code, entry_name, discriminator_hex, source_repo
ORDER BY fallback_count DESC, entry_name;
-- 05. Non-swap safety: non-swap event must not materialize as trade.
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 = 'pump_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 = 'pump_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;
-- 06. Failed transaction safety: failed tx must not materialize as business trade.
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 = 'pump_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;
-- 07. Decoded without coverage entry.
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 = 'pump_swap'
AND ce.local_event_kind = de.event_kind
WHERE de.protocol_name = 'pump_swap'
AND ce.id IS NULL
GROUP BY de.event_kind
ORDER BY decoded_count DESC, de.event_kind;
-- 08. Multi-target materialization.
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 re.id) AS reward_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 re.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_reward_events re
ON re.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 = 'pump_swap'
GROUP BY de.event_kind
HAVING materialized_target_count > 1
ORDER BY materialized_target_count DESC, de.event_kind;
-- 09. Unexplained successful non-materialized events.
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_reward_events re
ON re.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 = 'pump_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 re.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;
-- 10. Materialization summary.
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 re.id) AS reward_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_reward_events re
ON re.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 = 'pump_swap'
GROUP BY de.event_kind
ORDER BY de.event_kind;
-- 11. PumpSwap local instruction discriminator coverage.
-- Expected result after Solscan IDL/log-name promotion: covered rows only, except the Anchor self-CPI log carrier.
-- Rows tagged coverage_gap must be promoted to an explicit local decoder status or excluded with proof.
SELECT
io.instruction_name,
io.discriminator_hex,
COUNT(*) AS observed_count,
COUNT(DISTINCT io.signature) AS tx_count,
CASE
WHEN io.discriminator_hex = 'e445a52e51cb9a1d' THEN 'technical_anchor_self_cpi_log'
WHEN ce.id IS NULL THEN 'coverage_gap'
ELSE 'covered'
END AS observation_coverage_status,
ce.local_event_kind,
ce.expected_db_target,
ce.proof_status
FROM k_sol_instruction_observations io
LEFT JOIN k_sol_dex_event_coverage_entries ce
ON ce.decoder_code = 'pump_swap'
AND ce.discriminator_hex = io.discriminator_hex
AND (
ce.entry_name = io.instruction_name
OR ce.local_event_kind = ('pump_swap.' || io.instruction_name)
)
WHERE io.decoder_code = 'pump_swap'
GROUP BY
io.instruction_name,
io.discriminator_hex,
observation_coverage_status,
ce.local_event_kind,
ce.expected_db_target,
ce.proof_status
ORDER BY observed_count DESC, io.instruction_name, io.discriminator_hex;
-- 12. PumpSwap successful trade candidates without materialized trade.
-- Expected result: empty unless skipTradeReason explicitly documents why amounts/direction are incomplete.
SELECT
de.event_kind,
json_extract(de.payload_json, '$.skipTradeReason') AS skip_trade_reason,
COUNT(*) AS decoded_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 = 'pump_swap'
AND de.event_kind IN (
'pump_swap.buy',
'pump_swap.sell',
'pump_swap.buy_exact_quote_in'
)
AND (
tx.err_json IS NULL
OR tx.err_json = ''
OR tx.err_json = 'null'
)
GROUP BY de.event_kind, skip_trade_reason
HAVING COUNT(te.id) = 0
AND COALESCE(TRIM(skip_trade_reason), '') = ''
ORDER BY decoded_count DESC, de.event_kind;
-- 13. PumpSwap exact amount-source summary.
-- Trades must come from transaction/vault deltas or another exact amount source, never from instruction bounds alone.
SELECT
de.event_kind,
json_extract(de.payload_json, '$.amountSource') AS amount_source,
COUNT(*) AS decoded_count,
COUNT(te.id) AS trade_count
FROM k_sol_dex_decoded_events de
LEFT JOIN k_sol_trade_events te
ON te.decoded_event_id = de.id
WHERE de.protocol_name = 'pump_swap'
AND de.event_kind IN (
'pump_swap.buy',
'pump_swap.sell',
'pump_swap.buy_exact_quote_in'
)
GROUP BY de.event_kind, amount_source
ORDER BY de.event_kind, amount_source;
-- 14. PumpSwap trade/candle parity.
-- Expected result: no trade without at least one candle row after candle aggregation.
SELECT
de.event_kind,
COUNT(DISTINCT te.id) AS trade_count,
COUNT(DISTINCT pc.id) AS candle_count
FROM k_sol_dex_decoded_events de
JOIN k_sol_trade_events te
ON te.decoded_event_id = de.id
LEFT JOIN k_sol_pair_candles pc
ON pc.pair_id = te.pair_id
WHERE de.protocol_name = 'pump_swap'
GROUP BY de.event_kind
ORDER BY de.event_kind;