0.7.47-1FE5

This commit is contained in:
2026-05-31 16:43:19 +02:00
parent 7bd6593015
commit 8b09e82b3b
39 changed files with 24260 additions and 332 deletions

View File

@@ -79,4 +79,11 @@
0.7.46 - Meteora DAMM v1 events : extension conservatoire du decoder `meteora_damm_v1` à partir du mapping upstream Git decoder source `meteora-pools-decoder` et du corpus local fourni pour les discriminants observés `07a68aabceabecf4`, `3095dc823d0b09b2`, `856d2cb338ee7221`, `a9204f8988e84689`, `3657a51345e3dae0` et `1513d02bed3eff57` ; ajout des events create_pool, add/remove liquidity, claim_fee, create_lock_escrow et lock_liquidity ; ajout des exports publics associés ; raccordement de la persistance DEX et de la classification non-trade ; passage du replay local à `dex_decode.v0.7.46.damm_v1_events1`. Le programme Meteora Vault reste seulement référencé comme compte/programme associé quand il apparaît dans les comptes DAMM v1 ; aucun `program_id` vault nest marqué vérifié sans corpus direct dédié. 0.7.46 - Meteora DAMM v1 events : extension conservatoire du decoder `meteora_damm_v1` à partir du mapping upstream Git decoder source `meteora-pools-decoder` et du corpus local fourni pour les discriminants observés `07a68aabceabecf4`, `3095dc823d0b09b2`, `856d2cb338ee7221`, `a9204f8988e84689`, `3657a51345e3dae0` et `1513d02bed3eff57` ; ajout des events create_pool, add/remove liquidity, claim_fee, create_lock_escrow et lock_liquidity ; ajout des exports publics associés ; raccordement de la persistance DEX et de la classification non-trade ; passage du replay local à `dex_decode.v0.7.46.damm_v1_events1`. Le programme Meteora Vault reste seulement référencé comme compte/programme associé quand il apparaît dans les comptes DAMM v1 ; aucun `program_id` vault nest marqué vérifié sans corpus direct dédié.
0.7.46-demo3 - Correction ciblée de Demo3 pour la découverte/backfill : ajout dun décodage léger des instructions `meteora_damm_v1` connues par discriminant upstream Git dans `onchain_dex_pair_discovery`, classification instruction-scoped prioritaire pour éviter quun `Swap` soit classé `add_liquidity` à cause de logs mixtes de transaction, filtrage `target_event` strict sur les surfaces explicites, conservation des transactions mixtes quand un target explicite est demandé malgré `excludeSwaps`, et ajout des cibles UI `create_lock_escrow` / `lock_liquidity`. 0.7.46-demo3 - Correction ciblée de Demo3 pour la découverte/backfill : ajout dun décodage léger des instructions `meteora_damm_v1` connues par discriminant upstream Git dans `onchain_dex_pair_discovery`, classification instruction-scoped prioritaire pour éviter quun `Swap` soit classé `add_liquidity` à cause de logs mixtes de transaction, filtrage `target_event` strict sur les surfaces explicites, conservation des transactions mixtes quand un target explicite est demandé malgré `excludeSwaps`, et ajout des cibles UI `create_lock_escrow` / `lock_liquidity`.
0.7.46-demo3-paged - Amélioration Demo3 discovery : pagination `getSignaturesForAddress` via `before_signature` / `until_signature`, scan de plusieurs adresses source dans une seule requête, déduplication des signatures multi-pool, compteur de pages, curseurs `next_before` par adresse et ordre de traitement `newest_first` / `oldest_first` pour constituer un corpus depuis les premières signatures dun pool sans promouvoir de nouveau `program_id`. 0.7.46-demo3-paged - Amélioration Demo3 discovery : pagination `getSignaturesForAddress` via `before_signature` / `until_signature`, scan de plusieurs adresses source dans une seule requête, déduplication des signatures multi-pool, compteur de pages, curseurs `next_before` par adresse et ordre de traitement `newest_first` / `oldest_first` pour constituer un corpus depuis les premières signatures dun pool sans promouvoir de nouveau `program_id`.
0.7.46-final - Renommage documentaire et payload des anciens statuts/fonctions liés au dépôt source vers une terminologie générique `upstream_git_*` : `proofStatus` utilise désormais `upstream_git_local_corpus_observed`, `upstream_git_mapped_unverified` et `upstream_git_layout_unverified`; les payloads DAMM v1 utilisent `upstreamInstructionName`; la documentation prépare `0.7.47` comme Upstream Git Registry / DEX discovery preparation au lieu dune tranche DAMM v2 immédiate. 0.7.46-final - Renommage documentaire et payload des anciens statuts/fonctions liés au dépôt source vers une terminologie générique `upstream_git_*` : `proofStatus` utilise désormais `upstream_git_local_corpus_observed`, `upstream_git_mapped_unverified` et `upstream_git_layout_unverified`; les payloads DAMM v1 utilisent `upstreamInstructionName`; la documentation prépare `0.7.47` comme Upstream Git Registry / DEX discovery preparation au lieu dune tranche DAMM v2 immédiate.
0.7.47 - Upstream Git Registry / DEX discovery preparation : ajout dun registre générique `upstream_git` pour indexer `program_id`, discriminants dinstructions/events, noms dentrées et familles de programmes depuis Carbon et sources Git/IDL externes ; extension Demo3 aux targets multi-surfaces, orderbook, burn/mint/transfer/wrap/unwrap/stake ; ajout de groupes de signatures réussies/échouées pour alimenter Demo2 ; maintien strict de linvariant : aucune entrée upstream Git ne produit trade/candle sans decoder spécialisé et corpus local.
0.7.47-openbook-v2-audit - Ajout dun decoder local `openbook_v2` audit-only : instructions `place_order`, `cancel_order_by_client_order_id`, `consume_events`, `settle_funds`, `close_open_orders_account`; cleanup du fallback `upstream_git.instruction_match`; extraction audit des `Program return` et `Program data`; mapping des logs `FillLog`, `OpenOrdersPositionLog`, `TotalOrderFillEvent`, `SettleFundsLog`; aucune matérialisation trade/candle.
0.7.47-phoenix-v1-audit - Ajout dun decoder local `phoenix_v1` audit-only : `order_place`, `order_cancel`, `funds_withdraw`, `log`; parsing strict des instructions log `0x0f`; décodage audit du header Phoenix log et des events `Reduce`, `Place`, `TimeInForce`; correction du mapping `PlaceMultiplePostOnlyOrders` tag `0x10`; aucune matérialisation trade/candle.
0.7.47-doc-matrix - Révision documentaire : ajout dune matrice DEX dédiée, ajout explicite des sources Git/IDL à consulter, et redécoupage du plan `0.7.48+` en un DEX/version par tranche afin déviter les lots “tous events/tous decoders” trop larges.
0.7.47-doc-event-coverage - Ajout d'une matrice événementielle complémentaire `DEX_EVENT_COVERAGE_MATRIX.md` pour suivre, par DEX/version, les familles `swap`, `pool_create`, `liquidity`, `position`, `fee`, `reward`, `admin/config`, `mint`, `burn`, `transfer`, `account_create/close`, `wrap/unwrap`, `orderbook`, `vault`, `lock/unlock`, `launch` et `migration`; ajout de `DB_EVENT_MODEL_REVIEW.md` pour clarifier que `k_sol_dex_decoded_events` suffit à l'audit-only mais que des tables transversales sont nécessaires pour exploiter transfers, orderbook, vault, launch/migration et coverage upstream en requêtes métier.

View File

