This commit is contained in:
2026-05-30 01:14:30 +02:00
parent ffa4acbccb
commit 7bd6593015
20 changed files with 4359 additions and 456 deletions

View File

@@ -75,4 +75,8 @@
0.7.42 - Consolidation famille Raydium : audit conservatoire des instructions Raydium non décodées, décodage CLMM legacy `swap`, cleanup des audits remplacés, classification HTTP `getTransaction` comme requête lourde avec retry/backoff de backfill, mapping des événements non-swap prouvés `raydium_clmm` (`increase_liquidity_v2`, `decrease_liquidity_v2`, `open_position_with_token22_nft`, `close_position`) et `raydium_cpmm` (`initialize`, `withdraw`, `collect_creator_fee`), matérialisation de 25 liquidity events, 1 lifecycle event et 2 fee events sur corpus élargi, conservation des non-swaps AMM v4 legacy en audit.
0.7.43-E5C - Reprise documentaire et normalisation DEX-first : `0.7.43` est conservé comme point de reprise non clos pour le lot Meteora, la suite est redécoupée par DEX/version séparés, le besoin dun ledger de décodage/replay est acté, les statuts `known` / `observed` / `decoded` / `materialized` / `verified_by_corpus` deviennent obligatoires, et aucun `program_id` ne doit être marqué vérifié sans preuve/corpus reproductible.
0.7.44 - Ledger de décodage/replay DEX : ajout de `k_sol_dex_decode_replay_ledger`, des DTO/entities/queries associés, des re-exports DB/lib, et intégration dans le replay local pour skipper uniquement létape de décodage DEX lorsquun passage certifié existe pour la même version logique de decoder. Les transactions multi-event ou multi-token restent marquées `unsafe` et sont redécodées sauf option future plus explicite ; le replay continue de reconstruire détection, matérialisation, trades, candles et classifications à partir des events persistés.
0.7.45 - Meteora DLMM normalisation finale : consolidation séparée de `meteora_dlmm` sur corpus dédié, maintien du wrapper Anchor `anchor_self_cpi_log` `e445a52e51cb9a1d`, enrichissement des swaps via `Swap` / `Swap2Evt`, cleanup des audits Anchor CPI swap déjà couverts, ajout des events Carbon/IDL observés et vérifiés par corpus (`lb_pair_create_event`, `add_liquidity_event`, `remove_liquidity_event`, `claim_fee_event`, `position_create_event`, `position_close_event`, `close_position_if_empty`, `remove_liquidity_by_range2`, `add_liquidity_by_strategy2`, `add_liquidity_by_weight`), conservation des deux audits résiduels `e8abf2613a4d232d` en `instruction_audit` faute de mapping Carbon/IDL confirmé, matérialisation locale validée avec `15` liquidity events et `6` lifecycle events sur le corpus DLMM élargi, et version logique replay `dex_decode.v0.7.45.dlmm_add_liquidity_strategies1`. Aucun nouveau `program_id` nest déclaré vérifié sans preuve/corpus reproductible.
0.7.45 - Meteora DLMM normalisation finale : consolidation séparée de `meteora_dlmm` sur corpus dédié, maintien du wrapper Anchor `anchor_self_cpi_log` `e445a52e51cb9a1d`, enrichissement des swaps via `Swap` / `Swap2Evt`, cleanup des audits Anchor CPI swap déjà couverts, ajout des events upstream Git/IDL observés et vérifiés par corpus (`lb_pair_create_event`, `add_liquidity_event`, `remove_liquidity_event`, `claim_fee_event`, `position_create_event`, `position_close_event`, `close_position_if_empty`, `remove_liquidity_by_range2`, `add_liquidity_by_strategy2`, `add_liquidity_by_weight`), conservation des deux audits résiduels `e8abf2613a4d232d` en `instruction_audit` faute de mapping upstream Git/IDL confirmé, matérialisation locale validée avec `15` liquidity events et `6` lifecycle events sur le corpus DLMM élargi, et version logique replay `dex_decode.v0.7.45.dlmm_add_liquidity_strategies1`. Aucun nouveau `program_id` nest déclaré vérifié sans preuve/corpus reproductible.
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-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.

View File

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

View File

