0.7.46
This commit is contained in:
@@ -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 d’un 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 lorsqu’un 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` n’est 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` n’est 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 n’est marqué vérifié sans corpus direct dédié.
|
||||
0.7.46-demo3 - Correction ciblée de Demo3 pour la découverte/backfill : ajout d’un 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 qu’un `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 d’un 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 d’une tranche DAMM v2 immédiate.
|
||||
|
||||
@@ -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"
|
||||
|
||||
54
README.md
54
README.md
@@ -4,7 +4,7 @@
|
||||
|
||||
`khadhroony-bobobot` est un workspace Rust destiné à la détection, au décodage, à l’analyse et, à terme, au trading semi-automatisé de tokens Solana.
|
||||
|
||||
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 d’audits ; 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 n’a é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 n’a é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` n’est ajouté sans corpus direct séparé.
|
||||
|
||||
Demo3 dispose ensuite d’une 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 l’UI, 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` n’est plus dédiée à un seul DEX. Elle doit introduire un registre upstream Git générique pour les `program_id`, discriminants d’instructions, discriminants d’events, noms d’instructions 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 qu’elles 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.
|
||||
|
||||
212
ROADMAP.md
212
ROADMAP.md
@@ -1067,13 +1067,13 @@ Fait :
|
||||
- constitution d’un 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 s’ils 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 d’un 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 qu’un swap ne ressorte pas comme liquidity, et inversement, quand les logs de transaction sont mixtes ;
|
||||
- `excludeSwaps` ne supprime plus toute une transaction mixte lorsqu’un `target_event` explicite est sélectionné, afin de permettre la découverte d’instructions 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 n’est 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 d’instructions, discriminants d’events et noms d’instructions 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 d’un 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 ;
|
||||
- n’utiliser les entrées upstream Git que comme indices de découverte et d’audit tant qu’elles 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 à l’analyse : `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 qu’il 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 d’indices, 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 d’origine 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 d’instructions 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 d’instructions 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 qu’ils 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 : s’assurer 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 l’invariant : 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 d’une 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 l’UI d’inspection ;
|
||||
- maintenir l’interdiction 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 l’observation 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 à l’analyse `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 d’anomalie ;
|
||||
- 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 d’ouvrir l’analyse `0.8.x`.
|
||||
À faire : bases neuves, compteurs globaux et par DEX, diagnostics bloquants, samples d’anomalie, 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 l’absence de `pool_id/pair_id` local pour leurs pools, pas par un échec de décodage.
|
||||
|
||||
@@ -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>
|
||||
|
||||
|
||||
@@ -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.
|
||||
*/
|
||||
|
||||
@@ -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.
|
||||
*/
|
||||
|
||||
@@ -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, };
|
||||
@@ -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, "&")
|
||||
@@ -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)}`);
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "kb-demo-app",
|
||||
"private": true,
|
||||
"version": "0.7.45",
|
||||
"version": "0.7.46",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
|
||||
@@ -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> {
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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
@@ -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(),
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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
Reference in New Issue
Block a user