@@ -8,7 +8,7 @@ members = [
] ]
[workspace.package] [workspace.package]
version = "0.7.46" version = "0.7.47"
edition = "2024" edition = "2024"
license = "MIT" license = "MIT"
repository = "https://git.sasedev.com/Sasedev/khadhroony-bobobot" repository = "https://git.sasedev.com/Sasedev/khadhroony-bobobot"
@@ -31,16 +31,15 @@ reqwest = { version = "^0.13", default-features = false, features = ["charset",
rustls = { version = "^0.23", features = ["aws-lc-rs"] } rustls = { version = "^0.23", features = ["aws-lc-rs"] }
serde = { version = "^1.0", features = ["derive"] } serde = { version = "^1.0", features = ["derive"] }
serde_json = { version = "^1.0", features = [] } serde_json = { version = "^1.0", features = [] }
solana-account-decoder-client-types = { version = ">=4.0.0-rc.1", features = ["zstd"] } solana-account-decoder-client-types = { version = "^4.0", features = ["zstd"] }
solana-address-lookup-table-interface = { version = "^3.1", features = ["serde"] } solana-address-lookup-table-interface = { version = "^3.1", features = ["serde"] }
solana-client = { version = ">=4.0.0-rc.0", features = [] }
solana-compute-budget-interface = { version = "^3.0", features = ["borsh", "serde"] } solana-compute-budget-interface = { version = "^3.0", features = ["borsh", "serde"] }
solana-rpc-client-api = { version = ">=4.0.0-rc.0", features = [] } solana-rpc-client-api = { version = ">=4.0.0-rc.0", features = [] }
solana-rpc-client-types = { version = ">=4.0.0-rc.0", features = [] } #solana-rpc-client-types = { version = "^4.0", features = [] }
solana-sdk = { version = "^4.0", features = ["full"] } solana-sdk = { version = "^4.0", features = ["full"] }
solana-sdk-ids = { version = "^3.1", features = [] } solana-sdk-ids = { version = "^3.1", features = [] }
solana-system-interface = { version = "^3.2", features = ["alloc", "serde", "std"] } solana-system-interface = { version = "^3.2", features = ["alloc", "serde", "std"] }
solana-transaction-status-client-types = { version = ">=4.0.0-rc.1", features = [] } solana-transaction-status-client-types = { version = "^4.0", features = [] }
spl-associated-token-account-interface = { version = "^2.0", features = ["borsh"] } spl-associated-token-account-interface = { version = "^2.0", features = ["borsh"] }
spl-memo-interface = { version = "^2.1", features = [] } spl-memo-interface = { version = "^2.1", features = [] }
spl-token-interface = { version = "^3.0", features = [] } spl-token-interface = { version = "^3.0", features = [] }
@@ -89,3 +88,4 @@ single_match = "allow"
manual_unwrap_or_default = "allow" manual_unwrap_or_default = "allow"
manual_find = "allow" manual_find = "allow"
explicit_counter_loop = "allow" explicit_counter_loop = "allow"
get_first = "allow"

291
DB_EVENT_MODEL_REVIEW.md Normal file
View File

@@ -0,0 +1,291 @@
# Database Event Model Review — `khadhroony-bobobot` `0.7.47-1FE5`
## Conclusion courte
La base actuelle est **suffisante pour continuer le décodage exhaustif en audit-only**, parce que `k_sol_dex_decoded_events` garde les events décodés avec `payload_json`.
La base actuelle est **partiellement insuffisante pour exploiter tous les events en requêtes métier**, parce que certaines familles importantes nont pas encore de tables dédiées ou de modèle normalisé :
- transfers SPL / Token-2022 ;
- token account create/close ;
- wrap/unwrap SOL ;
- orderbook orders/fills/settlements ;
- vault deposit/withdraw ;
- launch/migration ;
- lock/unlock LP ;
- staking/unstaking ;
- coverage matrix persistée par discriminator/event.
## Ce qui existe déjà
Daprès le README, le modèle contient déjà notamment :
- `k_sol_chain_transactions`;
- `k_sol_chain_instructions`;
- `k_sol_dex_decoded_events`;
- `k_sol_trade_events`;
- `k_sol_liquidity_events`;
- `k_sol_pool_lifecycle_events`;
- `k_sol_fee_events`;
- `k_sol_reward_events`;
- `k_sol_pool_admin_events`;
- `k_sol_token_mint_events`;
- `k_sol_token_burn_events`;
- `k_sol_transaction_classifications`;
- `k_sol_protocol_candidates`;
- `k_sol_dex_decode_replay_ledger`.
Ces tables couvrent déjà les besoins principaux :
| Besoin | Couverture actuelle |
|---|---|
| Stockage brut/audit de tout event décodé | Oui, via `k_sol_dex_decoded_events.payload_json`. |
| Trades/candles | Oui. |
| Liquidity/lifecycle/fee/reward/admin | Oui, au moins structurellement. |
| Mint/burn | Oui, structurellement. |
| Replay/skip sûr | Oui, via ledger. |
| Event coverage attendu vs observé | Non ou seulement implicite. |
| Transfers/token account lifecycle | Non spécialisé. |
| Orderbook events | Non spécialisé. |
| Vault events | Non spécialisé. |
| Launch/migration | Non spécialisé ou dispersé. |
## Ne pas modifier la DB trop vite
Il ne faut pas créer une table pour chaque DEX ou chaque event upstream.
La bonne stratégie :
1. Décoder tout ce quon peut en `k_sol_dex_decoded_events`.
2. Ajouter `eventFamily`, `eventSemanticKind`, `eventActionability`, `proofStatus`, `sourceRepo`, `sourcePath`.
3. Matérialiser seulement les familles prouvées et utiles.
4. Ajouter des tables transversales uniquement quand plusieurs DEX en ont besoin.
## Ajouts DB recommandés
### 1. `k_sol_dex_event_coverage_entries`
But : stocker ce qui est **attendu/listé** depuis les sources upstream, même si non observé.
Colonnes conceptuelles :
```text
id
decoder_code
program_id
program_family
surface_kind
source_repo
source_path
entry_kind -- instruction/event/account/log/program_data
entry_name
discriminator_hex
discriminator_len
event_family -- swap/burn/mint/admin/etc.
expected_db_target
proof_status
local_event_kind
observed_count
materialized_count
trade_count
first_signature
last_signature
notes
created_at
updated_at
```
Rôle : rendre la couverture objectivable. Exemple : “Carbon liste 42 instructions Raydium CPMM, notre code en décode 18, 4 sont matérialisées”.
### 2. `k_sol_token_transfer_events`
But : matérialiser les transfers significatifs hors trade.
Colonnes conceptuelles :
```text
id
transaction_id
instruction_id
decoded_event_id
signature
slot
program_id
token_program_id
mint
source_token_account
destination_token_account
source_owner
destination_owner
amount_raw
amount_ui
transfer_kind -- transfer, transfer_checked, routed_transfer, vault_transfer
reason -- audit_only, vault_movement, migration, settlement, unknown
payload_json
```
Important : ne pas créer de trade depuis cette table. Elle sert au risque/analyse.
### 3. `k_sol_token_account_events`
But : suivre create/close/init ATA/token accounts.
```text
id
transaction_id
instruction_id
decoded_event_id
signature
slot
event_kind -- create_ata, init_account, close_account, wrap_sol, unwrap_sol
account_address
mint
owner
token_program_id
lamports_delta
payload_json
```
Cela aide à comprendre WSOL wrap/unwrap, close accounts, cleanup bots, préparation de trades.
### 4. `k_sol_orderbook_events`
But : stocker OpenBook/Phoenix et autres CLOB sans les confondre avec swaps AMM.
```text
id
transaction_id
instruction_id
decoded_event_id
signature
slot
protocol_name
market_account
event_kind -- order_place, order_cancel, order_fill, settle_funds, consume_events, open_orders_create, open_orders_close
side
price_lots
base_lots
quote_lots
maker
taker
client_order_id
raw_event_name
interpretation_status
payload_json
```
Les fills ne deviennent `trade_events` que quand maker/taker, base/quote, lots/decimals et sens économique sont validés.
### 5. `k_sol_vault_events`
But : suivre vault deposit/withdraw, Meteora Vault, Kamino/Vault-like programs.
```text
id
transaction_id
instruction_id
decoded_event_id
signature
slot
protocol_name
vault_account
event_kind -- deposit, withdraw, claim, rebalance, update_config
mint_a
mint_b
amount_a_raw
amount_b_raw
owner
payload_json
```
### 6. `k_sol_launch_events`
But : séparer launch/bonding/migration du DEX effectif.
```text
id
transaction_id
instruction_id
decoded_event_id
signature
slot
launch_protocol
event_kind -- create, buy, sell, migrate, graduate, initialize_curve, close_curve
token_mint
curve_account
migration_target_program
migration_pool
quote_mint
amount_token_raw
amount_quote_raw
payload_json
```
### 7. `k_sol_liquidity_lock_events`
But : traiter LP lock/unlock explicitement.
```text
id
transaction_id
instruction_id
decoded_event_id
signature
slot
protocol_name
pool_id
pair_id
lock_account
owner
event_kind -- create_lock, lock, unlock, extend_lock, close_lock
lp_mint
lp_amount_raw
unlock_time
payload_json
```
## Alternative minimaliste
Si on veut éviter trop de migrations immédiates, le minimum à ajouter dabord est :
1. `k_sol_dex_event_coverage_entries`;
2. `k_sol_token_transfer_events`;
3. `k_sol_orderbook_events`.
Les autres tables peuvent attendre.
## Impact sur le plan des versions
Chaque version DEX doit répondre à deux questions :
### Couverture decoder
- A-t-on listé tous les events upstream ?
- A-t-on un decoder audit pour chaque discriminator connu ?
- Les events non observés sont-ils marqués `upstream_git_mapped_unverified` ?
### Couverture DB
- Levent peut-il rester dans `k_sol_dex_decoded_events` ?
- Doit-il être matérialisé dans une table existante ?
- Faut-il une table transversale nouvelle ?
- Une table nouvelle peut-elle servir à plusieurs DEX ?
## Décision pratique pour `0.7.48`
Avant de reprendre `raydium_cpmm`, faire une micro-tranche DB/doc :
```text
0.7.48-pre — event coverage + DB model checkpoint
```
Objectif :
- ajouter ou documenter `k_sol_dex_event_coverage_entries`;
- ne pas encore ajouter toutes les tables métier ;
- produire un rapport par DEX :
- listed events,
- decoded events,
- materialized events,
- missing DB target,
- trade_count invariant.

204
DEX_DECODER_MATRIX.md Normal file
View File

@@ -0,0 +1,204 @@
# DEX Decoder Matrix — `khadhroony-bobobot` `0.7.47-1FE5`
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.
## Règle de lecture
| Statut | Sens |
|---|---|
| `supported` | Decoder local actif et matérialisation métier possible sur corpus validé. |
| `partial` | Decoder partiel ou surface partielle : des events existent, mais la couverture nest pas complète. |
| `audit-only` | Decoder local spécialisé, mais aucun trade/candle/matérialisation marché. |
| `planned` | Program id ou surface connue, mais decoder non prioritaire ou non activé. |
| `to_verify` | Source externe ou candidat : aucun statut vérifié sans corpus local. |
| `ignored/historical` | À conserver pour historique ou compatibilité, mais non prioritaire. |
## Sources upstream à comparer
- Carbon decoders : `https://github.com/sevenlabs-hq/carbon/tree/main/decoders`
- Solana Streamer : `https://github.com/0xfnzero/solana-streamer`
- Sol Parser SDK IDLs : `https://github.com/0xfnzero/sol-parser-sdk/tree/main/idl`
- 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`
- OpenBook v2 : `https://github.com/openbook-dex/openbook-v2`
- Phoenix on-chain MM : `https://github.com/all-in-one-blockchain/phoenix-onchain-mm`
- Vybe DEX/AMM support list : `https://docs.vybenetwork.com/docs/available-dexs-amms`
## Matrice prioritaire DEX/version
| Ordre | DEX/version | État actuel | Fait | Reste à faire |
|---:|---|---|---|---|
| 1 | `raydium_cpmm` | `supported` | Swaps matérialisés ; premiers non-swaps prouvés (`initialize`, `withdraw`, `collect_creator_fee`) ; trades/candles OK. | Comparer tous les discriminants Carbon/IDL ; compléter fees/admin/lifecycle ; rejouer sur base dédiée ; vérifier absence daudits orphelins. |
| 2 | `raydium_clmm` | `supported` | Swaps `swap`/`swap_v2` ; events positions/liquidité prouvés ; matérialisation non-trade existante. | Repasser tous les events Carbon/IDL : open/close position, rewards, protocol fees, Token-2022 ; compléter audit → materialized si corpus. |
| 3 | `pump_swap` | `supported` | `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. |
| 4 | `pump_fun` | `partial / 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. |
| 5 | `meteora_dbc` | `partial` | 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é. |
| 6 | `meteora_dlmm` | `supported` | 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. |
| 7 | `meteora_damm_v1` | `supported / partial events` | Couverture `0.7.46` : swap, create_pool, add/remove liquidity, claim_fee, create_lock_escrow, lock_liquidity. | Vérifier les surfaces upstream non observées ; améliorer rattachement pool/pair pour remove_liquidity non matérialisés ; revalidation stricte. |
| 8 | `meteora_damm_v2` | `partial` | `swap`, `instruction_audit`, registry/discriminants et corpus Demo3 existent. | Couvrir tous les events Carbon/source : create pool, liquidity, fees, dynamic config, admin ; déterminer actionability des swaps ; matérialiser si montants fiables. |
| 9 | `phoenix_v1` | `audit-only` | Decoder local audit-only ; `log_audit`, order place/cancel, withdraw ; parsing strict `0x0f`; events `Reduce`, `Place`, `TimeInForce` observés ; `trade_count=0`. | Terminer tous les events Git : `Fill`, `FillSummary`, `Fee`, `Evict`, `ExpiredOrder`, etc. ; ajouter counts/flags audit ; seulement ensuite étudier trade materialization. |
| 10 | `openbook_v2` | `audit-only` | Decoder local audit-only ; instructions order/cancel/consume/settle ; `Program data` mappé : `FillLog`, `OpenOrdersPositionLog`, `TotalOrderFillEvent`, `SettleFundsLog`; `trade_count=0`. | Vérifier layouts fill/out et sens maker/taker/base/quote ; ajouter table audit éventuelle ; ne matérialiser trades quaprès validation du sens économique. |
| 11 | `orca_whirlpools` | `partial` | Premier decoder historique présent ; swaps/create_pool partiels. | Comparer Carbon/IDL complet ; couvrir liquidity, positions, fees/rewards, tick arrays ; valider swaps exploitables et non-trades. |
| 12 | `raydium_launchlab` | `planned launch` | Entrée canonique locale LaunchLab/Launchpad ; program id connu localement. | Décoder launch/migration ; ne pas confondre avec CPMM/CLMM/AMM v4 ; rattacher aux pools tradables. |
| 13 | `meteora_vault` | `to_verify` | Présent comme indice upstream / compte associé. | Corpus direct obligatoire ; decoder séparé si events vault réels ; aucune promotion via DAMM indirect. |
| 14 | `fluxbeam` | `partial/to_verify` | Decoder initial existant ; Demo3 peut produire des candidats. | Vérifier source/IDL ; compléter swap, pool, liquidity, fees/admin ; matérialisation uniquement après corpus. |
| 15 | `dexlab` | `partial/to_verify` | Decoder initial historique ; ancienne entrée beta supprimée. | Reconfirmer program id/source ; décoder events disponibles ; distinguer DexLab natif et liens OpenBook/market. |
| 16 | `lifinity_amm_v2` | `to_verify` | Program id listé par sources externes/Vybe ; pas de corpus concluant. | Trouver IDL/source ; Demo3 par program/market ; audit-only dabord. |
| 17 | `stabble_stable_swap` / `stabble_weighted_swap` | `to_verify` | Program ids/indices via sources externes ; candidats Demo3 observables. | Source/IDL + corpus + decoder audit-only ; déterminer surface AMM et montants exploitables. |
| 18 | `bonkswap` | `to_verify` | Program id/Carbon/Vybe selon registre ; swaps candidats possibles. | Vérifier program id, source et corpus ; décoder tous events ; pas de trade sans montants. |
| 19 | `boop` / `boop_fun` | `to_verify / launch` | Entrée de découverte. | Séparer launch surface et swap effectif ; corpus + source obligatoire. |
| 20 | `moonshot` / `moonit` | `to_verify / launch` | Moonshot buy/sell observés via upstream candidates ; Moonit launch attribution historique. | Source/IDL + migration + rattachement pools ; éviter heuristiques seules. |
| 21 | `heaven` | `to_verify` | Program id/candidat ajouté en matrice. | Vérifier sil est launch, AMM ou les deux ; corpus dédié. |
| 22 | `printr` | `to_verify` | Preset Demo3 ajouté ; candidats observables. | Source/IDL, discriminants, corpus, decoder audit-only. |
| 23 | `metadao_*` | `to_verify` | Presets spécifiques : launchpad, bid wall, futarchy, AMM. | Traiter par programme séparé ; ne pas utiliser mint ids comme program ids ; corpus obligatoire. |
| 24 | `raydium_stable_swap` | `planned/historical` | Entrée conservée. | Reprendre uniquement si corpus réel ; stable AMM séparé de CPMM/CLMM. |
| 25 | `jupiter_*`, `dflow_aggregator_v4`, `okx_dex` | `aggregator_router` | Registry/discovery pour contexte transactionnel. | Ne pas matérialiser en DEX direct ; utiliser pour routeSource/routing/context. |
## Checklist obligatoire par DEX/version
Pour chaque DEX ou version, la tranche doit fermer les points suivants :
- [ ] Source Git/IDL recensée.
- [ ] Tous les `program_id` vérifiés localement ou marqués `to_verify`.
- [ ] Tous les discriminants dinstructions listés.
- [ ] Tous les discriminants devents/logs listés.
- [ ] Demo3 corpus constitué.
- [ ] Demo2 backfill de signatures réussies.
- [ ] Replay forcé sur base `0.7.47+`.
- [ ] SQL : decoded events par kind.
- [ ] SQL : `trade_count=0` pour audit-only.
- [ ] SQL : trade/candle uniquement si montants exploitables.
- [ ] Cleanup `upstream_git.instruction_match` si decoder spécialisé local.
- [ ] Décision finale : `audit-only`, `materialized`, `partial`, ou `to_verify`.
## Notes de matérialisation
Un event peut devenir `materialized` uniquement si :
1. la transaction est `OK` ;
2. les comptes nécessaires sont identifiés ;
3. les mints sont fiables ;
4. les montants sont fiables ;
5. le sens base/quote est validé ;
6. les requêtes SQL prouvent labsence de faux trades/candles ;
7. les tests et clippy sont verts.
## Annexe — snapshot de `kb_lib/src/dex_support_matrix.rs` (`1FE5`)
| Code | Rôle | Surface | Program id status | Observed | Decoded | Materialized | Status | Skip reason |
|---|---|---|---|---:|---:|---:|---|---|
| `pump_fun` | `launch_surface` | `launch` | `known` | non | oui | oui | `partial` | launch_surface_requires_migration_linking_before_live_trading |
| `pump_swap` | `dex_effective` | `AMM` | `known` | oui | oui | oui | `supported` | |
| `raydium_cpmm` | `dex_effective` | `AMM` | `known` | oui | oui | oui | `supported` | |
| `raydium_clmm` | `dex_effective` | `CLMM` | `known` | oui | oui | oui | `supported` | |
| `raydium_amm_v4` | `dex_effective` | `AMM` | `known` | oui | oui | oui | `supported` | |
| `raydium_launchlab` | `launch_surface` | `launch` | `known` | non | non | non | `planned` | decoder_and_materialization_not_enabled |
| `raydium_liquidity_locking` | `to_verify` | `liquidity_locking` | `to_verify` | non | non | non | `to_verify` | upstream_git_program_id_requires_local_corpus_verification |
| `raydium_router` | `aggregator_router` | `router` | `known` | non | non | non | `partial` | router_not_materialized_as_direct_trade_surface |
| `raydium_stable_swap` | `dex_effective` | `AMM` | `known` | non | non | non | `planned` | deprecated_program_not_prioritized |
| `meteora_dlmm` | `dex_effective` | `DLMM` | `known` | oui | oui | oui | `supported` | |
| `meteora_dlc` | `to_verify` | `unknown` | `to_verify` | non | non | non | `to_verify` | surface_and_program_id_to_verify |
| `meteora_damm_v1` | `dex_effective` | `AMM` | `known` | oui | oui | non | `partial` | meteora_damm_v1_swap_without_amount_payload |
| `meteora_damm_v2` | `dex_effective` | `AMM` | `known` | non | oui | oui | `partial` | not_observed_in_0_7_28_replay |
| `meteora_dbc` | `dex_effective` | `bonding_curve` | `known` | non | oui | oui | `partial` | not_observed_in_0_7_28_replay |
| `orca_whirlpools` | `dex_effective` | `CLMM` | `known` | non | oui | oui | `partial` | not_observed_in_0_7_28_replay |
| `fluxbeam` | `dex_effective` | `AMM` | `known` | non | oui | oui | `partial` | not_observed_in_0_7_28_replay |
| `dexlab` | `dex_effective` | `AMM` | `known` | non | oui | oui | `partial` | not_observed_in_0_7_28_replay |
| `bags` | `launch_surface` | `launch` | `unknown` | non | non | non | `planned` | program_id_to_verify |
| `letsbonk` | `launch_surface` | `launch` | `unknown` | non | non | non | `planned` | program_id_to_verify |
| `bonk` | `launch_surface` | `launch` | `unknown` | non | non | non | `planned` | program_id_to_verify |
| `bonk_fun` | `launch_surface` | `launch` | `unknown` | non | non | non | `planned` | program_id_to_verify |
| `okx_dex` | `aggregator_router` | `aggregator` | `to_verify` | non | non | non | `planned` | program_id_to_verify |
| `boop_fun` | `launch_surface` | `launch` | `to_verify` | non | non | non | `planned` | program_id_to_verify |
| `moonshot` | `launch_surface` | `launch` | `to_verify` | non | non | non | `planned` | historical_entities_py_program_id_requires_corpus_verification |
| `believe` | `launch_surface` | `launch` | `unknown` | non | non | non | `planned` | program_id_to_verify |
| `metadao` | `to_verify` | `unknown` | `to_verify` | non | non | non | `to_verify` | umbrella_surface_programs_split_into_specific_discovery_targets |
| `metadao_launchpad_v0_7_0` | `to_verify` | `launch` | `to_verify` | non | non | non | `to_verify` | official_metadao_program_id_requires_local_corpus_verification |
| `metadao_bid_wall_v0_7_0` | `to_verify` | `unknown` | `to_verify` | non | non | non | `to_verify` | official_metadao_program_id_requires_local_corpus_verification |
| `metadao_futarchy_v0_6_0` | `to_verify` | `unknown` | `to_verify` | non | non | non | `to_verify` | official_metadao_program_id_requires_local_corpus_verification |
| `metadao_amm_v0_5_0` | `to_verify` | `AMM` | `to_verify` | non | non | non | `to_verify` | official_metadao_program_id_requires_local_corpus_verification |
| `printr` | `to_verify` | `unknown` | `to_verify` | non | non | non | `to_verify` | solscan_program_label_requires_local_corpus_verification |
| `zora` | `to_verify` | `unknown` | `to_verify` | non | non | non | `to_verify` | solscan_program_label_requires_local_corpus_verification |
| `moonit` | `launch_surface` | `launch` | `unknown` | non | non | non | `planned` | program_id_to_verify |
| `launchbeam` | `launch_surface` | `launch` | `unknown` | non | non | non | `planned` | program_id_to_verify |
| `heaven` | `launch_surface` | `launch` | `to_verify` | non | non | non | `planned` | program_id_to_verify |
| `gavel` | `to_verify` | `auction` | `to_verify` | non | non | non | `to_verify` | upstream_git_program_id_requires_local_corpus_verification |
| `pump_fees` | `to_verify` | `fee_program` | `to_verify` | non | non | non | `to_verify` | upstream_git_program_id_requires_local_corpus_verification |
| `meteora_pools` | `dex_effective` | `AMM` | `alias_of_meteora_damm_v1` | non | non | non | `to_verify` | program_id_alias_held_by_meteora_damm_v1 |
| `dflow_aggregator_v4` | `aggregator_router` | `aggregator` | `to_verify` | non | non | non | `to_verify` | upstream_git_program_id_requires_local_corpus_verification |
| `drift_v2` | `to_verify` | `perps` | `to_verify` | non | non | non | `to_verify` | upstream_git_program_id_requires_local_corpus_verification |
| `jupiter_swap` | `aggregator_router` | `aggregator` | `to_verify` | non | non | non | `to_verify` | upstream_git_program_id_requires_local_corpus_verification |
| `jupiter_dca` | `aggregator_router` | `dca` | `to_verify` | non | non | non | `to_verify` | upstream_git_program_id_requires_local_corpus_verification |
| `jupiter_limit_order` | `aggregator_router` | `limit_order` | `to_verify` | non | non | non | `to_verify` | upstream_git_program_id_requires_local_corpus_verification |
| `jupiter_limit_order_2` | `aggregator_router` | `limit_order` | `to_verify` | non | non | non | `to_verify` | upstream_git_program_id_requires_local_corpus_verification |
| `jupiter_perpetuals` | `to_verify` | `perps` | `to_verify` | non | non | non | `to_verify` | upstream_git_program_id_requires_local_corpus_verification |
| `jupiter_lend` | `to_verify` | `lending` | `to_verify` | non | non | non | `to_verify` | upstream_git_program_id_requires_local_corpus_verification |
| `kamino_lending` | `to_verify` | `lending` | `to_verify` | non | non | non | `to_verify` | upstream_git_program_id_requires_local_corpus_verification |
| `kamino_vault` | `to_verify` | `vault` | `to_verify` | non | non | non | `to_verify` | upstream_git_program_id_requires_local_corpus_verification |
| `kamino_farms` | `to_verify` | `farms` | `to_verify` | non | non | non | `to_verify` | upstream_git_program_id_requires_local_corpus_verification |
| `kamino_limit_order` | `to_verify` | `limit_order` | `to_verify` | non | non | non | `to_verify` | upstream_git_program_id_requires_local_corpus_verification |
| `marginfi_v2` | `to_verify` | `lending` | `to_verify` | non | non | non | `to_verify` | upstream_git_program_id_requires_local_corpus_verification |
| `onchain_labs_dex_v1` | `dex_effective` | `AMM` | `alias_of_okx_dex` | non | non | non | `to_verify` | program_id_alias_held_by_okx_dex |
| `onchain_labs_dex_v2` | `dex_effective` | `AMM` | `to_verify` | non | non | non | `to_verify` | upstream_git_program_id_requires_local_corpus_verification |
| `pancake_swap` | `dex_effective` | `AMM` | `to_verify` | non | non | non | `to_verify` | upstream_git_program_id_requires_local_corpus_verification |
| `vertigo` | `dex_effective` | `AMM` | `to_verify` | non | non | non | `to_verify` | upstream_git_program_id_requires_local_corpus_verification |
| `virtuals` | `launch_surface` | `launch` | `to_verify` | non | non | non | `to_verify` | upstream_git_program_id_requires_local_corpus_verification |
| `wavebreak` | `dex_effective` | `AMM` | `to_verify` | non | non | non | `to_verify` | upstream_git_program_id_requires_local_corpus_verification |
| `aldrin` | `to_verify` | `unknown` | `to_verify` | non | non | non | `to_verify` | historical_entities_py_program_id_requires_corpus_verification |
| `aldrin_v2` | `to_verify` | `unknown` | `to_verify` | non | non | non | `to_verify` | historical_entities_py_program_id_requires_corpus_verification |
| `crema` | `to_verify` | `unknown` | `to_verify` | non | non | non | `to_verify` | historical_entities_py_program_id_requires_corpus_verification |
| `cropper` | `to_verify` | `unknown` | `to_verify` | non | non | non | `to_verify` | historical_entities_py_program_id_requires_corpus_verification |
| `lifinity_v1` | `to_verify` | `unknown` | `to_verify` | non | non | non | `to_verify` | historical_entities_py_program_id_requires_corpus_verification |
| `lifinity_v2` | `to_verify` | `unknown` | `to_verify` | non | non | non | `to_verify` | historical_entities_py_program_id_requires_corpus_verification |
| `mercurial` | `to_verify` | `unknown` | `to_verify` | non | non | non | `to_verify` | historical_entities_py_program_id_requires_corpus_verification |
| `orca_v1` | `to_verify` | `unknown` | `to_verify` | non | non | non | `to_verify` | historical_entities_py_program_id_requires_corpus_verification |
| `orca_v2` | `to_verify` | `unknown` | `to_verify` | non | non | non | `to_verify` | historical_entities_py_program_id_requires_corpus_verification |
| `phoenix` | `to_verify` | `unknown` | `to_verify` | non | non | non | `to_verify` | historical_entities_py_program_id_requires_corpus_verification |
| `saber` | `to_verify` | `unknown` | `to_verify` | non | non | non | `to_verify` | historical_entities_py_program_id_requires_corpus_verification |
| `saber_decimals` | `to_verify` | `unknown` | `to_verify` | non | non | non | `to_verify` | historical_entities_py_program_id_requires_corpus_verification |
| `openbook_v2` | `to_verify` | `unknown` | `to_verify` | non | non | non | `to_verify` | historical_entities_py_program_id_requires_corpus_verification |
| `fox` | `to_verify` | `unknown` | `to_verify` | non | non | non | `to_verify` | historical_entities_py_program_id_requires_corpus_verification |
| `sanctum_infinity` | `to_verify` | `unknown` | `to_verify` | non | non | non | `to_verify` | historical_entities_py_program_id_requires_corpus_verification |
| `saros` | `to_verify` | `unknown` | `to_verify` | non | non | non | `to_verify` | historical_entities_py_program_id_requires_corpus_verification |
| `stabble_stable_swap` | `to_verify` | `unknown` | `to_verify` | non | non | non | `to_verify` | historical_entities_py_program_id_requires_corpus_verification |
| `stabble_weighted_swap` | `to_verify` | `unknown` | `to_verify` | non | non | non | `to_verify` | historical_entities_py_program_id_requires_corpus_verification |
| `stepn` | `to_verify` | `unknown` | `to_verify` | non | non | non | `to_verify` | historical_entities_py_program_id_requires_corpus_verification |
| `solayer` | `to_verify` | `unknown` | `to_verify` | non | non | non | `to_verify` | historical_entities_py_program_id_requires_corpus_verification |
| `penguin` | `to_verify` | `unknown` | `to_verify` | non | non | non | `to_verify` | historical_entities_py_program_id_requires_corpus_verification |
| `sanctum` | `to_verify` | `unknown` | `to_verify` | non | non | non | `to_verify` | historical_entities_py_program_id_requires_corpus_verification |
| `one_dex` | `to_verify` | `unknown` | `to_verify` | non | non | non | `to_verify` | historical_entities_py_program_id_requires_corpus_verification |
| `solfi` | `to_verify` | `unknown` | `to_verify` | non | non | non | `to_verify` | historical_entities_py_program_id_requires_corpus_verification |
| `bonkswap` | `to_verify` | `unknown` | `to_verify` | non | non | non | `to_verify` | historical_entities_py_program_id_requires_corpus_verification |
| `guacswap` | `to_verify` | `unknown` | `to_verify` | non | non | non | `to_verify` | historical_entities_py_program_id_requires_corpus_verification |
| `invariant` | `to_verify` | `unknown` | `to_verify` | non | non | non | `to_verify` | historical_entities_py_program_id_requires_corpus_verification |
| `oasis` | `to_verify` | `unknown` | `to_verify` | non | non | non | `to_verify` | historical_entities_py_program_id_requires_corpus_verification |
| `token_swap` | `to_verify` | `unknown` | `to_verify` | non | non | non | `to_verify` | historical_entities_py_program_id_requires_corpus_verification |
| `helium_network` | `to_verify` | `unknown` | `to_verify` | non | non | non | `to_verify` | historical_entities_py_program_id_requires_corpus_verification |
| `marinade_liquid_staking` | `to_verify` | `unknown` | `to_verify` | non | non | non | `to_verify` | historical_entities_py_program_id_requires_corpus_verification |
| `francium_yield_pools` | `to_verify` | `unknown` | `to_verify` | non | non | non | `to_verify` | historical_entities_py_program_id_requires_corpus_verification |
| `marinade_governance` | `to_verify` | `unknown` | `to_verify` | non | non | non | `to_verify` | historical_entities_py_program_id_requires_corpus_verification |
| `serum_dao` | `to_verify` | `unknown` | `to_verify` | non | non | non | `to_verify` | historical_entities_py_program_id_requires_corpus_verification |
| `port_finance` | `to_verify` | `unknown` | `to_verify` | non | non | non | `to_verify` | historical_entities_py_program_id_requires_corpus_verification |
| `solend_classic` | `to_verify` | `unknown` | `to_verify` | non | non | non | `to_verify` | historical_entities_py_program_id_requires_corpus_verification |
| `hyperspace_nft_amm` | `to_verify` | `unknown` | `to_verify` | non | non | non | `to_verify` | historical_entities_py_program_id_requires_corpus_verification |
| `magic_eden_nft_amm` | `to_verify` | `unknown` | `to_verify` | non | non | non | `to_verify` | historical_entities_py_program_id_requires_corpus_verification |
| `raydium_staking_early` | `to_verify` | `unknown` | `to_verify` | non | non | non | `to_verify` | historical_entities_py_program_id_requires_corpus_verification |
| `orca_aquafarm_v1` | `to_verify` | `unknown` | `to_verify` | non | non | non | `to_verify` | historical_entities_py_program_id_requires_corpus_verification |
| `quarry_merge_mining` | `to_verify` | `unknown` | `to_verify` | non | non | non | `to_verify` | historical_entities_py_program_id_requires_corpus_verification |
| `goosefx_v1` | `to_verify` | `AMM` | `to_verify` | non | non | non | `to_verify` | vybe_supported_dex_amm_requires_local_corpus_and_decoder_source |
| `obric_v2` | `to_verify` | `AMM` | `to_verify` | non | non | non | `to_verify` | vybe_supported_dex_amm_requires_local_corpus_and_decoder_source |
| `ondo_global_market` | `to_verify` | `unknown` | `to_verify` | non | non | non | `to_verify` | vybe_supported_dex_amm_requires_local_corpus_and_decoder_source |
| `scorch` | `to_verify` | `AMM` | `to_verify` | non | non | non | `to_verify` | vybe_supported_dex_amm_requires_local_corpus_and_decoder_source |
| `zerofi` | `to_verify` | `AMM` | `to_verify` | non | non | non | `to_verify` | vybe_supported_dex_amm_requires_local_corpus_and_decoder_source |
| `manifest_clob` | `to_verify` | `orderbook` | `to_verify` | non | non | non | `to_verify` | vybe_supported_dex_amm_requires_local_corpus_and_decoder_source |
| `alphaq` | `to_verify` | `AMM` | `to_verify` | non | non | non | `to_verify` | vybe_supported_dex_amm_requires_local_corpus_and_decoder_source |
| `goonfi` | `to_verify` | `AMM` | `to_verify` | non | non | non | `to_verify` | vybe_supported_dex_amm_requires_local_corpus_and_decoder_source |
| `goonfi_v2` | `to_verify` | `AMM` | `to_verify` | non | non | non | `to_verify` | vybe_supported_dex_amm_requires_local_corpus_and_decoder_source |
| `byreal` | `to_verify` | `AMM` | `to_verify` | non | non | non | `to_verify` | vybe_supported_dex_amm_requires_local_corpus_and_decoder_source |
| `bisonfi` | `to_verify` | `AMM` | `to_verify` | non | non | non | `to_verify` | vybe_supported_dex_amm_requires_local_corpus_and_decoder_source |
| `fusionamm` | `to_verify` | `AMM` | `to_verify` | non | non | non | `to_verify` | vybe_supported_dex_amm_requires_local_corpus_and_decoder_source |
| `woofi` | `to_verify` | `AMM` | `to_verify` | non | non | non | `to_verify` | vybe_supported_dex_amm_requires_local_corpus_and_decoder_source |
| `aquifer` | `to_verify` | `unknown` | `to_verify` | non | non | non | `to_verify` | vybe_supported_dex_amm_requires_local_corpus_and_decoder_source |
| `humidifi` | `to_verify` | `unknown` | `to_verify` | non | non | non | `to_verify` | vybe_supported_dex_amm_requires_local_corpus_and_decoder_source |
| `solfi_v2` | `to_verify` | `AMM` | `to_verify` | non | non | non | `to_verify` | vybe_supported_dex_amm_requires_local_corpus_and_decoder_source |

View File

@@ -0,0 +1,232 @@
# DEX Event Coverage Matrix — `khadhroony-bobobot` `0.7.47-1FE5`
Cette matrice complète `DEX_DECODER_MATRIX.md`.
La matrice précédente répondait à la question : **quel DEX/version est couvert ?**
Cette matrice répond à la question : **quels events/instructions doivent être décodés, audit-only ou matérialisés ?**
## Principe
Lobjectif nest plus seulement de décoder les swaps. Lobjectif est de décoder le maximum dévénements disponibles dans les sources Git/IDL et dans le corpus local, parce que certains événements non-trade peuvent influencer une décision de trading :
- perte ou ajout de liquidité ;
- burn de tokens ou de LP ;
- mint anormal ;
- lock/unlock de liquidité ;
- close account / fermeture de position ;
- admin/config changes ;
- fees/rewards ;
- migration launch → DEX ;
- market/orderbook activity ;
- vault deposit/withdraw ;
- changement de market cap ou de supply dérivée.
Un event peut donc être important même sil ne produit jamais de `trade_event` ou de candle.
## Règle de couverture
Pour chaque DEX/version, on doit viser trois niveaux :
| Niveau | Description |
|---|---|
| `listed` | Levent/instruction existe dans une source Git/IDL/Carbon/Vybe/autre. |
| `decoded_audit` | Le code local reconnaît levent et le persiste dans `k_sol_dex_decoded_events` avec payload structuré ou audit-only. |
| `materialized` | Levent alimente une table métier spécialisée : trade, liquidity, lifecycle, fee, reward, admin, mint, burn, orderbook, vault, launch/migration, etc. |
Ne pas sauter directement de `listed` à `materialized`.
## Univers minimal devents à suivre
Cette liste doit devenir la grille commune pour toutes les tranches `0.7.48+`.
| Famille | Exemples | Impact possible | Table actuelle / cible |
|---|---|---|---|
| `swap/trade` | swap, buy, sell, route swap, exact in/out | Prix, volume, candles | `k_sol_trade_events`, `k_sol_pair_metrics`, `k_sol_pair_candles` |
| `pool_create` | initialize, create_pool, initialize_market | Découverte pool/pair | `k_sol_pool_lifecycle_events`, `k_sol_pools`, `k_sol_pairs` |
| `liquidity_add` | add_liquidity, deposit liquidity, bootstrap | Profondeur, risque, market cap indirect | `k_sol_liquidity_events` |
| `liquidity_remove` | remove_liquidity, withdraw liquidity | Rug/liquidity drain | `k_sol_liquidity_events` |
| `position_open` | open_position, init_position | CLMM/DLMM state | `k_sol_pool_lifecycle_events` ou future `k_sol_position_events` |
| `position_close` | close_position, close_position_if_empty | Sortie de LP, risque | `k_sol_pool_lifecycle_events` ou future `k_sol_position_events` |
| `fee` | claim_fee, collect_protocol_fee, collect_creator_fee | Rentabilité, activité pool | `k_sol_fee_events` |
| `reward` | claim_reward, fund_reward, update_reward | Incitations, farming | `k_sol_reward_events` |
| `admin/config` | set_config, update_fee, pause, whitelist, authority change | Risque protocole/pool | `k_sol_pool_admin_events` |
| `mint` | token mint, LP mint, position NFT mint | Supply, launch, LP | `k_sol_token_mint_events` + future token activity |
| `burn` | token burn, LP burn, position NFT burn | Supply, LP burn, risque/réassurance | `k_sol_token_burn_events` + future token activity |
| `transfer` | SPL transfer, Token-2022 transfer, routed transfer | Flux wallet/vault, whale movement | future `k_sol_token_transfer_events` |
| `account_create` | ATA create, token account init | Préparation trade/wallet/vault | future `k_sol_token_account_events` |
| `account_close` | close token account, close open orders | Sortie position/wallet cleanup | future `k_sol_token_account_events` ou `k_sol_orderbook_events` |
| `wrap/unwrap` | wrap SOL, unwrap SOL, close WSOL ATA | Routing, PnL, trade prep | future token account/activity |
| `order_place` | place_order, post_only, IOC | Orderbook pressure | future `k_sol_orderbook_events` |
| `order_cancel` | cancel_order, cancel_all, reduce order | Changement intention | future `k_sol_orderbook_events` |
| `order_fill` | FillLog, fill event, trade event | Trade réel orderbook | future `k_sol_orderbook_events`; trade seulement après validation économique |
| `settle_funds` | settle_funds, withdraw funds | Finalisation CLOB | future `k_sol_orderbook_events` |
| `consume_events` | crank/event queue processing | Orderbook fill/out | future `k_sol_orderbook_events` |
| `vault_deposit` | deposit into vault | TVL/risque | future `k_sol_vault_events` |
| `vault_withdraw` | withdraw from vault | TVL drain | future `k_sol_vault_events` |
| `lock` | lock_liquidity, create_lock_escrow | LP lock/risk | `k_sol_pool_lifecycle_events` ou future lock table |
| `unlock` | unlock, release escrow | Risque de retrait LP | future lock/lifecycle |
| `launch` | create bonding curve, launch pool | Origine token | future `k_sol_launch_events` |
| `migration` | migrate to DEX, migrate liquidity | Passage launch → tradable | future `k_sol_launch_events`, pool origins |
| `stake/unstake` | stake LP/token, unstake | Incentives/withdraw risk | future staking/reward events |
| `oracle/price` | oracle update, price account update | Pricing/risk | future oracle/context events |
| `unknown` | unmapped discriminator | Dette de décodage | `k_sol_dex_decoded_events` audit-only |
## Matrice de couverture par DEX/version
Légende :
- `M` = matérialisé déjà ou historiquement validé ;
- `A` = audit-only local ;
- `P` = partiel / doit être complété ;
- `L` = listé upstream, non validé localement ;
- `-` = non applicable connu ;
- `?` = à vérifier.
| DEX/version | swap | pool create | liq add/remove | position | fee/reward | admin/config | mint/burn | transfer/account | orderbook | vault | launch/migration | état immédiat |
|---|---:|---:|---:|---:|---:|---:|---:|---:|---:|---:|---:|---|
| `raydium_cpmm` | M | P | P | - | P | P | ? | ? | - | - | ? | Reprendre en `0.7.48`, comparer tous events Carbon/IDL/fnzero. |
| `raydium_clmm` | M | P | M/P | M/P | P | P | P | ? | - | - | ? | Reprendre en `0.7.49`, compléter positions/rewards/fees/admin. |
| `pump_swap` | M | P | P | - | P | P | ? | P | - | - | P | Reprendre en `0.7.50`, couvrir buy/sell/cashback/fee/volume/admin. |
| `pump_fun` | P | P | - | - | ? | P | P | P | - | - | M/P | Reprendre en `0.7.51`, launch/bonding/migration/buy/sell/create/update. |
| `meteora_dbc` | P | P | P | - | P | P | P | ? | - | P | M/P | Reprendre en `0.7.52`, séparer bonding, swap, migration, config, fees. |
| `meteora_dlmm` | M | M | M | M | M/P | P | ? | ? | - | ? | ? | Reprendre en `0.7.53` pour exhaustive upstream + audits résiduels. |
| `meteora_damm_v1` | M/P | M | M/P | - | M/P | P | ? | ? | - | P | ? | Reprendre en `0.7.54`, vérifier toutes surfaces upstream non observées. |
| `meteora_damm_v2` | P | P | L/P | - | L/P | L/P | ? | ? | - | P | ? | Reprendre en `0.7.55`, decoder tous events Carbon/source. |
| `phoenix_v1` | A | A/P | - | - | A/P | A/P | - | A/P | A | - | - | Continuer audit des events Git, pas de trade/candle. |
| `openbook_v2` | A | A/P | - | - | A/P | A/P | - | A/P | A | - | - | Audit-only avancé, matérialisation orderbook future. |
| `orca_whirlpools` | P | P | L/P | L/P | L/P | L/P | P | ? | - | - | ? | Reprendre en `0.7.58`, IDL complet + corpus dédié. |
| `raydium_launchlab` | - | P | ? | - | ? | P | P | P | - | - | P | Launch/migration après DEX effectifs. |
| `bonkswap` | L | L | L | - | L | L | ? | ? | - | - | L | Vérifier source/corpus. |
| `moonshot` | L/P | P | ? | - | ? | P | P | P | - | - | P | Séparer launch, buy/sell, migration. |
| `heaven` | L/P | L/P | L/P | - | L/P | L/P | P | P | - | ? | P | Vérifier AMM vs launch. |
| `goosefx_v1` | L/P | L/P | L/P | ? | ? | ? | ? | ? | ? | ? | ? | Vybe/Demo3 candidates ; source nécessaire. |
| `obric_v2` | L/P | L/P | ? | ? | ? | ? | ? | ? | ? | ? | ? | Bon candidat après sources. |
| `solfi_v2` | L/P | L/P | ? | ? | ? | ? | ? | ? | ? | ? | ? | Bon candidat après sources. |
## Points critiques manquants dans lancienne matrice
### `burn`
`burn` doit être une famille de première classe, pas seulement une sous-note. Il peut signaler :
- burn de LP tokens ;
- burn de supply token ;
- fermeture ou destruction indirecte de position ;
- réduction du risque de dump si le burn est réel et vérifié ;
- au contraire, faux signal si le burn ne concerne pas la bonne mint ou si le compte propriétaire est ambigu.
Action : ajouter `burn` à toutes les checklists DEX et aux diagnostics de couverture.
### `transfer`
Les transfers ne sont pas des trades par défaut, mais ils sont nécessaires pour :
- repérer vault movements ;
- détecter migration / liquidity routing ;
- comprendre des orderbook settle/fill ;
- analyser whale movement ou sortie de pool.
Action : prévoir une table dédiée plutôt que tout stocker uniquement dans `payload_json`.
### `account close/create`
Les close/create ATA sont utiles pour détecter :
- fin de route WSOL ;
- sortie de position ;
- cleanup après swap ;
- close open-orders account ;
- activité de bots.
Action : famille dédiée `token_account` / `account_lifecycle`.
## Checklist exhaustive par DEX
Pour chaque DEX/version, la tranche doit remplir un tableau événementiel :
| Colonne | Description |
|---|---|
| `source_repo` | Git/IDL/source utilisée. |
| `source_path` | Chemin exact du fichier source. |
| `decoder_code` | Code interne/upstream. |
| `program_id` | Program id ou `to_verify`. |
| `entry_kind` | `instruction`, `event`, `account`, `log`, `program_data`. |
| `entry_name` | Nom source exact. |
| `discriminator_hex` | Discriminator ou tag. |
| `discriminator_len` | Longueur en octets. |
| `event_family` | Famille commune : swap, burn, admin, order_fill, etc. |
| `local_event_kind` | Event local produit. |
| `local_status` | `not_implemented`, `audit_only`, `decoded`, `materialized`. |
| `db_target` | Table cible ou `k_sol_dex_decoded_events_only`. |
| `proof_status` | Statut upstream/local. |
| `observed_count` | Count local après replay. |
| `materialized_count` | Count table métier. |
| `trade_count` | Count trades générés, doit être 0 sauf swap validé. |
| `notes` | Ambiguïtés, layout, corpus, reste à faire. |
## Requêtes SQL génériques de couverture
### Couverture decoded events par programme
```sql
SELECT
protocol_name,
event_kind,
program_id,
json_extract(payload_json, '$.upstreamEntryName') AS upstream_entry_name,
json_extract(payload_json, '$.upstreamDiscriminatorHex') AS upstream_discriminator_hex,
COUNT(*) AS n
FROM k_sol_dex_decoded_events
GROUP BY
protocol_name,
event_kind,
program_id,
upstream_entry_name,
upstream_discriminator_hex
ORDER BY protocol_name, n DESC;
```
### Sécurité trades pour audit-only
```sql
SELECT
de.protocol_name,
de.event_kind,
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.event_kind LIKE '%_audit'
OR de.protocol_name IN ('upstream_git', 'phoenix_v1', 'openbook_v2')
GROUP BY de.protocol_name, de.event_kind
ORDER BY trade_count DESC;
```
### Vérifier burn/mint présents dans decoded payloads
```sql
SELECT
protocol_name,
event_kind,
json_extract(payload_json, '$.eventLifecycleKind') AS lifecycle,
json_extract(payload_json, '$.eventActionability') AS actionability,
COUNT(*) AS n
FROM k_sol_dex_decoded_events
WHERE event_kind LIKE '%burn%'
OR event_kind LIKE '%mint%'
OR lifecycle IN ('burn', 'mint')
GROUP BY protocol_name, event_kind, lifecycle, actionability
ORDER BY n DESC;
```
## Décision
À partir de maintenant, une tranche DEX nest pas complète si elle ne liste que les swaps. Elle doit explicitement indiquer :
- events source listés ;
- events décodés audit-only ;
- events matérialisés ;
- events volontairement non implémentés ;
- events non observés localement ;
- trous de DB éventuels.

View File

@@ -0,0 +1,191 @@
# Prompt de reprise — khadhroony-bobobot `0.7.47-1FE5`
Reprise du projet `khadhroony-bobobot`.
## Archive de départ
Utiliser comme base de travail :
```text
kb_lib-v0.7.47-1FE5-full.zip
```
Joindre aussi les docs mises à jour :
```text
README.md
ROADMAP.md
CHANGELOG.md
DEX_DECODER_MATRIX.md
```
## Décision de planification
Ne plus tenter “tous les events de tous les decoders” dans une seule session. Lobjectif reste de couvrir tous les decoders disponibles dans Carbon et les sources Git/IDL, mais par tranches DEX/version.
Ordre cible :
```text
raydium_cpmm
raydium_clmm
pump_swap
pump_fun
meteora_dbc
meteora_dlmm
meteora_damm_v1
meteora_damm_v2
phoenix_v1
openbook_v2
orca_whirlpools
launch surfaces
DEX historiques / candidats
```
## Sources upstream obligatoires
Ces sources sont des indices de décodage, pas des preuves de validation locale :
```text
https://github.com/sevenlabs-hq/carbon/tree/main/decoders
https://github.com/0xfnzero/solana-streamer
https://github.com/0xfnzero/sol-parser-sdk/tree/main/idl
https://github.com/pinax-network/substreams-solana-idls/tree/main/src
https://github.com/hodlwarden/solana-tx-parser/tree/main/src
https://github.com/openbook-dex/openbook-v2
https://github.com/all-in-one-blockchain/phoenix-onchain-mm
https://docs.vybenetwork.com/docs/available-dexs-amms
```
## État validé
Dernier état validé côté `kb_lib` :
```text
cargo test -p kb_lib
371 passed
```
Clippy doit être relancé à chaque tranche :
```bash
cargo clippy -p kb_lib --all-targets -- -D warnings
```
## 0.7.47 acquis
- Upstream Git Registry ajouté.
- Demo3 étendu : multi-target, multi-source, pagination, orderbook targets, burn/mint/transfer/wrap/unwrap/stake.
- Demo2 backfill signature fonctionnel.
- Replay local avec ledger.
- OpenBook v2 decoder audit-only.
- Phoenix v1 decoder audit-only.
- Les decoders audit-only ne produisent aucun trade/candle.
## OpenBook v2
Program id :
```text
opnb2LAfJYbRMAHHvqjCwQxanZn7ReEHp1k81EohpZb
```
État :
```text
audit-only local decoder
upstream_git fallback cleaned
Program data mapped:
- FillLog
- OpenOrdersPositionLog
- TotalOrderFillEvent
- SettleFundsLog
trade_count = 0
```
Ne pas activer de trade/candle tant que maker/taker/base/quote et les lots ne sont pas validés.
## Phoenix v1
Program id :
```text
PhoeNiXZ8ByJGLkxNfZRnkUfjvmuYqLR89jjFHGqdXY
```
État :
```text
audit-only local decoder
log instruction strict 0x0f
events observed:
- Reduce
- Place
- TimeInForce
currentInstructionTag mappings:
- 0x09 CancelUpToWithFreeFunds
- 0x0c WithdrawFunds
- 0x10 PlaceMultiplePostOnlyOrders
trade_count = 0
```
Prochaine action préférée : finir Phoenix v1 avec tous les events disponibles dans les sources Git/IDL, mais rester audit-only jusquà validation économique.
## Contraintes
- Rust 2024.
- Pas de `mod.rs`.
- Fichiers Rust avec entête `// file: ...`.
- Exposition centralisée via `lib.rs`.
- `#![deny(unreachable_pub)]`, `#![warn(missing_docs)]`.
- Pas de `anyhow`.
- Pas de `thiserror`.
- Pas de `?`, `unwrap`, `expect` dans le code applicatif.
- Utiliser `match`, `if let Err`, `let Err = ... else`.
- Si une requête DB est ajoutée/modifiée, mettre à jour `kb_lib/src/db.rs`, puis `kb_lib/src/lib.rs` si nécessaire.
## Méthode par DEX/version
Pour chaque DEX/version :
1. inspecter Carbon + autres sources Git/IDL ;
2. lister tous les discriminants instructions/events ;
3. compléter `upstream_registry` / matrice si nécessaire ;
4. utiliser Demo3 pour corpus ;
5. backfill Demo2 ;
6. replay forcé ;
7. valider SQL ;
8. ajouter decoder audit-only ou materialized selon preuve ;
9. supprimer les doublons `upstream_git.instruction_match` si decoder spécialisé ;
10. ne jamais produire trade/candle sans montants exploitables et sens économique validé.
## Requêtes de sécurité audit-only
```sql
SELECT
de.protocol_name,
de.event_kind,
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 IN ('openbook_v2', 'phoenix_v1')
GROUP BY de.protocol_name, de.event_kind
ORDER BY trade_count DESC;
```
Attendu :
```text
trade_count = 0
```
## Livrable attendu
Pour chaque tranche :
- fichiers ajoutés/modifiés seulement ;
- archive zip ;
- commandes de test ;
- requêtes SQL ;
- notes sur ce qui reste non vérifié ;
- ne pas prétendre quun event ou program id est vérifié sans corpus local.

View File

@@ -0,0 +1,155 @@
# Prompt de reprise — khadhroony-bobobot `0.7.47-1FE5` / Event coverage
Reprise du projet `khadhroony-bobobot`.
## Archive de départ
Utiliser :
```text
khadhroony-bobobot-v0.7.47-1FE5-full.zip
```
Et les docs :
```text
README.md
ROADMAP.md
CHANGELOG.md
DEX_DECODER_MATRIX.md
DEX_EVENT_COVERAGE_MATRIX.md
DB_EVENT_MODEL_REVIEW.md
```
## Décision de reprise
Ne pas essayer de “faire tous les events de tous les DEX” dans une seule session.
La stratégie est maintenant :
```text
un DEX/version = une tranche
tous les events listés = audit coverage
matérialisation seulement après corpus + SQL + invariants
```
## Sources Git/IDL à utiliser systématiquement
- https://github.com/sevenlabs-hq/carbon/tree/main/decoders
- https://github.com/0xfnzero/solana-streamer
- https://github.com/0xfnzero/sol-parser-sdk/tree/main/idl
- https://github.com/pinax-network/substreams-solana-idls/tree/main/src
- https://github.com/hodlwarden/solana-tx-parser/tree/main/src
- https://github.com/openbook-dex/openbook-v2
- https://github.com/all-in-one-blockchain/phoenix-onchain-mm
- https://docs.vybenetwork.com/docs/available-dexs-amms
## Objectif événementiel
Décoder le maximum devents, pas seulement les swaps.
Inclure explicitement :
```text
swap
pool_create
add_liquidity
remove_liquidity
position_open
position_close
fee
reward
admin/config
mint
burn
transfer
account_create
account_close
wrap_sol
unwrap_sol
order_place
order_cancel
order_fill
consume_events
settle_funds
vault_deposit
vault_withdraw
lock
unlock
launch
migration
stake
unstake
unknown/unmapped audit
```
Raison : burn, perte de liquidité, changements admin/config, vault withdraw, migration, mint anormal, close account, etc. peuvent influencer une décision de trading même si ce ne sont pas des trades.
## Base de données
La base actuelle suffit pour audit-only via `k_sol_dex_decoded_events`, mais elle nest pas suffisante pour tout exploiter en requêtes métier.
À considérer avant ou pendant `0.7.48` :
```text
k_sol_dex_event_coverage_entries
k_sol_token_transfer_events
k_sol_token_account_events
k_sol_orderbook_events
k_sol_vault_events
k_sol_launch_events
k_sol_liquidity_lock_events
```
Priorité minimale :
```text
1. k_sol_dex_event_coverage_entries
2. k_sol_token_transfer_events
3. k_sol_orderbook_events
```
## Ordre des versions
```text
0.7.48-pre event coverage + DB model checkpoint
0.7.48 raydium_cpmm
0.7.49 raydium_clmm
0.7.50 pump_swap
0.7.51 pump_fun
0.7.52 meteora_dbc
0.7.53 meteora_dlmm
0.7.54 meteora_damm_v1
0.7.55 meteora_damm_v2
0.7.56 phoenix_v1
0.7.57 openbook_v2
0.7.58 orca_whirlpools
0.7.59 launch surfaces
0.7.60 DEX historiques/candidats
0.7.61 validation consolidée
```
## Règles fixes
- Un event non-trade ne produit jamais trade/candle.
- Une transaction failed reste audit, jamais trade/candle.
- Un discriminator upstream nest pas une preuve métier.
- Un program id upstream nest pas vérifié sans corpus local.
- Chaque decoder spécialisé doit remplacer le fallback `upstream_git.instruction_match` pour éviter les doublons.
- Tout event connu mais non observé reste `upstream_git_mapped_unverified`.
- Tout event observé mais non matérialisé reste audit-only ou decoded, pas materialized.
## Prochaine tâche recommandée
Commencer par :
```text
0.7.48-pre — event coverage + DB model checkpoint
```
Livrables attendus :
1. ajouter/documenter une table de couverture event/discriminator ;
2. générer un rapport de couverture par DEX/version ;
3. préparer `raydium_cpmm` avec la liste complète des events depuis Carbon/fnzero/IDL ;
4. ne pas changer encore la matérialisation trade/candle.

View File

@@ -0,0 +1,249 @@
# Prompt de reprise — khadhroony-bobobot `0.7.47`
Reprise du projet `khadhroony-bobobot`.
## Contexte
Le workspace contient principalement :
- `kb_lib` : logique métier Solana/DEX, clients HTTP/WS, décodage, détection, SQLite, replay, validation, diagnostics, metadata, candles, signaux ;
- `kb_demo_app` : application Tauri V2 de démo/inspection. Elle doit rester une façade UI et ne pas contenir de logique métier DEX profonde.
La version `0.7.46` est clôturée sur `meteora_damm_v1`.
## État validé à la fin de `0.7.46`
- `cargo test -p kb_lib` : vert.
- `cargo clippy -p kb_lib --all-targets -- -D warnings` : vert.
- Demo2 et Demo3 fonctionnent bien pour backfill et discovery.
- Demo3 supporte :
- multi-target ;
- multi-source ;
- pagination `before` / `until` ;
- `max_pages` ;
- `newest_first` / `oldest_first`.
- Le replay local supporte le ledger de décodage/replay et les options `skipDexDecode` / `forceDexDecode`.
- `meteora_damm_v1` couvre les surfaces observées localement :
- `swap` ;
- `claim_fee` ;
- `create_lock_escrow` ;
- `lock_liquidity` ;
- `remove_liquidity` ;
- `add_liquidity` ;
- `create_pool`.
- Les statuts/payloads ne doivent plus utiliser une terminologie spécifique à un dépôt externe particulier.
- Les statuts génériques attendus sont :
- `upstream_git_unverified` ;
- `upstream_git_mapped_unverified` ;
- `upstream_git_local_corpus_observed` ;
- `upstream_git_local_corpus_materialized` ;
- `upstream_git_layout_unverified` si le layout est connu depuis une source Git mais pas encore validé localement.
## Décision base de données pour `0.7.47`
La version `0.7.46` est considérée finalisée. Ne pas demander ni proposer de replay forcé de lancienne base `0.7.46` pour clôturer cette version, sauf demande explicite.
Pour `0.7.47`, le développement et la validation doivent se faire de préférence sur une **nouvelle base SQLite dédiée**. Cette base servira à constituer un corpus propre avec Demo3 et Demo2 sur plusieurs paires, pools ou programmes de DEX différents.
Règles pratiques :
- lancienne base `0.7.46` peut servir de référence historique ou de comparaison, mais elle ne doit pas être migrée ou redécodée automatiquement ;
- si une validation DB est nécessaire en `0.7.47`, elle doit cibler la nouvelle base de travail `0.7.47` ;
- les backfills `0.7.47` doivent être construits à partir de Demo3 discovery puis Demo2 signature/pool backfill ;
- le replay local reste utile, mais seulement après constitution du corpus `0.7.47`, pas comme étape de finalisation de `0.7.46` ;
- les entrées du registre upstream Git restent des indices tant quelles ne sont pas observées sur cette nouvelle base ou sur un corpus explicitement indiqué.
## Objectif de `0.7.47`
`0.7.47` est dédiée à :
```text
Upstream Git Registry / DEX discovery preparation
```
Lobjectif nest pas de valider directement un DEX unique. Lobjectif est de créer un registre générique permettant dindexer les `program_id`, discriminants dinstructions, discriminants devents, noms dinstructions, familles de programmes et types de surfaces issus de dépôts Git externes de decoders Solana.
Ces entrées sont des **indices de découverte**, pas des preuves métier. Elles doivent rester non vérifiées tant quelles ne sont pas confirmées par :
1. Demo3 discovery ;
2. backfill signature/pool via Demo2 ;
3. replay local sur la base de travail `0.7.47` ;
4. requêtes SQL de validation sur cette même base ;
5. invariants métier : pas de faux trade, pas de fausse candle, pas de promotion de `program_id` sans corpus.
## Noms recommandés
Modules possibles :
```text
kb_lib/src/upstream_registry.rs
kb_lib/src/upstream_registry_types.rs
kb_lib/src/upstream_registry_match.rs
kb_lib/src/upstream_registry_generated.rs
```
Nom fonctionnel :
```text
Upstream Git Registry
```
Ne pas utiliser de nom de dépôt externe spécifique dans les noms publics, les statuts, les tables ou les payloads métier.
## Première tranche attendue
Créer une première version statique du registre dans `kb_lib`, sans modifier la DB si ce nest pas nécessaire.
Chaque entrée de registre devrait contenir au minimum :
```text
source_repo
source_path
decoder_code
program_id
program_family
surface_kind
entry_kind = instruction | event | account | program
entry_name
discriminator_hex
discriminator_len
proof_status
notes
```
Les entrées de registre doivent être exposées à `kb_demo_app` via une commande Demo3 ou une commande dédiée, mais la logique reste dans `kb_lib`.
## Familles à indexer en priorité
DEX / AMM / CLMM / orderbook :
```text
meteora-damm-v2
meteora-dbc
meteora-dlmm
meteora-vault
raydium-amm-v4
raydium-clmm
raydium-cpmm
raydium-launchpad
raydium-liquidity-locking
raydium-stable-swap
orca-whirlpool
fluxbeam
lifinity-amm-v2
phoenix-v1
openbook-v2
stabble-stable-swap
stabble-weighted-swap
bonkswap
boop
moonshot
heaven
okx-dex
pancake-swap
vertigo
virtuals
wavebreak
onchain-labs-dex-v1
onchain-labs-dex-v2
```
Agrégateurs / ordres / perps / lending :
```text
jupiter-swap
jupiter-dca
jupiter-limit-order
jupiter-limit-order-2
jupiter-perpetuals
jupiter-lend
kamino-lending
kamino-vault
kamino-farms
kamino-limit-order
drift-v2
marginfi-v2
dflow-aggregator-v4
zeta
```
Contexte transactionnel non DEX :
```text
system-program
token-program
token-2022
associated-token-account
address-lookup-table
memo-program
stake-program
mpl-token-metadata
mpl-core
bubblegum
name-service
marinade-finance
solayer-restaking-program
swig
sharky
circle-message-transmitter-v2
circle-token-messenger-v2
```
## Règles de validation
- Une entrée upstream Git reste `upstream_git_unverified` tant quelle na pas été vue localement.
- Une entrée branchée dans un decoder mais jamais vue localement reste `upstream_git_mapped_unverified`.
- Une entrée vue après Demo3/backfill/replay peut devenir `upstream_git_local_corpus_observed`.
- Une entrée qui alimente correctement une table métier dédiée peut devenir `upstream_git_local_corpus_materialized`.
- Aucun `program_id`, event ou discriminator ne doit être déclaré vérifié uniquement parce quil existe dans un dépôt Git externe.
- Aucun event non-trade ne doit produire trade, metric ou candle.
## Contraintes de code
- Rust 2024.
- Pas de `mod.rs`.
- Fichiers Rust avec entête `// file: ...`.
- Fichiers `.toml` avec entête `# file: ...`.
- Exposition centralisée via `lib.rs`.
- `#![deny(unreachable_pub)]` et `#![warn(missing_docs)]`.
- Pas de `anyhow`.
- Pas de `thiserror`.
- Pas de `?`, `unwrap`, `expect` dans le code applicatif.
- Utiliser `match`, `if let Err`, `let Err = ... else`.
- Tests verts à chaque étape.
- Si une requête DB est ajoutée/modifiée, mettre à jour les re-exports dans `kb_lib/src/db.rs`, puis `kb_lib/src/lib.rs` si nécessaire.
## Format de livraison attendu
Pour chaque tranche :
1. expliquer brièvement les fichiers touchés ;
2. fournir une archive des fichiers ajoutés/modifiés seulement, avec larborescence du projet ;
3. indiquer les commandes de test à lancer ;
4. indiquer les requêtes SQL utiles si validation DB nécessaire ;
5. ne jamais prétendre quun `program_id` ou un event est vérifié sans preuve/corpus.
dexlab
bags / letsbonk / bonk_fun
believe
moonit
launchbeam
metadao / metaDAO
printr
zora
aldrin
aldrin_v2
crema
cropper
cropper_legacy
guacswap
invariant
lifinity_v1
openbook_v1 / serum-style legacy
orca_v1
orca_v2
saber
saros
serum_v3
token_swap

View File

@@ -6,6 +6,44 @@
Ce document reflète le point de reprise `0.7.43-E5C` et létat de consolidation atteint après `0.7.45` pour `meteora_dlmm`. La version Cargo a évolué ensuite à `0.7.46` côté workspace. Le lot Meteora initialement ouvert en bloc a été redécoupé : `meteora_dlmm` est traité séparément, puis la suite reprend `meteora_damm_v1`, `meteora_damm_v2` et `meteora_dbc` un par un. Ce document reflète le point de reprise `0.7.43-E5C` et létat de consolidation atteint après `0.7.45` pour `meteora_dlmm`. La version Cargo a évolué ensuite à `0.7.46` côté workspace. Le lot Meteora initialement ouvert en bloc a été redécoupé : `meteora_dlmm` est traité séparément, puis la suite reprend `meteora_damm_v1`, `meteora_damm_v2` et `meteora_dbc` un par un.
## État de reprise actuel `0.7.47-1FE5`
Le point de reprise courant nest plus `0.7.43-E5C`. La branche de travail actuelle est `0.7.47-1FE5` : le registre upstream Git est en place, OpenBook v2 et Phoenix v1 disposent de decoders locaux **audit-only**, et la suite doit être conduite par DEX/version au lieu de tenter tous les events en une seule session.
Les nouveaux chemins audit-only doivent rester non matérialisants : aucun event OpenBook v2 ou Phoenix v1 ne doit produire de trade, metric ou candle tant que le sens économique complet nest pas validé.
Voir aussi :
- `DEX_DECODER_MATRIX.md` pour la matrice DEX détaillée ;
- `ROADMAP.md` pour le plan révisé `0.7.48` à `0.7.61` ;
- `CHANGELOG.md` pour les tranches `0.7.47-*`.
## Sources upstream Git / IDL à utiliser en `0.7.47+`
Les sources externes ci-dessous sont des **indices de décodage**, pas des preuves métier. Elles servent à extraire des `program_id`, discriminants, IDL, layouts et noms dinstructions/events, mais toute promotion locale doit rester conditionnée à :
1. observation Demo3 ;
2. backfill Demo2 ;
3. replay local sur la base de travail ;
4. requêtes SQL de validation ;
5. invariants : aucun faux trade, aucune fausse candle, aucun `program_id` promu sans corpus.
Sources prioritaires :
| Source | Usage attendu |
|---|---|
| `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/sol-parser-sdk/tree/main/idl` | 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/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/all-in-one-blockchain/phoenix-onchain-mm` | Source Phoenix/MM complémentaire pour corpus et intégration. |
| `https://docs.vybenetwork.com/docs/available-dexs-amms` | Source de découverte de DEX/AMM supportés par Vybe, à traiter comme index externe non vérifié localement. |
## 1. Objectif ## 1. Objectif
Lobjectif opérationnel est de construire progressivement une application capable de : Lobjectif opérationnel est de construire progressivement une application capable de :
@@ -388,3 +426,15 @@ La version `0.7.47` nest plus dédiée à un seul DEX. Elle doit introduire u
Les entrées de ce registre sont des indices de découverte, pas des preuves métier. Elles doivent être marquées `upstream_git_unverified` ou `upstream_git_mapped_unverified` tant quelles ne sont pas confirmées par Demo3, backfill, replay local et requêtes SQL. Les entrées de ce registre sont des indices de découverte, pas des preuves métier. Elles doivent être marquées `upstream_git_unverified` ou `upstream_git_mapped_unverified` tant quelles ne sont pas confirmées par Demo3, backfill, replay local et requêtes SQL.
Le registre sert à accélérer la constitution de corpus pour les DEX et surfaces suivantes : Meteora DAMM v2/DBC/Vault, Raydium Launchpad/Stable/Locking, Orca Whirlpools, FluxBeam, DexLab, Lifinity AMM v2, Phoenix/OpenBook, Stabble, BonkSwap, Boop, Moonshot, Heaven, Wavebreak, Vertigo, Virtuals, Pancake Swap, OKX DEX, Jupiter/Kamino/Drift et autres programmes utiles à la découverte. Le registre sert à accélérer la constitution de corpus pour les DEX et surfaces suivantes : Meteora DAMM v2/DBC/Vault, Raydium Launchpad/Stable/Locking, Orca Whirlpools, FluxBeam, DexLab, Lifinity AMM v2, Phoenix/OpenBook, Stabble, BonkSwap, Boop, Moonshot, Heaven, Wavebreak, Vertigo, Virtuals, Pancake Swap, OKX DEX, Jupiter/Kamino/Drift et autres programmes utiles à la découverte.
## Note 0.7.47-1FE5 — Event coverage et modèle DB
La matrice DEX/version doit être complétée par une matrice événementielle exhaustive. Le projet ne vise pas seulement les swaps : les events `burn`, `mint`, `transfer`, `account_close`, `lock/unlock`, `vault_deposit/withdraw`, `admin/config`, `order_fill`, `settle_funds`, `launch` et `migration` peuvent influencer une décision de trading.
Voir :
- `DEX_DECODER_MATRIX.md` pour le statut par DEX/version ;
- `DEX_EVENT_COVERAGE_MATRIX.md` pour les familles d'events à couvrir ;
- `DB_EVENT_MODEL_REVIEW.md` pour les ajouts DB à envisager avant `0.7.48+`.

View File

@@ -2,6 +2,55 @@
# khadhroony-bobobot — Roadmap # khadhroony-bobobot — Roadmap
## 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 : `DEX_DECODER_MATRIX.md`.
Règles de planification :
- chaque DEX/version doit avoir sa propre phase de corpus ;
- chaque phase doit lister explicitement les sources Git/IDL consultées ;
- tous les events/instructions disponibles dans les sources doivent être inventoriés, même si seuls certains deviennent matérialisés ;
- le statut par event doit rester séparé : `upstream_git_unverified`, `upstream_git_mapped_unverified`, `upstream_git_local_corpus_observed`, `audit-only`, `materialized` ;
- un decoder local spécialisé peut remplacer `upstream_git.instruction_match`, mais ne doit pas créer de trade/candle sans validation de montants et de sens économique ;
- OpenBook v2 et Phoenix v1 restent `audit-only` à ce stade, malgré leurs layouts partiellement décodés.
### Sources upstream obligatoires à vérifier
| Source | Usage |
|---|---|
| `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/sol-parser-sdk/tree/main/idl` | IDL complémentaires. |
| `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/openbook-dex/openbook-v2` | Source officielle OpenBook v2. |
| `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. |
### Plan révisé `0.7.48` à `0.7.61`
| Version cible | Scope | Objectif de clôture |
|---|---|---|
| `0.7.48` | `raydium_cpmm` | Reprendre tous les discriminants/events depuis Carbon/Solana Streamer ; vérifier swaps, liquidity, fees/admin ; confirmer matérialisation trade/candle et non-trade. |
| `0.7.49` | `raydium_clmm` | Couvrir toutes les instructions CLMM : swaps, positions, liquidity, fees/rewards, Token-2022 ; valider matérialisation non-trade. |
| `0.7.50` | `pump_swap` | Couvrir `buy/sell` et tous les events auxiliaires disponibles : fees, cashback, volume accumulator, admin/config. |
| `0.7.51` | `pump_fun` | Traiter launch/bonding/migration ; séparer création token, buy/sell bonding, migration vers DEX effectif. |
| `0.7.52` | `meteora_dbc` | Couverture DBC : bonding curve, swap, migration, launch attribution, fees/admin, non-trade. |
| `0.7.53` | `meteora_dlmm` | Audit final de parité avec sources Git/IDL ; fermer ou documenter les audits résiduels. |
| `0.7.54` | `meteora_damm_v1` | Parité upstream complète ; résoudre les cas non matérialisés faute de pool/pair quand possible. |
| `0.7.55` | `meteora_damm_v2` | Couverture DAMM v2 complète : create, swap, liquidity, fees/admin/config ; décider trade actionability. |
| `0.7.56` | `phoenix_v1` | Finir tous les events Git disponibles en audit ; préparer mais ne pas activer trade materialization. |
| `0.7.57` | `openbook_v2` | Finir layouts logs/events ; définir conditions futures de trade/candle sans les activer par défaut. |
| `0.7.58` | `orca_whirlpools` | Reprendre Whirlpools depuis IDL/source : swaps, pools, positions, liquidity, fees/rewards. |
| `0.7.59` | Launch surfaces | Raydium LaunchLab/Launchpad, PumpFun migration, Moonshot/Moonit, Boop, Heaven, Bags, LetsBonk. |
| `0.7.60` | DEX historiques / candidats | FluxBeam, DexLab, Lifinity, Stabble, BonkSwap, GooseFX, Obric, SolFi, etc. par corpus. |
| `0.7.61` | Validation consolidée | Rejouer une base neuve multi-DEX, vérifier matrice, zéro faux trade/candle, rapport de couverture par DEX/event. |
Ce plan remplace les anciens regroupements larges `0.7.50+` qui mélangeaient plusieurs DEX dans une même version.
## 1. Objet du projet ## 1. Objet du projet
`khadhroony-bobobot` est un workspace Rust destiné à la détection, lobservation, lanalyse de patterns et, à terme, à lexécution semi-automatisée dachats/ventes de tokens sur la blockchain Solana. `khadhroony-bobobot` est un workspace Rust destiné à la détection, lobservation, lanalyse de patterns et, à terme, à lexécution semi-automatisée dachats/ventes de tokens sur la blockchain Solana.
@@ -1188,6 +1237,21 @@ Familles prioritaires à indexer en premier :
Aucun de ces programmes ne doit être marqué `verified_by_corpus` uniquement parce quil existe dans un dépôt Git externe. Aucun de ces programmes ne doit être marqué `verified_by_corpus` uniquement parce quil existe dans un dépôt Git externe.
### 6.079B. Version `0.7.48-pre` — Event coverage et checkpoint DB
Objectif : éviter de limiter la matrice aux DEX/versions et imposer une couverture événementielle exhaustive avant la reprise DEX par DEX.
À faire :
- maintenir `DEX_EVENT_COVERAGE_MATRIX.md` en plus de `DEX_DECODER_MATRIX.md` ;
- lister pour chaque DEX/version tous les events/instructions/logs connus depuis Carbon, fnzero, IDL, Pinax, HODL Warden, OpenBook, Phoenix et Vybe ;
- inclure explicitement les familles non-trade : `burn`, `mint`, `transfer`, `account_create`, `account_close`, `wrap_sol`, `unwrap_sol`, `lock`, `unlock`, `vault_deposit`, `vault_withdraw`, `admin/config`, `fee`, `reward`, `launch`, `migration` ;
- vérifier si la DB actuelle suffit ou si une table transversale doit être ajoutée ;
- prioriser `k_sol_dex_event_coverage_entries`, puis `k_sol_token_transfer_events` et `k_sol_orderbook_events` ;
- ne pas créer de trade/candle depuis ces nouveaux chemins sans validation économique et corpus.
### 6.080. Version `0.7.48` — `meteora_damm_v2` séparé ### 6.080. Version `0.7.48` — `meteora_damm_v2` séparé
Objectif : reprendre `meteora_damm_v2` comme DEX effectif séparé après disponibilité du registre upstream Git. Objectif : reprendre `meteora_damm_v2` comme DEX effectif séparé après disponibilité du registre upstream Git.

View File

@@ -138,13 +138,85 @@
<input id="demo3TargetPoolAdminInput" class="form-check-input" type="checkbox" name="demo3TargetEventInput" value="pool_admin" /> <input id="demo3TargetPoolAdminInput" class="form-check-input" type="checkbox" name="demo3TargetEventInput" value="pool_admin" />
<label for="demo3TargetPoolAdminInput" class="form-check-label">pool_admin</label> <label for="demo3TargetPoolAdminInput" class="form-check-label">pool_admin</label>
</div> </div>
<div class="form-check form-check-inline">
<input id="demo3TargetBurnInput" class="form-check-input" type="checkbox" name="demo3TargetEventInput" value="burn" />
<label for="demo3TargetBurnInput" class="form-check-label">burn</label>
</div>
<div class="form-check form-check-inline">
<input id="demo3TargetMintInput" class="form-check-input" type="checkbox" name="demo3TargetEventInput" value="mint" />
<label for="demo3TargetMintInput" class="form-check-label">mint</label>
</div>
<div class="form-check form-check-inline">
<input id="demo3TargetTransferInput" class="form-check-input" type="checkbox" name="demo3TargetEventInput" value="transfer" />
<label for="demo3TargetTransferInput" class="form-check-label">transfer</label>
</div>
<div class="form-check form-check-inline">
<input id="demo3TargetCloseAccountInput" class="form-check-input" type="checkbox" name="demo3TargetEventInput" value="close_account" />
<label for="demo3TargetCloseAccountInput" class="form-check-label">close_account</label>
</div>
<div class="form-check form-check-inline">
<input id="demo3TargetTokenAccountCreateInput" class="form-check-input" type="checkbox" name="demo3TargetEventInput" value="token_account_create" />
<label for="demo3TargetTokenAccountCreateInput" class="form-check-label">token_account_create</label>
</div>
<div class="form-check form-check-inline">
<input id="demo3TargetWrapSolInput" class="form-check-input" type="checkbox" name="demo3TargetEventInput" value="wrap_sol" />
<label for="demo3TargetWrapSolInput" class="form-check-label">wrap_sol</label>
</div>
<div class="form-check form-check-inline">
<input id="demo3TargetUnwrapSolInput" class="form-check-input" type="checkbox" name="demo3TargetEventInput" value="unwrap_sol" />
<label for="demo3TargetUnwrapSolInput" class="form-check-label">unwrap_sol</label>
</div>
<div class="form-check form-check-inline">
<input id="demo3TargetStakeInput" class="form-check-input" type="checkbox" name="demo3TargetEventInput" value="stake" />
<label for="demo3TargetStakeInput" class="form-check-label">stake</label>
</div>
<div class="form-check form-check-inline">
<input id="demo3TargetUnstakeInput" class="form-check-input" type="checkbox" name="demo3TargetEventInput" value="unstake" />
<label for="demo3TargetUnstakeInput" class="form-check-label">unstake</label>
</div>
<div class="form-check form-check-inline">
<input id="demo3TargetOrderPlaceInput" class="form-check-input" type="checkbox" name="demo3TargetEventInput" value="order_place" />
<label for="demo3TargetOrderPlaceInput" class="form-check-label">order_place</label>
</div>
<div class="form-check form-check-inline">
<input id="demo3TargetOrderCancelInput" class="form-check-input" type="checkbox" name="demo3TargetEventInput" value="order_cancel" />
<label for="demo3TargetOrderCancelInput" class="form-check-label">order_cancel</label>
</div>
<div class="form-check form-check-inline">
<input id="demo3TargetOrderFillInput" class="form-check-input" type="checkbox" name="demo3TargetEventInput" value="order_fill" />
<label for="demo3TargetOrderFillInput" class="form-check-label">order_fill</label>
</div>
<div class="form-check form-check-inline">
<input id="demo3TargetSettleFundsInput" class="form-check-input" type="checkbox" name="demo3TargetEventInput" value="settle_funds" />
<label for="demo3TargetSettleFundsInput" class="form-check-label">settle_funds</label>
</div>
<div class="form-check form-check-inline">
<input id="demo3TargetConsumeEventsInput" class="form-check-input" type="checkbox" name="demo3TargetEventInput" value="consume_events" />
<label for="demo3TargetConsumeEventsInput" class="form-check-label">consume_events</label>
</div>
<div class="form-check form-check-inline">
<input id="demo3TargetMarketCreateInput" class="form-check-input" type="checkbox" name="demo3TargetEventInput" value="market_create" />
<label for="demo3TargetMarketCreateInput" class="form-check-label">market_create</label>
</div>
<div class="form-check form-check-inline">
<input id="demo3TargetOpenOrdersCreateInput" class="form-check-input" type="checkbox" name="demo3TargetEventInput" value="open_orders_create" />
<label for="demo3TargetOpenOrdersCreateInput" class="form-check-label">open_orders_create</label>
</div>
<div class="form-check form-check-inline">
<input id="demo3TargetOpenOrdersCloseInput" class="form-check-input" type="checkbox" name="demo3TargetEventInput" value="open_orders_close" />
<label for="demo3TargetOpenOrdersCloseInput" class="form-check-label">open_orders_close</label>
</div>
<div class="form-check form-check-inline">
<input id="demo3TargetCloseMarketInput" class="form-check-input" type="checkbox" name="demo3TargetEventInput" value="close_market" />
<label for="demo3TargetCloseMarketInput" class="form-check-label">close_market</label>
</div>
<div class="form-check form-check-inline"> <div class="form-check form-check-inline">
<input id="demo3TargetUnknownNonSwapInput" class="form-check-input" type="checkbox" name="demo3TargetEventInput" value="unknown_non_swap" /> <input id="demo3TargetUnknownNonSwapInput" class="form-check-input" type="checkbox" name="demo3TargetEventInput" value="unknown_non_swap" />
<label for="demo3TargetUnknownNonSwapInput" class="form-check-label">unknown_non_swap</label> <label for="demo3TargetUnknownNonSwapInput" class="form-check-label">unknown_non_swap</label>
</div> </div>
</div> </div>
<div class="form-text">Leave all unchecked for generic discovery. Check several surfaces to scan once and keep candidates matching any selected target.</div> <div class="form-text">Leave all unchecked for generic discovery. Check several surfaces to scan once and keep candidates matching any selected target.</div>
<div class="form-text">Use this to find corpus signatures for non-swap decoders without promoting unverified events.</div> <div class="form-text">Use this to find corpus signatures for non-swap decoders without promoting unverified events. Leave all unchecked to request target='any'.</div>
</div> </div>
<div class="col-6"> <div class="col-6">
<label for="demo3HttpRoleInput" class="form-label">HTTP role</label> <label for="demo3HttpRoleInput" class="form-label">HTTP role</label>

View File

@@ -0,0 +1,54 @@
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
/**
* One upstream registry entry exposed through Demo3.
*/
export type Demo3UpstreamRegistryEntry = {
/**
* Repository name or bootstrap locator that produced the entry.
*/
sourceRepo: string | null,
/**
* Repository-relative path or bootstrap path that produced the entry.
*/
sourcePath: string | null,
/**
* Stable decoder code used by the registry.
*/
decoderCode: string,
/**
* Optional Solana program id when already known by the source entry.
*/
programId: string | null,
/**
* Program family used to group related programs.
*/
programFamily: string,
/**
* Surface kind such as AMM, CLMM, launch, aggregator or core Solana.
*/
surfaceKind: string,
/**
* Entry kind: instruction, event, account or program.
*/
entryKind: string,
/**
* Source-level entry name.
*/
entryName: string,
/**
* Optional discriminator bytes encoded as lowercase hexadecimal.
*/
discriminatorHex: string | null,
/**
* Optional discriminator byte length.
*/
discriminatorLen: number | null,
/**
* Current proof status.
*/
proofStatus: string,
/**
* Notes that preserve uncertainty and validation requirements.
*/
notes: string, };

View File

@@ -0,0 +1,15 @@
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
import type { Demo3UpstreamRegistryResult } from "./Demo3UpstreamRegistryResult";
/**
* Payload returned by the Demo3 upstream registry command.
*/
export type Demo3UpstreamRegistryPayload = {
/**
* Pretty JSON representation of the registry result.
*/
resultJson: string,
/**
* Structured registry result.
*/
result: Demo3UpstreamRegistryResult, };

View File

@@ -0,0 +1,21 @@
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
import type { Demo3UpstreamRegistryEntry } from "./Demo3UpstreamRegistryEntry";
import type { Demo3UpstreamRegistrySearchRequest } from "./Demo3UpstreamRegistrySearchRequest";
import type { Demo3UpstreamRegistrySummary } from "./Demo3UpstreamRegistrySummary";
/**
* Structured upstream registry result exposed through Demo3.
*/
export type Demo3UpstreamRegistryResult = {
/**
* Normalized request used by kb_lib.
*/
request: Demo3UpstreamRegistrySearchRequest,
/**
* Registry summary.
*/
summary: Demo3UpstreamRegistrySummary,
/**
* Matching entries.
*/
entries: Array<Demo3UpstreamRegistryEntry>, };

View File

@@ -0,0 +1,34 @@
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
/**
* Search request for the static upstream registry exposed through Demo3.
*/
export type Demo3UpstreamRegistrySearchRequest = {
/**
* Optional decoder-code filter.
*/
decoderCode: string | null,
/**
* Optional program-id filter.
*/
programId: string | null,
/**
* Optional program-family filter.
*/
programFamily: string | null,
/**
* Optional surface-kind filter.
*/
surfaceKind: string | null,
/**
* Optional entry-kind filter.
*/
entryKind: string | null,
/**
* Optional proof-status filter.
*/
proofStatus: string | null,
/**
* Optional maximum number of entries to return.
*/
limit: number | null, };

View File

@@ -0,0 +1,58 @@
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
/**
* Summary of the upstream registry snapshot exposed through Demo3.
*/
export type Demo3UpstreamRegistrySummary = {
/**
* Total static registry entry count before filtering.
*/
totalEntryCount: number,
/**
* Returned entry count after filtering.
*/
returnedEntryCount: number,
/**
* Number of entries that have a program id.
*/
entriesWithProgramIdCount: number,
/**
* Number of entries that have a discriminator.
*/
entriesWithDiscriminatorCount: number,
/**
* Number of program-level seed entries.
*/
programEntryCount: number,
/**
* Number of instruction entries.
*/
instructionEntryCount: number,
/**
* Number of event entries.
*/
eventEntryCount: number,
/**
* Number of account entries.
*/
accountEntryCount: number,
/**
* Number of entries still unverified from upstream Git or seed data.
*/
upstreamGitUnverifiedCount: number,
/**
* Number of entries mapped into decoders but not locally observed.
*/
upstreamGitMappedUnverifiedCount: number,
/**
* Number of entries observed in the local corpus.
*/
upstreamGitLocalCorpusObservedCount: number,
/**
* Number of entries materialized in local business tables.
*/
upstreamGitLocalCorpusMaterializedCount: number,
/**
* Number of layout entries still unverified locally.
*/
upstreamGitLayoutUnverifiedCount: number, };

View File

@@ -36,6 +36,22 @@ const presets: Demo3Preset[] = [
{ label: "Meteora DBC", dexCode: "meteora_dbc", programId: "dbcij3LWUppWqq96dh6gJWwBifmcGfLSB5D4DuSMaqN", description: "Meteora DBC." }, { label: "Meteora DBC", dexCode: "meteora_dbc", programId: "dbcij3LWUppWqq96dh6gJWwBifmcGfLSB5D4DuSMaqN", description: "Meteora DBC." },
{ label: "Orca Whirlpools", dexCode: "orca_whirlpools", programId: "whirLbMiicVdio4qvUfM5KAg6Ct8VwpYzGff3uctyCc", description: "Orca Whirlpools CLMM." }, { label: "Orca Whirlpools", dexCode: "orca_whirlpools", programId: "whirLbMiicVdio4qvUfM5KAg6Ct8VwpYzGff3uctyCc", description: "Orca Whirlpools CLMM." },
{ label: "FluxBeam", dexCode: "fluxbeam", programId: "FLUXubRmkEi2q6K3Y9kBPg9248ggaZVsoSFhtJHSrm1X", description: "FluxBeam." }, { label: "FluxBeam", dexCode: "fluxbeam", programId: "FLUXubRmkEi2q6K3Y9kBPg9248ggaZVsoSFhtJHSrm1X", description: "FluxBeam." },
{ label: "GooseFX V1 (Vybe)", dexCode: "goosefx_v1", programId: "GAMMA7meSFWaBXF25oSUgmGRwaW6sCMFLmBNiMSdbHVT", description: "Entrée Vybe; à vérifier par corpus local." },
{ label: "Obric V2 (Vybe)", dexCode: "obric_v2", programId: "obriQD1zbpyLz95G5n7nJe6a4DPjpFwa5XYPoNm113y", description: "Entrée Vybe; à vérifier par corpus local." },
{ label: "Ondo Global Market (Vybe)", dexCode: "ondo_global_market", programId: "XzTT4XB8m7sLD2xi6snefSasaswsKCxx5Tifjondogm", description: "Entrée Vybe; à vérifier par corpus local." },
{ label: "Scorch (Vybe)", dexCode: "scorch", programId: "SCoRcH8c2dpjvcJD6FiPbCSQyQgu3PcUAWj2Xxx3mqn", description: "Entrée Vybe; à vérifier par corpus local." },
{ label: "ZeroFi (Vybe)", dexCode: "zerofi", programId: "ZERor4xhbUycZ6gb9ntrhqscUcZmAbQDjEAtCf4hbZY", description: "Entrée Vybe; à vérifier par corpus local." },
{ label: "Manifest CLOB (Vybe)", dexCode: "manifest_clob", programId: "MNFSTqtC93rEfYHB6hF82sKdZpUDFWkViLByLd1k1Ms", description: "Entrée Vybe orderbook; à vérifier par corpus local." },
{ label: "AlphaQ (Vybe)", dexCode: "alphaq", programId: "ALPHAQmeA7bjrVuccPsYPiCvsi428SNwte66Srvs4pHA", description: "Entrée Vybe; à vérifier par corpus local." },
{ label: "Goonfi (Vybe)", dexCode: "goonfi", programId: "goonERTdGsjnkZqWuVjs73BZ3Pb9qoCUdBUL17BnS5j", description: "Entrée Vybe; à vérifier par corpus local." },
{ label: "Goonfi V2 (Vybe)", dexCode: "goonfi_v2", programId: "goonuddtQRrWqqn5nFyczVKaie28f3kDkHWkHtURSLE", description: "Entrée Vybe; à vérifier par corpus local." },
{ label: "Byreal (Vybe)", dexCode: "byreal", programId: "REALQqNEomY6cQGZJUGwywTBD2UmDT32rZcNnfxQ5N2", description: "Entrée Vybe; à vérifier par corpus local." },
{ label: "BisonFi (Vybe)", dexCode: "bisonfi", programId: "BiSoNHVpsVZW2F7rx2eQ59yQwKxzU5NvBcmKshCSUypi", description: "Entrée Vybe; à vérifier par corpus local." },
{ label: "FusionAMM (Vybe)", dexCode: "fusionamm", programId: "fUSioN9YKKSa3CUC2YUc4tPkHJ5Y6XW1yz8y6F7qWz9", description: "Entrée Vybe; à vérifier par corpus local." },
{ label: "Woofi (Vybe)", dexCode: "woofi", programId: "WooFif76YGRNjk1pA8wCsN67aQsD9f9iLsz4NcJ1AVb", description: "Entrée Vybe; à vérifier par corpus local." },
{ label: "Aquifer (Vybe)", dexCode: "aquifer", programId: "AQU1FRd7papthgdrwPTTq5JacJh8YtwEXaBfKU3bTz45", description: "Entrée Vybe; à vérifier par corpus local." },
{ label: "Humidifi (Vybe)", dexCode: "humidifi", programId: "9H6tua7jkLhdm3w8BvgpTn5LZNU7g4ZynDmCiNN3q6Rp", description: "Entrée Vybe; à vérifier par corpus local." },
{ label: "SolFi V2 (Vybe)", dexCode: "solfi_v2", programId: "SV2EYYJyRz2YhfXwXnhNAevDEui5Q6yrfyo13WtupPF", description: "Entrée Vybe; à vérifier par corpus local." },
{ label: "DexLab", dexCode: "dexlab", programId: "DSwpgjMvXhtGn6BsbqmacdBZyfLj6jSWf3HJpdJtmg6N", description: "DexLab Swap/Pool." }, { label: "DexLab", dexCode: "dexlab", programId: "DSwpgjMvXhtGn6BsbqmacdBZyfLj6jSWf3HJpdJtmg6N", description: "DexLab Swap/Pool." },
{ label: "Aldrin (historical)", dexCode: "aldrin", programId: "AMM55ShdkoGRB5jVYPjWziwk8m5MpwyDgsMWHaMSQWH6", description: "Program id historique importé depuis entities.py; à vérifier par corpus." }, { label: "Aldrin (historical)", dexCode: "aldrin", programId: "AMM55ShdkoGRB5jVYPjWziwk8m5MpwyDgsMWHaMSQWH6", description: "Program id historique importé depuis entities.py; à vérifier par corpus." },
{ label: "Aldrin V2 (historical)", dexCode: "aldrin_v2", programId: "CURVGoZn8zycx6FXwwevgBTB2gVvdbGTEpvMJDbgs2t4", description: "Program id historique importé depuis entities.py; à vérifier par corpus." }, { label: "Aldrin V2 (historical)", dexCode: "aldrin_v2", programId: "CURVGoZn8zycx6FXwwevgBTB2gVvdbGTEpvMJDbgs2t4", description: "Program id historique importé depuis entities.py; à vérifier par corpus." },
@@ -49,7 +65,6 @@ const presets: Demo3Preset[] = [
{ label: "Phoenix (historical)", dexCode: "phoenix", programId: "PhoeNiXZ8ByJGLkxNfZRnkUfjvmuYqLR89jjFHGqdXY", description: "Program id historique importé depuis entities.py; à vérifier par corpus." }, { label: "Phoenix (historical)", dexCode: "phoenix", programId: "PhoeNiXZ8ByJGLkxNfZRnkUfjvmuYqLR89jjFHGqdXY", description: "Program id historique importé depuis entities.py; à vérifier par corpus." },
{ label: "Saber (historical)", dexCode: "saber", programId: "SSwpkEEcbUqx4vtoEByFjSkhKdCT862DNVb52nZg1UZ", description: "Program id historique importé depuis entities.py; à vérifier par corpus." }, { label: "Saber (historical)", dexCode: "saber", programId: "SSwpkEEcbUqx4vtoEByFjSkhKdCT862DNVb52nZg1UZ", description: "Program id historique importé depuis entities.py; à vérifier par corpus." },
{ label: "OpenBook V2 (historical)", dexCode: "openbook_v2", programId: "opnb2LAfJYbRMAHHvqjCwQxanZn7ReEHp1k81EohpZb", description: "Program id historique importé depuis entities.py; à vérifier par corpus." }, { label: "OpenBook V2 (historical)", dexCode: "openbook_v2", programId: "opnb2LAfJYbRMAHHvqjCwQxanZn7ReEHp1k81EohpZb", description: "Program id historique importé depuis entities.py; à vérifier par corpus." },
{ label: "OpenBook (historical)", dexCode: "openbook", programId: "srmqPvymJeFKQ4zGQed1GFppgkRHL9kaELCbyksJtPX", description: "Program id historique importé depuis entities.py; à vérifier par corpus." },
{ label: "Fox (historical)", dexCode: "fox", programId: "HyhpEq587ANShDdbx1mP4dTmDZC44CXWft29oYQXDb53", description: "Program id historique importé depuis entities.py; à vérifier par corpus." }, { label: "Fox (historical)", dexCode: "fox", programId: "HyhpEq587ANShDdbx1mP4dTmDZC44CXWft29oYQXDb53", description: "Program id historique importé depuis entities.py; à vérifier par corpus." },
{ label: "Sanctum Infinity (historical)", dexCode: "sanctum_infinity", programId: "5ocnV1qiCgaQR8Jb8xWnVbApfaygJ8tNoZfgPwsgx9kx", description: "Program id historique importé depuis entities.py; à vérifier par corpus." }, { label: "Sanctum Infinity (historical)", dexCode: "sanctum_infinity", programId: "5ocnV1qiCgaQR8Jb8xWnVbApfaygJ8tNoZfgPwsgx9kx", description: "Program id historique importé depuis entities.py; à vérifier par corpus." },
{ label: "Saros (historical)", dexCode: "saros", programId: "SSwapUtytfBdBn1b9NUGG6foMVPtcWgpRU32HToDUZr", description: "Program id historique importé depuis entities.py; à vérifier par corpus." }, { label: "Saros (historical)", dexCode: "saros", programId: "SSwapUtytfBdBn1b9NUGG6foMVPtcWgpRU32HToDUZr", description: "Program id historique importé depuis entities.py; à vérifier par corpus." },
@@ -70,15 +85,8 @@ const presets: Demo3Preset[] = [
{ label: "Invariant (historical)", dexCode: "invariant", programId: "HyaB3W9q6XdA5xwpU4XnSZV94htfmbmqJXZcEbRaJutt", description: "Program id historique importé depuis entities.py; à vérifier par corpus." }, { label: "Invariant (historical)", dexCode: "invariant", programId: "HyaB3W9q6XdA5xwpU4XnSZV94htfmbmqJXZcEbRaJutt", description: "Program id historique importé depuis entities.py; à vérifier par corpus." },
{ label: "Token Swap (historical)", dexCode: "token_swap", programId: "SwaPpA9LAaLfeLi3a68M4DjnLqgtticKg6CnyNwgAC8", description: "Program id historique importé depuis entities.py; à vérifier par corpus." }, { label: "Token Swap (historical)", dexCode: "token_swap", programId: "SwaPpA9LAaLfeLi3a68M4DjnLqgtticKg6CnyNwgAC8", description: "Program id historique importé depuis entities.py; à vérifier par corpus." },
{ label: "Helium Network (historical)", dexCode: "helium_network", programId: "treaf4wWBBty3fHdyBpo35Mz84M8k3heKXmjmi9vFt5", description: "Program id historique importé depuis entities.py; à vérifier par corpus." }, { label: "Helium Network (historical)", dexCode: "helium_network", programId: "treaf4wWBBty3fHdyBpo35Mz84M8k3heKXmjmi9vFt5", description: "Program id historique importé depuis entities.py; à vérifier par corpus." },
{ label: "Raydium Legacy V2 (historical)", dexCode: "raydium_legacy_v2", programId: "27haf8L6oxUeXrHrgEgsexjSY5hbVUWEmvv9Nyxg8vQv", description: "Program id historique importé depuis entities.py; à vérifier par corpus." },
{ label: "Raydium Legacy V3 (historical)", dexCode: "raydium_legacy_v3", programId: "7quYqsZdpWSZ3qgDextersDqoKjZy7aCgwHBBfRb7KPt", description: "Program id historique importé depuis entities.py; à vérifier par corpus." },
{ label: "Serum V3 (historical)", dexCode: "serum_v3", programId: "9xQeWvG816bUx9EPjHmaT23yvVM2ZWbrrpZb9PusVFin", description: "Program id historique importé depuis entities.py; à vérifier par corpus." },
{ label: "Mango Private Pools (historical)", dexCode: "mango_private_pools", programId: "AtdP2iyfh6xBGwVZzHvY73E7uKKkZBTH2siHh3ZuEf1P", description: "Program id historique importé depuis entities.py; à vérifier par corpus." },
{ label: "Marinade Liquid Staking (historical)", dexCode: "marinade_liquid_staking", programId: "MarBmsSgKXdrN1egZf5sqe1TMai9K1rChYNDJgjq7aD", description: "Program id historique importé depuis entities.py; à vérifier par corpus." }, { label: "Marinade Liquid Staking (historical)", dexCode: "marinade_liquid_staking", programId: "MarBmsSgKXdrN1egZf5sqe1TMai9K1rChYNDJgjq7aD", description: "Program id historique importé depuis entities.py; à vérifier par corpus." },
{ label: "Step Finance Pools (historical)", dexCode: "step_finance_pools", programId: "StepAscQoEioFxxWGnh2sLBDFp9d8rvKz2Yp39iDpyT", description: "Program id historique importé depuis entities.py; à vérifier par corpus." },
{ label: "Francium Yield Pools (historical)", dexCode: "francium_yield_pools", programId: "FC81tbGt6JWRXidaWYFXxGnTk4VgobhJHATvTRVMqgWj", description: "Program id historique importé depuis entities.py; à vérifier par corpus." }, { label: "Francium Yield Pools (historical)", dexCode: "francium_yield_pools", programId: "FC81tbGt6JWRXidaWYFXxGnTk4VgobhJHATvTRVMqgWj", description: "Program id historique importé depuis entities.py; à vérifier par corpus." },
{ label: "Cropper Legacy (historical)", dexCode: "cropper_legacy", programId: "CyZuD7RPDcrqCGbNvzrNVs1zpCQehqp7SuXB7rdFKSzo", description: "Program id historique importé depuis entities.py; à vérifier par corpus." },
{ label: "Dexlab Beta (historical)", dexCode: "dexlab_beta", programId: "9qvG1zP8ZzY1sTnExx7mkyxhW1063YHVYZxMYz2HkM4m", description: "Program id historique importé depuis entities.py; à vérifier par corpus." },
{ label: "Marinade Governance (historical)", dexCode: "marinade_governance", programId: "GovER5Lthms3bLBqWub97yVrMmEogzX7xNjdXpPPCVZw", description: "Program id historique importé depuis entities.py; à vérifier par corpus." }, { label: "Marinade Governance (historical)", dexCode: "marinade_governance", programId: "GovER5Lthms3bLBqWub97yVrMmEogzX7xNjdXpPPCVZw", description: "Program id historique importé depuis entities.py; à vérifier par corpus." },
{ label: "Serum DAO (historical)", dexCode: "serum_dao", programId: "SPoo1Ku8WFXoNDMHPsrGSTSG1Y47rzgn41SLUNakuHy", description: "Program id historique importé depuis entities.py; à vérifier par corpus." }, { label: "Serum DAO (historical)", dexCode: "serum_dao", programId: "SPoo1Ku8WFXoNDMHPsrGSTSG1Y47rzgn41SLUNakuHy", description: "Program id historique importé depuis entities.py; à vérifier par corpus." },
{ label: "Port Finance (historical)", dexCode: "port_finance", programId: "Port7uDYB3wk6GJAw4KT1WpTeMtSu9bTcChBHkX2LfR", description: "Program id historique importé depuis entities.py; à vérifier par corpus." }, { label: "Port Finance (historical)", dexCode: "port_finance", programId: "Port7uDYB3wk6GJAw4KT1WpTeMtSu9bTcChBHkX2LfR", description: "Program id historique importé depuis entities.py; à vérifier par corpus." },
@@ -88,10 +96,12 @@ const presets: Demo3Preset[] = [
{ label: "Raydium Staking Early (historical)", dexCode: "raydium_staking_early", programId: "EhhTKczWMGQt46ynNeRX1WfeagwwJd7ufHvCDjRxjo5Q", description: "Program id historique importé depuis entities.py; à vérifier par corpus." }, { label: "Raydium Staking Early (historical)", dexCode: "raydium_staking_early", programId: "EhhTKczWMGQt46ynNeRX1WfeagwwJd7ufHvCDjRxjo5Q", description: "Program id historique importé depuis entities.py; à vérifier par corpus." },
{ label: "Orca Aquafarm V1 (historical)", dexCode: "orca_aquafarm_v1", programId: "82yxjeMsvaURa4MbZZ7WZZHfobirZYkH1zF8fmeGtyaQ", description: "Program id historique importé depuis entities.py; à vérifier par corpus." }, { label: "Orca Aquafarm V1 (historical)", dexCode: "orca_aquafarm_v1", programId: "82yxjeMsvaURa4MbZZ7WZZHfobirZYkH1zF8fmeGtyaQ", description: "Program id historique importé depuis entities.py; à vérifier par corpus." },
{ label: "Quarry Merge Mining (historical)", dexCode: "quarry_merge_mining", programId: "QMNeHCGYnLVDn1icRAfQZpjPLBNkfGbSKRB83G5d8KB", description: "Program id historique importé depuis entities.py; à vérifier par corpus." }, { label: "Quarry Merge Mining (historical)", dexCode: "quarry_merge_mining", programId: "QMNeHCGYnLVDn1icRAfQZpjPLBNkfGbSKRB83G5d8KB", description: "Program id historique importé depuis entities.py; à vérifier par corpus." },
{ label: "SWAB Finance Beta (historical)", dexCode: "swab_finance_beta", programId: "SWABxNGyxEBVoNRGn6RvYBt5UqercSE5PBHuJeYXYHq", description: "Program id historique importé depuis entities.py; à vérifier par corpus." }, { label: "Printr", dexCode: "printr", programId: "T8HsGYv7sMk3kTnyaRqZrbRPuntYzdh12evXBkprint", description: "Candidat Printr; à vérifier par corpus local." },
{ label: "CRAFT Early (historical)", dexCode: "craft_early", programId: "CrAFTUv7zKXBaS5471aBiARBu6x7nP4rDzr8xwBewbr1", description: "Program id historique importé depuis entities.py; à vérifier par corpus." }, { label: "metaDAO — umbrella (manual)", dexCode: "metadao", programId: "", description: "Umbrella MetaDAO. Choisir de préférence une surface spécifique ci-dessous." },
{ label: "metaDAO", dexCode: "metadao", programId: "", description: "DEX à vérifier. Aucun program id n'est inventé." }, { label: "metaDAO Launchpad v0.7.0", dexCode: "metadao_launchpad_v0_7_0", programId: "moontUzsdepotRGe5xsfip7vLPTJnVuafqdUWexVnPM", description: "Programme MetaDAO officiel candidat; à vérifier par corpus local." },
{ label: "Printr", dexCode: "printr", programId: "", description: "DEX à vérifier. Aucun program id n'est inventé." }, { label: "metaDAO Bid Wall v0.7.0", dexCode: "metadao_bid_wall_v0_7_0", programId: "WALL8ucBuUyL46QYxwYJjidaFYhdvxUFrgvBxPshERx", description: "Programme MetaDAO officiel candidat; à vérifier par corpus local." },
{ label: "metaDAO Futarchy v0.6.0", dexCode: "metadao_futarchy_v0_6_0", programId: "FUTARELBfJfQ8RDGhg1wdhddq1odMAJUePHFuBYfUxKq", description: "Programme MetaDAO officiel candidat; à vérifier par corpus local." },
{ label: "metaDAO AMM v0.5.0", dexCode: "metadao_amm_v0_5_0", programId: "AMMJdEiCCa8mdugg6JPF7gFirmmxisTfDJoSNSUi5zDJ", description: "Programme MetaDAO officiel candidat; à vérifier par corpus local." },
]; ];
let lastResultJson = ""; let lastResultJson = "";
@@ -109,6 +119,16 @@ function valueOrNull(value: string): string | null {
return trimmed === "" ? null : trimmed; return trimmed === "" ? null : trimmed;
} }
function invalidBase58Characters(value: string): string[] {
const invalid = new Set<string>();
for (const character of value.trim()) {
if (!/^[1-9A-HJ-NP-Za-km-z]$/.test(character)) {
invalid.add(character);
}
}
return Array.from(invalid);
}
function isSolanaAddressLike(value: string): boolean { function isSolanaAddressLike(value: string): boolean {
const trimmed = value.trim(); const trimmed = value.trim();
if (trimmed.length < 32 || trimmed.length > 44) { if (trimmed.length < 32 || trimmed.length > 44) {
@@ -161,6 +181,10 @@ function validateOnchainRequest(request: Demo3OnchainDexDiscoveryRequest): void
validateOptionalSignature(request.beforeSignature, "Before signature"); validateOptionalSignature(request.beforeSignature, "Before signature");
validateOptionalSignature(request.untilSignature, "Until signature"); validateOptionalSignature(request.untilSignature, "Until signature");
if (request.programId !== null && !isSolanaAddressLike(request.programId)) { if (request.programId !== null && !isSolanaAddressLike(request.programId)) {
const invalidCharacters = invalidBase58Characters(request.programId);
if (invalidCharacters.length > 0) {
throw new Error(`Program id filter must be a valid Solana base58 program id. Invalid character(s): ${invalidCharacters.join(", ")}.`);
}
throw new Error("Program id filter must be a valid Solana program id, or empty when using a preset that resolves it."); throw new Error("Program id filter must be a valid Solana program id, or empty when using a preset that resolves it.");
} }
} }

View File

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

View File

@@ -285,6 +285,239 @@ pub(crate) async fn demo3_search_local_dex_corpus(
}) })
} }
/// Search request for the static upstream registry exposed through Demo3.
#[derive(Clone, Debug, serde::Serialize, serde::Deserialize, TS)]
#[ts(export, export_to = "../frontend/ts/bindings/Demo3UpstreamRegistrySearchRequest.ts")]
#[serde(rename_all = "camelCase")]
pub(crate) struct Demo3UpstreamRegistrySearchRequest {
/// Optional decoder-code filter.
pub decoder_code: std::option::Option<std::string::String>,
/// Optional program-id filter.
pub program_id: std::option::Option<std::string::String>,
/// Optional program-family filter.
pub program_family: std::option::Option<std::string::String>,
/// Optional surface-kind filter.
pub surface_kind: std::option::Option<std::string::String>,
/// Optional entry-kind filter.
pub entry_kind: std::option::Option<std::string::String>,
/// Optional proof-status filter.
pub proof_status: std::option::Option<std::string::String>,
/// Optional maximum number of entries to return.
pub limit: std::option::Option<u32>,
}
/// Payload returned by the Demo3 upstream registry command.
#[derive(Clone, Debug, serde::Serialize, TS)]
#[ts(export, export_to = "../frontend/ts/bindings/Demo3UpstreamRegistryPayload.ts")]
#[serde(rename_all = "camelCase")]
pub(crate) struct Demo3UpstreamRegistryPayload {
/// Pretty JSON representation of the registry result.
pub result_json: std::string::String,
/// Structured registry result.
pub result: Demo3UpstreamRegistryResult,
}
/// Structured upstream registry result exposed through Demo3.
#[derive(Clone, Debug, serde::Serialize, TS)]
#[ts(export, export_to = "../frontend/ts/bindings/Demo3UpstreamRegistryResult.ts")]
#[serde(rename_all = "camelCase")]
pub(crate) struct Demo3UpstreamRegistryResult {
/// Normalized request used by kb_lib.
pub request: Demo3UpstreamRegistrySearchRequest,
/// Registry summary.
pub summary: Demo3UpstreamRegistrySummary,
/// Matching entries.
pub entries: std::vec::Vec<Demo3UpstreamRegistryEntry>,
}
/// Summary of the upstream registry snapshot exposed through Demo3.
#[derive(Clone, Debug, serde::Serialize, TS)]
#[ts(export, export_to = "../frontend/ts/bindings/Demo3UpstreamRegistrySummary.ts")]
#[serde(rename_all = "camelCase")]
pub(crate) struct Demo3UpstreamRegistrySummary {
/// Total static registry entry count before filtering.
#[ts(type = "number")]
pub total_entry_count: usize,
/// Returned entry count after filtering.
#[ts(type = "number")]
pub returned_entry_count: usize,
/// Number of entries that have a program id.
#[ts(type = "number")]
pub entries_with_program_id_count: usize,
/// Number of entries that have a discriminator.
#[ts(type = "number")]
pub entries_with_discriminator_count: usize,
/// Number of program-level seed entries.
#[ts(type = "number")]
pub program_entry_count: usize,
/// Number of instruction entries.
#[ts(type = "number")]
pub instruction_entry_count: usize,
/// Number of event entries.
#[ts(type = "number")]
pub event_entry_count: usize,
/// Number of account entries.
#[ts(type = "number")]
pub account_entry_count: usize,
/// Number of entries still unverified from upstream Git or seed data.
#[ts(type = "number")]
pub upstream_git_unverified_count: usize,
/// Number of entries mapped into decoders but not locally observed.
#[ts(type = "number")]
pub upstream_git_mapped_unverified_count: usize,
/// Number of entries observed in the local corpus.
#[ts(type = "number")]
pub upstream_git_local_corpus_observed_count: usize,
/// Number of entries materialized in local business tables.
#[ts(type = "number")]
pub upstream_git_local_corpus_materialized_count: usize,
/// Number of layout entries still unverified locally.
#[ts(type = "number")]
pub upstream_git_layout_unverified_count: usize,
}
/// One upstream registry entry exposed through Demo3.
#[derive(Clone, Debug, serde::Serialize, TS)]
#[ts(export, export_to = "../frontend/ts/bindings/Demo3UpstreamRegistryEntry.ts")]
#[serde(rename_all = "camelCase")]
pub(crate) struct Demo3UpstreamRegistryEntry {
/// Repository name or bootstrap locator that produced the entry.
pub source_repo: std::option::Option<std::string::String>,
/// Repository-relative path or bootstrap path that produced the entry.
pub source_path: std::option::Option<std::string::String>,
/// Stable decoder code used by the registry.
pub decoder_code: std::string::String,
/// Optional Solana program id when already known by the source entry.
pub program_id: std::option::Option<std::string::String>,
/// Program family used to group related programs.
pub program_family: std::string::String,
/// Surface kind such as AMM, CLMM, launch, aggregator or core Solana.
pub surface_kind: std::string::String,
/// Entry kind: instruction, event, account or program.
pub entry_kind: std::string::String,
/// Source-level entry name.
pub entry_name: std::string::String,
/// Optional discriminator bytes encoded as lowercase hexadecimal.
pub discriminator_hex: std::option::Option<std::string::String>,
/// Optional discriminator byte length.
pub discriminator_len: std::option::Option<u16>,
/// Current proof status.
pub proof_status: std::string::String,
/// Notes that preserve uncertainty and validation requirements.
pub notes: std::string::String,
}
/// Searches the static upstream registry from Demo3.
#[tauri::command]
pub(crate) fn demo3_search_upstream_registry(
request: Demo3UpstreamRegistrySearchRequest,
) -> Result<Demo3UpstreamRegistryPayload, std::string::String> {
let service = kb_lib::UpstreamRegistryService::new();
let lib_request = to_lib_upstream_registry_request(&request);
let lib_result = service.search(&lib_request);
let ui_result = from_lib_upstream_registry_result(lib_result);
let result_json_result = serde_json::to_string_pretty(&ui_result);
let result_json = match result_json_result {
Ok(result_json) => result_json,
Err(error) => {
return Err(format!("cannot serialize upstream registry result: {}", error));
},
};
return Ok(Demo3UpstreamRegistryPayload {
result_json,
result: ui_result,
});
}
fn to_lib_upstream_registry_request(
request: &Demo3UpstreamRegistrySearchRequest,
) -> kb_lib::UpstreamRegistrySearchRequestDto {
return kb_lib::UpstreamRegistrySearchRequestDto {
decoder_code: normalize_optional_text(request.decoder_code.clone()),
program_id: normalize_optional_text(request.program_id.clone()),
program_family: normalize_optional_text(request.program_family.clone()),
surface_kind: normalize_optional_text(request.surface_kind.clone()),
entry_kind: normalize_optional_text(request.entry_kind.clone()),
proof_status: normalize_optional_text(request.proof_status.clone()),
limit: match request.limit {
Some(limit) => Some(limit as usize),
None => None,
},
};
}
fn from_lib_upstream_registry_result(
result: kb_lib::UpstreamRegistrySearchResultDto,
) -> Demo3UpstreamRegistryResult {
let mut entries = std::vec::Vec::new();
for entry in result.entries {
entries.push(from_lib_upstream_registry_entry(entry));
}
return Demo3UpstreamRegistryResult {
request: from_lib_upstream_registry_request(result.request),
summary: from_lib_upstream_registry_summary(result.summary),
entries,
};
}
fn from_lib_upstream_registry_request(
request: kb_lib::UpstreamRegistrySearchRequestDto,
) -> Demo3UpstreamRegistrySearchRequest {
return Demo3UpstreamRegistrySearchRequest {
decoder_code: request.decoder_code,
program_id: request.program_id,
program_family: request.program_family,
surface_kind: request.surface_kind,
entry_kind: request.entry_kind,
proof_status: request.proof_status,
limit: match request.limit {
Some(limit) => Some(limit as u32),
None => None,
},
};
}
fn from_lib_upstream_registry_summary(
summary: kb_lib::UpstreamRegistrySummaryDto,
) -> Demo3UpstreamRegistrySummary {
return Demo3UpstreamRegistrySummary {
total_entry_count: summary.total_entry_count,
returned_entry_count: summary.returned_entry_count,
entries_with_program_id_count: summary.entries_with_program_id_count,
entries_with_discriminator_count: summary.entries_with_discriminator_count,
program_entry_count: summary.program_entry_count,
instruction_entry_count: summary.instruction_entry_count,
event_entry_count: summary.event_entry_count,
account_entry_count: summary.account_entry_count,
upstream_git_unverified_count: summary.upstream_git_unverified_count,
upstream_git_mapped_unverified_count: summary.upstream_git_mapped_unverified_count,
upstream_git_local_corpus_observed_count: summary
.upstream_git_local_corpus_observed_count,
upstream_git_local_corpus_materialized_count: summary
.upstream_git_local_corpus_materialized_count,
upstream_git_layout_unverified_count: summary.upstream_git_layout_unverified_count,
};
}
fn from_lib_upstream_registry_entry(
entry: kb_lib::UpstreamRegistryEntryDto,
) -> Demo3UpstreamRegistryEntry {
return Demo3UpstreamRegistryEntry {
source_repo: entry.source_repo,
source_path: entry.source_path,
decoder_code: entry.decoder_code,
program_id: entry.program_id,
program_family: entry.program_family,
surface_kind: entry.surface_kind,
entry_kind: entry.entry_kind,
entry_name: entry.entry_name,
discriminator_hex: entry.discriminator_hex,
discriminator_len: entry.discriminator_len,
proof_status: entry.proof_status,
notes: entry.notes,
};
}
fn to_lib_search_request( fn to_lib_search_request(
request: &Demo3LocalDexCorpusSearchRequest, request: &Demo3LocalDexCorpusSearchRequest,
) -> kb_lib::LocalDexCorpusSearchRequestDto { ) -> kb_lib::LocalDexCorpusSearchRequestDto {

View File

@@ -125,6 +125,7 @@ pub async fn run() -> Result<(), kb_lib::Error> {
stop_ws_clients, stop_ws_clients,
crate::demo3::open_demo3_window, crate::demo3::open_demo3_window,
crate::demo3::demo3_search_local_dex_corpus, crate::demo3::demo3_search_local_dex_corpus,
crate::demo3::demo3_search_upstream_registry,
crate::demo3::demo3_discover_onchain_dex_pairs, crate::demo3::demo3_discover_onchain_dex_pairs,
crate::demo3old::open_demo3old_window, crate::demo3old::open_demo3old_window,
crate::demo3old::demo3old_search_local_dex_corpus, crate::demo3old::demo3old_search_local_dex_corpus,

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

View File

@@ -18,6 +18,9 @@ pub const ASSOCIATED_TOKEN_PROGRAM_ID: &str = "ATokenGPvbdGVxr1b2hvZbsiqW5xWH25e
/// @see solana_sdk_ids::address_lookup_table::ID /// @see solana_sdk_ids::address_lookup_table::ID
pub const ADDRESS_LOOKUP_TABLE_PROGRAM_ID: &str = "AddressLookupTab1e1111111111111111111111111"; pub const ADDRESS_LOOKUP_TABLE_PROGRAM_ID: &str = "AddressLookupTab1e1111111111111111111111111";
/// SPL Memo program id. ("MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr").
pub const MEMO_PROGRAM_ID: &str = "MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr";
/// BPF Loader program identifier. ("BPFLoader1111111111111111111111111111111111"). /// BPF Loader program identifier. ("BPFLoader1111111111111111111111111111111111").
/// @see solana_sdk_ids::bpf_loader_deprecated::ID /// @see solana_sdk_ids::bpf_loader_deprecated::ID
pub const BPF_LOADER_DEPRECATED_PROGRAM_ID: &str = "BPFLoader1111111111111111111111111111111111"; pub const BPF_LOADER_DEPRECATED_PROGRAM_ID: &str = "BPFLoader1111111111111111111111111111111111";
@@ -164,6 +167,57 @@ pub const DEXLAB_PROGRAM_ID: &str = "DSwpgjMvXhtGn6BsbqmacdBZyfLj6jSWf3HJpdJtmg6
/// FluxBeam program id. ("FLUXubRmkEi2q6K3Y9kBPg9248ggaZVsoSFhtJHSrm1X"). /// FluxBeam program id. ("FLUXubRmkEi2q6K3Y9kBPg9248ggaZVsoSFhtJHSrm1X").
pub const FLUXBEAM_PROGRAM_ID: &str = "FLUXubRmkEi2q6K3Y9kBPg9248ggaZVsoSFhtJHSrm1X"; pub const FLUXBEAM_PROGRAM_ID: &str = "FLUXubRmkEi2q6K3Y9kBPg9248ggaZVsoSFhtJHSrm1X";
/// GooseFX v1 program id from Vybe supported DEX/AMM documentation.
pub const GOOSEFX_V1_PROGRAM_ID: &str = "GAMMA7meSFWaBXF25oSUgmGRwaW6sCMFLmBNiMSdbHVT";
/// Obric v2 program id from Vybe supported DEX/AMM documentation.
pub const OBRIC_V2_PROGRAM_ID: &str = "obriQD1zbpyLz95G5n7nJe6a4DPjpFwa5XYPoNm113y";
/// Ondo Global Market program id from Vybe supported DEX/AMM documentation.
pub const ONDO_GLOBAL_MARKET_PROGRAM_ID: &str = "XzTT4XB8m7sLD2xi6snefSasaswsKCxx5Tifjondogm";
/// Scorch program id from Vybe supported DEX/AMM documentation.
pub const SCORCH_PROGRAM_ID: &str = "SCoRcH8c2dpjvcJD6FiPbCSQyQgu3PcUAWj2Xxx3mqn";
/// ZeroFi program id from Vybe supported DEX/AMM documentation.
pub const ZEROFI_PROGRAM_ID: &str = "ZERor4xhbUycZ6gb9ntrhqscUcZmAbQDjEAtCf4hbZY";
/// Manifest CLOB program id from Vybe supported DEX/AMM documentation.
pub const MANIFEST_CLOB_PROGRAM_ID: &str = "MNFSTqtC93rEfYHB6hF82sKdZpUDFWkViLByLd1k1Ms";
/// AlphaQ program id from Vybe supported DEX/AMM documentation.
pub const ALPHAQ_PROGRAM_ID: &str = "ALPHAQmeA7bjrVuccPsYPiCvsi428SNwte66Srvs4pHA";
/// Goonfi program id from Vybe supported DEX/AMM documentation.
pub const GOONFI_PROGRAM_ID: &str = "goonERTdGsjnkZqWuVjs73BZ3Pb9qoCUdBUL17BnS5j";
/// Goonfi v2 program id from Vybe supported DEX/AMM documentation.
pub const GOONFI_V2_PROGRAM_ID: &str = "goonuddtQRrWqqn5nFyczVKaie28f3kDkHWkHtURSLE";
/// Byreal program id from Vybe supported DEX/AMM documentation.
pub const BYREAL_PROGRAM_ID: &str = "REALQqNEomY6cQGZJUGwywTBD2UmDT32rZcNnfxQ5N2";
/// BisonFi program id from Vybe supported DEX/AMM documentation.
pub const BISONFI_PROGRAM_ID: &str = "BiSoNHVpsVZW2F7rx2eQ59yQwKxzU5NvBcmKshCSUypi";
/// FusionAMM program id from Vybe supported DEX/AMM documentation.
pub const FUSIONAMM_PROGRAM_ID: &str = "fUSioN9YKKSa3CUC2YUc4tPkHJ5Y6XW1yz8y6F7qWz9";
/// Woofi program id from Vybe supported DEX/AMM documentation.
pub const WOOFI_PROGRAM_ID: &str = "WooFif76YGRNjk1pA8wCsN67aQsD9f9iLsz4NcJ1AVb";
/// Aquifer program id from Vybe supported DEX/AMM documentation.
pub const AQUIFER_PROGRAM_ID: &str = "AQU1FRd7papthgdrwPTTq5JacJh8YtwEXaBfKU3bTz45";
/// Humidifi program id from Vybe supported DEX/AMM documentation.
pub const HUMIDIFI_PROGRAM_ID: &str = "9H6tua7jkLhdm3w8BvgpTn5LZNU7g4ZynDmCiNN3q6Rp";
/// SolFi v2 program id from Vybe supported DEX/AMM documentation.
pub const SOLFI_V2_PROGRAM_ID: &str = "SV2EYYJyRz2YhfXwXnhNAevDEui5Q6yrfyo13WtupPF";
/// Gavel program id extracted from upstream Git decoder source.
pub const GAVEL_PROGRAM_ID: &str = "srAMMzfVHVAtgSJc8iH6CfKzuWuUTzLHVCE81QU1rgi";
/// Meteora DAMM v1 program id. ("Eo7WjKq67rjJQSZxS6z3YkapzY3eMj6Xy8X5EQVn5UaB"). /// Meteora DAMM v1 program id. ("Eo7WjKq67rjJQSZxS6z3YkapzY3eMj6Xy8X5EQVn5UaB").
pub const METEORA_DAMM_V1_PROGRAM_ID: &str = "Eo7WjKq67rjJQSZxS6z3YkapzY3eMj6Xy8X5EQVn5UaB"; pub const METEORA_DAMM_V1_PROGRAM_ID: &str = "Eo7WjKq67rjJQSZxS6z3YkapzY3eMj6Xy8X5EQVn5UaB";
@@ -178,32 +232,17 @@ pub const METEORA_DBC_PROGRAM_ID: &str = "dbcij3LWUppWqq96dh6gJWwBifmcGfLSB5D4Du
/// DLMM = Dynamic Liquidity Market Maker. /// DLMM = Dynamic Liquidity Market Maker.
pub const METEORA_DLMM_PROGRAM_ID: &str = "LBUZKhRxPF3XUpBCjp4YzTKgLccjZhTSDM9YuVaPwxo"; pub const METEORA_DLMM_PROGRAM_ID: &str = "LBUZKhRxPF3XUpBCjp4YzTKgLccjZhTSDM9YuVaPwxo";
/// MetaDAO META active token mint identifier from MetaDAO documentation.
pub const METADAO_META_MINT_ID: &str = "METAwkXcqyXKy1AtsSgJ8JiUHwGCafnZL38n3vYmeta";
/// MetaDAO METAC legacy token mint identifier from MetaDAO documentation.
pub const METADAO_METAC_LEGACY_MINT_ID: &str = "METADDFL6wWMWEoKTFJwcThTbUmtarRJZjRpzUvkxhr";
/// MetaDAO-linked P2P token mint candidate observed on Solscan.
///
/// This is a token mint, not a DEX program id. It is exposed for discovery only.
pub const METADAO_P2P_MINT_ID: &str = "P2PXup1ZvMpCDkJn3PQxtBYgxeCSfH39SFeurGSmeta";
/// MetaDAO Launchpad v0.7.0 program id from MetaDAO documentation and Solscan. /// MetaDAO Launchpad v0.7.0 program id from MetaDAO documentation and Solscan.
pub const METADAO_LAUNCHPAD_V0_7_0_PROGRAM_ID: &str = pub const METADAO_LAUNCHPAD_V0_7_0_PROGRAM_ID: &str = "moontUzsdepotRGe5xsfip7vLPTJnVuafqdUWexVnPM";
"moontUzsdepotRGe5xsfip7vLPTJnVuafqdUWexVnPM";
/// MetaDAO Bid Wall v0.7.0 program id from MetaDAO documentation. /// MetaDAO Bid Wall v0.7.0 program id from MetaDAO documentation.
pub const METADAO_BID_WALL_V0_7_0_PROGRAM_ID: &str = pub const METADAO_BID_WALL_V0_7_0_PROGRAM_ID: &str = "WALL8ucBuUyL46QYxwYJjidaFYhdvxUFrgvBxPshERx";
"WALL8ucBuUyL46QYxwYJjidaFYhdvxUFrgvBxPshERx";
/// MetaDAO Futarchy v0.6.0 program id from MetaDAO documentation. /// MetaDAO Futarchy v0.6.0 program id from MetaDAO documentation.
pub const METADAO_FUTARCHY_V0_6_0_PROGRAM_ID: &str = pub const METADAO_FUTARCHY_V0_6_0_PROGRAM_ID: &str = "FUTARELBfJfQ8RDGhg1wdhddq1odMAJUePHFuBYfUxKq";
"FUTARELBfJfQ8RDGhg1wdhddq1odMAJUePHFuBYfUxKq";
/// MetaDAO AMM v0.5.0 program id from MetaDAO documentation. /// MetaDAO AMM v0.5.0 program id from MetaDAO documentation.
pub const METADAO_AMM_V0_5_0_PROGRAM_ID: &str = pub const METADAO_AMM_V0_5_0_PROGRAM_ID: &str = "AMMJdEiCCa8mdugg6JPF7gFirmmxisTfDJoSNSUi5zDJ";
"AMMJdEiCCa8mdugg6JPF7gFirmmxisTfDJoSNSUi5zDJ";
/// Printr program id candidate observed on Solscan. /// Printr program id candidate observed on Solscan.
/// ///
@@ -224,6 +263,9 @@ pub const PUMP_FUN_PROGRAM_ID: &str = "6EF8rrecthR5Dkzon8Nwu78hRvfCKubJ14M5uBEwF
/// PumpSwap / PumpAMM program id. ("pAMMBay6oceH9fJKBRHGP5D4bD4sWpmSwMn52FMfXEA"). /// PumpSwap / PumpAMM program id. ("pAMMBay6oceH9fJKBRHGP5D4bD4sWpmSwMn52FMfXEA").
pub const PUMP_SWAP_PROGRAM_ID: &str = "pAMMBay6oceH9fJKBRHGP5D4bD4sWpmSwMn52FMfXEA"; pub const PUMP_SWAP_PROGRAM_ID: &str = "pAMMBay6oceH9fJKBRHGP5D4bD4sWpmSwMn52FMfXEA";
/// Pump Fees program id extracted from upstream Git decoder source.
pub const PUMP_FEES_PROGRAM_ID: &str = "pfeeUxB6jkeY1Hxd7CsFCAjcbHA9rWtchMGdZ6VojVZ";
/// Raydium AmmV4 program id. ("675kPX9MHTjS2zt1qfr1NYHuzeLXfQM9H24wFSUt1Mp8"). /// Raydium AmmV4 program id. ("675kPX9MHTjS2zt1qfr1NYHuzeLXfQM9H24wFSUt1Mp8").
pub const RAYDIUM_AMM_V4_PROGRAM_ID: &str = "675kPX9MHTjS2zt1qfr1NYHuzeLXfQM9H24wFSUt1Mp8"; pub const RAYDIUM_AMM_V4_PROGRAM_ID: &str = "675kPX9MHTjS2zt1qfr1NYHuzeLXfQM9H24wFSUt1Mp8";
@@ -233,7 +275,7 @@ pub const RAYDIUM_CLMM_PROGRAM_ID: &str = "CAMMCzo5YL8w4VFF8KVHrK22GGUsp5VTaW7gr
/// Raydium CPMM mainnet program id. ("CPMMoo8L3F4NbTegBCKVNunggL7H1ZpdTHKxQB5qKP1C"). /// Raydium CPMM mainnet program id. ("CPMMoo8L3F4NbTegBCKVNunggL7H1ZpdTHKxQB5qKP1C").
pub const RAYDIUM_CPMM_PROGRAM_ID: &str = "CPMMoo8L3F4NbTegBCKVNunggL7H1ZpdTHKxQB5qKP1C"; pub const RAYDIUM_CPMM_PROGRAM_ID: &str = "CPMMoo8L3F4NbTegBCKVNunggL7H1ZpdTHKxQB5qKP1C";
/// Raydium LaunchLab program id. ("LanMV9sAd7wArD4vJFi2qDdfnVhFxYSUg6eADduJ3uj"). /// Raydium LaunchLab / Launchpad program id. ("LanMV9sAd7wArD4vJFi2qDdfnVhFxYSUg6eADduJ3uj").
pub const RAYDIUM_LAUNCHLAB_PROGRAM_ID: &str = "LanMV9sAd7wArD4vJFi2qDdfnVhFxYSUg6eADduJ3uj"; pub const RAYDIUM_LAUNCHLAB_PROGRAM_ID: &str = "LanMV9sAd7wArD4vJFi2qDdfnVhFxYSUg6eADduJ3uj";
/// Raydium AMM routing program id. ("routeUGWgWzqBWFcrCfv8tritsqukccJPu3q5GPP3xS"). /// Raydium AMM routing program id. ("routeUGWgWzqBWFcrCfv8tritsqukccJPu3q5GPP3xS").
@@ -242,6 +284,132 @@ pub const RAYDIUM_AMM_ROUTING_PROGRAM_ID: &str = "routeUGWgWzqBWFcrCfv8tritsqukc
/// Raydium Stable Swap AMM program id, deprecated. ("5quBtoiQqxF9Jv6KYKctB59NT3gtJD2Y65kdnB1Uev3h"). /// Raydium Stable Swap AMM program id, deprecated. ("5quBtoiQqxF9Jv6KYKctB59NT3gtJD2Y65kdnB1Uev3h").
pub const RAYDIUM_STABLE_SWAP_AMM_PROGRAM_ID: &str = "5quBtoiQqxF9Jv6KYKctB59NT3gtJD2Y65kdnB1Uev3h"; pub const RAYDIUM_STABLE_SWAP_AMM_PROGRAM_ID: &str = "5quBtoiQqxF9Jv6KYKctB59NT3gtJD2Y65kdnB1Uev3h";
/// Bonkswap program id extracted from upstream Git decoder source.
pub const BONKSWAP_PROGRAM_ID: &str = "BSwp6bEBihVLdqJRKGgzjcGLHkcTuzmSo1TQkHepzH8p";
/// Boop program id extracted from upstream Git decoder source.
pub const BOOP_PROGRAM_ID: &str = "boop8hVGQGqehUK2iVEMEnMrL5RbjywRzHKBmBE7ry4";
/// DFlow Aggregator v4 program id extracted from upstream Git decoder source.
pub const DFLOW_AGGREGATOR_V4_PROGRAM_ID: &str = "DF1ow4tspfHX9JwWJsAb9epbkA8hmpSEAtxXy1V27QBH";
/// Drift v2 program id extracted from upstream Git decoder source.
pub const DRIFT_V2_PROGRAM_ID: &str = "dRiftyHA39MWEi3m9aunc5MzRF1JYuBsbn6VPcn33UH";
/// Heaven program id extracted from upstream Git decoder source.
pub const HEAVEN_PROGRAM_ID: &str = "HEAVENoP2qxoeuF8Dj2oT1GHEnu49U5mJYkdeC8BAX2o";
/// Jupiter Swap program id extracted from upstream Git decoder source.
pub const JUPITER_SWAP_PROGRAM_ID: &str = "JUP6LkbZbjS1jKKwapdHNy74zcZ3tLUZoi5QNyVTaV4";
/// Jupiter DCA program id extracted from upstream Git decoder source.
pub const JUPITER_DCA_PROGRAM_ID: &str = "DCA265Vj8a9CEuX1eb1LWRnDT7uK6q1xMipnNyatn23M";
/// Jupiter Lend program id extracted from upstream Git decoder source.
pub const JUPITER_LEND_PROGRAM_ID: &str = "jupeiUmn818Jg1ekPURTpr4mFo29p46vygyykFJ3wZC";
/// Jupiter Limit Order program id extracted from upstream Git decoder source.
pub const JUPITER_LIMIT_ORDER_PROGRAM_ID: &str = "jupoNjAxXgZ4rjzxzPMP4oxduvQsQtZzyknqvzYNrNu";
/// Jupiter Limit Order 2 program id extracted from upstream Git decoder source.
pub const JUPITER_LIMIT_ORDER_2_PROGRAM_ID: &str = "j1o2qRpjcyUwEvwtcfhEQefh773ZgjxcVRry7LDqg5X";
/// Jupiter Perpetuals program id extracted from upstream Git decoder source.
pub const JUPITER_PERPETUALS_PROGRAM_ID: &str = "PERPHjGBqRHArX4DySjwM6UJHiR3sWAatqfdBS2qQJu";
/// Kamino Farms program id extracted from upstream Git decoder source.
pub const KAMINO_FARMS_PROGRAM_ID: &str = "FarmsPZpWu9i7Kky8tPN37rs2TpmMrAZrC7S7vJa91Hr";
/// Kamino Lending program id extracted from upstream Git decoder source.
pub const KAMINO_LENDING_PROGRAM_ID: &str = "KLend2g3cP87fffoy8q1mQqGKjrxjC8boSyAYavgmjD";
/// Kamino Limit Order program id extracted from upstream Git decoder source.
pub const KAMINO_LIMIT_ORDER_PROGRAM_ID: &str = "LiMoM9rMhrdYrfzUCxQppvxCSG1FcrUK9G8uLq4A1GF";
/// Kamino Vault program id extracted from upstream Git decoder source.
pub const KAMINO_VAULT_PROGRAM_ID: &str = "kvauTFR8qm1dhniz6pYuBZkuene3Hfrs1VQhVRgCNrr";
/// Lifinity AMM v2 program id extracted from upstream Git decoder source.
pub const LIFINITY_AMM_V2_PROGRAM_ID: &str = "2wT8Yq49kHgDzXuPxZSaeLaH1qbmGXtEyPy64bL7aD3c";
/// Marginfi v2 program id extracted from upstream Git decoder source.
pub const MARGINFI_V2_PROGRAM_ID: &str = "MFv2hWf31Z9kbCa1snEPYctwafyhdvnV7FZnsebVacA";
/// Marinade Finance program id extracted from upstream Git decoder source.
pub const MARINADE_FINANCE_PROGRAM_ID: &str = "MarBmsSgKXdrN1egZf5sqe1TMai9K1rChYNDJgjq7aD";
/// Meteora Vault program id extracted from upstream Git decoder source.
pub const METEORA_VAULT_PROGRAM_ID: &str = "24Uqj9JCLxUeoC3hGfh5W3s9FM9uCHDS2SG3LYwBpyTi";
/// Moonshot program id extracted from upstream Git decoder source.
pub const MOONSHOT_PROGRAM_ID: &str = "MoonCVVNZFSYkqNXP6bxHLPL6QQJiMagDL3qcqUQTrG";
/// OKX DEX program id extracted from upstream Git decoder source.
pub const OKX_DEX_PROGRAM_ID: &str = "6m2CDdhRgxpH4WjvdzxAYbGxwdGUz5MziiL5jek2kBma";
/// Onchain Labs DEX v2 program id extracted from upstream Git decoder source.
pub const ONCHAIN_LABS_DEX_V2_PROGRAM_ID: &str = "proVF4pMXVaYqmy4NjniPh4pqKNfMmsihgd4wdkCX3u";
/// OpenBook v2 program id extracted from upstream Git decoder source.
pub const OPENBOOK_V2_PROGRAM_ID: &str = "opnb2LAfJYbRMAHHvqjCwQxanZn7ReEHp1k81EohpZb";
/// Pancake Swap program id extracted from upstream Git decoder source.
pub const PANCAKE_SWAP_PROGRAM_ID: &str = "HpNfyc2Saw7RKkQd8nEL4khUcuPhQ7WwY1B2qjx8jxFq";
/// Phoenix v1 program id extracted from upstream Git decoder source.
pub const PHOENIX_V1_PROGRAM_ID: &str = "PhoeNiXZ8ByJGLkxNfZRnkUfjvmuYqLR89jjFHGqdXY";
/// Raydium Liquidity Locking program id extracted from upstream Git decoder source.
pub const RAYDIUM_LIQUIDITY_LOCKING_PROGRAM_ID: &str =
"LockrWmn6K5twhz3y9w1dQERbmgSaRkfnTeTKbpofwE";
/// Sharky program id extracted from upstream Git decoder source.
pub const SHARKY_PROGRAM_ID: &str = "SHARKobtfF1bHhxD2eqftjHBdVSCbKo9JtgK71FhELP";
/// Solayer Restaking program id extracted from upstream Git decoder source.
pub const SOLAYER_RESTAKING_PROGRAM_ID: &str = "sSo1iU21jBrU9VaJ8PJib1MtorefUV4fzC9GURa2KNn";
/// Stabble stable-swap program id extracted from upstream Git decoder source.
pub const STABBLE_STABLE_SWAP_PROGRAM_ID: &str = "swapNyd8XiQwJ6ianp9snpu4brUqFxadzvHebnAXjJZ";
/// Stabble weighted-swap program id extracted from upstream Git decoder source.
pub const STABBLE_WEIGHTED_SWAP_PROGRAM_ID: &str = "swapFpHZwjELNnjvThjajtiVmkz3yPQEHjLtka2fwHW";
/// Swig program id extracted from upstream Git decoder source.
pub const SWIG_PROGRAM_ID: &str = "swigypWHEksbC64pWKwah1WTeh9JXwx8H1rJHLdbQMB";
/// Vertigo program id extracted from upstream Git decoder source.
pub const VERTIGO_PROGRAM_ID: &str = "vrTGoBuy5rYSxAfV3jaRJWHH6nN9WK4NRExGxsk1bCJ";
/// Virtuals program id extracted from upstream Git decoder source.
pub const VIRTUALS_PROGRAM_ID: &str = "5U3EU2ubXtK84QcRjWVmYt9RaDyA8gKxdUrPFXmZyaki";
/// Wavebreak program id extracted from upstream Git decoder source.
pub const WAVEBREAK_PROGRAM_ID: &str = "waveQX2yP3H1pVU8djGvEHmYg8uamQ84AuyGtpsrXTF";
/// Zeta program id extracted from upstream Git decoder source.
pub const ZETA_PROGRAM_ID: &str = "ZETAxsqBRek56DhiGXrn75yj2NHU3aYUnxvHXpkf3aD";
/// Circle Message Transmitter v2 program id extracted from upstream Git decoder source.
pub const CIRCLE_MESSAGE_TRANSMITTER_V2_PROGRAM_ID: &str =
"CCTPV2Sm4AdWt5296sk4P66VBZ7bEhcARwFaaS9YPbeC";
/// Circle Token Messenger v2 program id extracted from upstream Git decoder source.
pub const CIRCLE_TOKEN_MESSENGER_V2_PROGRAM_ID: &str =
"CCTPV2vPZJS2u2BBsUoscuikbYjnpFmbFsvVuJdgUMQe";
/// MPL Token Metadata program id extracted from upstream Git decoder source.
pub const MPL_TOKEN_METADATA_PROGRAM_ID: &str = "metaqbxxUerdq28cj1RbAWkYQm3ybzjb6a8bt518x1s";
/// MPL Core program id extracted from upstream Git decoder source.
pub const MPL_CORE_PROGRAM_ID: &str = "CoREENxT6tW1HoK8ypY1SxRMZTcVPm7R94rH4PZNhX7d";
/// Bubblegum program id extracted from upstream Git decoder source.
pub const BUBBLEGUM_PROGRAM_ID: &str = "BGUMAp9Gq7iTEuizy4pqaxsTyUCBK68MDfK752saRPUY";
/// Solana Name Service program id extracted from upstream Git decoder source.
pub const NAME_SERVICE_PROGRAM_ID: &str = "namesLPneVptA9Z5rqUDD9tMTWEJwofgaYwp8cawRkX";
/// Known Solana arbitrage/sandwich bot program id observed in local corpus. /// Known Solana arbitrage/sandwich bot program id observed in local corpus.
/// ///
/// This is not treated as a DEX program. It is used only to tag protocol /// This is not treated as a DEX program. It is used only to tag protocol

View File

@@ -8,7 +8,9 @@ mod meteora_damm_v1;
mod meteora_damm_v2; mod meteora_damm_v2;
mod meteora_dbc; mod meteora_dbc;
mod meteora_dlmm; mod meteora_dlmm;
mod openbook_v2;
mod orca_whirlpools; mod orca_whirlpools;
mod phoenix_v1;
mod pump_fun; mod pump_fun;
mod pump_swap; mod pump_swap;
mod raydium_amm_v4; mod raydium_amm_v4;
@@ -47,10 +49,16 @@ pub use meteora_dlmm::MeteoraDlmmLiquidityDecoded;
pub use meteora_dlmm::MeteoraDlmmPoolLifecycleDecoded; pub use meteora_dlmm::MeteoraDlmmPoolLifecycleDecoded;
pub use meteora_dlmm::MeteoraDlmmRewardDecoded; pub use meteora_dlmm::MeteoraDlmmRewardDecoded;
pub use meteora_dlmm::MeteoraDlmmSwapDecoded; pub use meteora_dlmm::MeteoraDlmmSwapDecoded;
pub use openbook_v2::OpenBookV2AuditDecoded;
pub use openbook_v2::OpenBookV2DecodedEvent;
pub use openbook_v2::OpenBookV2Decoder;
pub use orca_whirlpools::OrcaWhirlpoolsCreatePoolDecoded; pub use orca_whirlpools::OrcaWhirlpoolsCreatePoolDecoded;
pub use orca_whirlpools::OrcaWhirlpoolsDecodedEvent; pub use orca_whirlpools::OrcaWhirlpoolsDecodedEvent;
pub use orca_whirlpools::OrcaWhirlpoolsDecoder; pub use orca_whirlpools::OrcaWhirlpoolsDecoder;
pub use orca_whirlpools::OrcaWhirlpoolsSwapDecoded; pub use orca_whirlpools::OrcaWhirlpoolsSwapDecoded;
pub use phoenix_v1::PhoenixV1AuditDecoded;
pub use phoenix_v1::PhoenixV1DecodedEvent;
pub use phoenix_v1::PhoenixV1Decoder;
pub use pump_fun::PumpFunCreateV2TokenDecoded; pub use pump_fun::PumpFunCreateV2TokenDecoded;
pub use pump_fun::PumpFunDecodedEvent; pub use pump_fun::PumpFunDecodedEvent;
pub use pump_fun::PumpFunDecoder; pub use pump_fun::PumpFunDecoder;

File diff suppressed because it is too large Load Diff

1325
kb_lib/src/dex/phoenix_v1.rs Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -124,7 +124,6 @@ mod tests {
fn planned_launch_surfaces_are_present_but_disabled() { fn planned_launch_surfaces_are_present_but_disabled() {
let codes = [ let codes = [
"raydium_launchlab", "raydium_launchlab",
"raydium_launchpad",
"letsbonk", "letsbonk",
"boop_fun", "boop_fun",
"moonshot", "moonshot",

View File

@@ -20,6 +20,8 @@ pub struct DexDecodeService {
meteora_damm_v2_decoder: crate::MeteoraDammV2Decoder, meteora_damm_v2_decoder: crate::MeteoraDammV2Decoder,
fluxbeam_decoder: crate::FluxbeamDecoder, fluxbeam_decoder: crate::FluxbeamDecoder,
dexlab_decoder: crate::DexlabDecoder, dexlab_decoder: crate::DexlabDecoder,
openbook_v2_decoder: crate::OpenBookV2Decoder,
phoenix_v1_decoder: crate::PhoenixV1Decoder,
} }
impl DexDecodeService { impl DexDecodeService {
@@ -40,6 +42,8 @@ impl DexDecodeService {
meteora_damm_v2_decoder: crate::MeteoraDammV2Decoder::new(), meteora_damm_v2_decoder: crate::MeteoraDammV2Decoder::new(),
fluxbeam_decoder: crate::FluxbeamDecoder::new(), fluxbeam_decoder: crate::FluxbeamDecoder::new(),
dexlab_decoder: crate::DexlabDecoder::new(), dexlab_decoder: crate::DexlabDecoder::new(),
openbook_v2_decoder: crate::OpenBookV2Decoder::new(),
phoenix_v1_decoder: crate::PhoenixV1Decoder::new(),
}; };
} }
@@ -163,6 +167,36 @@ impl DexDecodeService {
if let Err(error) = append_result { if let Err(error) = append_result {
return Err(error); return Err(error);
} }
let append_result = append_persisted_events_result(
&mut persisted,
self.decode_and_persist_openbook_v2_audit_events(&transaction, &instructions)
.await,
);
if let Err(error) = append_result {
return Err(error);
}
let append_result = append_persisted_events_result(
&mut persisted,
self.decode_and_persist_phoenix_v1_audit_events(&transaction, &instructions)
.await,
);
if let Err(error) = append_result {
return Err(error);
}
let decoded_instruction_ids = decoded_instruction_ids_from_persisted_events(&persisted);
let append_result = append_persisted_events_result(
&mut persisted,
self.decode_and_persist_upstream_registry_matches(
&transaction,
&instructions,
&decoded_instruction_ids,
)
.await,
);
if let Err(error) = append_result {
return Err(error);
}
return Ok(persisted); return Ok(persisted);
} }
@@ -218,9 +252,46 @@ impl DexDecodeService {
if let Err(error) = cleanup_result { if let Err(error) = cleanup_result {
return Err(error); return Err(error);
} }
let cleanup_result = self
.delete_replaced_upstream_registry_match(
transaction_id,
instruction_id,
protocol_name,
event_kind,
)
.await;
if let Err(error) = cleanup_result {
return Err(error);
}
return Ok(materialized); return Ok(materialized);
} }
async fn delete_replaced_upstream_registry_match(
&self,
transaction_id: i64,
instruction_id: i64,
protocol_name: &str,
event_kind: &str,
) -> Result<(), crate::Error> {
if protocol_name == crate::UPSTREAM_REGISTRY_PROTOCOL_NAME {
return Ok(());
}
if event_kind == crate::UPSTREAM_REGISTRY_INSTRUCTION_MATCH_EVENT_KIND {
return Ok(());
}
let delete_result = crate::query_dex_decoded_events_delete_by_key(
self.database.as_ref(),
transaction_id,
Some(instruction_id),
crate::UPSTREAM_REGISTRY_INSTRUCTION_MATCH_EVENT_KIND,
)
.await;
match delete_result {
Ok(_) => return Ok(()),
Err(error) => return Err(error),
}
}
async fn delete_replaced_instruction_audit( async fn delete_replaced_instruction_audit(
&self, &self,
transaction_id: i64, transaction_id: i64,
@@ -248,6 +319,92 @@ impl DexDecodeService {
} }
} }
async fn decode_and_persist_upstream_registry_matches(
&self,
transaction: &crate::ChainTransactionDto,
instructions: &[crate::ChainInstructionDto],
already_decoded_instruction_ids: &std::collections::HashSet<i64>,
) -> Result<std::vec::Vec<crate::DexDecodedEventDto>, crate::Error> {
let transaction_id = match transaction.id {
Some(transaction_id) => transaction_id,
None => {
return Err(crate::Error::InvalidState(format!(
"transaction '{}' has no internal id",
transaction.signature
)));
},
};
let decoded_events_result = crate::query_dex_decoded_events_list_by_transaction_id(
self.database.as_ref(),
transaction_id,
)
.await;
let decoded_events = match decoded_events_result {
Ok(decoded_events) => decoded_events,
Err(error) => return Err(error),
};
let mut decoded_instruction_ids = already_decoded_instruction_ids.clone();
for decoded_event in &decoded_events {
let instruction_id = match decoded_event.instruction_id {
Some(instruction_id) => instruction_id,
None => continue,
};
decoded_instruction_ids.insert(instruction_id);
}
let mut persisted = std::vec::Vec::new();
for instruction in instructions {
let instruction_id = match instruction.id {
Some(instruction_id) => instruction_id,
None => continue,
};
if decoded_instruction_ids.contains(&instruction_id) {
continue;
}
let program_id = match instruction.program_id.as_ref() {
Some(program_id) => program_id,
None => continue,
};
let data_base58 = parse_instruction_data_base58(instruction.data_json.as_deref());
let registry_match =
crate::upstream_registry_match::upstream_registry_match_instruction_data(
program_id.as_str(),
data_base58.as_deref(),
);
let registry_match = match registry_match {
Some(registry_match) => registry_match,
None => continue,
};
let payload = build_upstream_registry_instruction_match_payload(
transaction,
instruction,
&registry_match,
data_base58.as_deref(),
);
let persist_result = self
.materialize_named_dex_event(
transaction,
transaction_id,
instruction_id,
crate::UPSTREAM_REGISTRY_PROTOCOL_NAME,
program_id.clone(),
crate::UPSTREAM_REGISTRY_INSTRUCTION_MATCH_EVENT_KIND,
None,
None,
None,
None,
None,
payload,
)
.await;
let persisted_event = match persist_result {
Ok(persisted_event) => persisted_event,
Err(error) => return Err(error),
};
persisted.push(persisted_event);
}
return Ok(persisted);
}
async fn persist_dexlab_event( async fn persist_dexlab_event(
&self, &self,
transaction: &crate::ChainTransactionDto, transaction: &crate::ChainTransactionDto,
@@ -1480,6 +1637,104 @@ impl DexDecodeService {
return Ok(persisted); return Ok(persisted);
} }
async fn persist_openbook_v2_event(
&self,
transaction: &crate::ChainTransactionDto,
decoded_event: &crate::OpenBookV2DecodedEvent,
) -> Result<crate::DexDecodedEventDto, crate::Error> {
match decoded_event {
crate::OpenBookV2DecodedEvent::Audit(event) => {
return self
.materialize_named_dex_event(
transaction,
event.transaction_id,
event.instruction_id,
"openbook_v2",
event.program_id.clone(),
event.event_kind.as_str(),
None,
event.market_account.clone(),
event.token_a_mint.clone(),
event.token_b_mint.clone(),
None,
event.payload_json.clone(),
)
.await;
},
}
}
async fn decode_and_persist_openbook_v2_audit_events(
&self,
transaction: &crate::ChainTransactionDto,
instructions: &[crate::ChainInstructionDto],
) -> Result<std::vec::Vec<crate::DexDecodedEventDto>, crate::Error> {
let decoded_result = self.openbook_v2_decoder.decode_transaction(transaction, instructions);
let decoded_events = match decoded_result {
Ok(decoded_events) => decoded_events,
Err(error) => return Err(error),
};
let mut persisted = std::vec::Vec::new();
for decoded_event in &decoded_events {
let persist_result = self.persist_openbook_v2_event(transaction, decoded_event).await;
let persisted_event = match persist_result {
Ok(persisted_event) => persisted_event,
Err(error) => return Err(error),
};
persisted.push(persisted_event);
}
return Ok(persisted);
}
async fn persist_phoenix_v1_event(
&self,
transaction: &crate::ChainTransactionDto,
decoded_event: &crate::PhoenixV1DecodedEvent,
) -> Result<crate::DexDecodedEventDto, crate::Error> {
match decoded_event {
crate::PhoenixV1DecodedEvent::Audit(event) => {
return self
.materialize_named_dex_event(
transaction,
event.transaction_id,
event.instruction_id,
"phoenix_v1",
event.program_id.clone(),
event.event_kind.as_str(),
None,
event.market_account.clone(),
event.token_a_mint.clone(),
event.token_b_mint.clone(),
None,
event.payload_json.clone(),
)
.await;
},
}
}
async fn decode_and_persist_phoenix_v1_audit_events(
&self,
transaction: &crate::ChainTransactionDto,
instructions: &[crate::ChainInstructionDto],
) -> Result<std::vec::Vec<crate::DexDecodedEventDto>, crate::Error> {
let decoded_result = self.phoenix_v1_decoder.decode_transaction(transaction, instructions);
let decoded_events = match decoded_result {
Ok(decoded_events) => decoded_events,
Err(error) => return Err(error),
};
let mut persisted = std::vec::Vec::new();
for decoded_event in &decoded_events {
let persist_result = self.persist_phoenix_v1_event(transaction, decoded_event).await;
let persisted_event = match persist_result {
Ok(persisted_event) => persisted_event,
Err(error) => return Err(error),
};
persisted.push(persisted_event);
}
return Ok(persisted);
}
async fn decode_and_persist_fluxbeam_events( async fn decode_and_persist_fluxbeam_events(
&self, &self,
transaction: &crate::ChainTransactionDto, transaction: &crate::ChainTransactionDto,
@@ -1964,7 +2219,7 @@ fn build_meteora_instruction_audit_payload(
return serde_json::json!({ return serde_json::json!({
"decoder": protocol_name, "decoder": protocol_name,
"eventKind": event_kind, "eventKind": event_kind,
"signature": transaction.signature, "signature": transaction.signature.clone(),
"instructionId": instruction.id, "instructionId": instruction.id,
"instructionIndex": instruction.instruction_index, "instructionIndex": instruction.instruction_index,
"innerInstructionIndex": instruction.inner_instruction_index, "innerInstructionIndex": instruction.inner_instruction_index,
@@ -2023,7 +2278,7 @@ fn build_raydium_instruction_audit_payload(
return serde_json::json!({ return serde_json::json!({
"decoder": protocol_name, "decoder": protocol_name,
"eventKind": event_kind, "eventKind": event_kind,
"signature": transaction.signature, "signature": transaction.signature.clone(),
"instructionId": instruction.id, "instructionId": instruction.id,
"instructionIndex": instruction.instruction_index, "instructionIndex": instruction.instruction_index,
"innerInstructionIndex": instruction.inner_instruction_index, "innerInstructionIndex": instruction.inner_instruction_index,
@@ -2073,6 +2328,58 @@ fn candidate_raydium_audit_pool_account(
return accounts.get(spec.candidate_pool_account_index).cloned(); return accounts.get(spec.candidate_pool_account_index).cloned();
} }
fn build_upstream_registry_instruction_match_payload(
transaction: &crate::ChainTransactionDto,
instruction: &crate::ChainInstructionDto,
registry_match: &crate::UpstreamRegistryEntryDto,
data_base58: std::option::Option<&str>,
) -> serde_json::Value {
let data_bytes = instruction_data_bytes_from_base58(data_base58);
let data_byte_len = match data_bytes.as_ref() {
Some(data_bytes) => {
let len_result = u64::try_from(data_bytes.len());
match len_result {
Ok(len) => serde_json::Value::Number(serde_json::Number::from(len)),
Err(_) => serde_json::Value::Null,
}
},
None => serde_json::Value::Null,
};
return serde_json::json!({
"decoder": crate::UPSTREAM_REGISTRY_PROTOCOL_NAME,
"matchKind": "instruction_discriminator",
"signature": transaction.signature.clone(),
"slot": transaction.slot,
"instructionId": instruction.id,
"instructionIndex": instruction.instruction_index,
"innerInstructionIndex": instruction.inner_instruction_index,
"parentInstructionId": instruction.parent_instruction_id,
"stackHeight": instruction.stack_height,
"programId": instruction.program_id.clone(),
"programName": instruction.program_name.clone(),
"accounts": parse_instruction_accounts_value(instruction.accounts_json.as_str()),
"accountCount": parse_instruction_accounts_vec(instruction.accounts_json.as_str()).len(),
"dataBase58": data_base58,
"dataByteLen": data_byte_len,
"upstreamSourceRepo": registry_match.source_repo.clone(),
"upstreamSourcePath": registry_match.source_path.clone(),
"upstreamDecoderCode": registry_match.decoder_code.clone(),
"upstreamProgramFamily": registry_match.program_family.clone(),
"upstreamSurfaceKind": registry_match.surface_kind.clone(),
"upstreamEntryKind": registry_match.entry_kind.clone(),
"upstreamEntryName": registry_match.entry_name.clone(),
"upstreamDiscriminatorHex": registry_match.discriminator_hex.clone(),
"upstreamDiscriminatorLen": registry_match.discriminator_len,
"upstreamProofStatus": registry_match.proof_status.clone(),
"upstreamNotes": registry_match.notes.clone(),
"tradeCandidate": false,
"candleCandidate": false,
"nonTradeUseful": false,
"skipTradeReason": "upstream_git_unverified_registry_match",
"skipCandleReason": "upstream_git_unverified_registry_match"
});
}
fn parse_instruction_accounts_vec(accounts_json: &str) -> std::vec::Vec<std::string::String> { fn parse_instruction_accounts_vec(accounts_json: &str) -> std::vec::Vec<std::string::String> {
let accounts_result = serde_json::from_str::<std::vec::Vec<std::string::String>>(accounts_json); let accounts_result = serde_json::from_str::<std::vec::Vec<std::string::String>>(accounts_json);
match accounts_result { match accounts_result {
@@ -2146,6 +2453,20 @@ fn append_persisted_events(
} }
} }
fn decoded_instruction_ids_from_persisted_events(
persisted: &[crate::DexDecodedEventDto],
) -> std::collections::HashSet<i64> {
let mut decoded_instruction_ids = std::collections::HashSet::<i64>::new();
for decoded_event in persisted {
let instruction_id = match decoded_event.instruction_id {
Some(instruction_id) => instruction_id,
None => continue,
};
decoded_instruction_ids.insert(instruction_id);
}
return decoded_instruction_ids;
}
fn append_persisted_events_result( fn append_persisted_events_result(
target: &mut std::vec::Vec<crate::DexDecodedEventDto>, target: &mut std::vec::Vec<crate::DexDecodedEventDto>,
source_result: Result<std::vec::Vec<crate::DexDecodedEventDto>, crate::Error>, source_result: Result<std::vec::Vec<crate::DexDecodedEventDto>, crate::Error>,
@@ -3223,4 +3544,60 @@ mod tests {
); );
assert_eq!(super::instruction_audit_event_kind_by_protocol("unknown"), None); assert_eq!(super::instruction_audit_event_kind_by_protocol("unknown"), None);
} }
#[test]
fn upstream_registry_match_payload_is_never_trade_or_candle_candidate() {
let transaction = crate::ChainTransactionDto::new(
"upstream-registry-test-signature".to_string(),
Some(123),
Some(123456),
Some("test".to_string()),
None,
None,
None,
"{}".to_string(),
);
let instruction = crate::ChainInstructionDto::new(
1,
None,
0,
None,
Some(crate::METEORA_DAMM_V2_PROGRAM_ID.to_string()),
None,
None,
"[]".to_string(),
Some("data".to_string()),
None,
None,
);
let registry_match = crate::UpstreamRegistryEntryDto {
source_repo: Some("sevenlabs-hq/carbon".to_string()),
source_path: Some("decoders/example.rs".to_string()),
decoder_code: "meteora-damm-v2".to_string(),
program_id: Some(crate::METEORA_DAMM_V2_PROGRAM_ID.to_string()),
program_family: "meteora".to_string(),
surface_kind: "amm".to_string(),
entry_kind: crate::ENTRY_KIND_INSTRUCTION.to_string(),
entry_name: "swap".to_string(),
discriminator_hex: Some("f8c69e91e17587c8".to_string()),
discriminator_len: Some(8),
proof_status: crate::PROOF_STATUS_UPSTREAM_GIT_UNVERIFIED.to_string(),
notes: "test".to_string(),
};
let payload = super::build_upstream_registry_instruction_match_payload(
&transaction,
&instruction,
&registry_match,
Some("data"),
);
assert_eq!(payload.get("tradeCandidate").and_then(serde_json::Value::as_bool), Some(false));
assert_eq!(
payload.get("candleCandidate").and_then(serde_json::Value::as_bool),
Some(false)
);
assert_eq!(
payload.get("upstreamProofStatus").and_then(serde_json::Value::as_str),
Some(crate::PROOF_STATUS_UPSTREAM_GIT_UNVERIFIED)
);
}
} }

View File

@@ -84,6 +84,20 @@ impl DexDetectService {
Some(route) => route, Some(route) => route,
None => continue, None => continue,
}; };
if crate::dex_detection_route::dex_detection_route_requires_full_pool_context(route)
&& !crate::dex_detection_route::decoded_event_has_full_pool_context(decoded_event)
{
tracing::trace!(
decoded_event_id = ?decoded_event.id,
protocol_name = %decoded_event.protocol_name,
event_kind = %decoded_event.event_kind,
pool_account = ?decoded_event.pool_account,
token_a_mint = ?decoded_event.token_a_mint,
token_b_mint = ?decoded_event.token_b_mint,
"skipping business-level dex detection for incomplete decoded pool context"
);
continue;
}
let detect_result = match route { let detect_result = match route {
crate::dex_detection_route::DexDetectionRoute::RaydiumAmmV4Initialize2Pool => { crate::dex_detection_route::DexDetectionRoute::RaydiumAmmV4Initialize2Pool => {
self.detect_raydium_initialize2_pool(&transaction, decoded_event).await self.detect_raydium_initialize2_pool(&transaction, decoded_event).await

View File

@@ -133,15 +133,122 @@ pub(crate) fn dex_detection_route(
} }
} }
fn is_incomplete_pump_swap_decoded_event(decoded_event: &crate::DexDecodedEventDto) -> bool { /// Returns true when a route materializes a pool and therefore requires
/// pool account plus both token mints before it can run safely.
pub(crate) fn dex_detection_route_requires_full_pool_context(
route: crate::dex_detection_route::DexDetectionRoute,
) -> bool {
match route {
crate::dex_detection_route::DexDetectionRoute::PumpFunCreateV2Token => return false,
crate::dex_detection_route::DexDetectionRoute::SkipIncompletePumpSwapTrade => return false,
_ => return true,
}
}
/// Returns true when a decoded event has the minimum fields required for
/// deterministic pool, pair and listing materialization.
pub(crate) fn decoded_event_has_full_pool_context(
decoded_event: &crate::DexDecodedEventDto,
) -> bool {
if decoded_event.pool_account.is_none() { if decoded_event.pool_account.is_none() {
return true; return false;
} }
if decoded_event.token_a_mint.is_none() { if decoded_event.token_a_mint.is_none() {
return true; return false;
} }
if decoded_event.token_b_mint.is_none() { if decoded_event.token_b_mint.is_none() {
return true; return false;
}
return true;
}
fn is_incomplete_pump_swap_decoded_event(decoded_event: &crate::DexDecodedEventDto) -> bool {
return !crate::dex_detection_route::decoded_event_has_full_pool_context(decoded_event);
}
#[cfg(test)]
mod tests {
fn make_decoded_event(
protocol_name: &str,
event_kind: &str,
pool_account: std::option::Option<&str>,
token_a_mint: std::option::Option<&str>,
token_b_mint: std::option::Option<&str>,
) -> crate::DexDecodedEventDto {
return crate::DexDecodedEventDto::new(
1,
Some(1),
protocol_name.to_string(),
"Program1111111111111111111111111111111111".to_string(),
event_kind.to_string(),
pool_account.map(std::string::ToString::to_string),
None,
token_a_mint.map(std::string::ToString::to_string),
token_b_mint.map(std::string::ToString::to_string),
None,
"{}".to_string(),
);
}
#[test]
fn complete_pool_context_requires_pool_and_two_token_mints() {
let complete = make_decoded_event(
"meteora_damm_v2",
"meteora_damm_v2.swap",
Some("Pool111"),
Some("TokenA111"),
Some("TokenB111"),
);
assert!(crate::dex_detection_route::decoded_event_has_full_pool_context(&complete));
let missing_token_a = make_decoded_event(
"meteora_damm_v2",
"meteora_damm_v2.swap",
Some("Pool111"),
None,
Some("TokenB111"),
);
assert!(!crate::dex_detection_route::decoded_event_has_full_pool_context(
&missing_token_a
));
}
#[test]
fn meteora_swap_route_requires_full_pool_context() {
let event = make_decoded_event(
"meteora_damm_v2",
"meteora_damm_v2.swap",
Some("Pool111"),
None,
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!(crate::dex_detection_route::dex_detection_route_requires_full_pool_context(
route
));
assert!(!crate::dex_detection_route::decoded_event_has_full_pool_context(&event));
}
#[test]
fn pump_fun_create_token_route_does_not_require_full_pool_context() {
let event = make_decoded_event(
"pump_fun",
"pump_fun.create_v2_token",
Some("BondingCurve111"),
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!(!crate::dex_detection_route::dex_detection_route_requires_full_pool_context(
route
));
} }
return false;
} }

View File

@@ -266,9 +266,15 @@ pub fn classify_dex_event_actionability_code(
/// Returns true for decoded audit-only events retained for corpus analysis. /// Returns true for decoded audit-only events retained for corpus analysis.
pub fn is_dex_informational_event_kind(event_kind: &str) -> bool { pub fn is_dex_informational_event_kind(event_kind: &str) -> bool {
if event_kind == crate::UPSTREAM_REGISTRY_INSTRUCTION_MATCH_EVENT_KIND {
return true;
}
if event_kind.contains(".instruction_audit") { if event_kind.contains(".instruction_audit") {
return true; return true;
} }
if event_kind.ends_with("_audit") {
return true;
}
if event_kind.contains(".unknown_instruction") { if event_kind.contains(".unknown_instruction") {
return true; return true;
} }
@@ -1145,4 +1151,38 @@ mod tests {
"non_trade_useful" "non_trade_useful"
); );
} }
#[test]
fn classifies_audit_suffix_events_as_informational() {
assert!(super::is_dex_informational_event_kind("openbook_v2.settle_funds_audit"));
assert_eq!(
super::classify_dex_event_category_code("openbook_v2.settle_funds_audit"),
"informational"
);
assert_eq!(
super::classify_dex_event_actionability_code(
"openbook_v2.settle_funds_audit",
false,
false,
),
"informational"
);
assert!(!super::is_dex_trade_event_kind("openbook_v2.order_place_audit"));
}
#[test]
fn classifies_upstream_registry_instruction_match_as_informational() {
assert!(super::is_dex_informational_event_kind(
crate::UPSTREAM_REGISTRY_INSTRUCTION_MATCH_EVENT_KIND
));
assert_eq!(
super::classify_dex_event_category_code(
crate::UPSTREAM_REGISTRY_INSTRUCTION_MATCH_EVENT_KIND
),
"informational"
);
assert!(!super::is_dex_trade_event_kind(
crate::UPSTREAM_REGISTRY_INSTRUCTION_MATCH_EVENT_KIND
));
}
} }

File diff suppressed because it is too large Load Diff

View File

@@ -13,13 +13,7 @@ struct BuiltinLaunchSurfaceSpec {
const BUILTIN_LAUNCH_SURFACE_SPECS: &[BuiltinLaunchSurfaceSpec] = &[ const BUILTIN_LAUNCH_SURFACE_SPECS: &[BuiltinLaunchSurfaceSpec] = &[
BuiltinLaunchSurfaceSpec { BuiltinLaunchSurfaceSpec {
code: "raydium_launchlab", code: "raydium_launchlab",
name: "Raydium LaunchLab", name: "Raydium LaunchLab / Launchpad",
protocol_family: "raydium",
enabled: true,
},
BuiltinLaunchSurfaceSpec {
code: "raydium_launchpad",
name: "Raydium Launchpad",
protocol_family: "raydium", protocol_family: "raydium",
enabled: true, enabled: true,
}, },
@@ -988,10 +982,9 @@ mod tests {
Ok(surface_ids) => surface_ids, Ok(surface_ids) => surface_ids,
Err(error) => panic!("target launch surfaces must seed: {}", error), Err(error) => panic!("target launch surfaces must seed: {}", error),
}; };
assert_eq!(surface_ids.len(), 10); assert_eq!(surface_ids.len(), 9);
let required_codes = [ let required_codes = [
"raydium_launchlab", "raydium_launchlab",
"raydium_launchpad",
"letsbonk", "letsbonk",
"bonk_fun", "bonk_fun",
"bags", "bags",

View File

@@ -111,6 +111,14 @@ mod tx_model;
mod tx_resolution; mod tx_resolution;
/// Shared generic types for `kb_lib`. /// Shared generic types for `kb_lib`.
mod types; mod types;
/// Upstream Git registry service facade.
mod upstream_registry;
/// Static upstream Git registry bootstrap data.
mod upstream_registry_generated;
/// Upstream Git registry matching helpers.
mod upstream_registry_match;
/// Upstream Git registry DTOs and static entry types.
mod upstream_registry_types;
/// Wallet-holding observation service. /// Wallet-holding observation service.
mod wallet_holding_observation; mod wallet_holding_observation;
/// Wallet-observation service. /// Wallet-observation service.
@@ -147,19 +155,37 @@ pub use config::WsEndpointConfig;
/// Address Lookup Table program identifier. ("AddressLookupTab1e1111111111111111111111111"). /// Address Lookup Table program identifier. ("AddressLookupTab1e1111111111111111111111111").
/// @see solana_sdk::pubkey::Pubkey = solana_sdk_ids::address_lookup_table::ID /// @see solana_sdk::pubkey::Pubkey = solana_sdk_ids::address_lookup_table::ID
pub use constants::ADDRESS_LOOKUP_TABLE_PROGRAM_ID; pub use constants::ADDRESS_LOOKUP_TABLE_PROGRAM_ID;
/// AlphaQ program id from Vybe supported DEX/AMM documentation.
pub use constants::ALPHAQ_PROGRAM_ID;
/// Aquifer program id from Vybe supported DEX/AMM documentation.
pub use constants::AQUIFER_PROGRAM_ID;
/// Arbitrage Bot (6MWVT) / Arbitrage or Sandwich Bot. ("6MWVTis8rmmk6Vt9zmAJJbmb3VuLpzoQ1aHH4N6wQEGh"). /// Arbitrage Bot (6MWVT) / Arbitrage or Sandwich Bot. ("6MWVTis8rmmk6Vt9zmAJJbmb3VuLpzoQ1aHH4N6wQEGh").
pub use constants::ARBITRAGE_BOT_6MWVT_PROGRAM_ID; pub use constants::ARBITRAGE_BOT_6MWVT_PROGRAM_ID;
/// Associated Token Account program identifier. ("ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"). /// Associated Token Account program identifier. ("ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL").
/// @see solana_sdk::pubkey::Pubkey = spl_associated_token_account_interface::program::ID /// @see solana_sdk::pubkey::Pubkey = spl_associated_token_account_interface::program::ID
pub use constants::ASSOCIATED_TOKEN_PROGRAM_ID; pub use constants::ASSOCIATED_TOKEN_PROGRAM_ID;
/// BisonFi program id from Vybe supported DEX/AMM documentation.
pub use constants::BISONFI_PROGRAM_ID;
/// Canonical Bonk token mint identifier. /// Canonical Bonk token mint identifier.
pub use constants::BONK_MINT_ID; pub use constants::BONK_MINT_ID;
/// Bonkswap program id extracted from upstream Git decoder source.
pub use constants::BONKSWAP_PROGRAM_ID;
/// Boop program id extracted from upstream Git decoder source.
pub use constants::BOOP_PROGRAM_ID;
/// BPF Loader program identifier. ("BPFLoader1111111111111111111111111111111111"). /// BPF Loader program identifier. ("BPFLoader1111111111111111111111111111111111").
/// @see solana_sdk::pubkey::Pubkey = solana_sdk_ids::bpf_loader_deprecated::ID /// @see solana_sdk::pubkey::Pubkey = solana_sdk_ids::bpf_loader_deprecated::ID
pub use constants::BPF_LOADER_DEPRECATED_PROGRAM_ID; pub use constants::BPF_LOADER_DEPRECATED_PROGRAM_ID;
/// BPF Loader program identifier. ("BPFLoaderUpgradeab1e11111111111111111111111"). /// BPF Loader program identifier. ("BPFLoaderUpgradeab1e11111111111111111111111").
/// @see solana_sdk::pubkey::Pubkey = solana_sdk_ids::bpf_loader_upgradeable::ID /// @see solana_sdk::pubkey::Pubkey = solana_sdk_ids::bpf_loader_upgradeable::ID
pub use constants::BPF_LOADER_UPGRADEABLE_PROGRAM_ID; pub use constants::BPF_LOADER_UPGRADEABLE_PROGRAM_ID;
/// Bubblegum program id extracted from upstream Git decoder source.
pub use constants::BUBBLEGUM_PROGRAM_ID;
/// Byreal program id from Vybe supported DEX/AMM documentation.
pub use constants::BYREAL_PROGRAM_ID;
/// Circle Message Transmitter v2 program id extracted from upstream Git decoder source.
pub use constants::CIRCLE_MESSAGE_TRANSMITTER_V2_PROGRAM_ID;
/// Circle Token Messenger v2 program id extracted from upstream Git decoder source.
pub use constants::CIRCLE_TOKEN_MESSENGER_V2_PROGRAM_ID;
/// Compute Budget program identifier. ("ComputeBudget111111111111111111111111111111"). /// Compute Budget program identifier. ("ComputeBudget111111111111111111111111111111").
/// @see solana_sdk::pubkey::Pubkey = solana_sdk_ids::compute_budget::ID /// @see solana_sdk::pubkey::Pubkey = solana_sdk_ids::compute_budget::ID
pub use constants::COMPUTE_BUDGET_PROGRAM_ID; pub use constants::COMPUTE_BUDGET_PROGRAM_ID;
@@ -168,6 +194,10 @@ pub use constants::COMPUTE_BUDGET_PROGRAM_ID;
pub use constants::CONFIG_PROGRAM_ID; pub use constants::CONFIG_PROGRAM_ID;
/// DexLab Swap/Pool program id. ("DSwpgjMvXhtGn6BsbqmacdBZyfLj6jSWf3HJpdJtmg6N"). /// DexLab Swap/Pool program id. ("DSwpgjMvXhtGn6BsbqmacdBZyfLj6jSWf3HJpdJtmg6N").
pub use constants::DEXLAB_PROGRAM_ID; pub use constants::DEXLAB_PROGRAM_ID;
/// DFlow Aggregator v4 program id extracted from upstream Git decoder source.
pub use constants::DFLOW_AGGREGATOR_V4_PROGRAM_ID;
/// Drift v2 program id extracted from upstream Git decoder source.
pub use constants::DRIFT_V2_PROGRAM_ID;
/// ED25519 program identifier. ("Ed25519SigVerify111111111111111111111111111"). /// ED25519 program identifier. ("Ed25519SigVerify111111111111111111111111111").
/// @see solana_sdk::pubkey::Pubkey = solana_sdk_ids::ed25519_program::ID /// @see solana_sdk::pubkey::Pubkey = solana_sdk_ids::ed25519_program::ID
pub use constants::ED25519_PROGRAM_ID; pub use constants::ED25519_PROGRAM_ID;
@@ -176,14 +206,58 @@ pub use constants::ED25519_PROGRAM_ID;
pub use constants::FEATURE_PROGRAM_ID; pub use constants::FEATURE_PROGRAM_ID;
/// FluxBeam program id. ("FLUXubRmkEi2q6K3Y9kBPg9248ggaZVsoSFhtJHSrm1X"). /// FluxBeam program id. ("FLUXubRmkEi2q6K3Y9kBPg9248ggaZVsoSFhtJHSrm1X").
pub use constants::FLUXBEAM_PROGRAM_ID; pub use constants::FLUXBEAM_PROGRAM_ID;
/// FusionAMM program id from Vybe supported DEX/AMM documentation.
pub use constants::FUSIONAMM_PROGRAM_ID;
/// Gavel program id extracted from upstream Git decoder source.
pub use constants::GAVEL_PROGRAM_ID;
/// Goonfi program id from Vybe supported DEX/AMM documentation.
pub use constants::GOONFI_PROGRAM_ID;
/// Goonfi v2 program id from Vybe supported DEX/AMM documentation.
pub use constants::GOONFI_V2_PROGRAM_ID;
/// GooseFX v1 program id from Vybe supported DEX/AMM documentation.
pub use constants::GOOSEFX_V1_PROGRAM_ID;
/// Heaven program id extracted from upstream Git decoder source.
pub use constants::HEAVEN_PROGRAM_ID;
/// Humidifi program id from Vybe supported DEX/AMM documentation.
pub use constants::HUMIDIFI_PROGRAM_ID;
/// Incinerator program identifier. ("1nc1nerator11111111111111111111111111111111"). /// Incinerator program identifier. ("1nc1nerator11111111111111111111111111111111").
/// @see solana_sdk::pubkey::Pubkey = solana_sdk_ids::incinerator::ID /// @see solana_sdk::pubkey::Pubkey = solana_sdk_ids::incinerator::ID
pub use constants::INCINERATOR_PROGRAM_ID; pub use constants::INCINERATOR_PROGRAM_ID;
/// Canonical Jupiter governance token mint identifier. /// Canonical Jupiter governance token mint identifier.
pub use constants::JUP_MINT_ID; pub use constants::JUP_MINT_ID;
/// Jupiter DCA program id extracted from upstream Git decoder source.
pub use constants::JUPITER_DCA_PROGRAM_ID;
/// Jupiter Lend program id extracted from upstream Git decoder source.
pub use constants::JUPITER_LEND_PROGRAM_ID;
/// Jupiter Limit Order 2 program id extracted from upstream Git decoder source.
pub use constants::JUPITER_LIMIT_ORDER_2_PROGRAM_ID;
/// Jupiter Limit Order program id extracted from upstream Git decoder source.
pub use constants::JUPITER_LIMIT_ORDER_PROGRAM_ID;
/// Jupiter Perpetuals program id extracted from upstream Git decoder source.
pub use constants::JUPITER_PERPETUALS_PROGRAM_ID;
/// Jupiter Swap program id extracted from upstream Git decoder source.
pub use constants::JUPITER_SWAP_PROGRAM_ID;
/// Kamino Farms program id extracted from upstream Git decoder source.
pub use constants::KAMINO_FARMS_PROGRAM_ID;
/// Kamino Lending program id extracted from upstream Git decoder source.
pub use constants::KAMINO_LENDING_PROGRAM_ID;
/// Kamino Limit Order program id extracted from upstream Git decoder source.
pub use constants::KAMINO_LIMIT_ORDER_PROGRAM_ID;
/// Kamino Vault program id extracted from upstream Git decoder source.
pub use constants::KAMINO_VAULT_PROGRAM_ID;
/// Lifinity AMM v2 program id extracted from upstream Git decoder source.
pub use constants::LIFINITY_AMM_V2_PROGRAM_ID;
/// Loader V4 program identifier. ("LoaderV411111111111111111111111111111111111"). /// Loader V4 program identifier. ("LoaderV411111111111111111111111111111111111").
/// @see solana_sdk::pubkey::Pubkey = solana_sdk_ids::loader_v4::ID /// @see solana_sdk::pubkey::Pubkey = solana_sdk_ids::loader_v4::ID
pub use constants::LOADER_V4_PROGRAM_ID; pub use constants::LOADER_V4_PROGRAM_ID;
/// Manifest CLOB program id from Vybe supported DEX/AMM documentation.
pub use constants::MANIFEST_CLOB_PROGRAM_ID;
/// Marginfi v2 program id extracted from upstream Git decoder source.
pub use constants::MARGINFI_V2_PROGRAM_ID;
/// Marinade Finance program id extracted from upstream Git decoder source.
pub use constants::MARINADE_FINANCE_PROGRAM_ID;
/// SPL Memo program id.
pub use constants::MEMO_PROGRAM_ID;
/// MetaDAO AMM v0.5.0 program id. /// MetaDAO AMM v0.5.0 program id.
pub use constants::METADAO_AMM_V0_5_0_PROGRAM_ID; pub use constants::METADAO_AMM_V0_5_0_PROGRAM_ID;
/// MetaDAO Bid Wall v0.7.0 program id. /// MetaDAO Bid Wall v0.7.0 program id.
@@ -192,12 +266,6 @@ pub use constants::METADAO_BID_WALL_V0_7_0_PROGRAM_ID;
pub use constants::METADAO_FUTARCHY_V0_6_0_PROGRAM_ID; pub use constants::METADAO_FUTARCHY_V0_6_0_PROGRAM_ID;
/// MetaDAO Launchpad v0.7.0 program id. /// MetaDAO Launchpad v0.7.0 program id.
pub use constants::METADAO_LAUNCHPAD_V0_7_0_PROGRAM_ID; pub use constants::METADAO_LAUNCHPAD_V0_7_0_PROGRAM_ID;
/// MetaDAO META active token mint identifier.
pub use constants::METADAO_META_MINT_ID;
/// MetaDAO METAC legacy token mint identifier.
pub use constants::METADAO_METAC_LEGACY_MINT_ID;
/// MetaDAO-linked P2P token mint candidate.
pub use constants::METADAO_P2P_MINT_ID;
/// Meteora DAMM v1 program id. ("Eo7WjKq67rjJQSZxS6z3YkapzY3eMj6Xy8X5EQVn5UaB"). /// Meteora DAMM v1 program id. ("Eo7WjKq67rjJQSZxS6z3YkapzY3eMj6Xy8X5EQVn5UaB").
pub use constants::METEORA_DAMM_V1_PROGRAM_ID; pub use constants::METEORA_DAMM_V1_PROGRAM_ID;
/// Meteora DAMM v2 program id. ("cpamdpZCGKUy5JxQXB4dcpGPiikHawvSWAd6mEn1sGG"). /// Meteora DAMM v2 program id. ("cpamdpZCGKUy5JxQXB4dcpGPiikHawvSWAd6mEn1sGG").
@@ -206,13 +274,39 @@ pub use constants::METEORA_DAMM_V2_PROGRAM_ID;
pub use constants::METEORA_DBC_PROGRAM_ID; pub use constants::METEORA_DBC_PROGRAM_ID;
/// Meteora DLMM program id. ("LBUZKhRxPF3XUpBCjp4YzTKgLccjZhTSDM9YuVaPwxo"). /// Meteora DLMM program id. ("LBUZKhRxPF3XUpBCjp4YzTKgLccjZhTSDM9YuVaPwxo").
pub use constants::METEORA_DLMM_PROGRAM_ID; pub use constants::METEORA_DLMM_PROGRAM_ID;
/// Meteora Vault program id extracted from upstream Git decoder source.
pub use constants::METEORA_VAULT_PROGRAM_ID;
/// Moonshot program id extracted from upstream Git decoder source.
pub use constants::MOONSHOT_PROGRAM_ID;
/// MPL Core program id extracted from upstream Git decoder source.
pub use constants::MPL_CORE_PROGRAM_ID;
/// MPL Token Metadata program id extracted from upstream Git decoder source.
pub use constants::MPL_TOKEN_METADATA_PROGRAM_ID;
/// Solana Name Service program id extracted from upstream Git decoder source.
pub use constants::NAME_SERVICE_PROGRAM_ID;
/// Native Loader program identifier. ("NativeLoader1111111111111111111111111111111"). /// Native Loader program identifier. ("NativeLoader1111111111111111111111111111111").
/// @see solana_sdk::pubkey::Pubkey = solana_sdk_ids::native_loader::ID /// @see solana_sdk::pubkey::Pubkey = solana_sdk_ids::native_loader::ID
pub use constants::NATIVE_LOADER_PROGRAM_ID; pub use constants::NATIVE_LOADER_PROGRAM_ID;
/// Obric v2 program id from Vybe supported DEX/AMM documentation.
pub use constants::OBRIC_V2_PROGRAM_ID;
/// OKX DEX program id extracted from upstream Git decoder source.
pub use constants::OKX_DEX_PROGRAM_ID;
/// Onchain Labs DEX v2 program id extracted from upstream Git decoder source.
pub use constants::ONCHAIN_LABS_DEX_V2_PROGRAM_ID;
/// Ondo Global Market program id from Vybe supported DEX/AMM documentation.
pub use constants::ONDO_GLOBAL_MARKET_PROGRAM_ID;
/// OpenBook v2 program id extracted from upstream Git decoder source.
pub use constants::OPENBOOK_V2_PROGRAM_ID;
/// Orca Whirlpools program id. ("whirLbMiicVdio4qvUfM5KAg6Ct8VwpYzGff3uctyCc"). /// Orca Whirlpools program id. ("whirLbMiicVdio4qvUfM5KAg6Ct8VwpYzGff3uctyCc").
pub use constants::ORCA_WHIRLPOOLS_PROGRAM_ID; pub use constants::ORCA_WHIRLPOOLS_PROGRAM_ID;
/// Pancake Swap program id extracted from upstream Git decoder source.
pub use constants::PANCAKE_SWAP_PROGRAM_ID;
/// Phoenix v1 program id extracted from upstream Git decoder source.
pub use constants::PHOENIX_V1_PROGRAM_ID;
/// Printr program id candidate observed on Solscan. /// Printr program id candidate observed on Solscan.
pub use constants::PRINTR_PROGRAM_ID; pub use constants::PRINTR_PROGRAM_ID;
/// Pump Fees program id extracted from upstream Git decoder source.
pub use constants::PUMP_FEES_PROGRAM_ID;
/// Pump.fun program id. ("6EF8rrecthR5Dkzon8Nwu78hRvfCKubJ14M5uBEwF6P"). /// Pump.fun program id. ("6EF8rrecthR5Dkzon8Nwu78hRvfCKubJ14M5uBEwF6P").
pub use constants::PUMP_FUN_PROGRAM_ID; pub use constants::PUMP_FUN_PROGRAM_ID;
/// PumpSwap / PumpAMM program id. ("pAMMBay6oceH9fJKBRHGP5D4bD4sWpmSwMn52FMfXEA"). /// PumpSwap / PumpAMM program id. ("pAMMBay6oceH9fJKBRHGP5D4bD4sWpmSwMn52FMfXEA").
@@ -227,28 +321,44 @@ pub use constants::RAYDIUM_AMM_V4_PROGRAM_ID;
pub use constants::RAYDIUM_CLMM_PROGRAM_ID; pub use constants::RAYDIUM_CLMM_PROGRAM_ID;
/// Raydium CPMM mainnet program id. ("CPMMoo8L3F4NbTegBCKVNunggL7H1ZpdTHKxQB5qKP1C"). /// Raydium CPMM mainnet program id. ("CPMMoo8L3F4NbTegBCKVNunggL7H1ZpdTHKxQB5qKP1C").
pub use constants::RAYDIUM_CPMM_PROGRAM_ID; pub use constants::RAYDIUM_CPMM_PROGRAM_ID;
/// Raydium LaunchLab program id. ("LanMV9sAd7wArD4vJFi2qDdfnVhFxYSUg6eADduJ3uj"). /// Raydium LaunchLab / Launchpad program id. ("LanMV9sAd7wArD4vJFi2qDdfnVhFxYSUg6eADduJ3uj").
pub use constants::RAYDIUM_LAUNCHLAB_PROGRAM_ID; pub use constants::RAYDIUM_LAUNCHLAB_PROGRAM_ID;
/// Raydium Liquidity Locking program id extracted from upstream Git decoder source.
pub use constants::RAYDIUM_LIQUIDITY_LOCKING_PROGRAM_ID;
/// Raydium Stable Swap AMM program id, deprecated. ("5quBtoiQqxF9Jv6KYKctB59NT3gtJD2Y65kdnB1Uev3h"). /// Raydium Stable Swap AMM program id, deprecated. ("5quBtoiQqxF9Jv6KYKctB59NT3gtJD2Y65kdnB1Uev3h").
pub use constants::RAYDIUM_STABLE_SWAP_AMM_PROGRAM_ID; pub use constants::RAYDIUM_STABLE_SWAP_AMM_PROGRAM_ID;
/// Scorch program id from Vybe supported DEX/AMM documentation.
pub use constants::SCORCH_PROGRAM_ID;
/// Secp256k1 program identifier. ("KeccakSecp256k11111111111111111111111111111"). /// Secp256k1 program identifier. ("KeccakSecp256k11111111111111111111111111111").
/// @see solana_sdk::pubkey::Pubkey = solana_sdk_ids::secp256k1_program::ID /// @see solana_sdk::pubkey::Pubkey = solana_sdk_ids::secp256k1_program::ID
pub use constants::SECP256K1_PROGRAM_ID; pub use constants::SECP256K1_PROGRAM_ID;
/// Secp256r1 program identifier. ("Secp256r1SigVerify1111111111111111111111111"). /// Secp256r1 program identifier. ("Secp256r1SigVerify1111111111111111111111111").
/// @see solana_sdk::pubkey::Pubkey = solana_sdk_ids::secp256r1_program::ID /// @see solana_sdk::pubkey::Pubkey = solana_sdk_ids::secp256r1_program::ID
pub use constants::SECP256R1_PROGRAM_ID; pub use constants::SECP256R1_PROGRAM_ID;
/// Sharky program id extracted from upstream Git decoder source.
pub use constants::SHARKY_PROGRAM_ID;
/// Solayer Restaking program id extracted from upstream Git decoder source.
pub use constants::SOLAYER_RESTAKING_PROGRAM_ID;
/// SolFi v2 program id from Vybe supported DEX/AMM documentation.
pub use constants::SOLFI_V2_PROGRAM_ID;
/// SPL Token-2022 program identifier. ("TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb"). /// SPL Token-2022 program identifier. ("TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb").
/// @see solana_sdk::pubkey::Pubkey = spl_token_2022_interface::ID /// @see solana_sdk::pubkey::Pubkey = spl_token_2022_interface::ID
pub use constants::SPL_TOKEN_2022_PROGRAM_ID; pub use constants::SPL_TOKEN_2022_PROGRAM_ID;
/// SPL Token program identifier. ("TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"). /// SPL Token program identifier. ("TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA").
/// @see solana_sdk::pubkey::Pubkey = spl_token_interface::ID /// @see solana_sdk::pubkey::Pubkey = spl_token_interface::ID
pub use constants::SPL_TOKEN_PROGRAM_ID; pub use constants::SPL_TOKEN_PROGRAM_ID;
/// Stabble stable-swap program id extracted from upstream Git decoder source.
pub use constants::STABBLE_STABLE_SWAP_PROGRAM_ID;
/// Stabble weighted-swap program id extracted from upstream Git decoder source.
pub use constants::STABBLE_WEIGHTED_SWAP_PROGRAM_ID;
/// Stake Config program identifier. ("StakeConfig11111111111111111111111111111111"). /// Stake Config program identifier. ("StakeConfig11111111111111111111111111111111").
/// @see solana_sdk::pubkey::Pubkey = solana_sdk_ids::stake::config::ID /// @see solana_sdk::pubkey::Pubkey = solana_sdk_ids::stake::config::ID
pub use constants::STAKE_CONFIG_PROGRAM_ID; pub use constants::STAKE_CONFIG_PROGRAM_ID;
/// Stake program identifier. ("Stake11111111111111111111111111111111111111"). /// Stake program identifier. ("Stake11111111111111111111111111111111111111").
/// @see solana_sdk::pubkey::Pubkey = solana_sdk_ids::stake::ID /// @see solana_sdk::pubkey::Pubkey = solana_sdk_ids::stake::ID
pub use constants::STAKE_PROGRAM_ID; pub use constants::STAKE_PROGRAM_ID;
/// Swig program id extracted from upstream Git decoder source.
pub use constants::SWIG_PROGRAM_ID;
/// System program identifier. ("11111111111111111111111111111111"). /// System program identifier. ("11111111111111111111111111111111").
/// @see solana_sdk::pubkey::Pubkey = solana_sdk_ids::system_program::ID /// @see solana_sdk::pubkey::Pubkey = solana_sdk_ids::system_program::ID
pub use constants::SYSTEM_PROGRAM_ID; pub use constants::SYSTEM_PROGRAM_ID;
@@ -295,12 +405,24 @@ pub use constants::SYSVAR_STAKE_HISTORY_PROGRAM_ID;
pub use constants::USDC_MINT_ID; pub use constants::USDC_MINT_ID;
/// Canonical Solana USDT mint identifier. /// Canonical Solana USDT mint identifier.
pub use constants::USDT_MINT_ID; pub use constants::USDT_MINT_ID;
/// Vertigo program id extracted from upstream Git decoder source.
pub use constants::VERTIGO_PROGRAM_ID;
/// Virtuals program id extracted from upstream Git decoder source.
pub use constants::VIRTUALS_PROGRAM_ID;
/// Vote program identifier. ("Vote111111111111111111111111111111111111111"). /// Vote program identifier. ("Vote111111111111111111111111111111111111111").
/// @see solana_sdk::pubkey::Pubkey = solana_sdk_ids::vote::ID /// @see solana_sdk::pubkey::Pubkey = solana_sdk_ids::vote::ID
pub use constants::VOTE_PROGRAM_ID; pub use constants::VOTE_PROGRAM_ID;
/// Wavebreak program id extracted from upstream Git decoder source.
pub use constants::WAVEBREAK_PROGRAM_ID;
/// Woofi program id from Vybe supported DEX/AMM documentation.
pub use constants::WOOFI_PROGRAM_ID;
/// Wrapped SOL mint identifier. ("So11111111111111111111111111111111111111112"). /// Wrapped SOL mint identifier. ("So11111111111111111111111111111111111111112").
/// @see solana_sdk::pubkey::Pubkey = spl_token_interface::native_mint::ID /// @see solana_sdk::pubkey::Pubkey = spl_token_interface::native_mint::ID
pub use constants::WSOL_MINT_ID; pub use constants::WSOL_MINT_ID;
/// ZeroFi program id from Vybe supported DEX/AMM documentation.
pub use constants::ZEROFI_PROGRAM_ID;
/// Zeta program id extracted from upstream Git decoder source.
pub use constants::ZETA_PROGRAM_ID;
/// Zk El Gamal Proof program identifier. ("ZkE1Gama1Proof11111111111111111111111111111"). /// Zk El Gamal Proof program identifier. ("ZkE1Gama1Proof11111111111111111111111111111").
/// @see solana_sdk::pubkey::Pubkey = solana_sdk_ids::zk_elgamal_proof_program::ID /// @see solana_sdk::pubkey::Pubkey = solana_sdk_ids::zk_elgamal_proof_program::ID
pub use constants::ZK_ELGAMAL_PROOF_PROGRAM_ID; pub use constants::ZK_ELGAMAL_PROOF_PROGRAM_ID;
@@ -943,6 +1065,12 @@ pub use dex::MeteoraDlmmPoolLifecycleDecoded;
pub use dex::MeteoraDlmmRewardDecoded; pub use dex::MeteoraDlmmRewardDecoded;
/// Decoded Meteora DLMM swap event. /// Decoded Meteora DLMM swap event.
pub use dex::MeteoraDlmmSwapDecoded; pub use dex::MeteoraDlmmSwapDecoded;
/// Decoded OpenBook v2 audit event.
pub use dex::OpenBookV2AuditDecoded;
/// Decoded OpenBook v2 event.
pub use dex::OpenBookV2DecodedEvent;
/// OpenBook v2 audit-only decoder.
pub use dex::OpenBookV2Decoder;
/// Decoded Orca Whirlpools create-pool event. /// Decoded Orca Whirlpools create-pool event.
pub use dex::OrcaWhirlpoolsCreatePoolDecoded; pub use dex::OrcaWhirlpoolsCreatePoolDecoded;
/// Decoded Orca Whirlpools event. /// Decoded Orca Whirlpools event.
@@ -951,6 +1079,12 @@ pub use dex::OrcaWhirlpoolsDecodedEvent;
pub use dex::OrcaWhirlpoolsDecoder; pub use dex::OrcaWhirlpoolsDecoder;
/// Decoded Orca Whirlpools swap event. /// Decoded Orca Whirlpools swap event.
pub use dex::OrcaWhirlpoolsSwapDecoded; pub use dex::OrcaWhirlpoolsSwapDecoded;
/// Decoded Phoenix v1 audit event.
pub use dex::PhoenixV1AuditDecoded;
/// Decoded Phoenix v1 event.
pub use dex::PhoenixV1DecodedEvent;
/// Phoenix v1 audit-only decoder.
pub use dex::PhoenixV1Decoder;
/// Decoded Pump.fun `create_v2` token event. /// Decoded Pump.fun `create_v2` token event.
pub use dex::PumpFunCreateV2TokenDecoded; pub use dex::PumpFunCreateV2TokenDecoded;
/// Decoded Pump.fun event. /// Decoded Pump.fun event.
@@ -1164,8 +1298,12 @@ pub use local_pipeline_validation::validate_local_pipeline_diagnostics_summary;
pub use non_trade_event_materialization::NonTradeEventMaterializationResult; pub use non_trade_event_materialization::NonTradeEventMaterializationResult;
/// Materializes useful non-trade decoded DEX events. /// Materializes useful non-trade decoded DEX events.
pub use non_trade_event_materialization::NonTradeEventMaterializationService; pub use non_trade_event_materialization::NonTradeEventMaterializationService;
/// Backfill signatures grouped by candidate kind.
pub use onchain_dex_pair_discovery::OnchainDexBackfillSignaturesByKindDto;
/// Candidate account inferred from generic transaction evidence. /// Candidate account inferred from generic transaction evidence.
pub use onchain_dex_pair_discovery::OnchainDexCandidateAccountDto; pub use onchain_dex_pair_discovery::OnchainDexCandidateAccountDto;
/// Candidate summary grouped by kind and transaction success state.
pub use onchain_dex_pair_discovery::OnchainDexCandidateKindSummaryDto;
/// Cursor hint for one on-chain DEX discovery source address. /// Cursor hint for one on-chain DEX discovery source address.
pub use onchain_dex_pair_discovery::OnchainDexPaginationCursorDto; pub use onchain_dex_pair_discovery::OnchainDexPaginationCursorDto;
/// Candidate transaction/instruction observed on-chain for one DEX program id. /// Candidate transaction/instruction observed on-chain for one DEX program id.
@@ -1266,6 +1404,40 @@ pub use tx_resolution::WsTransactionResolutionRelay;
pub use tx_resolution::WsTransactionResolutionRelayStats; pub use tx_resolution::WsTransactionResolutionRelayStats;
/// Generic connection state used by transport clients. /// Generic connection state used by transport clients.
pub use types::ConnectionState; pub use types::ConnectionState;
/// Read-only upstream registry service.
pub use upstream_registry::UpstreamRegistryService;
/// Static upstream registry entry kind for account layouts.
pub use upstream_registry_types::ENTRY_KIND_ACCOUNT;
/// Static upstream registry entry kind for events.
pub use upstream_registry_types::ENTRY_KIND_EVENT;
/// Static upstream registry entry kind for instructions.
pub use upstream_registry_types::ENTRY_KIND_INSTRUCTION;
/// Static upstream registry entry kind for programs.
pub use upstream_registry_types::ENTRY_KIND_PROGRAM;
/// Generic upstream Git proof status for layout-only unverified entries.
pub use upstream_registry_types::PROOF_STATUS_UPSTREAM_GIT_LAYOUT_UNVERIFIED;
/// Generic upstream Git proof status for locally materialized entries.
pub use upstream_registry_types::PROOF_STATUS_UPSTREAM_GIT_LOCAL_CORPUS_MATERIALIZED;
/// Generic upstream Git proof status for locally observed entries.
pub use upstream_registry_types::PROOF_STATUS_UPSTREAM_GIT_LOCAL_CORPUS_OBSERVED;
/// Generic upstream Git proof status for decoder-mapped unverified entries.
pub use upstream_registry_types::PROOF_STATUS_UPSTREAM_GIT_MAPPED_UNVERIFIED;
/// Generic upstream Git proof status for unverified entries.
pub use upstream_registry_types::PROOF_STATUS_UPSTREAM_GIT_UNVERIFIED;
/// Generic event kind used for instruction-level upstream registry matches.
pub use upstream_registry_types::UPSTREAM_REGISTRY_INSTRUCTION_MATCH_EVENT_KIND;
/// Generic protocol name used for upstream registry matches that are not business-materialized.
pub use upstream_registry_types::UPSTREAM_REGISTRY_PROTOCOL_NAME;
/// Static upstream registry entry.
pub use upstream_registry_types::UpstreamRegistryEntry;
/// Owned upstream registry entry DTO.
pub use upstream_registry_types::UpstreamRegistryEntryDto;
/// Upstream registry search request DTO.
pub use upstream_registry_types::UpstreamRegistrySearchRequestDto;
/// Upstream registry search result DTO.
pub use upstream_registry_types::UpstreamRegistrySearchResultDto;
/// Upstream registry summary DTO.
pub use upstream_registry_types::UpstreamRegistrySummaryDto;
/// One wallet-holding observation result. /// One wallet-holding observation result.
pub use wallet_holding_observation::WalletHoldingObservationResult; pub use wallet_holding_observation::WalletHoldingObservationResult;
/// Wallet-holding observation service. /// Wallet-holding observation service.

View File

@@ -658,7 +658,7 @@ fn build_success_dex_decode_replay_ledger(
fn count_effective_decoded_events(decoded_events: &[crate::DexDecodedEventDto]) -> usize { fn count_effective_decoded_events(decoded_events: &[crate::DexDecodedEventDto]) -> usize {
let mut count = 0_usize; let mut count = 0_usize;
for event in decoded_events { for event in decoded_events {
if is_instruction_audit_event(event) { if is_replay_audit_only_event(event) {
continue; continue;
} }
count += 1; count += 1;
@@ -666,8 +666,17 @@ fn count_effective_decoded_events(decoded_events: &[crate::DexDecodedEventDto])
return count; return count;
} }
fn is_instruction_audit_event(event: &crate::DexDecodedEventDto) -> bool { fn is_replay_audit_only_event(event: &crate::DexDecodedEventDto) -> bool {
return event.event_kind.ends_with(".instruction_audit"); if event.event_kind.ends_with(".instruction_audit") {
return true;
}
if event.event_kind.ends_with("_audit") {
return true;
}
if event.event_kind == crate::UPSTREAM_REGISTRY_INSTRUCTION_MATCH_EVENT_KIND {
return true;
}
return false;
} }
fn count_distinct_decoded_event_token_mints(decoded_events: &[crate::DexDecodedEventDto]) -> usize { fn count_distinct_decoded_event_token_mints(decoded_events: &[crate::DexDecodedEventDto]) -> usize {
@@ -759,6 +768,20 @@ mod tests {
assert!(ledger.can_skip_decode()); assert!(ledger.can_skip_decode());
} }
#[test]
fn ledger_treats_audit_suffix_events_as_audit_only() {
let events = vec![
make_decoded_event("openbook_v2.settle_funds_audit", Some("mint-a"), Some("mint-b")),
make_decoded_event("openbook_v2.order_place_audit", None, None),
];
let ledger = super::build_success_dex_decode_replay_ledger(1, "sig", events.as_slice())
.expect("ledger must build");
assert_eq!(ledger.event_count, 2);
assert_eq!(ledger.status_reason.as_deref(), Some("decode completed and certified for skip: event_count=2, effective_event_count=0, instruction_audit_count=2, distinct_token_mint_count=2"));
assert!(!ledger.force_replay_required);
assert!(ledger.can_skip_decode());
}
#[test] #[test]
fn ledger_keeps_multiple_effective_events_unsafe() { fn ledger_keeps_multiple_effective_events_unsafe() {
let events = vec![ let events = vec![

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,58 @@
// file: kb_lib/src/upstream_registry.rs
//! Public service facade for the upstream Git registry.
//!
//! The registry is intentionally read-only in this tranche and does not touch
//! SQLite. It only exposes discovery seeds and static upstream-derived metadata
//! for UI inspection and later corpus validation.
/// Read-only upstream registry service.
#[derive(Clone, Debug, Default)]
pub struct UpstreamRegistryService;
impl UpstreamRegistryService {
/// Creates a new read-only upstream registry service.
pub fn new() -> crate::UpstreamRegistryService {
return crate::UpstreamRegistryService;
}
/// Returns every static registry entry.
pub fn all_entries(&self) -> std::vec::Vec<crate::UpstreamRegistryEntryDto> {
return crate::upstream_registry_match::upstream_registry_all_entries();
}
/// Searches the static registry with optional filters.
pub fn search(
&self,
request: &crate::UpstreamRegistrySearchRequestDto,
) -> crate::UpstreamRegistrySearchResultDto {
return crate::upstream_registry_match::upstream_registry_search(request);
}
}
#[cfg(test)]
mod tests {
#[test]
fn service_returns_static_registry_entries() {
let service = crate::UpstreamRegistryService::new();
let entries = service.all_entries();
assert!(!entries.is_empty());
}
#[test]
fn service_search_preserves_normalized_request() {
let service = crate::UpstreamRegistryService::new();
let request = crate::UpstreamRegistrySearchRequestDto {
decoder_code: Some("raydium-cpmm".to_string()),
program_id: None,
program_family: None,
surface_kind: None,
entry_kind: None,
proof_status: None,
limit: Some(10),
};
let result = service.search(&request);
assert_eq!(result.request, request);
assert!(!result.entries.is_empty());
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,651 @@
// file: kb_lib/src/upstream_registry_match.rs
//! Matching, filtering and summarizing helpers for the upstream Git registry.
/// Returns all static upstream registry entries as owned DTOs.
pub(crate) fn upstream_registry_all_entries() -> std::vec::Vec<crate::UpstreamRegistryEntryDto> {
let mut entries = std::vec::Vec::new();
for entry in crate::upstream_registry_generated::UPSTREAM_REGISTRY_ENTRIES {
entries.push(entry.to_dto());
}
return entries;
}
/// Searches static upstream registry entries with optional filters.
pub(crate) fn upstream_registry_search(
request: &crate::UpstreamRegistrySearchRequestDto,
) -> crate::UpstreamRegistrySearchResultDto {
let total_entry_count = crate::upstream_registry_generated::UPSTREAM_REGISTRY_ENTRIES.len();
let mut entries = std::vec::Vec::new();
for entry in crate::upstream_registry_generated::UPSTREAM_REGISTRY_ENTRIES {
if !matches_request(entry, request) {
continue;
}
entries.push(entry.to_dto());
if let Some(limit) = request.limit {
if entries.len() >= limit {
break;
}
}
}
let summary = summarize(total_entry_count, entries.as_slice());
return crate::UpstreamRegistrySearchResultDto {
request: request.clone(),
summary,
entries,
};
}
fn matches_request(
entry: &crate::UpstreamRegistryEntry,
request: &crate::UpstreamRegistrySearchRequestDto,
) -> bool {
if !matches_string_filter(entry.decoder_code, &request.decoder_code) {
return false;
}
if !matches_optional_string_filter(entry.program_id, &request.program_id) {
return false;
}
if !matches_string_filter(entry.program_family, &request.program_family) {
return false;
}
if !matches_string_filter(entry.surface_kind, &request.surface_kind) {
return false;
}
if !matches_string_filter(entry.entry_kind, &request.entry_kind) {
return false;
}
if !matches_string_filter(entry.proof_status, &request.proof_status) {
return false;
}
return true;
}
fn matches_string_filter(value: &str, filter: &std::option::Option<std::string::String>) -> bool {
let filter_value = match filter {
Some(filter_value) => filter_value.trim(),
None => return true,
};
if filter_value.is_empty() {
return true;
}
return value.eq_ignore_ascii_case(filter_value);
}
fn matches_optional_string_filter(
value: std::option::Option<&str>,
filter: &std::option::Option<std::string::String>,
) -> bool {
let filter_value = match filter {
Some(filter_value) => filter_value.trim(),
None => return true,
};
if filter_value.is_empty() {
return true;
}
let value = match value {
Some(value) => value,
None => return false,
};
return value.eq_ignore_ascii_case(filter_value);
}
fn summarize(
total_entry_count: usize,
entries: &[crate::UpstreamRegistryEntryDto],
) -> crate::UpstreamRegistrySummaryDto {
let mut entries_with_program_id_count = 0_usize;
let mut entries_with_discriminator_count = 0_usize;
let mut program_entry_count = 0_usize;
let mut instruction_entry_count = 0_usize;
let mut event_entry_count = 0_usize;
let mut account_entry_count = 0_usize;
let mut upstream_git_unverified_count = 0_usize;
let mut upstream_git_mapped_unverified_count = 0_usize;
let mut upstream_git_local_corpus_observed_count = 0_usize;
let mut upstream_git_local_corpus_materialized_count = 0_usize;
let mut upstream_git_layout_unverified_count = 0_usize;
for entry in entries {
if entry.program_id.is_some() {
entries_with_program_id_count += 1;
}
if entry.discriminator_hex.is_some() {
entries_with_discriminator_count += 1;
}
match entry.entry_kind.as_str() {
crate::ENTRY_KIND_PROGRAM => program_entry_count += 1,
crate::ENTRY_KIND_INSTRUCTION => instruction_entry_count += 1,
crate::ENTRY_KIND_EVENT => event_entry_count += 1,
crate::ENTRY_KIND_ACCOUNT => account_entry_count += 1,
_ => (),
}
match entry.proof_status.as_str() {
crate::PROOF_STATUS_UPSTREAM_GIT_UNVERIFIED => upstream_git_unverified_count += 1,
crate::PROOF_STATUS_UPSTREAM_GIT_MAPPED_UNVERIFIED => {
upstream_git_mapped_unverified_count += 1;
},
crate::PROOF_STATUS_UPSTREAM_GIT_LOCAL_CORPUS_OBSERVED => {
upstream_git_local_corpus_observed_count += 1;
},
crate::PROOF_STATUS_UPSTREAM_GIT_LOCAL_CORPUS_MATERIALIZED => {
upstream_git_local_corpus_materialized_count += 1;
},
crate::PROOF_STATUS_UPSTREAM_GIT_LAYOUT_UNVERIFIED => {
upstream_git_layout_unverified_count += 1;
},
_ => (),
}
}
return crate::UpstreamRegistrySummaryDto {
total_entry_count,
returned_entry_count: entries.len(),
entries_with_program_id_count,
entries_with_discriminator_count,
program_entry_count,
instruction_entry_count,
event_entry_count,
account_entry_count,
upstream_git_unverified_count,
upstream_git_mapped_unverified_count,
upstream_git_local_corpus_observed_count,
upstream_git_local_corpus_materialized_count,
upstream_git_layout_unverified_count,
};
}
/// Matches raw base58 instruction data against upstream registry discriminator entries.
pub(crate) fn upstream_registry_match_instruction_data(
program_id: &str,
data_base58: std::option::Option<&str>,
) -> std::option::Option<crate::UpstreamRegistryEntryDto> {
let data = decode_base58_instruction_data(data_base58);
let data = match data {
Some(data) => data,
None => return None,
};
let mut selected_entry: std::option::Option<&crate::UpstreamRegistryEntry> = None;
let mut selected_len = 0_usize;
for entry in crate::upstream_registry_generated::UPSTREAM_REGISTRY_ENTRIES {
if entry.entry_kind != crate::ENTRY_KIND_INSTRUCTION {
continue;
}
let entry_program_id = match entry.program_id {
Some(entry_program_id) => entry_program_id,
None => continue,
};
if entry_program_id != program_id {
continue;
}
let discriminator_hex = match entry.discriminator_hex {
Some(discriminator_hex) => discriminator_hex,
None => continue,
};
let discriminator_len = match discriminator_len_usize(entry.discriminator_len) {
Some(discriminator_len) => discriminator_len,
None => continue,
};
if !data_prefix_matches_discriminator(data.as_slice(), discriminator_len, discriminator_hex)
{
continue;
}
if discriminator_len > selected_len {
selected_entry = Some(entry);
selected_len = discriminator_len;
}
}
let selected_entry = match selected_entry {
Some(selected_entry) => selected_entry,
None => return None,
};
return Some(selected_entry.to_dto());
}
fn discriminator_len_usize(
discriminator_len: std::option::Option<u16>,
) -> std::option::Option<usize> {
let discriminator_len = match discriminator_len {
Some(discriminator_len) => discriminator_len,
None => return None,
};
if discriminator_len == 0 {
return None;
}
return Some(usize::from(discriminator_len));
}
fn decode_base58_instruction_data(
data_base58: std::option::Option<&str>,
) -> std::option::Option<std::vec::Vec<u8>> {
let data_base58 = match data_base58 {
Some(data_base58) => data_base58.trim(),
None => return None,
};
if data_base58.is_empty() {
return None;
}
let decoded_result = bs58::decode(data_base58).into_vec();
match decoded_result {
Ok(decoded) => return Some(decoded),
Err(_) => return None,
}
}
fn data_prefix_matches_discriminator(
data: &[u8],
discriminator_len: usize,
discriminator_hex: &str,
) -> bool {
if data.len() < discriminator_len {
return false;
}
let data_prefix_hex = bytes_prefix_to_hex(data, discriminator_len);
return data_prefix_hex == discriminator_hex;
}
fn bytes_prefix_to_hex(data: &[u8], len: usize) -> std::string::String {
let mut text = std::string::String::new();
let mut index = 0_usize;
while index < len {
let byte = data[index];
text.push_str(format!("{byte:02x}").as_str());
index += 1;
}
return text;
}
#[cfg(test)]
mod tests {
#[test]
fn registry_contains_openbook_v2_idl_instruction_discriminators_without_local_verification() {
let expected = [
("create_market", "67e261ebc8bcfbfe"),
("close_market", "589af8ba300e7bf4"),
("create_open_orders_account", "ccb5afde287dbc47"),
("close_open_orders_account", "b04a73d236b35b67"),
("place_order", "33c29baf6d82606a"),
("place_take_order", "032c47031ac7cb55"),
("consume_events", "dd91b1341f2f3fc9"),
("consume_given_events", "d1e336046dac2947"),
("cancel_order", "5f81edf00831df84"),
("cancel_order_by_client_order_id", "73b2c908afb77b77"),
("cancel_all_orders", "c453f3ab1164a08f"),
("deposit", "f223c68952e1f2b6"),
("settle_funds", "ee40a3604bab1021"),
("sweep_fees", "afe1624776422294"),
];
let all_entries = crate::upstream_registry_match::upstream_registry_all_entries();
for (entry_name, discriminator_hex) in expected {
let mut found = false;
for entry in all_entries.as_slice() {
if entry.decoder_code == "openbook-v2"
&& entry.program_id.as_deref() == Some(crate::OPENBOOK_V2_PROGRAM_ID)
&& entry.entry_kind == crate::ENTRY_KIND_INSTRUCTION
&& entry.entry_name == entry_name
&& entry.discriminator_hex.as_deref() == Some(discriminator_hex)
&& entry.discriminator_len == Some(8)
&& entry.proof_status == crate::PROOF_STATUS_UPSTREAM_GIT_UNVERIFIED
{
found = true;
break;
}
}
assert!(
found,
"missing OpenBook v2 discriminator entry '{}', '{}'",
entry_name, discriminator_hex
);
}
}
#[test]
fn openbook_v2_place_take_order_discriminator_matches_raw_data() {
let data =
bs58::encode([3_u8, 44_u8, 71_u8, 3_u8, 26_u8, 199_u8, 203_u8, 85_u8]).into_string();
let matched = crate::upstream_registry_match::upstream_registry_match_instruction_data(
crate::OPENBOOK_V2_PROGRAM_ID,
Some(data.as_str()),
);
let matched = match matched {
Some(matched) => matched,
None => panic!("OpenBook v2 place_take_order discriminator must match"),
};
assert_eq!(matched.decoder_code, "openbook-v2".to_string());
assert_eq!(matched.entry_name, "place_take_order".to_string());
assert_eq!(matched.discriminator_hex, Some("032c47031ac7cb55".to_string()));
}
#[test]
fn registry_contains_priority_family_program_seeds() {
let expected_codes = [
"meteora-damm-v2",
"meteora-dbc",
"meteora-dlmm",
"meteora-vault",
"raydium-amm-v4",
"raydium-clmm",
"raydium-cpmm",
"raydium-launchpad",
"raydium-liquidity-locking",
"raydium-stable-swap",
"orca-whirlpool",
"fluxbeam",
"lifinity-amm-v2",
"phoenix-v1",
"openbook-v2",
"stabble-stable-swap",
"stabble-weighted-swap",
"bonkswap",
"boop",
"moonshot",
"heaven",
"okx-dex",
"pancake-swap",
"vertigo",
"virtuals",
"wavebreak",
"onchain-labs-dex-v1",
"onchain-labs-dex-v2",
"jupiter-swap",
"jupiter-dca",
"jupiter-limit-order",
"jupiter-limit-order-2",
"jupiter-perpetuals",
"jupiter-lend",
"kamino-lending",
"kamino-vault",
"kamino-farms",
"kamino-limit-order",
"drift-v2",
"marginfi-v2",
"dflow-aggregator-v4",
"zeta",
"system-program",
"token-program",
"token-2022",
"associated-token-account",
"address-lookup-table",
"memo-program",
"stake-program",
"mpl-token-metadata",
"mpl-core",
"bubblegum",
"name-service",
"marinade-finance",
"solayer-restaking-program",
"swig",
"sharky",
"circle-message-transmitter-v2",
"circle-token-messenger-v2",
];
let all_entries = crate::upstream_registry_match::upstream_registry_all_entries();
for expected_code in expected_codes {
let mut found = false;
for entry in all_entries.as_slice() {
if entry.decoder_code == expected_code
&& entry.entry_kind == crate::ENTRY_KIND_PROGRAM
{
found = true;
break;
}
}
assert!(found, "missing upstream registry code '{}'", expected_code);
}
}
#[test]
fn registry_has_no_duplicate_entry_keys() {
let all_entries = crate::upstream_registry_match::upstream_registry_all_entries();
let mut seen = std::collections::BTreeSet::new();
for entry in all_entries.as_slice() {
let key = (
entry.decoder_code.as_str(),
entry.program_id.as_deref(),
entry.entry_kind.as_str(),
entry.entry_name.as_str(),
entry.discriminator_hex.as_deref(),
);
assert!(
seen.insert(key),
"duplicate upstream registry entry: decoder={} program_id={:?} kind={} name={} discriminator={:?}",
entry.decoder_code,
entry.program_id,
entry.entry_kind,
entry.entry_name,
entry.discriminator_hex
);
}
}
#[test]
fn registry_has_no_duplicate_program_entry_for_same_decoder_and_program_id() {
let all_entries = crate::upstream_registry_match::upstream_registry_all_entries();
let mut seen = std::collections::BTreeSet::new();
for entry in all_entries.as_slice() {
if entry.entry_kind != crate::ENTRY_KIND_PROGRAM {
continue;
}
let key = (entry.decoder_code.as_str(), entry.program_id.as_deref());
assert!(
seen.insert(key),
"duplicate upstream registry program entry: decoder={} program_id={:?}",
entry.decoder_code,
entry.program_id
);
}
}
#[test]
fn registry_has_no_duplicate_program_id_for_program_rows() {
let all_entries = crate::upstream_registry_match::upstream_registry_all_entries();
let mut seen = std::collections::BTreeMap::<&str, &str>::new();
for entry in all_entries.as_slice() {
if entry.entry_kind != crate::ENTRY_KIND_PROGRAM {
continue;
}
let program_id = match entry.program_id.as_deref() {
Some(program_id) => program_id,
None => continue,
};
match seen.insert(program_id, entry.decoder_code.as_str()) {
Some(previous_decoder) => panic!(
"duplicate upstream registry program_id {} for {} and {}",
program_id, previous_decoder, entry.decoder_code
),
None => (),
}
}
}
#[test]
fn registry_has_no_duplicate_program_discriminator_keys() {
let all_entries = crate::upstream_registry_match::upstream_registry_all_entries();
let mut seen = std::collections::BTreeSet::new();
for entry in all_entries.as_slice() {
if entry.entry_kind == crate::ENTRY_KIND_PROGRAM {
continue;
}
let program_id = match entry.program_id.as_deref() {
Some(program_id) => program_id,
None => continue,
};
let discriminator_hex = match entry.discriminator_hex.as_deref() {
Some(discriminator_hex) => discriminator_hex,
None => continue,
};
let key = (
program_id,
entry.entry_kind.as_str(),
entry.entry_name.as_str(),
discriminator_hex,
);
assert!(
seen.insert(key),
"duplicate upstream registry discriminator key: program_id={} kind={} name={} discriminator={}",
program_id,
entry.entry_kind,
entry.entry_name,
discriminator_hex
);
}
}
#[test]
fn registry_does_not_claim_local_corpus_verification_in_bootstrap_tranche() {
let all_entries = crate::upstream_registry_match::upstream_registry_all_entries();
for entry in all_entries.as_slice() {
assert_eq!(entry.proof_status, crate::PROOF_STATUS_UPSTREAM_GIT_UNVERIFIED);
assert_ne!(entry.proof_status, crate::PROOF_STATUS_UPSTREAM_GIT_LOCAL_CORPUS_OBSERVED);
assert_ne!(
entry.proof_status,
crate::PROOF_STATUS_UPSTREAM_GIT_LOCAL_CORPUS_MATERIALIZED
);
}
}
#[test]
fn registry_contains_all_meteora_damm_v2_instruction_discriminators_without_local_verification()
{
let all_entries = crate::upstream_registry_match::upstream_registry_all_entries();
let expected_entries = [
("add_liquidity", "b59d59438fb63448"),
("claim_partner_fee", "61ce27695e5e7e94"),
("claim_position_fee", "b4269a118521a2d3"),
("claim_protocol_fee", "a5e4853063f9ff21"),
("claim_reward", "955fb5f25e5a9ea2"),
("cpi_event", "bcd8a66c1aa68eb6"),
("initialize_pool", "5fb40aac54aee828"),
("remove_liquidity", "5055d14818ceb16c"),
("swap", "f8c69e91e17587c8"),
("swap2", "414b3f4ceb5b5b88"),
("update_pool_fees", "76d9cbb33c084659"),
("withdraw_ineligible_reward", "94ce2ac3f7316708"),
];
for expected_entry in expected_entries {
let mut found = false;
for entry in all_entries.as_slice() {
if entry.decoder_code == "meteora-damm-v2"
&& entry.entry_kind == crate::ENTRY_KIND_INSTRUCTION
&& entry.entry_name == expected_entry.0
&& entry.discriminator_hex.as_deref() == Some(expected_entry.1)
&& entry.discriminator_len == Some(8)
&& entry.proof_status == crate::PROOF_STATUS_UPSTREAM_GIT_UNVERIFIED
{
found = true;
break;
}
}
assert!(
found,
"missing upstream Git discriminator entry '{}', '{}'",
expected_entry.0, expected_entry.1
);
}
}
#[test]
fn registry_contains_meteora_damm_v2_event_discriminators_without_local_verification() {
let all_entries = crate::upstream_registry_match::upstream_registry_all_entries();
let expected_entries =
[("evt_liquidity_change", "c5ab4e7fe0d3570d"), ("evt_swap2", "bd4233a826507599")];
for expected_entry in expected_entries {
let mut found = false;
for entry in all_entries.as_slice() {
if entry.decoder_code == "meteora-damm-v2"
&& entry.entry_kind == crate::ENTRY_KIND_EVENT
&& entry.entry_name == expected_entry.0
&& entry.discriminator_hex.as_deref() == Some(expected_entry.1)
&& entry.discriminator_len == Some(8)
&& entry.proof_status == crate::PROOF_STATUS_UPSTREAM_GIT_UNVERIFIED
{
found = true;
break;
}
}
assert!(found, "missing upstream Git event entry '{}'", expected_entry.0);
}
}
#[test]
fn matches_anchor_instruction_data_by_program_id_and_discriminator() {
let data =
[0xb5_u8, 0x9d_u8, 0x59_u8, 0x43_u8, 0x8f_u8, 0xb6_u8, 0x34_u8, 0x48_u8, 0x01_u8];
let data_base58 = bs58::encode(data).into_string();
let matched = crate::upstream_registry_match::upstream_registry_match_instruction_data(
crate::METEORA_DAMM_V2_PROGRAM_ID,
Some(data_base58.as_str()),
);
let matched = match matched {
Some(matched) => matched,
None => panic!("missing meteora-damm-v2 add_liquidity registry match"),
};
assert_eq!(matched.decoder_code, "meteora-damm-v2");
assert_eq!(matched.entry_name, "add_liquidity");
assert_eq!(matched.discriminator_hex.as_deref(), Some("b59d59438fb63448"));
assert_eq!(matched.proof_status, crate::PROOF_STATUS_UPSTREAM_GIT_UNVERIFIED);
}
#[test]
fn matches_one_byte_instruction_data_by_program_id_and_discriminator() {
let data = [0x00_u8, 0x10_u8, 0x20_u8];
let data_base58 = bs58::encode(data).into_string();
let matched = crate::upstream_registry_match::upstream_registry_match_instruction_data(
crate::PHOENIX_V1_PROGRAM_ID,
Some(data_base58.as_str()),
);
let matched = match matched {
Some(matched) => matched,
None => panic!("missing phoenix-v1 swap registry match"),
};
assert_eq!(matched.decoder_code, "phoenix-v1");
assert_eq!(matched.entry_name, "swap");
assert_eq!(matched.discriminator_hex.as_deref(), Some("00"));
assert_eq!(matched.discriminator_len, Some(1));
}
#[test]
fn registry_can_filter_by_program_id_and_family() {
let request = crate::UpstreamRegistrySearchRequestDto {
decoder_code: None,
program_id: Some(crate::RAYDIUM_CPMM_PROGRAM_ID.to_string()),
program_family: Some("raydium".to_string()),
surface_kind: None,
entry_kind: None,
proof_status: None,
limit: None,
};
let result = crate::upstream_registry_match::upstream_registry_search(&request);
assert!(result.entries.len() >= 2);
for entry in result.entries.as_slice() {
assert_eq!(entry.decoder_code, "raydium-cpmm");
}
}
#[test]
fn registry_uses_generic_upstream_git_status_names_only() {
let deprecated_external_repo_prefix = format!("{}{}", "car", "bon");
let forbidden_terms = [deprecated_external_repo_prefix];
let all_entries = crate::upstream_registry_match::upstream_registry_all_entries();
for entry in all_entries.as_slice() {
let payload = format!(
"{} {} {} {} {}",
entry.decoder_code,
entry.program_family,
entry.surface_kind,
entry.proof_status,
entry.notes
);
for forbidden_term in forbidden_terms.as_slice() {
assert!(
!payload.to_ascii_lowercase().contains(forbidden_term.as_str()),
"forbidden registry term found: {}",
forbidden_term
);
}
}
}
}

View File

@@ -0,0 +1,177 @@
// file: kb_lib/src/upstream_registry_types.rs
//! Shared DTOs and static-entry types for the upstream Git registry.
//!
//! The registry is a discovery index. It must not be interpreted as local
//! business proof that a DEX, instruction, event, trade, metric or candle has
//! been validated against the current SQLite corpus.
/// Registry entry kind for an instruction discriminator.
pub const ENTRY_KIND_INSTRUCTION: &str = "instruction";
/// Registry entry kind for an emitted event discriminator.
pub const ENTRY_KIND_EVENT: &str = "event";
/// Registry entry kind for an account discriminator or account layout.
pub const ENTRY_KIND_ACCOUNT: &str = "account";
/// Registry entry kind for a program-level seed row.
pub const ENTRY_KIND_PROGRAM: &str = "program";
/// Generic protocol name used for upstream registry matches that are not business-materialized.
pub const UPSTREAM_REGISTRY_PROTOCOL_NAME: &str = "upstream_git";
/// Generic event kind used for instruction-level upstream registry matches.
pub const UPSTREAM_REGISTRY_INSTRUCTION_MATCH_EVENT_KIND: &str = "upstream_git.instruction_match";
/// Generic upstream Git status for an entry that has not been observed locally.
pub const PROOF_STATUS_UPSTREAM_GIT_UNVERIFIED: &str = "upstream_git_unverified";
/// Generic upstream Git status for an entry wired into a decoder but not observed locally.
pub const PROOF_STATUS_UPSTREAM_GIT_MAPPED_UNVERIFIED: &str = "upstream_git_mapped_unverified";
/// Generic upstream Git status for an entry observed in the local corpus.
pub const PROOF_STATUS_UPSTREAM_GIT_LOCAL_CORPUS_OBSERVED: &str =
"upstream_git_local_corpus_observed";
/// Generic upstream Git status for an entry materialized in local business tables.
pub const PROOF_STATUS_UPSTREAM_GIT_LOCAL_CORPUS_MATERIALIZED: &str =
"upstream_git_local_corpus_materialized";
/// Generic upstream Git status for a known layout that is not yet locally validated.
pub const PROOF_STATUS_UPSTREAM_GIT_LAYOUT_UNVERIFIED: &str = "upstream_git_layout_unverified";
/// Static registry entry used by generated or hand-curated bootstrap data.
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct UpstreamRegistryEntry {
/// Repository name or bootstrap locator that produced the entry.
pub source_repo: std::option::Option<&'static str>,
/// Repository-relative path or bootstrap path that produced the entry.
pub source_path: std::option::Option<&'static str>,
/// Stable decoder code used by the registry.
pub decoder_code: &'static str,
/// Optional Solana program id when already known by the source entry.
pub program_id: std::option::Option<&'static str>,
/// Program family used to group related programs.
pub program_family: &'static str,
/// Surface kind such as AMM, CLMM, launch, aggregator or core Solana.
pub surface_kind: &'static str,
/// Entry kind: instruction, event, account or program.
pub entry_kind: &'static str,
/// Source-level entry name.
pub entry_name: &'static str,
/// Optional discriminator bytes encoded as lowercase hexadecimal.
pub discriminator_hex: std::option::Option<&'static str>,
/// Optional discriminator byte length.
pub discriminator_len: std::option::Option<u16>,
/// Current proof status.
pub proof_status: &'static str,
/// Notes that preserve uncertainty and validation requirements.
pub notes: &'static str,
}
impl UpstreamRegistryEntry {
/// Converts a static entry into an owned DTO for application/UI boundaries.
pub fn to_dto(&self) -> crate::UpstreamRegistryEntryDto {
return crate::UpstreamRegistryEntryDto {
source_repo: self.source_repo.map(std::string::ToString::to_string),
source_path: self.source_path.map(std::string::ToString::to_string),
decoder_code: self.decoder_code.to_string(),
program_id: self.program_id.map(std::string::ToString::to_string),
program_family: self.program_family.to_string(),
surface_kind: self.surface_kind.to_string(),
entry_kind: self.entry_kind.to_string(),
entry_name: self.entry_name.to_string(),
discriminator_hex: self.discriminator_hex.map(std::string::ToString::to_string),
discriminator_len: self.discriminator_len,
proof_status: self.proof_status.to_string(),
notes: self.notes.to_string(),
};
}
}
/// Owned registry entry DTO exposed at crate and UI boundaries.
#[derive(Clone, Debug, Eq, PartialEq, serde::Serialize)]
#[serde(rename_all = "camelCase")]
pub struct UpstreamRegistryEntryDto {
/// Repository name or bootstrap locator that produced the entry.
pub source_repo: std::option::Option<std::string::String>,
/// Repository-relative path or bootstrap path that produced the entry.
pub source_path: std::option::Option<std::string::String>,
/// Stable decoder code used by the registry.
pub decoder_code: std::string::String,
/// Optional Solana program id when already known by the source entry.
pub program_id: std::option::Option<std::string::String>,
/// Program family used to group related programs.
pub program_family: std::string::String,
/// Surface kind such as AMM, CLMM, launch, aggregator or core Solana.
pub surface_kind: std::string::String,
/// Entry kind: instruction, event, account or program.
pub entry_kind: std::string::String,
/// Source-level entry name.
pub entry_name: std::string::String,
/// Optional discriminator bytes encoded as lowercase hexadecimal.
pub discriminator_hex: std::option::Option<std::string::String>,
/// Optional discriminator byte length.
pub discriminator_len: std::option::Option<u16>,
/// Current proof status.
pub proof_status: std::string::String,
/// Notes that preserve uncertainty and validation requirements.
pub notes: std::string::String,
}
/// Search/filter request for registry entries.
#[derive(Clone, Debug, Default, Eq, PartialEq, serde::Serialize, serde::Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct UpstreamRegistrySearchRequestDto {
/// Optional decoder-code filter.
pub decoder_code: std::option::Option<std::string::String>,
/// Optional program-id filter.
pub program_id: std::option::Option<std::string::String>,
/// Optional program-family filter.
pub program_family: std::option::Option<std::string::String>,
/// Optional surface-kind filter.
pub surface_kind: std::option::Option<std::string::String>,
/// Optional entry-kind filter.
pub entry_kind: std::option::Option<std::string::String>,
/// Optional proof-status filter.
pub proof_status: std::option::Option<std::string::String>,
/// Optional maximum number of returned entries.
pub limit: std::option::Option<usize>,
}
/// Summary of the current registry snapshot.
#[derive(Clone, Debug, Eq, PartialEq, serde::Serialize)]
#[serde(rename_all = "camelCase")]
pub struct UpstreamRegistrySummaryDto {
/// Total static registry entry count before filtering.
pub total_entry_count: usize,
/// Returned entry count after filtering.
pub returned_entry_count: usize,
/// Number of entries that have a program id.
pub entries_with_program_id_count: usize,
/// Number of entries that have a discriminator.
pub entries_with_discriminator_count: usize,
/// Number of program-level seed entries.
pub program_entry_count: usize,
/// Number of instruction entries.
pub instruction_entry_count: usize,
/// Number of event entries.
pub event_entry_count: usize,
/// Number of account entries.
pub account_entry_count: usize,
/// Number of entries still unverified from upstream Git or seed data.
pub upstream_git_unverified_count: usize,
/// Number of entries mapped into decoders but not locally observed.
pub upstream_git_mapped_unverified_count: usize,
/// Number of entries observed in the local corpus.
pub upstream_git_local_corpus_observed_count: usize,
/// Number of entries materialized in local business tables.
pub upstream_git_local_corpus_materialized_count: usize,
/// Number of layout entries still unverified locally.
pub upstream_git_layout_unverified_count: usize,
}
/// Search result returned by the upstream registry service.
#[derive(Clone, Debug, Eq, PartialEq, serde::Serialize)]
#[serde(rename_all = "camelCase")]
pub struct UpstreamRegistrySearchResultDto {
/// Normalized request used for the search.
pub request: crate::UpstreamRegistrySearchRequestDto,
/// Registry summary.
pub summary: crate::UpstreamRegistrySummaryDto,
/// Matching entries.
pub entries: std::vec::Vec<crate::UpstreamRegistryEntryDto>,
}