@@ -4,7 +4,7 @@
`khadhroony-bobobot` est un workspace Rust destiné à la détection, au décodage, à lanalyse et, à terme, au trading semi-automatisé de tokens Solana.
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.45` côté `kb_lib`. 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.
## 1. Objectif
@@ -91,7 +91,7 @@ Les surfaces suivantes existent dans le code, dans la matrice ou dans le corpus
| `raydium_clmm` | DEX effectif consolidé partiellement | Swaps v2/legacy, positions et liquidity events prouvés sur corpus antérieur. |
| `raydium_amm_v4` | DEX effectif legacy | Swaps AMM v4 legacy matérialisés ; non-swaps legacy conservés en audit tant que le corpus ne permet pas une promotion fiable. |
| `meteora_dlmm` | DEX effectif consolidé en `0.7.45` | Swaps, Anchor CPI swap events, liquidity, positions, fees et lifecycle principaux validés par corpus local ; deux Anchor CPI audits résiduels `e8abf2613a4d232d` restent volontairement non mappés. |
| `meteora_damm_v1` | DEX effectif à reprendre séparément | Swaps présents ; plusieurs events restent en audit ou non actionnables. |
| `meteora_damm_v1` | DEX effectif en consolidation `0.7.46` | Swaps présents ; decoder étendu aux create_pool, liquidity, claim_fee et lock events DAMM v1 mappés upstream Git/corpus local. Validation DB à rejouer sur base dédiée. |
| `meteora_damm_v2` | DEX effectif à reprendre séparément | Swaps et create_pool observés ; nombreux audits à traiter. |
| `meteora_dbc` | launch/bonding + DEX effectif partiel à reprendre séparément | Gros volume daudits ; séparer bonding/launch, swap effectif et migration. |
| `orca_whirlpools` | DEX effectif à vérifier | À revalider par corpus dédié avant promotion. |
@@ -138,7 +138,17 @@ Validation locale finale sur la base DLMM dédiée :
| candles upsert | `2120` |
| audits DLMM résiduels | `2` |
Les deux audits restants sont `e445a52e51cb9a1d + e8abf2613a4d232d`. Ils restent en `meteora_dlmm.instruction_audit`, car aucun mapping Carbon/IDL suffisamment fiable na été confirmé. Ils ne bloquent pas la clôture de `0.7.45`.
Les deux audits restants sont `e445a52e51cb9a1d + e8abf2613a4d232d`. Ils restent en `meteora_dlmm.instruction_audit`, car aucun mapping upstream Git/IDL suffisamment fiable na été confirmé. Ils ne bloquent pas la clôture de `0.7.45`.
### 3.7. État de travail de `meteora_damm_v1` en `0.7.46`
La tranche `0.7.46` étend `meteora_damm_v1` à partir du mapping upstream Git decoder source `meteora-pools-decoder` et des discriminants observés dans le corpus local. Les events ajoutés couvrent `create_pool`, `add_liquidity`, `remove_liquidity`, `claim_fee`, `create_lock_escrow` et `lock_liquidity`.
La version logique du replay local devient `dex_decode.v0.7.46.damm_v1_events1`, ce qui force le redécodage des transactions certifiées sous la version `0.7.45` pour vérifier les nouveaux events DAMM v1.
Meteora Vault est traité prudemment : le programme associé peut apparaître comme compte dans les instructions DAMM v1, mais aucun decoder `meteora_vault` ni statut `verified_by_corpus` nest ajouté sans corpus direct séparé.
Demo3 dispose ensuite dune correction ciblée pour la découverte `meteora_damm_v1` : les discriminants DAMM v1 connus sont classés directement côté recherche on-chain, le filtrage `target_event` est strict sur les surfaces explicites, et les transactions mixtes ne sont plus éliminées globalement quand une cible précise est demandée. Cela sert à alimenter les backfills par signature ou par pool dans Demo Pipeline 2 sans déplacer de logique métier profonde dans `kb_demo_app`.
## 4. Matrice DEX : priorité révisée
@@ -301,11 +311,11 @@ La priorité immédiate après le point de reprise `0.7.43-E5C` est :
1. `0.7.43` : resynchronisation documentaire, normalisation DEX-first et gel du point de reprise ;
2. `0.7.44` : ajout du ledger de décodage/replay et options de replay `force` / skip sûr ;
3. `0.7.45` : reprise séparée de `meteora_dlmm` — clôturée côté corpus DLMM actuel ;
4. `0.7.46` : reprise séparée de `meteora_damm_v1` ;
5. `0.7.47` : reprise séparée de `meteora_damm_v2` ;
6. `0.7.48` : reprise séparée de `meteora_dbc` ;
7. `0.7.49` : revalidation séparée de `orca_whirlpools` ;
8. `0.7.50+` : FluxBeam, DexLab, metaDAO, Printr, puis launch surfaces.
4. `0.7.46` : reprise séparée de `meteora_damm_v1` — clôturée côté corpus DAMM v1 local ;
5. `0.7.47` : Upstream Git Registry / DEX discovery preparation — registre générique des `program_id`, discriminants, instructions et events issus de dépôts Git externes, tous non vérifiés par défaut ;
6. `0.7.48` : reprise séparée de `meteora_damm_v2` ;
7. `0.7.49` : reprise séparée de `meteora_dbc` ;
8. `0.7.50+` : validation progressive des autres DEX/surfaces issus du registre upstream Git : Orca, FluxBeam, DexLab, Lifinity, Phoenix, OpenBook, Stabble, BonkSwap, Boop, Moonshot, Heaven, Wavebreak, Vertigo, Virtuals, Pancake Swap, OKX DEX, Raydium Launchpad/Stable/Locking, puis launch surfaces.
Garde-fous constants :
@@ -350,3 +360,31 @@ Pour reprendre rapidement le codage dans une nouvelle session, fournir au minimu
- `kb_lib/src/db/queries.rs` et `kb_lib/src/db/queries/*`.
Ajouter `kb_demo_app/src/demo_pipeline*.rs`, `kb_demo_app/src/demo3.rs`, les fichiers frontend associés et les nouvelles démos seulement si la tâche concerne lUI, la recherche de corpus, les diagnostics affichés ou le watcher temps réel.
### Demo3 multi-target discovery
Demo3 can search several event surfaces in one on-chain scan by checking multiple target event boxes. Internally this uses the existing `targetEvent` field with comma-separated normalized values, preserving compatibility with older single-target calls.
### Demo3 paged / multi-source discovery
Demo3 can now scan one or several source addresses in a single on-chain discovery run. Source addresses may be pools, vaults, positions, config accounts or mints; the program id remains an instruction filter and no discovered address is promoted as verified automatically.
The on-chain discovery form supports Solana `getSignaturesForAddress` pagination through `beforeSignature`, `untilSignature`, `maxPages` and `scanOrder`. `newest_first` preserves Solana RPC order. `oldest_first` reverses the fetched window after paging, which is useful when enough pages have been fetched to include the creation-side history of a pool. The JSON result includes `nextBeforeByAddress` cursor hints for subsequent manual windows.
### Note 0.7.46 DAMM v1 upstream Git coverage
La couverture `meteora_damm_v1` inclut désormais les surfaces upstream Git decoder source `meteora-pools-decoder` connues. Les surfaces non rencontrées dans le corpus local restent marquées `upstream_git_mapped_unverified` et doivent être validées par Demo3 + backfill + replay avant dêtre considérées comme corpus-confirmed.
Sur le corpus local élargi, `swap`, `add_balance_liquidity`, `remove_balance_liquidity`, `claim_fee`, `create_lock_escrow`, `lock`, `InitializePermissionlessConstantProductPoolWithConfig` et `InitializePermissionlessConstantProductPoolWithConfig2` sont marqués `upstream_git_local_corpus_observed`.
### Note 0.7.47 Upstream Git Registry / DEX discovery preparation
La version `0.7.47` nest plus dédiée à un seul DEX. Elle doit introduire un registre upstream Git générique pour les `program_id`, discriminants dinstructions, discriminants devents, noms dinstructions et familles de programmes issus de dépôts Git externes de decoders Solana.
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.

View File

@@ -1067,13 +1067,13 @@ Fait :
- constitution dun corpus dédié `meteora_dlmm` via `Demo3`, backfill manuel des signatures anciennes du pool `HTvjzsfX3yU6BUodCjZ5vZkUrAxMDTrBs3CJaq43ashR`, puis backfill par pool address ;
- confirmation locale du programme DLMM observé `LBUZKhRxPF3XUpBCjp4YzTKgLccjZhTSDM9YuVaPwxo` dans les transactions du corpus ;
- traitement du wrapper Anchor `anchor_self_cpi_log` `e445a52e51cb9a1d` ;
- mapping prouvé localement et par IDL/Carbon des Anchor CPI swap events : `516ce3becdd00ac4` -> `Swap`, `2e7452d7941b544d` -> `Swap2Evt` ;
- mapping prouvé localement et par IDL/upstream Git des Anchor CPI swap events : `516ce3becdd00ac4` -> `Swap`, `2e7452d7941b544d` -> `Swap2Evt` ;
- enrichissement du payload `meteora_dlmm.swap` avec `anchorSwapEvent`, montants et fees CPI décodés ;
- cleanup conservatoire des audits Anchor CPI swap déjà couverts par un swap DLMM matérialisé ;
- ajout des events Anchor CPI non-swap DLMM observés : `lb_pair_create_event`, `add_liquidity_event`, `remove_liquidity_event`, `claim_fee_event`, `claim_reward_event` / `fund_reward_event` côté decoder, `position_create_event`, `position_close_event` ;
- promotion du discriminant direct `claim_fee2` vers `meteora_dlmm.claim_fee2` ;
- promotion de `close_position_if_empty` comme event de lifecycle/position close prouvé localement ;
- promotion de `remove_liquidity_by_range2`, `add_liquidity_by_strategy2` et `add_liquidity_by_weight` selon les layouts Carbon et le corpus local ;
- promotion de `remove_liquidity_by_range2`, `add_liquidity_by_strategy2` et `add_liquidity_by_weight` selon les layouts upstream Git et le corpus local ;
- matérialisation validée des families non-trade dans les tables dédiées, notamment `k_sol_liquidity_events`, `k_sol_pool_lifecycle_events` et `k_sol_fee_events` ;
- maintien du ledger replay avec `effective_event_count`, afin que les `.instruction_audit` informatifs ne rendent pas inutilement les transactions `unsafe` ;
- version logique finale du replay pour la tranche : `dex_decode.v0.7.45.dlmm_add_liquidity_strategies1` ;
@@ -1111,31 +1111,95 @@ Events DLMM observés après replay :
Limite conservée :
- `e445a52e51cb9a1d + e8abf2613a4d232d` reste en `meteora_dlmm.instruction_audit` avec `proofStatus = observed_local_corpus_anchor_self_cpi_log`, faute de mapping Carbon/IDL confirmé. Ces deux audits ne sont pas promus et ne bloquent pas la clôture de `0.7.45`.
- `e445a52e51cb9a1d + e8abf2613a4d232d` reste en `meteora_dlmm.instruction_audit` avec `proofStatus = observed_local_corpus_anchor_self_cpi_log`, faute de mapping upstream Git/IDL confirmé. Ces deux audits ne sont pas promus et ne bloquent pas la clôture de `0.7.45`.
Décision : `0.7.45` est clos pour `meteora_dlmm`. La suite immédiate est `0.7.46` sur `meteora_damm_v1` uniquement.
Décision : `0.7.45` est clos pour `meteora_dlmm`. La suite immédiate est `0.7.46 — Demo3 multi-target discovery enabled` sur `meteora_damm_v1` uniquement.
### 6.078. Version `0.7.46` — `meteora_damm_v1` séparé
Objectif : reprendre `meteora_damm_v1` sans le mélanger à DAMM v2, DBC ou DLMM.
À faire :
Tranche `0.7.46` engagée sur les audits `meteora_damm_v1` observés dans le corpus local et mappés contre upstream Git decoder source `meteora-pools-decoder`.
- valider les swaps exploitables et les cas sans amounts ;
- rechercher create/init pool, liquidity, fee/admin/config et autres events utiles ;
- maintenir la règle : pas de trade/candle si base/quote amount ou prix ne sont pas fiables ;
- produire un corpus SQL minimal pour chaque event promu.
Events DAMM v1 ajoutés côté decoder :
### 6.079. Version `0.7.47` — `meteora_damm_v2` séparé
Objectif : reprendre `meteora_damm_v2` comme DEX effectif séparé, avec traitement spécifique des nombreux `instruction_audit`.
- `meteora_damm_v1.create_pool` pour les créations constant-product avec config upstream Git `InitializePermissionlessConstantProductPoolWithConfig` et `InitializePermissionlessConstantProductPoolWithConfig2`, en plus des chemins legacy déjà présents ;
- `meteora_damm_v1.add_liquidity` pour `AddBalanceLiquidity`, `AddImbalanceLiquidity` et `BootstrapLiquidity` ;
- `meteora_damm_v1.remove_liquidity` pour `RemoveBalanceLiquidity` et `RemoveLiquiditySingleSide` ;
- `meteora_damm_v1.claim_fee` pour `ClaimFee` ;
- `meteora_damm_v1.create_lock_escrow` et `meteora_damm_v1.lock_liquidity` pour les instructions de verrouillage LP.
Discriminants DAMM v1 traités dans cette tranche :
| Discriminant | Mapping upstream Git | Event local | Statut |
|---|---|---|---|
| `07a68aabceabecf4` | `InitializePermissionlessConstantProductPoolWithConfig` | `meteora_damm_v1.create_pool` | observé dans corpus local |
| `3095dc823d0b09b2` | `InitializePermissionlessConstantProductPoolWithConfig2` | `meteora_damm_v1.create_pool` | observé dans corpus local |
| `856d2cb338ee7221` | `RemoveBalanceLiquidity` | `meteora_damm_v1.remove_liquidity` | observé dans corpus local |
| `a9204f8988e84689` | `ClaimFee` | `meteora_damm_v1.claim_fee` | observé dans corpus local |
| `3657a51345e3dae0` | `CreateLockEscrow` | `meteora_damm_v1.create_lock_escrow` | observé dans corpus local |
| `1513d02bed3eff57` | `Lock` | `meteora_damm_v1.lock_liquidity` | observé dans corpus local |
Discriminants DAMM v1 ajoutés au decoder pour complétude upstream Git, même sils devront rester soumis au corpus avant mention `verified_by_corpus` :
- `9118acc2db7d03be``InitializeCustomizablePermissionlessConstantProductPool` ;
- `a8e3323ebdab54b0``AddBalanceLiquidity` ;
- `4f237a54ad0f5dbf``AddImbalanceLiquidity` ;
- `04e4d747e1fd77ce``BootstrapLiquidity` ;
- `5454b142feb90afb``RemoveLiquiditySingleSide`.
Le replay passe à la version logique `dex_decode.v0.7.46.damm_v1_events1` afin de redécoder les transactions certifiées sous la version `0.7.45` quand la tranche DAMM v1 est rejouée.
Validation locale obtenue après replay :
- `meteora_damm_v1.instruction_audit` vide sur le corpus local DAMM v1 rejoué ;
- `meteora_damm_v1.claim_fee`, `create_pool`, `create_lock_escrow`, `lock_liquidity` et `remove_liquidity` matérialisés dans les tables non-trade attendues ;
- invariant maintenu : aucun event non-trade DAMM v1 ne produit de trade/candle ;
- `cargo test -p kb_lib` et `cargo clippy -p kb_lib --all-targets -- -D warnings` validés localement après correction du warning Clippy.
Correction Demo3 adossée à `0.7.46` :
- ajout dun décodage léger instruction-scoped pour `meteora_damm_v1` dans `onchain_dex_pair_discovery`, sans écriture DB et sans promotion de nouveau `program_id` ;
- les discriminants DAMM v1 connus par upstream Git/corpus sont classés directement en `swap`, `create_pool`, `add_liquidity`, `remove_liquidity`, `claim_fee`, `create_lock_escrow` ou `lock_liquidity` ;
- le filtre `target_event` devient strict pour les surfaces explicites afin quun swap ne ressorte pas comme liquidity, et inversement, quand les logs de transaction sont mixtes ;
- `excludeSwaps` ne supprime plus toute une transaction mixte lorsquun `target_event` explicite est sélectionné, afin de permettre la découverte dinstructions non-swap dans des routes agrégées ;
- les cibles UI `create_lock_escrow` et `lock_liquidity` sont ajoutées pour faciliter les backfills via Demo Pipeline 2.
Aucun `program_id` Meteora Vault nest promu comme vérifié sans corpus direct séparé.
### 6.079. Version `0.7.47` — Upstream Git Registry / DEX discovery preparation
Objectif : accélérer la découverte multi-DEX en indexant les `program_id`, discriminants dinstructions, discriminants devents et noms dinstructions issus de dépôts Git externes de decoders Solana, sans les considérer vérifiés par défaut.
À faire :
- créer un registre `upstream_registry` dans `kb_lib`, sans dépendre dun nom de dépôt particulier ;
- stocker pour chaque entrée : `source_repo`, `decoder_code`, `program_id`, famille, type de surface, instruction/event name, discriminator hex, longueur de discriminator, statut de preuve et notes ;
- utiliser les statuts génériques : `upstream_git_unverified`, `upstream_git_mapped_unverified`, `upstream_git_local_corpus_observed`, `upstream_git_local_corpus_materialized` ;
- exposer les entrées à Demo3 pour filtrer par decoder, famille, `program_id`, discriminant, instruction/event name ou statut ;
- permettre à Demo3 de rechercher `any_upstream_unverified` pour trouver des signatures candidates à backfiller ;
- ne produire aucun trade/candle/liquidity/fee/reward/admin automatique depuis le registre ;
- nutiliser les entrées upstream Git que comme indices de découverte et daudit tant quelles ne sont pas validées par Demo3 + backfill + replay + SQL ;
- garder `kb_demo_app` comme façade UI : toute logique de registry/mapping doit rester dans `kb_lib`.
Familles prioritaires à indexer en premier :
- DEX / AMM / CLMM / orderbook : `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 utiles au routage ou à lanalyse : `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 : `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`.
Aucun de ces programmes ne doit être marqué `verified_by_corpus` uniquement parce quil existe dans un dépôt Git externe.
### 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.
À faire :
- utiliser le registre `0.7.47` comme source dindices, pas comme preuve ;
- vérifier `cpamdpZCGKUy5JxQXB4dcpGPiikHawvSWAd6mEn1sGG` dans le corpus local avant de le marquer `verified_by_corpus` ;
- consolider `create_pool`, swaps exploitables, configs dynamiques, fees/admin et events lifecycle ;
- conserver les swaps sans payload montant/prix fiables comme `non_actionable_trade` ;
- ne promouvoir aucun event depuis `instruction_audit` sans signature de validation.
### 6.080. Version `0.7.48` — `meteora_dbc` séparé
### 6.081. Version `0.7.49` — `meteora_dbc` séparé
Objectif : séparer proprement bonding/launch, swap effectif, migration et attribution dorigine dans `meteora_dbc`.
À faire :
@@ -1145,7 +1209,7 @@ Objectif : séparer proprement bonding/launch, swap effectif, migration et attri
- éviter toute candle artificielle sur events de bonding/launch non pricés ;
- documenter les signatures/corpus avant toute promotion.
### 6.081. Version `0.7.49` — `orca_whirlpools` séparé
### 6.082. Version `0.7.50` — `orca_whirlpools` séparé
Objectif : revalider Orca Whirlpools par corpus dédié avant toute promotion au même niveau que Raydium/Meteora.
À faire :
@@ -1155,109 +1219,60 @@ Objectif : revalider Orca Whirlpools par corpus dédié avant toute promotion au
- matérialiser uniquement les events prouvés ;
- ajouter des diagnostics par event kind.
### 6.082. Version `0.7.50` — `fluxbeam` séparé
### 6.083. Version `0.7.51` — `fluxbeam` séparé
Objectif : vérifier FluxBeam comme DEX effectif distinct.
À faire :
À faire : constituer un corpus local, vérifier `program_id`, comptes, préfixes `data_json`, swaps, pools, liquidity et events non-trade prouvés.
- constituer un corpus local ;
- vérifier `program_id`, comptes, préfixes `data_json` et familles dinstructions utiles ;
- valider swaps, pools, liquidity et events non-trade prouvés ;
- marquer explicitement les parties heuristiques ou non-actionnables.
### 6.084. Version `0.7.52` — `dexlab` / OpenBook relation
Objectif : vérifier DexLab comme DEX effectif distinct sans le confondre avec OpenBook ou une couche de marché associée.
### 6.083. Version `0.7.51` — `dexlab` séparé
Objectif : vérifier DexLab comme DEX effectif distinct, sans le confondre avec OpenBook ou une autre couche de marché.
À faire : constituer un corpus local, vérifier `program_id`, comptes, préfixes `data_json`, swaps, pools et éventuels liens de market/pool.
À faire :
### 6.085. Version `0.7.53` — Lifinity / Phoenix / OpenBook / Stabble
Objectif : traiter les DEX/orderbooks supplémentaires identifiés par le registre upstream Git.
- constituer un corpus local ;
- vérifier `program_id`, comptes, préfixes `data_json` et familles dinstructions utiles ;
- valider swaps, pools et éventuels liens de market/pool ;
- conserver les cas partiels en audit.
À faire : valider séparément `lifinity_amm_v2`, `phoenix_v1`, `openbook_v2`, `stabble_stable_swap` et `stabble_weighted_swap`, sans matérialiser de trade avant preuve de montants exploitables.
### 6.084. Version `0.7.52` — `metaDAO` candidat DEX
Objectif : rechercher et vérifier metaDAO sans inventer de `program_id`.
### 6.086. Version `0.7.54` — BonkSwap / Boop / Moonshot / Heaven / Wavebreak / Vertigo / Virtuals / Pancake / OKX DEX
Objectif : vérifier les surfaces de swap/launch hybrides ou candidates découvertes via registre et corpus.
À faire :
À faire : séparer DEX effectif, launch surface, routeur/agrégateur et simple candidat ; ne promouvoir aucun `program_id` sans corpus local.
- rechercher les signatures/corpus via Demo3, DEX Screener ou sources externes de découverte ;
- ne considérer une source externe que comme indice ;
- promouvoir uniquement après preuve on-chain locale ;
- documenter chaque programme, event et limite.
### 6.087. Version `0.7.55` — Raydium surfaces complémentaires
Objectif : traiter `raydium_launchpad`, `raydium_liquidity_locking`, `raydium_stable_swap` et éventuelles surfaces Raydium non couvertes par CPMM/CLMM/AMM v4.
### 6.085. Version `0.7.53` — `printr` candidat DEX
Objectif : rechercher et vérifier Printr sans inventer de `program_id`.
À faire : distinguer launch, lock, stable AMM et AMM legacy ; garder les events non prouvés en audit.
À faire :
### 6.088. Version `0.7.56` — Aggregators, limit orders, perps et lending
Objectif : intégrer les programmes utiles au routage, aux ordres, aux perps ou au lending sans les confondre avec les DEX effectifs.
- rechercher les signatures/corpus via Demo3, DEX Screener ou sources externes de découverte ;
- ne considérer une source externe que comme indice ;
- promouvoir uniquement après preuve on-chain locale ;
- documenter chaque programme, event et limite.
À faire : classifier `jupiter_*`, `kamino_*`, `drift_v2`, `marginfi_v2`, `dflow_aggregator_v4`, `zeta` comme contexte/routing/ordres tant quils ne produisent pas directement une surface DEX matérialisable.
### 6.086. Version `0.7.54` — Couverture événementielle DEX consolidée
### 6.089. Version `0.7.57` — Couverture événementielle DEX consolidée
Objectif : sassurer que chaque DEX effectif supporté expose les événements utiles au scoring et au risque sans polluer les trades/candles.
À faire :
À faire : vérifier par DEX `swap`, liquidité, lifecycle, fees, rewards, admin/config, burns/mints utiles, et matérialiser uniquement les événements prouvés.
- vérifier par DEX la couverture `swap` / `tradeCandidate` / `candleCandidate` ;
- vérifier par DEX la couverture liquidité : add/remove/increase/decrease/open/close position ;
- vérifier par DEX les événements lifecycle : create/init/migrate/pause/resume/close ;
- vérifier par DEX les fees, rewards, creator fees, protocol fees et admin/config ;
- vérifier les burns/mints utiles au suivi token/pool sans les transformer en price-action ;
- matérialiser uniquement les événements prouvés dans les tables dédiées ;
- ajouter des compteurs et samples diagnostics par DEX et par type dévénement ;
- conserver linvariant : aucun fee/reward/admin/liquidity/lifecycle/burn non price-action ne produit de trade, metric ou candle.
### 6.087. Version `0.7.55` — `kb_demo_app` Demo4 : DEX Screener et sources externes de découverte
### 6.090. Version `0.7.58` — `kb_demo_app` Demo4 : DEX Screener et sources externes de découverte
Objectif : utiliser des sources externes comme aides à la découverte de corpus sans les traiter comme vérité métier.
À faire :
À faire : rechercher des paires par token mint, chain, DEX name, pool address ou program id lorsque disponible, comparer avec la base locale, copier les signatures/adresses candidates pour backfill, sans promotion automatique.
- ajouter une `Demo4` pour interroger DEX Screener ou une source équivalente ;
- rechercher des paires par token mint, chain, DEX name, pool address ou program id lorsque disponible ;
- comparer les résultats externes avec les objets locaux : tokens, pools, pairs, listings, decoded events et protocol candidates ;
- afficher les écarts : paire externe absente localement, pool local sans source externe, DEX label ambigu, program id manquant ;
- permettre de copier les signatures/adresses candidates pour backfill ;
- ne jamais promouvoir automatiquement un DEX, un `program_id` ou une paire sur la seule base dune réponse externe.
### 6.091. Version `0.7.59` — Démos spécialisées launch surfaces après DEX effectifs
Objectif : préparer des vues spécialisées pour les launch surfaces après stabilisation des DEX effectifs.
### 6.088. Version `0.7.56` — Démos spécialisées launch surfaces après DEX effectifs
Objectif : préparer des vues spécialisées pour les launch surfaces, mais seulement après stabilisation des DEX effectifs.
À faire : couvrir `pump_fun`, `raydium_launchpad`, `believe`, `bags`, `moonshot` / `moonit`, `boop_fun`, `letsbonk` / `bonk_fun`, `heaven`, avec séparation stricte entre launch origin, pool origin, DEX effectif et migration.
À faire plus tard :
- ajouter des démos dédiées à `pump_fun`, `raydium_launchpad` / `raydium_launchlab`, `believe`, `bags`, `moonshot` / `moonit`, `boop_fun`, `letsbonk` / `bonk_fun`, `heaven` ;
- distinguer `launch_origin`, `pool_origin`, `dex_effective` et `migration_target` ;
- rattacher les launch origins aux pools et paires uniquement lorsque les comptes permettent un matching fiable ;
- exposer les origins dans les diagnostics et lUI dinspection ;
- maintenir linterdiction de faux program ids, faux trades et fausses candles.
### 6.089. Version `0.7.57` — `kb_demo_app` Demo10 : watcher WebSocket live DEX
### 6.092. Version `0.7.60` — `kb_demo_app` Demo10 : watcher WebSocket live DEX
Objectif : valider le passage du replay/backfill vers lobservation temps réel contrôlée.
À faire :
À faire : sélectionner endpoints WS/HTTP et DEX/program ids à souscrire, utiliser le pipeline existant, afficher compteurs live, erreurs, subscriptions actives et derniers objets persistés.
- ajouter une démo temps réel type `Demo10` avec bouton `start` / `stop` ;
- permettre de sélectionner les endpoints WS/HTTP et les DEX/program ids à souscrire ;
- lancer le client WebSocket existant sans refactorer inutilement `ws_client.rs` / `ws_manager.rs` ;
- effectuer les `logsSubscribe`, `programSubscribe` ou `accountSubscribe` nécessaires selon le DEX ;
- détecter en temps réel mints, swaps, liquidités et autres événements utiles ;
- écrire en base via le pipeline existant : observations, transactions résolues, decoded events, pools/pairs/listings, trade events, candles et non-trade events ;
- afficher les compteurs live, erreurs, subscriptions actives et derniers objets persistés ;
- prévoir un arrêt propre avec unsubscribe avant close.
### 6.090. Version `0.7.58` — Validation DEX v1 consolidée
### 6.093. Version `0.7.61` — Validation DEX v1 consolidée
Objectif : rejouer tous les DEX effectifs supportés et valider les invariants du pipeline complet avant de revenir aux launch surfaces ou à lanalyse `0.8.x`.
À faire :
- rejouer des bases neuves couvrant tous les connecteurs DEX supportés ;
- vérifier les compteurs globaux et par DEX : decoded events, trade events, liquidity events, lifecycle events, fee events, reward events, admin events, burns/mints utiles, candles et analytic signals ;
- contrôler que chaque famille dévénements alimente uniquement les tables métier prévues ;
- vérifier les diagnostics bloquants et les samples danomalie ;
- documenter les corpus utilisés pour chaque DEX/surface ;
- conserver une matrice de support par DEX, variante, instruction et type dévénement ;
- verrouiller les invariants avant douvrir lanalyse `0.8.x`.
À faire : bases neuves, compteurs globaux et par DEX, diagnostics bloquants, samples danomalie, corpus documentés et matrice de support par DEX/variante/instruction/event.
### 6.091. Version `0.8.x` — Analyse et filtrage
Objectif : transformer les événements bruts en signaux exploitables.
@@ -1469,3 +1484,20 @@ Garde-fous constants :
- pas de metadata manquante bloquante ;
- pas de refactor réseau inutile tant que les clients HTTP/WS existants suffisent ;
- pas de skip replay sur transaction/instruction ambiguë, multi-token ou multi-event sans preuve ledger.
### Demo3 discovery note
Demo3 supports multiple selected target surfaces in one scan. The UI serializes selected checkboxes into the existing `targetEvent` filter as comma-separated values, so the backend remains backward compatible with single-target requests.
### Demo3 paged / multi-source note
Demo3 discovery now supports multiple source addresses, `before` / `until` pagination cursors, per-address max pages and `newest_first` / `oldest_first` processing order. This is intended for targeted corpus construction from known pool/pair addresses, especially when the first signatures can be identified externally with an explorer and then replayed/backfilled through Demo Pipeline 2. External explorers remain discovery aids only; verification still requires local decoder corpus and DB replay.
### 0.7.46 — clôture DAMM v1 upstream Git coverage
La tranche DAMM v1 doit couvrir les instructions/events listés par upstream Git decoder source `meteora-pools-decoder`. Les surfaces non observées localement sont volontairement persistées avec `proofStatus=upstream_git_mapped_unverified`; elles restent à valider par signatures réelles, replay et requêtes SQL.
Après backfills ciblés, les surfaces `swap` et `add_balance_liquidity` sont confirmées par corpus local et ne doivent plus rester en `upstream_git_mapped_unverified`. Les deux `remove_liquidity` non matérialisés en table liquidity sont expliqués par labsence de `pool_id/pair_id` local pour leurs pools, pas par un échec de décodage.

View File

@@ -64,30 +64,86 @@
</select>
</div>
<div class="col-6">
<label for="demo3SourceAddressInput" class="form-label">Source address</label>
<input id="demo3SourceAddressInput" type="text" class="form-control font-monospace" spellcheck="false" placeholder="pool / vault / position / config / mint address" />
<label for="demo3SourceAddressInput" class="form-label">Source addresses</label>
<textarea id="demo3SourceAddressInput" class="form-control font-monospace" rows="3" spellcheck="false" placeholder="pool / vault / position / config / mint addresses, one per line"></textarea>
</div>
<div class="col-12 form-text">Use address source to discover signatures around a pool, vault, position, config or mint while keeping the program id filter.</div>
<div class="col-12 form-text">Use address source to discover signatures around one or several pools, vaults, positions, configs or mints while keeping the program id filter.</div>
<div class="col-6">
<label for="demo3BeforeSignatureInput" class="form-label">Before signature</label>
<input id="demo3BeforeSignatureInput" type="text" class="form-control font-monospace" spellcheck="false" placeholder="optional pagination cursor" />
</div>
<div class="col-6">
<label for="demo3UntilSignatureInput" class="form-label">Until signature</label>
<input id="demo3UntilSignatureInput" type="text" class="form-control font-monospace" spellcheck="false" placeholder="optional stop cursor" />
</div>
<div class="col-6">
<label for="demo3MaxPagesInput" class="form-label">Max pages / address</label>
<input id="demo3MaxPagesInput" type="number" min="1" max="25" class="form-control" value="1" />
</div>
<div class="col-6">
<label for="demo3ScanOrderSelect" class="form-label">Scan order</label>
<select id="demo3ScanOrderSelect" class="form-select">
<option value="newest_first">newest_first</option>
<option value="oldest_first">oldest_first</option>
</select>
</div>
<div class="col-12 form-text">For pool creation analysis, scan a pool address with enough pages and use oldest_first to process the oldest fetched signatures first.</div>
</div>
<div class="row g-2">
<div class="col-12">
<label for="demo3TargetEventSelect" class="form-label">Target event</label>
<select id="demo3TargetEventSelect" class="form-select">
<option value="">Any / generic pair-pool discovery</option>
<option value="swap">swap</option>
<option value="add_liquidity">add_liquidity</option>
<option value="remove_liquidity">remove_liquidity</option>
<option value="claim_fee">claim_fee / collect_fee</option>
<option value="claim_reward">claim_reward</option>
<option value="position_open">position_open</option>
<option value="position_close">position_close</option>
<option value="pool_create">pool_create / initialize_pool</option>
<option value="pool_admin">pool_admin / config / authority</option>
<option value="unknown_non_swap">unknown_non_swap</option>
<option value="audit_non_swap_like">audit_non_swap_like</option>
<option value="unclassified_instruction">unclassified_instruction</option>
</select>
<label class="form-label">Target events</label>
<div id="demo3TargetEventCheckboxGroup" class="border rounded p-2 bg-body" style="max-height: 220px; overflow-y: auto;">
<div class="form-check form-check-inline">
<input id="demo3TargetSwapInput" class="form-check-input" type="checkbox" name="demo3TargetEventInput" value="swap" />
<label for="demo3TargetSwapInput" class="form-check-label">swap</label>
</div>
<div class="form-check form-check-inline">
<input id="demo3TargetAddLiquidityInput" class="form-check-input" type="checkbox" name="demo3TargetEventInput" value="add_liquidity" />
<label for="demo3TargetAddLiquidityInput" class="form-check-label">add_liquidity</label>
</div>
<div class="form-check form-check-inline">
<input id="demo3TargetRemoveLiquidityInput" class="form-check-input" type="checkbox" name="demo3TargetEventInput" value="remove_liquidity" />
<label for="demo3TargetRemoveLiquidityInput" class="form-check-label">remove_liquidity</label>
</div>
<div class="form-check form-check-inline">
<input id="demo3TargetClaimFeeInput" class="form-check-input" type="checkbox" name="demo3TargetEventInput" value="claim_fee" />
<label for="demo3TargetClaimFeeInput" class="form-check-label">claim_fee</label>
</div>
<div class="form-check form-check-inline">
<input id="demo3TargetClaimRewardInput" class="form-check-input" type="checkbox" name="demo3TargetEventInput" value="claim_reward" />
<label for="demo3TargetClaimRewardInput" class="form-check-label">claim_reward</label>
</div>
<div class="form-check form-check-inline">
<input id="demo3TargetCreateLockEscrowInput" class="form-check-input" type="checkbox" name="demo3TargetEventInput" value="create_lock_escrow" />
<label for="demo3TargetCreateLockEscrowInput" class="form-check-label">create_lock_escrow</label>
</div>
<div class="form-check form-check-inline">
<input id="demo3TargetLockLiquidityInput" class="form-check-input" type="checkbox" name="demo3TargetEventInput" value="lock_liquidity" />
<label for="demo3TargetLockLiquidityInput" class="form-check-label">lock_liquidity</label>
</div>
<div class="form-check form-check-inline">
<input id="demo3TargetPositionOpenInput" class="form-check-input" type="checkbox" name="demo3TargetEventInput" value="position_open" />
<label for="demo3TargetPositionOpenInput" class="form-check-label">position_open</label>
</div>
<div class="form-check form-check-inline">
<input id="demo3TargetPositionCloseInput" class="form-check-input" type="checkbox" name="demo3TargetEventInput" value="position_close" />
<label for="demo3TargetPositionCloseInput" class="form-check-label">position_close</label>
</div>
<div class="form-check form-check-inline">
<input id="demo3TargetPoolCreateInput" class="form-check-input" type="checkbox" name="demo3TargetEventInput" value="pool_create" />
<label for="demo3TargetPoolCreateInput" class="form-check-label">pool_create</label>
</div>
<div class="form-check form-check-inline">
<input id="demo3TargetPoolAdminInput" class="form-check-input" type="checkbox" name="demo3TargetEventInput" value="pool_admin" />
<label for="demo3TargetPoolAdminInput" class="form-check-label">pool_admin</label>
</div>
<div class="form-check form-check-inline">
<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>
</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">Use this to find corpus signatures for non-swap decoders without promoting unverified events.</div>
</div>
<div class="col-6">
@@ -156,6 +212,8 @@
<h2 class="h5 mb-3">Résumé</h2>
<div class="row g-2 small">
<div class="col-6"><strong>Signatures:</strong> <span id="demo3SummarySignatureCount">0</span></div>
<div class="col-6"><strong>Unique fetched:</strong> <span id="demo3SummaryUniqueFetchedSignatureCount">0</span></div>
<div class="col-6"><strong>Pages:</strong> <span id="demo3SummaryFetchedPageCount">0</span></div>
<div class="col-6"><strong>Unique candidates:</strong> <span id="demo3SummaryUniqueSignatureCount">0</span></div>
<div class="col-6"><strong>Tx fetched:</strong> <span id="demo3SummaryFetchedTxCount">0</span></div>
<div class="col-6"><strong>Missing tx:</strong> <span id="demo3SummaryMissingTxCount">0</span></div>
@@ -176,6 +234,10 @@
<strong>Backfill signatures:</strong>
<span id="demo3UniqueSignatureText" class="font-monospace">-</span>
</div>
<div class="small text-body-secondary mt-2">
<strong>Next before cursors:</strong>
<span id="demo3NextBeforeText" class="font-monospace">-</span>
</div>
</div>
</div>

View File

@@ -20,6 +20,26 @@ signatureSource: string | null,
* Optional source address used when signature_source is `address`.
*/
sourceAddress: string | null,
/**
* Optional extra source addresses used for multi-pool discovery.
*/
sourceAddresses: Array<string>,
/**
* Optional `before` cursor passed to Solana getSignaturesForAddress.
*/
beforeSignature: string | null,
/**
* Optional `until` cursor passed to Solana getSignaturesForAddress.
*/
untilSignature: string | null,
/**
* Maximum number of signature pages to fetch per source address.
*/
maxPages: number,
/**
* Signature processing order: newest_first or oldest_first.
*/
scanOrder: string | null,
/**
* Optional target event family used to find non-swap signatures.
*/

View File

@@ -1,5 +1,6 @@
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
import type { Demo3OnchainDexDiscoveryRequest } from "./Demo3OnchainDexDiscoveryRequest";
import type { Demo3OnchainDexPaginationCursor } from "./Demo3OnchainDexPaginationCursor";
import type { Demo3OnchainDexPairCandidate } from "./Demo3OnchainDexPairCandidate";
import type { Demo3OnchainDexRejectedCandidateSummary } from "./Demo3OnchainDexRejectedCandidateSummary";
@@ -27,6 +28,22 @@ resolvedSignatureSource: string,
* Address scanned with getSignaturesForAddress.
*/
resolvedSignatureAddress: string,
/**
* All addresses scanned with getSignaturesForAddress.
*/
resolvedSignatureAddresses: Array<string>,
/**
* Cursor hints by scanned address.
*/
nextBeforeByAddress: Array<Demo3OnchainDexPaginationCursor>,
/**
* Number of signature pages fetched.
*/
fetchedSignaturePageCount: number,
/**
* Number of unique fetched signatures after de-duplication.
*/
uniqueFetchedSignatureCount: number,
/**
* Number of unique candidate signatures.
*/

View File

@@ -0,0 +1,22 @@
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
/**
* Pagination cursor hint for one scanned source address.
*/
export type Demo3OnchainDexPaginationCursor = {
/**
* Scanned source address.
*/
address: string,
/**
* Signature usable as beforeSignature for the next page window.
*/
nextBeforeSignature: string | null,
/**
* Raw signature count fetched for this address.
*/
fetchedSignatureCount: number,
/**
* Page count fetched for this address.
*/
fetchedPageCount: number, };

View File

@@ -117,13 +117,49 @@ function isSolanaAddressLike(value: string): boolean {
return /^[1-9A-HJ-NP-Za-km-z]+$/.test(trimmed);
}
function splitSourceAddresses(value: string): string[] {
const seen = new Set<string>();
const addresses: string[] = [];
for (const token of value.split(/[\s,;]+/g)) {
const trimmed = token.trim();
if (trimmed === "" || seen.has(trimmed)) {
continue;
}
seen.add(trimmed);
addresses.push(trimmed);
}
return addresses;
}
function isSolanaSignatureLike(value: string): boolean {
const trimmed = value.trim();
if (trimmed.length < 64 || trimmed.length > 128) {
return false;
}
return /^[1-9A-HJ-NP-Za-km-z]+$/.test(trimmed);
}
function validateOptionalSignature(value: string | null, label: string): void {
if (value === null || value.trim() === "") {
return;
}
if (!isSolanaSignatureLike(value)) {
throw new Error(`${label} must be a valid Solana transaction signature.`);
}
}
function validateOnchainRequest(request: Demo3OnchainDexDiscoveryRequest): void {
if (request.signatureSource === "address") {
const sourceAddress = request.sourceAddress ?? "";
if (!isSolanaAddressLike(sourceAddress)) {
throw new Error("Signature source is 'address': Source address must be a real Solana account address, pool, vault, position, config or mint. It cannot be empty or the literal value 'address'.");
const addresses = request.sourceAddresses ?? [];
if (request.signatureSource === "address" && addresses.length === 0) {
throw new Error("Signature source is 'address': provide at least one Solana account, pool, vault, position, config or mint address.");
}
for (const address of addresses) {
if (!isSolanaAddressLike(address)) {
throw new Error(`Invalid source address '${address}'. Provide Solana account addresses separated by commas, spaces or new lines.`);
}
}
validateOptionalSignature(request.beforeSignature, "Before signature");
validateOptionalSignature(request.untilSignature, "Until signature");
if (request.programId !== null && !isSolanaAddressLike(request.programId)) {
throw new Error("Program id filter must be a valid Solana program id, or empty when using a preset that resolves it.");
}
@@ -143,6 +179,27 @@ function intValue(id: string, fallback: number): number {
return Number.isFinite(parsed) && parsed > 0 ? parsed : fallback;
}
function selectedTargetEvents(): string[] {
return Array.from(document.querySelectorAll<HTMLInputElement>('input[name="demo3TargetEventInput"]:checked'))
.map((input) => input.value.trim())
.filter((value) => value !== "");
}
function readTargetEventFilter(): string | null {
const selected = selectedTargetEvents();
return selected.length === 0 ? null : selected.join(",");
}
function targetEventLabel(targetEvent: string | null): string {
return targetEvent === null || targetEvent.trim() === "" ? "any" : targetEvent;
}
function clearTargetEventFilters(): void {
document.querySelectorAll<HTMLInputElement>('input[name="demo3TargetEventInput"]').forEach((input) => {
input.checked = false;
});
}
function escapeHtml(value: string): string {
return value
.replace(/&/g, "&amp;")
@@ -227,12 +284,18 @@ function applyPreset(indexText: string): void {
}
function readOnchainRequest(): Demo3OnchainDexDiscoveryRequest {
const sourceAddresses = splitSourceAddresses(byId<HTMLTextAreaElement>("demo3SourceAddressInput").value);
return {
dexCode: valueOrNull(byId<HTMLInputElement>("demo3DexCodeInput").value),
programId: valueOrNull(byId<HTMLInputElement>("demo3ProgramIdInput").value),
signatureSource: valueOrNull(byId<HTMLSelectElement>("demo3SignatureSourceSelect").value),
sourceAddress: valueOrNull(byId<HTMLInputElement>("demo3SourceAddressInput").value),
targetEvent: valueOrNull(byId<HTMLSelectElement>("demo3TargetEventSelect").value),
sourceAddress: sourceAddresses.length === 1 ? sourceAddresses[0] : null,
sourceAddresses,
beforeSignature: valueOrNull(byId<HTMLInputElement>("demo3BeforeSignatureInput").value),
untilSignature: valueOrNull(byId<HTMLInputElement>("demo3UntilSignatureInput").value),
maxPages: intValue("demo3MaxPagesInput", 1),
scanOrder: valueOrNull(byId<HTMLSelectElement>("demo3ScanOrderSelect").value),
targetEvent: readTargetEventFilter(),
excludeSwaps: byId<HTMLInputElement>("demo3ExcludeSwapsInput").checked,
includeFailed: byId<HTMLInputElement>("demo3IncludeFailedInput").checked,
httpRole: byId<HTMLInputElement>("demo3HttpRoleInput").value.trim() || "history_backfill",
@@ -258,12 +321,16 @@ function clearFilters(): void {
byId<HTMLInputElement>("demo3DexCodeInput").value = "";
byId<HTMLInputElement>("demo3ProgramIdInput").value = "";
byId<HTMLSelectElement>("demo3SignatureSourceSelect").value = "program_id";
byId<HTMLInputElement>("demo3SourceAddressInput").value = "";
byId<HTMLTextAreaElement>("demo3SourceAddressInput").value = "";
byId<HTMLInputElement>("demo3BeforeSignatureInput").value = "";
byId<HTMLInputElement>("demo3UntilSignatureInput").value = "";
byId<HTMLInputElement>("demo3MaxPagesInput").value = "1";
byId<HTMLSelectElement>("demo3ScanOrderSelect").value = "newest_first";
byId<HTMLInputElement>("demo3PairIdInput").value = "";
byId<HTMLInputElement>("demo3PoolAddressInput").value = "";
byId<HTMLInputElement>("demo3TokenMintInput").value = "";
byId<HTMLInputElement>("demo3SignatureInput").value = "";
byId<HTMLSelectElement>("demo3TargetEventSelect").value = "";
clearTargetEventFilters();
byId<HTMLInputElement>("demo3ExcludeSwapsInput").checked = false;
byId<HTMLInputElement>("demo3IncludeFailedInput").checked = true;
byId<HTMLSelectElement>("demo3PresetSelect").value = "";
@@ -272,6 +339,8 @@ function clearFilters(): void {
function renderOnchainResult(result: Demo3OnchainDexDiscoveryResult): void {
byId<HTMLElement>("demo3SummarySignatureCount").textContent = String(result.fetchedSignatureCount);
byId<HTMLElement>("demo3SummaryUniqueFetchedSignatureCount").textContent = String(result.uniqueFetchedSignatureCount);
byId<HTMLElement>("demo3SummaryFetchedPageCount").textContent = String(result.fetchedSignaturePageCount);
byId<HTMLElement>("demo3SummaryUniqueSignatureCount").textContent = String(result.uniqueSignatureCount);
byId<HTMLElement>("demo3SummaryFetchedTxCount").textContent = String(result.fetchedTransactionCount);
byId<HTMLElement>("demo3SummaryMissingTxCount").textContent = String(result.missingTransactionCount);
@@ -281,9 +350,11 @@ function renderOnchainResult(result: Demo3OnchainDexDiscoveryResult): void {
byId<HTMLElement>("demo3SummaryExtractedCandidateCount").textContent = String(result.extractedCandidateCount);
byId<HTMLElement>("demo3SummaryRejectedCandidateCount").textContent = String(result.targetRejectedCandidateCount);
byId<HTMLElement>("demo3SummaryCandidateCount").textContent = String(result.candidateCount);
const targetEvent = result.request.targetEvent ?? "any";
byId<HTMLElement>("demo3TargetText").textContent = `${result.resolvedDexCode ?? "custom"} / program=${result.resolvedProgramId} / source=${result.resolvedSignatureSource}:${result.resolvedSignatureAddress} / target=${targetEvent}`;
const targetEvent = targetEventLabel(result.request.targetEvent);
const sourceText = result.resolvedSignatureAddresses.length === 0 ? result.resolvedSignatureAddress : result.resolvedSignatureAddresses.join(",");
byId<HTMLElement>("demo3TargetText").textContent = `${result.resolvedDexCode ?? "custom"} / program=${result.resolvedProgramId} / source=${result.resolvedSignatureSource}:${sourceText} / target=${targetEvent} / order=${result.request.scanOrder ?? "newest_first"}`;
byId<HTMLElement>("demo3UniqueSignatureText").textContent = result.uniqueBackfillSignatures.length === 0 ? "-" : result.uniqueBackfillSignatures.join(", ");
byId<HTMLElement>("demo3NextBeforeText").textContent = result.nextBeforeByAddress.length === 0 ? "-" : result.nextBeforeByAddress.map((cursor) => `${cursor.address}:${cursor.nextBeforeSignature ?? "-"}`).join(" | ");
renderRejectedSummary(result);
renderOnchainCandidates(result.candidates);
}
@@ -374,14 +445,14 @@ async function discoverOnchain(): Promise<void> {
return;
}
setStatus("running", "text-bg-warning");
appendLogLine(`on-chain discovery dex='${request.dexCode ?? ""}' program='${request.programId ?? ""}' source='${request.signatureSource ?? "program_id"}:${request.sourceAddress ?? ""}' target='${request.targetEvent ?? "any"}' excludeSwaps='${request.excludeSwaps}' role='${request.httpRole}'`);
appendLogLine(`on-chain discovery dex='${request.dexCode ?? ""}' program='${request.programId ?? ""}' source='${request.signatureSource ?? "program_id"}:${request.sourceAddresses.join(",")}' target='${targetEventLabel(request.targetEvent)}' pages='${request.maxPages}' order='${request.scanOrder ?? "newest_first"}' before='${request.beforeSignature ?? ""}' until='${request.untilSignature ?? ""}' excludeSwaps='${request.excludeSwaps}' role='${request.httpRole}'`);
try {
const payload = await invoke<Demo3OnchainDexDiscoveryPayload>("demo3_discover_onchain_dex_pairs", { request });
lastResultJson = payload.resultJson;
byId<HTMLTextAreaElement>("demo3JsonTextarea").value = payload.resultJson;
renderOnchainResult(payload.result);
setStatus("ok", "text-bg-success");
appendLogLine(`on-chain discovery completed: candidates='${payload.result.candidateCount}' unique='${payload.result.uniqueSignatureCount}' signatures='${payload.result.fetchedSignatureCount}' extracted='${payload.result.extractedCandidateCount}' rejected='${payload.result.targetRejectedCandidateCount}' skippedSwapTx='${payload.result.skippedSwapLogTransactionCount}'`);
appendLogLine(`on-chain discovery completed: candidates='${payload.result.candidateCount}' unique='${payload.result.uniqueSignatureCount}' signatures='${payload.result.fetchedSignatureCount}' uniqueFetched='${payload.result.uniqueFetchedSignatureCount}' pages='${payload.result.fetchedSignaturePageCount}' extracted='${payload.result.extractedCandidateCount}' rejected='${payload.result.targetRejectedCandidateCount}' skippedSwapTx='${payload.result.skippedSwapLogTransactionCount}'`);
} catch (error) {
setStatus("error", "text-bg-danger");
appendLogLine(`on-chain discovery failed: ${String(error)}`);

View File

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

View File

@@ -435,6 +435,21 @@ pub(crate) struct Demo3OnchainDexDiscoveryRequest {
pub signature_source: std::option::Option<std::string::String>,
/// Optional source address used when signature_source is `address`.
pub source_address: std::option::Option<std::string::String>,
/// Optional extra source addresses used for multi-pool discovery.
#[serde(default)]
pub source_addresses: std::vec::Vec<std::string::String>,
/// Optional `before` cursor passed to Solana getSignaturesForAddress.
#[serde(default)]
pub before_signature: std::option::Option<std::string::String>,
/// Optional `until` cursor passed to Solana getSignaturesForAddress.
#[serde(default)]
pub until_signature: std::option::Option<std::string::String>,
/// Maximum number of signature pages to fetch per source address.
#[serde(default)]
pub max_pages: u32,
/// Signature processing order: newest_first or oldest_first.
#[serde(default)]
pub scan_order: std::option::Option<std::string::String>,
/// Optional target event family used to find non-swap signatures.
pub target_event: std::option::Option<std::string::String>,
/// Whether transactions containing swap-like logs should be skipped.
@@ -479,6 +494,16 @@ pub(crate) struct Demo3OnchainDexDiscoveryResult {
pub resolved_signature_source: std::string::String,
/// Address scanned with getSignaturesForAddress.
pub resolved_signature_address: std::string::String,
/// All addresses scanned with getSignaturesForAddress.
pub resolved_signature_addresses: std::vec::Vec<std::string::String>,
/// Cursor hints by scanned address.
pub next_before_by_address: std::vec::Vec<Demo3OnchainDexPaginationCursor>,
/// Number of signature pages fetched.
#[ts(type = "number")]
pub fetched_signature_page_count: usize,
/// Number of unique fetched signatures after de-duplication.
#[ts(type = "number")]
pub unique_fetched_signature_count: usize,
/// Number of unique candidate signatures.
#[ts(type = "number")]
pub unique_signature_count: usize,
@@ -517,6 +542,23 @@ pub(crate) struct Demo3OnchainDexDiscoveryResult {
pub candidates: std::vec::Vec<Demo3OnchainDexPairCandidate>,
}
/// Pagination cursor hint for one scanned source address.
#[derive(Clone, Debug, serde::Serialize, TS)]
#[ts(export, export_to = "../frontend/ts/bindings/Demo3OnchainDexPaginationCursor.ts")]
#[serde(rename_all = "camelCase")]
pub(crate) struct Demo3OnchainDexPaginationCursor {
/// Scanned source address.
pub address: std::string::String,
/// Signature usable as beforeSignature for the next page window.
pub next_before_signature: std::option::Option<std::string::String>,
/// Raw signature count fetched for this address.
#[ts(type = "number")]
pub fetched_signature_count: usize,
/// Page count fetched for this address.
#[ts(type = "number")]
pub fetched_page_count: usize,
}
/// Rejected on-chain discovery candidate summary.
#[derive(Clone, Debug, serde::Serialize, TS)]
#[ts(
@@ -685,6 +727,11 @@ fn to_lib_onchain_request(
program_id: normalize_optional_text(request.program_id.clone()),
signature_source: normalize_optional_text(request.signature_source.clone()),
source_address: normalize_optional_text(request.source_address.clone()),
source_addresses: request.source_addresses.clone(),
before_signature: normalize_optional_text(request.before_signature.clone()),
until_signature: normalize_optional_text(request.until_signature.clone()),
max_pages: request.max_pages,
scan_order: normalize_optional_text(request.scan_order.clone()),
target_event: normalize_optional_text(request.target_event.clone()),
exclude_swaps: request.exclude_swaps,
include_failed: request.include_failed,
@@ -708,6 +755,11 @@ fn from_lib_onchain_result(
program_id: result.request.program_id,
signature_source: result.request.signature_source,
source_address: result.request.source_address,
source_addresses: result.request.source_addresses,
before_signature: result.request.before_signature,
until_signature: result.request.until_signature,
max_pages: result.request.max_pages,
scan_order: result.request.scan_order,
target_event: result.request.target_event,
exclude_swaps: result.request.exclude_swaps,
include_failed: result.request.include_failed,
@@ -720,6 +772,10 @@ fn from_lib_onchain_result(
resolved_program_id: result.resolved_program_id,
resolved_signature_source: result.resolved_signature_source,
resolved_signature_address: result.resolved_signature_address,
resolved_signature_addresses: result.resolved_signature_addresses,
next_before_by_address: from_lib_onchain_pagination_cursors(result.next_before_by_address),
fetched_signature_page_count: result.fetched_signature_page_count,
unique_fetched_signature_count: result.unique_fetched_signature_count,
unique_signature_count: result.unique_signature_count,
unique_backfill_signatures: result.unique_backfill_signatures,
rejected_candidate_summary: from_lib_rejected_candidate_summary(
@@ -738,6 +794,21 @@ fn from_lib_onchain_result(
};
}
fn from_lib_onchain_pagination_cursors(
values: std::vec::Vec<kb_lib::OnchainDexPaginationCursorDto>,
) -> std::vec::Vec<Demo3OnchainDexPaginationCursor> {
let mut mapped = std::vec::Vec::new();
for value in values {
mapped.push(Demo3OnchainDexPaginationCursor {
address: value.address,
next_before_signature: value.next_before_signature,
fetched_signature_count: value.fetched_signature_count,
fetched_page_count: value.fetched_page_count,
});
}
return mapped;
}
fn from_lib_rejected_candidate_summary(
values: std::vec::Vec<kb_lib::OnchainDexRejectedCandidateSummaryDto>,
) -> std::vec::Vec<Demo3OnchainDexRejectedCandidateSummary> {

View File

@@ -1,7 +1,7 @@
{
"$schema": "https://schema.tauri.app/config/2",
"productName": "kb-demo-app",
"version": "0.7.45",
"version": "0.7.46",
"identifier": "com.sasedev.kb-demo-app",
"build": {
"beforeDevCommand": "npm run dev",

View File

@@ -26,6 +26,10 @@ pub use fluxbeam::FluxbeamSwapDecoded;
pub use meteora_damm_v1::MeteoraDammV1CreatePoolDecoded;
pub use meteora_damm_v1::MeteoraDammV1DecodedEvent;
pub use meteora_damm_v1::MeteoraDammV1Decoder;
pub use meteora_damm_v1::MeteoraDammV1FeeDecoded;
pub use meteora_damm_v1::MeteoraDammV1LiquidityDecoded;
pub use meteora_damm_v1::MeteoraDammV1PoolAdminDecoded;
pub use meteora_damm_v1::MeteoraDammV1PoolLifecycleDecoded;
pub use meteora_damm_v1::MeteoraDammV1SwapDecoded;
pub use meteora_damm_v2::MeteoraDammV2CreatePoolDecoded;
pub use meteora_damm_v2::MeteoraDammV2DecodedEvent;

File diff suppressed because it is too large Load Diff

View File

@@ -468,7 +468,7 @@ impl MeteoraDlmmDecoder {
"accounts": accounts,
"parsed": parsed_json,
"logMessages": log_messages,
"proofStatus": "observed_local_corpus_and_known_carbon_layout",
"proofStatus": "upstream_git_local_corpus_observed",
"position": position_account,
"actorWallet": actor_wallet,
"rentReceiver": rent_receiver,
@@ -992,7 +992,7 @@ fn decode_anchor_lb_pair_create_event(
"anchorEventName": "lb_pair_create_event",
"anchorEventDiscriminatorHex": "b94afc7d1bd7bc6f",
"anchorEventPayloadSize": data.len().saturating_sub(8),
"proofStatus": "known_carbon_layout_pending_local_corpus_validation",
"proofStatus": "upstream_git_layout_unverified",
"lbPair": lb_pair,
"poolAccount": lb_pair,
"binStep": bin_step,
@@ -1052,7 +1052,7 @@ fn decode_anchor_liquidity_event(
"anchorEventName": anchor_event_name,
"anchorEventDiscriminatorHex": event_discriminator_hex,
"anchorEventPayloadSize": data.len().saturating_sub(8),
"proofStatus": "known_carbon_layout_pending_local_corpus_validation",
"proofStatus": "upstream_git_layout_unverified",
"lbPair": lb_pair,
"poolAccount": lb_pair,
"from": from,
@@ -1112,7 +1112,7 @@ fn decode_anchor_claim_fee_event(
"anchorEventName": "claim_fee_event",
"anchorEventDiscriminatorHex": "4b7a9a308c4a7ba3",
"anchorEventPayloadSize": data.len().saturating_sub(8),
"proofStatus": "known_carbon_layout_pending_local_corpus_validation",
"proofStatus": "upstream_git_layout_unverified",
"lbPair": lb_pair,
"poolAccount": lb_pair,
"position": position,
@@ -1167,7 +1167,7 @@ fn decode_anchor_claim_reward_event(
"anchorEventName": "claim_reward_event",
"anchorEventDiscriminatorHex": "947486cc16ab555f",
"anchorEventPayloadSize": data.len().saturating_sub(8),
"proofStatus": "known_carbon_layout_pending_local_corpus_validation",
"proofStatus": "upstream_git_layout_unverified",
"lbPair": lb_pair,
"poolAccount": lb_pair,
"position": position,
@@ -1222,7 +1222,7 @@ fn decode_anchor_fund_reward_event(
"anchorEventName": "fund_reward_event",
"anchorEventDiscriminatorHex": "f6e43a8291aa4fcc",
"anchorEventPayloadSize": data.len().saturating_sub(8),
"proofStatus": "known_carbon_layout_pending_local_corpus_validation",
"proofStatus": "upstream_git_layout_unverified",
"lbPair": lb_pair,
"poolAccount": lb_pair,
"funder": funder,
@@ -1275,7 +1275,7 @@ fn decode_anchor_position_create_event(
"anchorEventName": "position_create_event",
"anchorEventDiscriminatorHex": "908efc549d352579",
"anchorEventPayloadSize": data.len().saturating_sub(8),
"proofStatus": "known_carbon_layout_pending_local_corpus_validation",
"proofStatus": "upstream_git_layout_unverified",
"lbPair": lb_pair,
"poolAccount": lb_pair,
"position": position,
@@ -1328,7 +1328,7 @@ fn decode_anchor_position_close_event(
"anchorEventName": "position_close_event",
"anchorEventDiscriminatorHex": "ffc4106b1cca3580",
"anchorEventPayloadSize": data.len().saturating_sub(8),
"proofStatus": "known_carbon_layout_pending_local_corpus_validation",
"proofStatus": "upstream_git_layout_unverified",
"position": position,
"owner": owner,
"actorWallet": owner
@@ -2248,7 +2248,7 @@ fn resolve_dlmm_instruction_proof_status(
MeteoraDlmmInstructionName::AddLiquidityByStrategy2
| MeteoraDlmmInstructionName::AddLiquidityByWeight
| MeteoraDlmmInstructionName::RemoveLiquidityByRange2 => {
return "observed_local_corpus_and_known_carbon_layout";
return "upstream_git_local_corpus_observed";
},
_ => return "decoded_from_instruction_discriminator_or_local_hint",
}
@@ -2858,7 +2858,7 @@ mod tests {
}
#[test]
fn meteora_dlmm_swap_accounts_are_mapped_from_carbon_layout() {
fn meteora_dlmm_swap_accounts_are_mapped_from_upstream_git_layout() {
let accounts = vec![
"LbPair111".to_string(),
"Bitmap111".to_string(),

View File

@@ -564,7 +564,7 @@ impl DexDecodeService {
None,
event.token_a_mint.clone(),
event.token_b_mint.clone(),
event.config_account.clone(),
event.lp_mint.clone(),
event.payload_json.clone(),
)
.await;
@@ -589,6 +589,78 @@ impl DexDecodeService {
)
.await;
},
crate::MeteoraDammV1DecodedEvent::Liquidity(event) => {
return self
.materialize_named_dex_event(
transaction,
event.transaction_id,
event.instruction_id,
"meteora_damm_v1",
event.program_id.clone(),
event.event_kind.as_str(),
event.pool_account.clone(),
None,
event.token_a_mint.clone(),
event.token_b_mint.clone(),
event.lp_mint.clone(),
event.payload_json.clone(),
)
.await;
},
crate::MeteoraDammV1DecodedEvent::Fee(event) => {
return self
.materialize_named_dex_event(
transaction,
event.transaction_id,
event.instruction_id,
"meteora_damm_v1",
event.program_id.clone(),
event.event_kind.as_str(),
event.pool_account.clone(),
None,
None,
None,
event.lp_mint.clone(),
event.payload_json.clone(),
)
.await;
},
crate::MeteoraDammV1DecodedEvent::PoolLifecycle(event) => {
return self
.materialize_named_dex_event(
transaction,
event.transaction_id,
event.instruction_id,
"meteora_damm_v1",
event.program_id.clone(),
event.event_kind.as_str(),
event.pool_account.clone(),
None,
event.token_a_mint.clone(),
event.token_b_mint.clone(),
event.lp_mint.clone(),
event.payload_json.clone(),
)
.await;
},
crate::MeteoraDammV1DecodedEvent::PoolAdmin(event) => {
return self
.materialize_named_dex_event(
transaction,
event.transaction_id,
event.instruction_id,
"meteora_damm_v1",
event.program_id.clone(),
event.event_kind.as_str(),
event.pool_account.clone(),
None,
None,
None,
None,
event.payload_json.clone(),
)
.await;
},
}
}
@@ -1927,6 +1999,9 @@ fn instruction_audit_event_kind_by_protocol(
"raydium_clmm" => return Some("raydium_clmm.instruction_audit"),
"raydium_cpmm" => return Some("raydium_cpmm.instruction_audit"),
"meteora_dlmm" => return Some("meteora_dlmm.instruction_audit"),
"meteora_damm_v1" => return Some("meteora_damm_v1.instruction_audit"),
"meteora_damm_v2" => return Some("meteora_damm_v2.instruction_audit"),
"meteora_dbc" => return Some("meteora_dbc.instruction_audit"),
_ => return None,
}
}
@@ -3134,6 +3209,18 @@ mod tests {
super::instruction_audit_event_kind_by_protocol("meteora_dlmm"),
Some("meteora_dlmm.instruction_audit")
);
assert_eq!(
super::instruction_audit_event_kind_by_protocol("meteora_damm_v1"),
Some("meteora_damm_v1.instruction_audit")
);
assert_eq!(
super::instruction_audit_event_kind_by_protocol("meteora_damm_v2"),
Some("meteora_damm_v2.instruction_audit")
);
assert_eq!(
super::instruction_audit_event_kind_by_protocol("meteora_dbc"),
Some("meteora_dbc.instruction_audit")
);
assert_eq!(super::instruction_audit_event_kind_by_protocol("unknown"), None);
}
}

View File

@@ -173,6 +173,9 @@ pub fn classify_dex_event_lifecycle_kind(event_kind: &str) -> DexEventLifecycleK
if is_dex_informational_event_kind(event_kind) {
return DexEventLifecycleKind::InstructionAudit;
}
if event_kind.contains(".create_lock_escrow") {
return DexEventLifecycleKind::PoolCreation;
}
if is_dex_token_burn_event_kind(event_kind) {
return DexEventLifecycleKind::Burn;
}
@@ -408,6 +411,12 @@ pub fn is_dex_fee_event_kind(event_kind: &str) -> bool {
if event_kind.contains("claim_fee") {
return true;
}
if event_kind.contains("withdraw_protocol_fees") {
return true;
}
if event_kind.contains("partner_claim_fee") {
return true;
}
return false;
}
@@ -424,6 +433,9 @@ pub fn is_dex_reward_event_kind(event_kind: &str) -> bool {
/// Returns true for pool, pair, launch, mint, burn or migration lifecycle events.
pub fn is_dex_pool_lifecycle_event_kind(event_kind: &str) -> bool {
if event_kind.contains(".create_lock_escrow") {
return true;
}
if event_kind.contains(".initialize_bin_array") {
return true;
}
@@ -534,6 +546,9 @@ pub fn is_dex_pair_creation_event_kind(event_kind: &str) -> bool {
/// Returns true for admin, configuration or permission changes.
pub fn is_dex_admin_event_kind(event_kind: &str) -> bool {
if event_kind.contains(".lock_liquidity") {
return true;
}
if event_kind.contains("admin") {
return true;
}
@@ -1102,4 +1117,32 @@ mod tests {
"non_trade_useful"
);
}
#[test]
fn classifies_damm_v1_lock_events_as_non_trade_useful() {
assert_eq!(
super::classify_dex_event_category_code("meteora_damm_v1.create_lock_escrow"),
"pool_lifecycle"
);
assert_eq!(
super::classify_dex_event_lifecycle_kind_code("meteora_damm_v1.create_lock_escrow"),
"pool_creation"
);
assert_eq!(
super::classify_dex_event_category_code("meteora_damm_v1.lock_liquidity"),
"admin"
);
assert_eq!(
super::classify_dex_event_lifecycle_kind_code("meteora_damm_v1.lock_liquidity"),
"admin_config"
);
assert_eq!(
super::classify_dex_event_actionability_code(
"meteora_damm_v1.lock_liquidity",
false,
false,
),
"non_trade_useful"
);
}
}

View File

@@ -901,6 +901,14 @@ pub use dex::MeteoraDammV1CreatePoolDecoded;
pub use dex::MeteoraDammV1DecodedEvent;
/// Meteora DAMM v1 decoder.
pub use dex::MeteoraDammV1Decoder;
/// Decoded Meteora DAMM v1 fee event.
pub use dex::MeteoraDammV1FeeDecoded;
/// Decoded Meteora DAMM v1 liquidity event.
pub use dex::MeteoraDammV1LiquidityDecoded;
/// Decoded Meteora DAMM v1 pool administration event.
pub use dex::MeteoraDammV1PoolAdminDecoded;
/// Decoded Meteora DAMM v1 pool lifecycle event.
pub use dex::MeteoraDammV1PoolLifecycleDecoded;
/// Decoded Meteora DAMM v1 swap event.
pub use dex::MeteoraDammV1SwapDecoded;
/// Decoded Meteora DAMM v2 create-pool event.
@@ -1158,6 +1166,8 @@ pub use non_trade_event_materialization::NonTradeEventMaterializationResult;
pub use non_trade_event_materialization::NonTradeEventMaterializationService;
/// Candidate account inferred from generic transaction evidence.
pub use onchain_dex_pair_discovery::OnchainDexCandidateAccountDto;
/// Cursor hint for one on-chain DEX discovery source address.
pub use onchain_dex_pair_discovery::OnchainDexPaginationCursorDto;
/// Candidate transaction/instruction observed on-chain for one DEX program id.
pub use onchain_dex_pair_discovery::OnchainDexPairCandidateDto;
/// Request for on-chain DEX pair/pool discovery.

View File

@@ -7,8 +7,7 @@
//! deterministic local pipeline over their signatures.
const LOCAL_PIPELINE_DEX_DECODER_SCOPE: &str = "dex_decode.local_pipeline";
const LOCAL_PIPELINE_DEX_DECODER_VERSION: &str =
"dex_decode.v0.7.45.dlmm_add_liquidity_strategies1";
const LOCAL_PIPELINE_DEX_DECODER_VERSION: &str = "dex_decode.v0.7.46.damm_v1_events1";
fn default_skip_certified_dex_decode() -> bool {
return true;

File diff suppressed because it is too large Load Diff