From be12f5810b06abb672ded4e922a7543e1484722d Mon Sep 17 00:00:00 2001 From: SinuS Von SifriduS Date: Tue, 16 Jun 2026 06:57:32 +0200 Subject: [PATCH] 0.7.55 --- CHANGELOG.md | 1 + Cargo.toml | 2 +- README.md | 69 +- ROADMAP.md | 41 +- docs/DEX_DECODER_MATRIX.md | 47 +- docs/DEX_EVENT_COVERAGE_MATRIX.md | 29 +- docs/prompts/PROMPT_0_7_56_METEORA_DBC.md | 216 ++ .../PUMP_FEES_EVENT_COVERAGE_REPORT.md | 134 + ...p65T869zMMBKyuUq6aD6EgTu8psMjkvj52pCf.json | 1 + ...HZwjELNnjvThjajtiVmkz3yPQEHjLtka2fwHW.json | 1 + ...dwDryooaGtiocG1u3xcYbRpiJzb283XfCZsDp.json | 1 + kb_demo_app/package.json | 2 +- kb_demo_app/tauri.conf.json | 2 +- kb_lib/src/db/queries/dex_decoded_event.rs | 4 +- kb_lib/src/dex.rs | 8 +- kb_lib/src/dex/pump_fees.rs | 2447 +++++++++++++++++ kb_lib/src/dex_decode.rs | 146 +- kb_lib/src/dex_event_classification.rs | 46 +- kb_lib/src/dex_event_coverage.rs | 144 +- kb_lib/src/instruction_observation_index.rs | 36 + kb_lib/src/lib.rs | 6 + kb_lib/src/upstream_registry_generated.rs | 227 ++ .../SQL_VALIDATION_PUMP_FEES_0_7_55.sql | 615 +++++ 23 files changed, 4118 insertions(+), 107 deletions(-) create mode 100644 docs/prompts/PROMPT_0_7_56_METEORA_DBC.md create mode 100644 docs/reports/PUMP_FEES_EVENT_COVERAGE_REPORT.md create mode 100644 idls/squads.SQDS4ep65T869zMMBKyuUq6aD6EgTu8psMjkvj52pCf.json create mode 100644 idls/tensor-cnft.TCMPhJdwDryooaGtiocG1u3xcYbRpiJzb283XfCZsDp.json create mode 100644 kb_lib/src/dex/pump_fees.rs create mode 100644 validation_sql/SQL_VALIDATION_PUMP_FEES_0_7_55.sql diff --git a/CHANGELOG.md b/CHANGELOG.md index d596c5b..ee5c129 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -87,3 +87,4 @@ 0.7.52 - Raydium Stable Swap event coverage clôturé : decoder legacy 1 octet pour la surface locale `00..0d`, matérialisation lifecycle/liquidity/admin/fee/orderbook selon contexte, swaps `swap_base_in/out` matérialisés uniquement depuis deltas de vaults exacts (`stable_swap_vault_balance_delta`), conservation des bornes d’instruction comme audit-only, failed transactions decoded-only avec skip reasons, validation locale 407 tests et clippy `-D warnings` OK. 0.7.53 - Clôture PumpSwap : décodage transaction/log complet, matérialisation `buy/sell/buy_exact_quote_in` depuis sources exactes, events Anchor audit-only, tests synthétiques IDL, validation globale coverage SQL et non-régression Raydium. 0.7.54 - Clôture Pump.fun : decoder maximal local depuis IDL Solscan/upstream, décodage des 40 instructions et 23 events Anchor connus, matérialisation validée des trades `buy/sell/buy_exact_sol_in` et `trade_event` v2/exact sans double-count, non-trades launch/fee/reward/admin selon contexte, validation SQL Pump.fun propre et ouverture de `0.7.55 pump_fees`. +0.7.55 - Clôture Pump Fees : decoder local maximal `pump_fees` depuis l'IDL locale, `29` instructions et `20` events Anchor couverts, tests synthétiques des Anchor IDL non observés, matérialisation prudente fee/reward/admin/lifecycle, `get_fees` decoded-only, transactions failed audit-only, aucun trade/candle direct et SQL de validation Pump Fees propre. diff --git a/Cargo.toml b/Cargo.toml index 35064e6..d3800ad 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,7 +8,7 @@ members = [ ] [workspace.package] -version = "0.7.54" +version = "0.7.55" edition = "2024" license = "MIT" repository = "https://git.sasedev.com/Sasedev/khadhroony-bobobot" diff --git a/README.md b/README.md index dcb47a1..2aefbe2 100644 --- a/README.md +++ b/README.md @@ -3,6 +3,53 @@ # khadhroony-bobobot + +## État final validé `0.7.55` — `pump_fees` + +La tranche `0.7.55 pump_fees` est clôturée côté decoder local maximal, coverage, tests synthétiques, matérialisation prudente et validation SQL. Le programme `pfeeUxB6jkeY1Hxd7CsFCAjcbHA9rWtchMGdZ6VojVZ` est traité comme surface fee/config/accounting : `get_fees` reste decoded-only, les claims social fee alimentent `k_sol_reward_events`, les donation/buyback alimentent `k_sol_fee_events`, les authority/config/tier/update alimentent `k_sol_pool_admin_events`, les créations/init/extend alimentent `k_sol_pool_lifecycle_events`, et aucun trade/candle direct n'est produit. + +Source IDL locale prioritaire : + +```text +idls/pump_fees.pfeeUxB6jkeY1Hxd7CsFCAjcbHA9rWtchMGdZ6VojVZ.json +``` + +Surface couverte : `29` instructions, `20` events Anchor, `9` accounts et `34` types. Les discriminators Solscan hors IDL locale `revoke_fee_sharing_authority_event` (`7217653c0ebe993e`) et `transfer_fee_sharing_authority_event` (`7c8fc6f54db808ec`) restent conservés en coverage comme surfaces futures `upstream_git_mapped_unverified`. + +Validation locale finale rapportée : + +```text +cargo test -p kb_lib -> 431 passed / 0 failed +cargo clippy -p kb_lib --all-targets -- -D warnings -> OK +127 replayed +0 decode skipped +150 ledger upserts +125 unsafe ledger rows +4 trades +0 liquidity +115 lifecycle +0 tokenAccount +16 candle upserts +instructionObservations = 2234 +resetDeleted = 1644 +catalog = 11 tokens / 10 pools / 10 pairs +``` + +Checks de fermeture Pump Fees : + +- fallback `upstream_git` `pump_fees` : vide ; +- `instruction_name` `pump_fees` vide : vide ; +- `event_family = unknown` ou vide pour instruction/event : vide ; +- decoded `pump_fees` sans coverage : vide ; +- fallback résiduel pour entrées couvertes localement : vide ; +- successful non-materialized sans skip/policy : vide ; +- failed transaction avec business materialization : vide ; +- multi-target materialization : vide ; +- anti-trade/candle direct `pump_fees` : vide ; +- watchlist globale : plus aucun `pump_fees`, reste seulement `jupiter_swap.route_v2` observé ponctuellement. + +Le SQL de validation propre et exécutable reste `validation_sql/SQL_VALIDATION_PUMP_FEES_0_7_55.sql`. + ## État final validé `0.7.54` — `pump_fun` La tranche `0.7.54 pump_fun` est clôturée côté coverage, décodage local maximal, matérialisation métier prudente et validation SQL. Elle ferme la surface Pump.fun principale avant l'ouverture de `0.7.55 pump_fees`. @@ -66,9 +113,8 @@ Checks de fermeture : - failed transaction avec business materialization : vide ; - multi-target materialization : vide ; - trade candidates Pump.fun sans matérialisation ni skip : vide ; -- watchlist globale : plus aucun `pump_fun`, backlog restant principalement `pump_fees`, puis `jupiter_swap` et `dflow_aggregator_v4`. +- watchlist globale : plus aucun `pump_fun` ; la surface suivante `pump_fees` a été traitée en `0.7.55`. -La suite immédiate est `0.7.55 pump_fees` sur nouvelle base SQLite, avec politique identique : tout ce qui peut être décodé doit l'être, et tout ce qui peut être matérialisé de manière fiable doit l'être. ## État final validé `0.7.53` — `pump_swap` @@ -555,14 +601,14 @@ Si une requête DB est ajoutée ou modifiée, mettre à jour les re-exports dans ## 8. Priorité immédiate -La priorité immédiate après la clôture `0.7.54 pump_fun` est la suivante : +La priorité immédiate après la clôture `0.7.55 pump_fees` est la suivante : -1. ouvrir `0.7.55 pump_fees` sur une base SQLite neuve ; -2. décoder toutes les instructions et tous les events connus de `idls/pump_fees.pfeeUxB6jkeY1Hxd7CsFCAjcbHA9rWtchMGdZ6VojVZ.json` ; -3. matérialiser tout ce qui est prouvable : fee accounting, fee sharing, social/donation fee PDA, buyback, config/admin, rewards éventuelles ; -4. ne créer aucun trade/candle direct pour `pump_fees` sauf preuve transactionnelle forte d'un swap économique autonome ; -5. maintenir les checks Pump.fun/PumpSwap/Raydium en non-régression ; -6. reporter Meteora/Jupiter/dFlow aux tranches prévues sauf si une dépendance stricte apparaît pendant l'analyse `pump_fees`. +1. ouvrir `0.7.56 meteora_dbc` sur une base SQLite neuve ; +2. décoder toutes les instructions/events DBC connus par l'IDL locale et les sources Git ; +3. matérialiser uniquement ce qui est prouvable : launch/bonding, swaps fiables, migration, liquidity, fees/admin/config ; +4. ne créer aucun trade/candle DBC sans montants, sens économique, pool/pair et mints fiables ; +5. maintenir les checks Pump.fun/PumpSwap/Pump Fees/Raydium en non-régression ; +6. garder `jupiter_swap.route_v2` en watchlist résiduelle sans ouvrir Jupiter avant phasage dédié. Garde-fous constants : @@ -674,8 +720,9 @@ La suite fonctionnelle reprend par Raydium avant Meteora : 5. `0.7.52` — `raydium_stable_swap` — clôturé ; 6. `0.7.53` — `pump_swap` — clôturé ; 7. `0.7.54` — `pump_fun` ; -8. `0.7.55` — `pump_fees` ; -9. `0.7.56+` — Meteora, routers/agrégateurs, Phoenix/OpenBook, Orca puis les autres DEX/surfaces. +8. `0.7.55` — `pump_fees` — clôturé ; +9. `0.7.56` — `meteora_dbc` ; +10. `0.7.57+` — Meteora restants, routers/agrégateurs, Phoenix/OpenBook, Orca puis les autres DEX/surfaces. `raydium_pool_v4.json` reste repoussé en audit conditionnel tardif, pas une tranche bloquante. diff --git a/ROADMAP.md b/ROADMAP.md index 82e8ccd..646f147 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -2,30 +2,37 @@ # khadhroony-bobobot — Roadmap -## État courant — clôture `0.7.54 pump_fun` et ouverture `0.7.55 pump_fees` +### `0.7.55 pump_fees` — clôturé -`0.7.54 pump_fun` est clos pour la surface Pump.fun principale. La tranche a transformé l'ouverture documentaire en decoder local maximal : inventaire des `40` instructions et `23` events Anchor de l'IDL locale, coverage local, décodage Anchor/self-CPI/log, tests synthétiques, routes catalogue et matérialisation prudente. +- Decoder local `pump_fees` clôturé pour les `29` instructions et `20` events Anchor de l'IDL locale. +- Registre enrichi avec les entrées locales absentes du decoder Carbon partiel et conservation de deux discriminators Solscan hors IDL comme surfaces futures. +- Classification validée : `get_fees` decoded-only, social fee claim vers reward, donation/buyback vers fee, config/authority/tier/admin vers admin/lifecycle. +- Tests synthétiques ajoutés pour les Anchor events IDL non observés dans le corpus. +- Invariants propres : aucun fallback `pump_fees`, aucun decoded sans coverage, aucune failed tx matérialisée, aucun multi-target, aucun trade/candle direct. +- Dernier replay : `127 replayed`, `150 ledger upserts`, `125 unsafe`, `115 lifecycle`, `2234 instructionObservations`, catalogue `11 tokens / 10 pools / 10 pairs`. -Décisions de clôture Pump.fun : +## État courant — clôture `0.7.55 pump_fees` et ouverture `0.7.56 meteora_dbc` -- `pump_fun.buy` et `pump_fun.sell` restent matérialisés directement quand les montants sont fiables ; -- `pump_fun.buy_exact_sol_in` est matérialisé directement, y compris quand un `Program data` tronqué permet d'extraire les montants exacts ; -- `pump_fun.buy_v2`, `pump_fun.sell_v2` et `pump_fun.buy_exact_quote_in_v2` sont decoded/audit/routing, mais ne sont pas matérialisés directement ; -- `pump_fun.trade_event` devient la source canonique des montants exécutés pour les v2/exact quand il est corrélé à l'instruction sans ambiguïté ; -- les `trade_event` déjà couverts par un trade direct reçoivent un skip explicite pour empêcher le double-count ; -- les non-trades Pump.fun sont matérialisés seulement vers `launch`, `fee`, `reward`, `admin` ou `lifecycle` quand le contexte est fiable ; sinon ils restent decoded-only/audit-only avec skip reason ; -- les validations `Q00`, `Q04`, `Q05`, `Q06`, `Q07`, `Q08`, `Q11` sont propres ; la watchlist globale ne contient plus de `pump_fun`. +`0.7.55 pump_fees` est clos pour la surface Pump Fees. La tranche couvre les `29` instructions et `20` events Anchor de l'IDL locale, avec tests synthétiques pour les Anchor events non observés. `get_fees` reste decoded-only, les rewards/fees/admin/lifecycle sont matérialisés uniquement sur transactions réussies et données fiables, les failed tx restent audit-only, et aucun trade/candle direct `pump_fees` n'est créé. -Replay final rapporté : `1679 replayed`, `89 trades`, `10 lifecycle`, `348 candle upserts`, `13905 instructionObservations`, catalogue `52 tokens / 50 pools / 50 pairs`. +Décisions de clôture Pump Fees : -### Phasage immédiat après `0.7.54` +- `pump_fees.get_fees` est decoded-only et ne représente pas une fee payée ; +- `claim_social_fee_pda*` et `social_fee_pda_claimed` alimentent `k_sol_reward_events` quand la transaction réussit ; +- `crank_donation_fee_pda`, `donation_fee_pda_cranked`, `sweep_buyback` et `sweep_buyback_event` alimentent `k_sol_fee_events` quand les montants sont fiables ; +- fee sharing/config/authority/tier/update alimentent `k_sol_pool_admin_events` ou `k_sol_pool_lifecycle_events` selon le contexte ; +- les discriminators Solscan `revoke_fee_sharing_authority_event` et `transfer_fee_sharing_authority_event` restent conservés en coverage comme surfaces futures non observées ; +- les validations fallback, decoded sans coverage, failed materialization, multi-target, successful non-materialized, anti-trade/candle et watchlist `pump_fees` sont propres. + +Replay final rapporté : `127 replayed`, `0 decode skipped`, `150 ledger upserts`, `125 unsafe`, `115 lifecycle`, `2234 instructionObservations`, catalogue `11 tokens / 10 pools / 10 pairs`. Tests : `431 passed`, clippy OK. + +### Phasage immédiat après `0.7.55` | Priorité | Tranche | Surface | Raison | |---:|---|---|---| -| 1 | `0.7.55` | `pump_fees` | Backlog global restant : `get_fees`, `create_fee_sharing_config`, `update_fee_shares`; programme fee associé à Pump. Tout décoder et tout matérialiser si fiable. | -| 2 | `0.7.56+` | `meteora_*` | Corriger les gaps locaux Meteora reportés volontairement. | -| 3 | ultérieur | `jupiter_swap` / agrégateurs | Routes et comptes auxiliaires à traiter sans double-count des DEX effectifs. | - +| 1 | `0.7.56` | `meteora_dbc` | Prochaine tranche programmée : launch/bonding, swaps exploitables, migration, fees/admin/config depuis corpus neuf. | +| 2 | `0.7.57+` | `meteora_*` | Corriger les gaps locaux Meteora reportés volontairement, surface par surface. | +| 3 | ultérieur | `jupiter_swap` / agrégateurs | `jupiter_swap.route_v2` reste en watchlist résiduelle ; traiter sans double-count des DEX effectifs. | ## 0.7.47-1FE5 — Décision de planification : ne plus viser “tous les events en une session” @@ -64,7 +71,7 @@ Exceptions : les comptes non-programmes (`platform_config`, token authority, com |---|---|---|---|---| | `0.7.53` | `pump_swap` | `pAMMBay6oceH9fJKBRHGP5D4bD4sWpmSwMn52FMfXEA` | Pump / AMM | **Clos** : `buy/sell/buy_exact_quote_in` matérialisés seulement depuis sources exactes ; events Anchor audit-only ; tests synthétiques IDL ; SQL global. | | `0.7.54` | `pump_fun` | `6EF8rrecthR5Dkzon8Nwu78hRvfCKubJ14M5uBEwF6P` | Pump / launch-bonding | **Clos** : decoder maximal IDL/local, trades directs `buy/sell/buy_exact_sol_in`, v2/exact via `trade_event`, non-trades matérialisés selon contexte, validations Pump.fun propres. | -| `0.7.55` | `pump_fees` | `pfeeUxB6jkeY1Hxd7CsFCAjcbHA9rWtchMGdZ6VojVZ` | Pump / fee | Couvrir fee accounting/claim/config observés ; aucun trade/candle direct. | +| `0.7.55` | `pump_fees` | `pfeeUxB6jkeY1Hxd7CsFCAjcbHA9rWtchMGdZ6VojVZ` | Pump / fee | **Clos** : `29` instructions, `20` events Anchor, fee/reward/admin/lifecycle, `get_fees` decoded-only, failed tx audit-only, aucun trade/candle direct. | | `0.7.56` | `meteora_dbc` | `dbcij3LWUppWqq96dh6gJWwBifmcGfLSB5D4DuSMaqN` | Meteora / DBC | Compléter launch/bonding, swaps exploitables, migration, fees/admin/config. | | `0.7.57` | `meteora_dlmm` | `LBUZKhRxPF3XUpBCjp4YzTKgLccjZhTSDM9YuVaPwxo` | Meteora / DLMM | Parité upstream finale : swaps, bins, positions, liquidity, fees/rewards/admin. | | `0.7.58` | `meteora_damm_v1` | `Eo7WjKq67rjJQSZxS6z3YkapzY3eMj6Xy8X5EQVn5UaB` | Meteora / DAMM v1 | Parité upstream finale : pools, swaps, liquidity, lock, fees/admin. | diff --git a/docs/DEX_DECODER_MATRIX.md b/docs/DEX_DECODER_MATRIX.md index 9ab3732..3c0598d 100644 --- a/docs/DEX_DECODER_MATRIX.md +++ b/docs/DEX_DECODER_MATRIX.md @@ -1,9 +1,17 @@ -# DEX Decoder Matrix — `khadhroony-bobobot` `0.7.54 pump_fun closed` +# DEX Decoder Matrix — `khadhroony-bobobot` `0.7.55 pump_fees closed` -## Note `0.7.54 closed` — Pump.fun clos, Pump Fees ensuite +## Note `0.7.55 closed` — Pump Fees clôturé, Meteora DBC ensuite + +La tranche `0.7.55` ferme `pump_fees` comme surface fee/config/accounting. Le decoder local couvre les `29` instructions et `20` events Anchor de l'IDL locale, avec tests synthétiques pour les Anchor events IDL non observés. Les transactions failed restent audit-only, `get_fees` reste decoded-only, et aucun trade/candle direct n'est créé. + +Deux discriminators Solscan non présents dans l'IDL locale restent conservés en coverage comme surfaces futures : `revoke_fee_sharing_authority_event` (`7217653c0ebe993e`) et `transfer_fee_sharing_authority_event` (`7c8fc6f54db808ec`). + +La prochaine tranche programmée est `0.7.56 meteora_dbc`. + +## Note `0.7.54 closed` — Pump.fun clôturé La tranche `0.7.54` ferme `pump_fun` avant `pump_fees`. La surface Pump.fun principale est couverte depuis le code local, l'IDL Solscan locale `idls/pump_fun.6EF8rrecthR5Dkzon8Nwu78hRvfCKubJ14M5uBEwF6P.json`, le registre upstream et le corpus SQLite. @@ -16,7 +24,7 @@ Décisions structurantes : - `pump_fun.trade_event` matérialise les v2/exact quand les montants exécutés et la corrélation instruction sont prouvés ; - les non-trades Pump.fun alimentent uniquement les tables business adaptées ou restent audit-only avec skip reason. -La prochaine tranche est `0.7.55 pump_fees`. +`pump_fees` est clôturé en `0.7.55`; la prochaine tranche est `0.7.56 meteora_dbc`. ## Note `0.7.53 final` — PumpSwap clôturé et sources IDL locales @@ -59,18 +67,19 @@ Cette matrice complète `kb_lib/src/dex_support_matrix.rs`. Elle documente **ce | 5 | `raydium_stable_swap` | `supported / 0.7.52 closed` | Decoder legacy 1 octet, surface `00..0d`, swaps matérialisés depuis deltas vault exacts. | Surveiller seulement de nouveaux discriminants ou `swap_event` observé. | | 6 | `raydium_pool_v4` | `to_verify / late-phase conditional audit` | IDL annexe mentionnée par fnzero, non présente dans l'archive locale, pas de program id/rôle confirmé ici. | Ne pas promouvoir tant que program id distinct, rôle exact et corpus exploitable ne sont pas confirmés. | | 7 | `pump_swap` | `supported / 0.7.53 closed` | `buy`, `sell` + `buy_exact_quote_in` matérialisable via `BuyEvent` exact ; instructions non-trade spécialisées : liquidity, fee/creator fee, admin/config, cashback/token incentives, volume accumulator ; events Anchor autonomes audit-only. | Trades/candles uniquement depuis montants exacts ; failed tx decoded-only ; `instruction_bounds_only` reste decoded-only ; tests synthétiques IDL et SQL global ajoutés. | -| 8 | `pump_fun` | `supported / 0.7.54 closed` | Surface launch/bonding/migration Pump.fun couverte localement ; trades directs et `trade_event` canonique validés. | Ne rouvrir que pour bug prouvé ou changement externe ; `pump_fees` suit en `0.7.55`. | -| 9 | `meteora_dbc` | `partial / 0.7.56 planned` | Swaps/instruction audits observés ; Demo3 donne du corpus. | Couverture complète DBC : launch/bonding curve, swap, migration, config/admin, fees ; matérialiser seulement ce qui est prouvé. | -| 10 | `meteora_dlmm` | `supported / 0.7.57 parity` | Couverture avancée validée en `0.7.45` : swaps, liquidity, positions, lifecycle, fees ; non-trade matérialisé. | Résoudre les audits résiduels non mappés ; comparer Carbon/IDL pour events rewards/admin restants ; revalidation base neuve. | -| 11 | `meteora_damm_v1` | `supported / 0.7.58 parity` | Couverture `0.7.46` : swap, create_pool, add/remove liquidity, claim_fee, create_lock_escrow, lock_liquidity. | Vérifier les surfaces upstream non observées ; améliorer rattachement pool/pair pour remove_liquidity non matérialisés ; revalidation stricte. | -| 12 | `meteora_damm_v2` | `partial / 0.7.59 planned` | `swap`, `instruction_audit`, registry/discriminants et corpus Demo3 existent. | Couvrir tous les events Carbon/source : create pool, liquidity, fees, dynamic config, admin ; déterminer actionability des swaps ; matérialiser si montants fiables. | -| 13 | `phoenix_v1` | `audit-only / 0.7.60 planned` | Decoder local audit-only ; `log_audit`, order place/cancel, withdraw ; parsing strict `0x0f`; events `Reduce`, `Place`, `TimeInForce` observés ; `trade_count=0`. | Terminer tous les events Git : `Fill`, `FillSummary`, `Fee`, `Evict`, `ExpiredOrder`, etc. ; ajouter counts/flags audit ; seulement ensuite étudier trade materialization. | -| 14 | `openbook_v2` | `audit-only / 0.7.61 planned` | Decoder local audit-only ; instructions order/cancel/consume/settle ; `Program data` mappé : `FillLog`, `OpenOrdersPositionLog`, `TotalOrderFillEvent`, `SettleFundsLog`; `trade_count=0`. | Vérifier layouts fill/out et sens maker/taker/base/quote ; ajouter table audit éventuelle ; ne matérialiser trades qu’après validation du sens économique. | -| 15 | `orca_whirlpools` | `partial / 0.7.62 planned` | Premier decoder historique présent ; swaps/create_pool partiels. | Comparer Carbon/IDL complet ; couvrir liquidity, positions, fees/rewards, tick arrays ; valider swaps exploitables et non-trades. | -| 16 | `legacy_launch_candidates` | `planned launch` | Anciennes entrées launch à réévaluer après `raydium_launchpad`. | Ne pas confondre Launchpad, LaunchLab, CPMM/CLMM/AMM v4 ; rattacher aux pools tradables seulement après corpus. | -| 17 | `meteora_vault` | `to_verify` | Présent comme indice upstream / compte associé. | Corpus direct obligatoire ; decoder séparé si events vault réels ; aucune promotion via DAMM indirect. | -| 18 | `fluxbeam` | `partial/to_verify` | Decoder initial existant ; Demo3 peut produire des candidats. | Vérifier source/IDL ; compléter swap, pool, liquidity, fees/admin ; matérialisation uniquement après corpus. | -| 19 | `dexlab` | `partial/to_verify` | Decoder initial historique ; ancienne entrée beta supprimée. | Reconfirmer program id/source ; décoder events disponibles ; distinguer DexLab natif et liens OpenBook/market. | +| 8 | `pump_fun` | `supported / 0.7.54 closed` | Surface launch/bonding/migration Pump.fun couverte localement ; trades directs et `trade_event` canonique validés. | Ne rouvrir que pour bug prouvé ou changement externe. | +| 9 | `pump_fees` | `supported / 0.7.55 closed` | Surface fee/config/accounting couverte localement : `29` instructions, `20` events Anchor, fee/reward/admin/lifecycle, tests synthétiques Anchor IDL non observés, failed tx audit-only. | Aucun trade/candle direct ; conserver les deux discriminators Solscan hors IDL comme futures surfaces non observées. | +| 10 | `meteora_dbc` | `partial / 0.7.56 planned` | Swaps/instruction audits observés ; Demo3 donne du corpus. | Couverture complète DBC : launch/bonding curve, swap, migration, config/admin, fees ; matérialiser seulement ce qui est prouvé. | +| 11 | `meteora_dlmm` | `supported / 0.7.57 parity` | Couverture avancée validée en `0.7.45` : swaps, liquidity, positions, lifecycle, fees ; non-trade matérialisé. | Résoudre les audits résiduels non mappés ; comparer Carbon/IDL pour events rewards/admin restants ; revalidation base neuve. | +| 12 | `meteora_damm_v1` | `supported / 0.7.58 parity` | Couverture `0.7.46` : swap, create_pool, add/remove liquidity, claim_fee, create_lock_escrow, lock_liquidity. | Vérifier les surfaces upstream non observées ; améliorer rattachement pool/pair pour remove_liquidity non matérialisés ; revalidation stricte. | +| 13 | `meteora_damm_v2` | `partial / 0.7.59 planned` | `swap`, `instruction_audit`, registry/discriminants et corpus Demo3 existent. | Couvrir tous les events Carbon/source : create pool, liquidity, fees, dynamic config, admin ; déterminer actionability des swaps ; matérialiser si montants fiables. | +| 14 | `phoenix_v1` | `audit-only / 0.7.60 planned` | Decoder local audit-only ; `log_audit`, order place/cancel, withdraw ; parsing strict `0x0f`; events `Reduce`, `Place`, `TimeInForce` observés ; `trade_count=0`. | Terminer tous les events Git : `Fill`, `FillSummary`, `Fee`, `Evict`, `ExpiredOrder`, etc. ; ajouter counts/flags audit ; seulement ensuite étudier trade materialization. | +| 15 | `openbook_v2` | `audit-only / 0.7.61 planned` | Decoder local audit-only ; instructions order/cancel/consume/settle ; `Program data` mappé : `FillLog`, `OpenOrdersPositionLog`, `TotalOrderFillEvent`, `SettleFundsLog`; `trade_count=0`. | Vérifier layouts fill/out et sens maker/taker/base/quote ; ajouter table audit éventuelle ; ne matérialiser trades qu’après validation du sens économique. | +| 16 | `orca_whirlpools` | `partial / 0.7.62 planned` | Premier decoder historique présent ; swaps/create_pool partiels. | Comparer Carbon/IDL complet ; couvrir liquidity, positions, fees/rewards, tick arrays ; valider swaps exploitables et non-trades. | +| 17 | `legacy_launch_candidates` | `planned launch` | Anciennes entrées launch à réévaluer après `raydium_launchpad`. | Ne pas confondre Launchpad, LaunchLab, CPMM/CLMM/AMM v4 ; rattacher aux pools tradables seulement après corpus. | +| 18 | `meteora_vault` | `to_verify` | Présent comme indice upstream / compte associé. | Corpus direct obligatoire ; decoder séparé si events vault réels ; aucune promotion via DAMM indirect. | +| 19 | `fluxbeam` | `partial/to_verify` | Decoder initial existant ; Demo3 peut produire des candidats. | Vérifier source/IDL ; compléter swap, pool, liquidity, fees/admin ; matérialisation uniquement après corpus. | +| 20 | `dexlab` | `partial/to_verify` | Decoder initial historique ; ancienne entrée beta supprimée. | Reconfirmer program id/source ; décoder events disponibles ; distinguer DexLab natif et liens OpenBook/market. | | 19 | `lifinity_v2` | `to_verify` | Program id listé par sources externes/Vybe ; pas de corpus concluant. | Trouver IDL/source ; Demo3 par program/market ; audit-only d’abord. | | 20 | `stabble_stable_swap` / `stabble_weighted_swap` | `to_verify` | Program ids/indices via sources externes ; candidats Demo3 observables. | Source/IDL + corpus + decoder audit-only ; déterminer surface AMM et montants exploitables. | | 21 | `bonkswap` | `to_verify` | Program id/Carbon/Vybe selon registre ; swaps candidats possibles. | Vérifier program id, source et corpus ; décoder tous events ; pas de trade sans montants. | @@ -151,7 +160,7 @@ Un event peut devenir `materialized` uniquement si : | `launchbeam` | `launch_surface` | `launch` | `unknown` | non | non | non | `planned` | program_id_to_verify | | `heaven` | `launch_surface` | `launch` | `to_verify` | non | non | non | `planned` | program_id_to_verify | | `gavel` | `to_verify` | `auction` | `to_verify` | non | non | non | `to_verify` | upstream_git_program_id_requires_local_corpus_verification | -| `pump_fees` | `to_verify` | `fee_program` | `to_verify` | non | non | non | `to_verify` | upstream_git_program_id_requires_local_corpus_verification | +| `pump_fees` | `supported` | `fee_program` | `pfeeUxB6jkeY1Hxd7CsFCAjcbHA9rWtchMGdZ6VojVZ` | non | non | non | `0.7.55 closed` | fee/reward/admin/lifecycle only ; no trade/candle | | `meteora_pools` | `dex_effective` | `AMM` | `alias_of_meteora_damm_v1` | non | non | non | `to_verify` | program_id_alias_held_by_meteora_damm_v1 | | `dflow_aggregator_v4` | `aggregator_router` | `aggregator` | `to_verify` | non | non | non | `to_verify` | upstream_git_program_id_requires_local_corpus_verification | | `drift_v2` | `to_verify` | `perps` | `to_verify` | non | non | non | `to_verify` | upstream_git_program_id_requires_local_corpus_verification | @@ -310,3 +319,9 @@ La tranche a été validée sur base SQLite dédiée : tous les discriminants `0 |---|---|---:|---|---|---| | `pump_swap` | `pAMMBay6oceH9fJKBRHGP5D4bD4sWpmSwMn52FMfXEA` | supported / 0.7.53 closed | upstream registry + `idls/pump_swap.pAMMBay6oceH9fJKBRHGP5D4bD4sWpmSwMn52FMfXEA.json` + corpus Demo3/replay | instructions : `buy`, `sell`, `buy_exact_quote_in`, `deposit`, `withdraw`, `create_pool`, `create_config`, `update_fee_config`, creator-fee, cashback, token incentives, volume accumulator, admin/config ; events Anchor autonomes audit-only | `buy_exact_quote_in` trade uniquement avec `pump_swap_anchor_buy_event`; aucun non-swap en trade/candle ; failed tx decoded-only ; `buy_event`/`sell_event` Program-data audit-only ; `transfer_creator_fees_to_pump_v2` et `update_buyback_config` confirmés par Solscan IDL ; `set_reserved_fee_recipient` conservé sur preuve log locale | + +## 0.7.55 — Pump Fees + +| Decoder | Program id | Statut | Source discriminants | Couverture locale | Règles métier | +|---|---|---:|---|---|---| +| `pump_fees` | `pfeeUxB6jkeY1Hxd7CsFCAjcbHA9rWtchMGdZ6VojVZ` | supported / `0.7.55 closed` | `idls/pump_fees.pfeeUxB6jkeY1Hxd7CsFCAjcbHA9rWtchMGdZ6VojVZ.json` + Carbon partiel + Solscan discriminators | `29` instructions et `20` events Anchor couverts ; tests synthétiques pour les Anchor events IDL non observés ; replay final propre ; watchlist `pump_fees` vide | Aucun trade/candle direct ; `get_fees` decoded-only ; social claim vers reward ; donation/buyback vers fee ; config/authority/tier/admin vers admin/lifecycle ; failed tx audit-only ; deux events Solscan hors IDL conservés non observés. | diff --git a/docs/DEX_EVENT_COVERAGE_MATRIX.md b/docs/DEX_EVENT_COVERAGE_MATRIX.md index c349ac1..bc6a531 100644 --- a/docs/DEX_EVENT_COVERAGE_MATRIX.md +++ b/docs/DEX_EVENT_COVERAGE_MATRIX.md @@ -280,5 +280,32 @@ Program id unique : `pAMMBay6oceH9fJKBRHGP5D4bD4sWpmSwMn52FMfXEA`. - `pump_swap` ne présente plus de decoded event local sans coverage dans le corpus de clôture. - `buy_exact_quote_in` est matérialisé seulement quand le `BuyEvent` Anchor donne les montants exacts ; les bornes d’instruction seules restent non actionnables. - Les events Anchor `*_event` sont décodés en audit-only pour éviter les doublons, sauf exception matérialisable explicitement testée. -- Les gaps globaux restants sont classés comme backlog upstream (`pump_fun`, puis `pump_fees`, `jupiter_swap`, agrégateurs), gaps Meteora reportés, ou observations non attribuées. +- Les gaps globaux Pump.fun/PumpSwap/Pump Fees sont fermés ; la watchlist résiduelle courante ne contient plus `pump_fees` et garde seulement `jupiter_swap.route_v2` comme observation ponctuelle. - Les checks Raydium AMM v4 / CLMM / CPMM normalisés sont vides ; aucune correction Raydium n’est incluse dans cette clôture. + +## 0.7.55 — Pump Fees + +| Entry group | Discriminants | Famille | Target attendu | Local event kind | Notes | +|---|---:|---|---|---|---| +| `get_fees` | `e7257e55cf5b3f34` | audit | decoded-only | `pump_fees.get_fees` | Calcul/preview de fees ; ne pas matérialiser comme fee payé sans transfert réalisé. | +| social fee claim/create | IDL locale | reward / lifecycle | `k_sol_reward_events` ou `k_sol_pool_lifecycle_events` | `pump_fees.claim_social_fee_pda*`, `pump_fees.social_fee_pda_*` | Claim matérialisable seulement avec montant/acteur/mint fiables et transaction OK. | +| donation fee PDA | IDL locale | fee / lifecycle | `k_sol_fee_events` ou `k_sol_pool_lifecycle_events` | `pump_fees.*donation_fee_pda*` | Crank avec montant fiable vers fee ; création de PDA vers lifecycle. | +| buyback | IDL locale | fee / admin/lifecycle | `k_sol_fee_events`, `k_sol_pool_admin_events` ou lifecycle | `pump_fees.*buyback*` | Sweep matérialisable comme fee si montant fiable ; init/update comme lifecycle/admin. | +| fee sharing/config/authority/tier | IDL locale + Carbon partiel | admin_config / lifecycle | `k_sol_pool_admin_events` ou `k_sol_pool_lifecycle_events` | `pump_fees.*config*`, `pump_fees.*authority*`, `pump_fees.*tier*`, `pump_fees.update_fee_shares*` | Aucun trade/candle ; decoded-only/audit-only si données métier insuffisantes. | + +## 0.7.55 Pump Fees — clôture + +Programme : `pfeeUxB6jkeY1Hxd7CsFCAjcbHA9rWtchMGdZ6VojVZ`. Source locale prioritaire : `idls/pump_fees.pfeeUxB6jkeY1Hxd7CsFCAjcbHA9rWtchMGdZ6VojVZ.json`. + +| Famille | Entrées | Target | Statut | Notes | +|---|---|---|---|---| +| fee calculation | `get_fees` / `e7257e55cf5b3f34` | `k_sol_dex_decoded_events_only` | decoded-only validé | Calcul/preview ; ne représente pas une fee payée. | +| reward/social fee | `claim_social_fee_pda`, `claim_social_fee_pda_v2`, `social_fee_pda_claimed` | `k_sol_reward_events` | matérialisé si tx OK | Écarts expliqués par failed tx. | +| donation fee | `create_donation_fee_pda`, `crank_donation_fee_pda`, events associés | lifecycle / `k_sol_fee_events` | matérialisé si tx OK | Création PDA vers lifecycle ; crank vers fee. | +| buyback | `initialize_buyback`, `sweep_buyback`, authority/rate-limit | lifecycle / fee / admin | matérialisé si tx OK | `sweep_buyback` et `sweep_buyback_event` alimentent fee. | +| fee sharing/config/authority/tier | config/reset/transfer/revoke/update/upsert | `k_sol_pool_admin_events` ou lifecycle | matérialisé si tx OK | Aucun trade/candle ; failed tx audit-only. | +| Anchor IDL non observés | `SetAuthorityEvent`, `SetClaimRateLimitEvent`, `SetDisableFlagsEvent`, `SetSocialClaimAuthorityEvent` | admin/audit | tests synthétiques | Aucun corpus Solscan au moment de clôture ; decoder conservé pour transactions futures. | +| Solscan hors IDL locale | `revoke_fee_sharing_authority_event`, `transfer_fee_sharing_authority_event` | admin | mapped_unverified | Discriminators trouvés via Solscan ; conservés comme surfaces futures. | + +Invariants propres : fallback `pump_fees` vide, decoded sans coverage vide, successful non-materialized sans skip/policy vide, failed tx matérialisée vide, multi-target vide, anti-trade/candle direct vide, watchlist globale sans `pump_fees`. + diff --git a/docs/prompts/PROMPT_0_7_56_METEORA_DBC.md b/docs/prompts/PROMPT_0_7_56_METEORA_DBC.md new file mode 100644 index 0000000..a35895f --- /dev/null +++ b/docs/prompts/PROMPT_0_7_56_METEORA_DBC.md @@ -0,0 +1,216 @@ +# Prompt de reprise — khadhroony-bobobot 0.7.56 — meteora_dbc + +Tu reprends le workspace Rust/Tauri `khadhroony-bobobot` après clôture technique de `0.7.55 pump_fees`. + +## 1. Archive et fichiers à fournir + +Utiliser l'archive la plus récente après clôture `0.7.55 pump_fees`. + +À considérer comme sources locales de savoir : + +- code Rust du workspace ; +- `README.md`, `ROADMAP.md`, `CHANGELOG.md` ; +- `docs/DEX_DECODER_MATRIX.md` ; +- `docs/DEX_EVENT_COVERAGE_MATRIX.md` ; +- `docs/reports/PUMP_FEES_EVENT_COVERAGE_REPORT.md` ; +- `validation_sql/SQL_VALIDATION_PUMP_FEES_0_7_55.sql` ; +- `idls/**`, en particulier `idls/meteora_dbc.dbcij3LWUppWqq96dh6gJWwBifmcGfLSB5D4DuSMaqN.json` ; +- les logs/requêtes SQL collés pendant la session. + +Ne pas supposer que la documentation est parfaite : vérifier contre le code, l'IDL locale, les sources Git et le corpus SQLite. + +## 2. État validé avant cette version + +`0.7.55 pump_fees` est clos. + +Replay final rapporté : + +```text +127 replayed +0 decode skipped +150 ledger upserts +125 unsafe ledger rows +4 trades +0 liquidity +115 lifecycle +0 tokenAccount +16 candle upserts +instructionObservations = 2234 +resetDeleted = 1644 +catalog = 11 tokens / 10 pools / 10 pairs +``` + +Validation build : + +```text +cargo test -p kb_lib -> 431 passed / 0 failed +cargo clippy -p kb_lib --all-targets -- -D warnings -> OK +``` + +Checks de fermeture Pump Fees : + +- fallback `upstream_git` `pump_fees` : vide ; +- `instruction_name` vide : vide ; +- `event_family = unknown` ou vide pour instruction/event : vide ; +- decoded `pump_fees` sans coverage : vide ; +- successful non-materialized sans skip/policy : vide ; +- failed transaction materialization safety : vide ; +- multi-target materialization safety : vide ; +- anti-trade/candle direct `pump_fees` : vide ; +- watchlist globale : plus aucun `pump_fees`, seulement `jupiter_swap.route_v2` ponctuel. + +Décisions Pump Fees à préserver : + +- `get_fees` est decoded-only ; +- claims social fee vers `k_sol_reward_events` seulement si succès et montant fiable ; +- donation/buyback vers `k_sol_fee_events` seulement si succès et montant fiable ; +- config/authority/tier/update vers `k_sol_pool_admin_events` ; +- create/init/extend vers `k_sol_pool_lifecycle_events` ; +- aucun trade/candle direct ; +- failed tx audit-only ; +- les discriminators Solscan `revoke_fee_sharing_authority_event` et `transfer_fee_sharing_authority_event` restent conservés comme futures surfaces non observées. + +Ne pas rouvrir `pump_fees`, `pump_fun`, `pump_swap` ou Raydium sauf bug prouvé par SQL/code. + +## 3. Objectif de `0.7.56 meteora_dbc` + +Ouvrir et clôturer la surface `meteora_dbc`. + +Program id cible : + +```text +dbcij3LWUppWqq96dh6gJWwBifmcGfLSB5D4DuSMaqN +``` + +IDL locale prioritaire : + +```text +idls/meteora_dbc.dbcij3LWUppWqq96dh6gJWwBifmcGfLSB5D4DuSMaqN.json +``` + +Règle forte : + +> Tout ce qui peut être décodé doit être décodé. Tout ce qui peut être matérialisé de façon fiable doit être matérialisé. Ce qui ne peut pas être matérialisé doit rester decoded-only/audit-only avec `skip*Reason` explicite. + +La surface DBC doit couvrir launch/bonding curve, pool/config lifecycle, swaps exploitables, migration, fees/admin/config, et events Anchor associés. + +## 4. Méthode obligatoire : nouvelle base SQLite + +Créer une nouvelle DB dédiée à `0.7.56 meteora_dbc`. + +Ne pas réutiliser l'ancienne DB de validation Pump Fees sauf pour lire des signatures de départ. + +Après chaque backfill ou patch decoder : + +```text +skipDexDecode=no +forceDexDecode=yes +deferInstructionObservations=yes +``` + +Puis : + +- refresh catalog ; +- replay local ; +- relancer SQL de validation ; +- noter les compteurs replay. + +## 5. Corpus et backfills + +Construire le corpus local à partir de : + +1. signatures `sample_signature` de la watchlist globale et des coverage gaps ; +2. filtres Solscan.io par program id + instruction/discriminator quand disponibles ; +3. Demo3 discovery multi-source/multi-target ; +4. batch backfill par groupes de signatures ; +5. program/signature backfill ciblé si nécessaire ; +6. signatures issues des requêtes SQL `instruction_observations`, fallback upstream et decoded-only résiduels. + +Inclure explicitement les transactions failed dans le corpus d'audit, mais ne jamais les matérialiser métier. + +## 6. Sources à comparer + +Comparer au minimum : + +- `idls/meteora_dbc.dbcij3LWUppWqq96dh6gJWwBifmcGfLSB5D4DuSMaqN.json` ; +- Carbon `meteora-dbc-decoder` ; +- Pinax/Substreams Solana IDLs si disponibles ; +- Solana Streamer / sol-parser-sdk si des layouts Meteora DBC y apparaissent ; +- code local existant `kb_lib/src/dex/*meteora*` ; +- `upstream_registry_generated.rs` ; +- corpus SQLite neuf. + +## 7. Matérialisation attendue + +Ne pas se contenter de decoded-only si une matérialisation fiable est possible. + +Cibles probables : + +- swaps exploitables -> `k_sol_trade_events` + candles si montants, sens, mint base/quote et pool/pair sont fiables ; +- pool/config/create -> `k_sol_pool_lifecycle_events` et catalog/pool/pair si comptes fiables ; +- liquidity/deposit/withdraw -> `k_sol_liquidity_events` si montants et pool fiables ; +- migration -> lifecycle/admin selon sémantique exacte ; +- fees/creator fees/admin/config -> `k_sol_fee_events` ou `k_sol_pool_admin_events` ; +- events sans contexte suffisant -> decoded-only/audit-only avec skip reason. + +Transactions failed : decoded-only/audit-only, jamais business matérialisées. + +## 8. SQL de validation attendu + +Créer : + +```text +validation_sql/SQL_VALIDATION_METEORA_DBC_0_7_56.sql +``` + +Requêtes minimales : + +1. upstream fallback samples `meteora_dbc` ; +2. local instruction observations `meteora_dbc` ; +3. coverage `meteora_dbc` ; +4. decoded events `meteora_dbc` sans coverage ; +5. residual upstream fallback pour entrées couvertes ; +6. successful non-materialized sans skip reason ; +7. failed transaction materialization safety ; +8. multi-target materialization safety ; +9. materialization summary par table, avec colonnes successful/failed ; +10. instruction observation versus coverage ; +11. anti-faux trade/candle pour events non swap ; +12. global watchlist après replay. + +## 9. Invariants de fermeture + +La tranche `0.7.56` ne doit être considérée close que si : + +- aucun fallback `upstream_git` `meteora_dbc` ne reste pour les entrées couvertes localement ; +- aucun decoded event `meteora_dbc` local sans coverage ; +- aucune transaction failed n'alimente une table métier ; +- aucun event multi-target incohérent ; +- aucune ligne successful non-materialized sans `skip*Reason` ; +- aucun faux trade/candle sur event non swap ; +- toutes les instructions/events de l'IDL locale sont soit décodés/matérialisés, soit audit-only, soit non observés mais couverts par tests synthétiques ; +- la watchlist globale ne contient plus de `meteora_dbc` comme backlog dominant. + +## 10. Documentation à mettre à jour en fin de tranche + +Mettre à jour : + +- `CHANGELOG.md` ; +- `README.md` ; +- `ROADMAP.md` ; +- `docs/DEX_DECODER_MATRIX.md` ; +- `docs/DEX_EVENT_COVERAGE_MATRIX.md` ; +- créer `docs/reports/METEORA_DBC_EVENT_COVERAGE_REPORT.md` ; +- créer `validation_sql/SQL_VALIDATION_METEORA_DBC_0_7_56.sql`. + +## 11. Format de livraison attendu + +Fournir un delta zip contenant uniquement les fichiers modifiés/ajoutés. + +Nom recommandé : + +```text +khadhroony-bobobot-v0.7.56-meteora_dbc-delta-N-files.zip +``` + +Inclure dans chaque livraison : résumé des changements, liste exacte des fichiers modifiés, commandes `cargo fmt`, `cargo test -p kb_lib`, `cargo clippy -p kb_lib --all-targets -- -D warnings`, replay recommandé, SQL à exécuter et résultats attendus. diff --git a/docs/reports/PUMP_FEES_EVENT_COVERAGE_REPORT.md b/docs/reports/PUMP_FEES_EVENT_COVERAGE_REPORT.md new file mode 100644 index 0000000..245b0e9 --- /dev/null +++ b/docs/reports/PUMP_FEES_EVENT_COVERAGE_REPORT.md @@ -0,0 +1,134 @@ + + +# Pump Fees event coverage report — 0.7.55 final + +## Portée + +Programme cible : `pfeeUxB6jkeY1Hxd7CsFCAjcbHA9rWtchMGdZ6VojVZ`. + +Source locale prioritaire : `idls/pump_fees.pfeeUxB6jkeY1Hxd7CsFCAjcbHA9rWtchMGdZ6VojVZ.json`. + +L'IDL locale couvre `29` instructions, `20` events Anchor, `9` accounts et `34` types. La tranche ajoute un decoder local maximal `pump_fees` depuis l'IDL locale, le registre Carbon partiel et les discriminators observés via Solscan/corpus. + +## Décisions métier + +- `pump_fees` est traité comme programme fee/config/accounting. +- Aucun `k_sol_trade_events` ni candle ne doit être créé pour `pump_fees` sans preuve transactionnelle stricte d'un swap autonome. +- `get_fees` reste decoded-only : il décrit un calcul/preview de fees, pas un paiement réalisé. +- Les claims social fee alimentent `k_sol_reward_events` seulement si transaction OK et montant/acteur/mint exploitables. +- Les flux donation/buyback alimentent `k_sol_fee_events` seulement si transaction OK et montant fiable. +- Les créations/configurations/autorités/tiers alimentent `k_sol_pool_lifecycle_events` ou `k_sol_pool_admin_events` selon classification. +- Les transactions failed restent decoded-only/audit-only. + +## Résultat build/test + +```text +cargo test -p kb_lib -> 431 passed / 0 failed +cargo clippy -p kb_lib --all-targets -- -D warnings -> OK +``` + +## Replay final rapporté + +```text +127 replayed +0 decode skipped +150 ledger upserts +125 unsafe ledger rows +4 trades +0 liquidity +115 lifecycle +0 tokenAccount +16 candle upserts +instructionObservations = 2234 +resetDeleted = 1644 +catalog = 11 tokens / 10 pools / 10 pairs +``` + +Les `4 trades` et `16 candle upserts` du replay proviennent d'autres surfaces du corpus local ; le contrôle anti-trade `pump_fees` est vide. + +## Coverage local + +### Instructions observées et couvertes + +| Instruction | Discriminator | Observation principale | Matérialisation | +|---|---:|---:|---| +| `sweep_buyback` | `8a21cc26cfa19fe2` | `96` | `k_sol_fee_events` | +| `create_fee_sharing_config` | `c34e564c6f34fbd5` | `42` observations / `36` decoded | `k_sol_pool_lifecycle_events` | +| `update_fee_shares` | `bd0d8863bba4ed23` | `26` observations / `16` decoded | `k_sol_pool_admin_events` | +| `claim_social_fee_pda_v2` | `114df0863abc3595` | `25` | `k_sol_reward_events` | +| `update_fee_shares_v2` | `6ffb31064e4e6a12` | `19` | `k_sol_pool_admin_events` | +| `claim_social_fee_pda` | `e115fb85a11ec7e2` | `15` observations / `10` decoded | `k_sol_reward_events` | +| `crank_donation_fee_pda` | `dc0abda7a9111945` | `14` | `k_sol_fee_events` | +| `get_fees` | `e7257e55cf5b3f34` | `13` observations / `5` decoded | decoded-only | +| `transfer_fee_sharing_authority` | `ca0a4bc8a422d260` | `12` | `k_sol_pool_admin_events` | +| `initialize_fee_program_global` | `23d78254e9387ca7` | `1` | `k_sol_pool_lifecycle_events` | + +Le discriminator `e445a52e51cb9a1d` est classé comme `pump_fees.anchor_self_cpi_log` transport Anchor self-CPI : `271` observations / `119` tx. + +### Instructions IDL non observées mais programmées + +Ces entrées n'ont pas de signature Solscan/corpus au moment de la clôture, mais restent décodables si des transactions futures apparaissent : + +| Instruction | Discriminator | Classification | Target prévu | +|---|---:|---|---| +| `reset_fee_sharing_config_v2` | `a9f511d15e5bf880` | admin_config | `k_sol_pool_admin_events` | +| `set_authority` | `85fa25156ea31a79` | admin_config | `k_sol_pool_admin_events` | +| `set_claim_rate_limit` | `b9d39faed4315804` | audit | decoded-only | +| `set_disable_flags` | `c2d9702372de33be` | admin_config | `k_sol_pool_admin_events` | +| `set_social_claim_authority` | `9336b89a88edb999` | admin_config | `k_sol_pool_admin_events` | + +### Anchor events IDL non observés mais testés synthétiquement + +| Event Anchor | Discriminator | Statut | +|---|---:|---| +| `SetAuthorityEvent` | `12af8442d0c957f2` | decoder + test synthétique | +| `SetClaimRateLimitEvent` | `0d8f8febb5133328` | decoder + test synthétique | +| `SetDisableFlagsEvent` | `0508b3413137917e` | decoder + test synthétique | +| `SetSocialClaimAuthorityEvent` | `3c767f84ef34fe0e` | decoder + test synthétique | + +### Discriminators Solscan hors IDL locale + +Ces discriminators ont été trouvés par filtres Solscan, mais ne sont pas observés dans le corpus local final et ne sont pas dans l'IDL locale fournie. Ils restent conservés en coverage comme surfaces futures : + +| Event | Discriminator | Statut | +|---|---:|---| +| `revoke_fee_sharing_authority_event` | `7217653c0ebe993e` | `upstream_git_mapped_unverified` | +| `transfer_fee_sharing_authority_event` | `7c8fc6f54db808ec` | `upstream_git_mapped_unverified` | + +## Écarts observed/materialized + +Les écarts `observed_count > materialized_count` sont expliqués par des transactions failed ou par une politique decoded-only. La requête `09b` documente notamment : + +| Entrée | Observed | Materialized | Explication | +|---|---:|---:|---| +| `get_fees` | `5` | `0` | decoded-only volontaire | +| `claim_social_fee_pda_v2` | `25` | `21` | `4` failed decoded | +| `create_fee_sharing_config` | `36` | `33` | `3` failed decoded | +| `initialize_fee_config` | `5` | `2` | `3` failed decoded | +| `social_fee_pda_claimed` | `17` | `15` | `2` failed decoded | +| `crank_donation_fee_pda` | `14` | `12` | `2` failed decoded | +| `update_fee_shares_v2` | `19` | `17` | `2` failed decoded | +| `revoke_fee_sharing_authority` | `6` | `5` | `1` failed decoded | + +La requête `06` confirme qu'il n'existe aucun successful non-materialized sans skip/policy explicite. + +## Checks de fermeture + +SQL dédié : `validation_sql/SQL_VALIDATION_PUMP_FEES_0_7_55.sql`. + +Résultats de fermeture rapportés : + +- fallback `upstream_git` `pump_fees` : vide ; +- `instruction_name` `pump_fees` vide : vide ; +- `event_family = unknown` ou vide pour instruction/event : vide ; +- decoded `pump_fees` sans coverage : vide ; +- fallback résiduel pour entrées couvertes localement : vide ; +- successful non-materialized sans skip/policy : vide ; +- failed transaction avec business materialization : vide ; +- multi-target materialization : vide ; +- anti-trade/candle direct `pump_fees` : vide ; +- watchlist globale : plus aucun `pump_fees`, reste seulement `jupiter_swap.route_v2` comme backlog ponctuel. + +## Statut final + +`0.7.55 pump_fees` est clôturé techniquement. Ne pas rouvrir `pump_fees`, `pump_fun`, `pump_swap` ou Raydium sans bug prouvé par SQL/code ou apparition d'un nouveau discriminant/corpus externe exploitable. diff --git a/idls/squads.SQDS4ep65T869zMMBKyuUq6aD6EgTu8psMjkvj52pCf.json b/idls/squads.SQDS4ep65T869zMMBKyuUq6aD6EgTu8psMjkvj52pCf.json new file mode 100644 index 0000000..92bcbac --- /dev/null +++ b/idls/squads.SQDS4ep65T869zMMBKyuUq6aD6EgTu8psMjkvj52pCf.json @@ -0,0 +1 @@ +{"version":"2.0.0","name":"squads_multisig_program","instructions":[{"name":"programConfigInit","docs":["Initialize the program config."],"accounts":[{"name":"programConfig","isMut":true,"isSigner":false},{"name":"initializer","isMut":true,"isSigner":true,"docs":["The hard-coded account that is used to initialize the program config once."]},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[{"name":"args","type":{"defined":"ProgramConfigInitArgs"}}]},{"name":"programConfigSetAuthority","docs":["Set the `authority` parameter of the program config."],"accounts":[{"name":"programConfig","isMut":true,"isSigner":false},{"name":"authority","isMut":false,"isSigner":true}],"args":[{"name":"args","type":{"defined":"ProgramConfigSetAuthorityArgs"}}]},{"name":"programConfigSetMultisigCreationFee","docs":["Set the `multisig_creation_fee` parameter of the program config."],"accounts":[{"name":"programConfig","isMut":true,"isSigner":false},{"name":"authority","isMut":false,"isSigner":true}],"args":[{"name":"args","type":{"defined":"ProgramConfigSetMultisigCreationFeeArgs"}}]},{"name":"programConfigSetTreasury","docs":["Set the `treasury` parameter of the program config."],"accounts":[{"name":"programConfig","isMut":true,"isSigner":false},{"name":"authority","isMut":false,"isSigner":true}],"args":[{"name":"args","type":{"defined":"ProgramConfigSetTreasuryArgs"}}]},{"name":"multisigCreate","docs":["Create a multisig."],"accounts":[{"name":"multisig","isMut":true,"isSigner":false},{"name":"createKey","isMut":false,"isSigner":true,"docs":["An ephemeral signer that is used as a seed for the Multisig PDA.","Must be a signer to prevent front-running attack by someone else but the original creator."]},{"name":"creator","isMut":true,"isSigner":true,"docs":["The creator of the multisig."]},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[{"name":"args","type":{"defined":"MultisigCreateArgs"}}]},{"name":"multisigCreateV2","docs":["Create a multisig."],"accounts":[{"name":"programConfig","isMut":false,"isSigner":false,"docs":["Global program config account."]},{"name":"treasury","isMut":true,"isSigner":false,"docs":["The treasury where the creation fee is transferred to."]},{"name":"multisig","isMut":true,"isSigner":false},{"name":"createKey","isMut":false,"isSigner":true,"docs":["An ephemeral signer that is used as a seed for the Multisig PDA.","Must be a signer to prevent front-running attack by someone else but the original creator."]},{"name":"creator","isMut":true,"isSigner":true,"docs":["The creator of the multisig."]},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[{"name":"args","type":{"defined":"MultisigCreateArgsV2"}}]},{"name":"multisigAddMember","docs":["Add a new member to the controlled multisig."],"accounts":[{"name":"multisig","isMut":true,"isSigner":false},{"name":"configAuthority","isMut":false,"isSigner":true,"docs":["Multisig `config_authority` that must authorize the configuration change."]},{"name":"rentPayer","isMut":true,"isSigner":true,"isOptional":true,"docs":["The account that will be charged or credited in case the multisig account needs to reallocate space,","for example when adding a new member or a spending limit.","This is usually the same as `config_authority`, but can be a different account if needed."]},{"name":"systemProgram","isMut":false,"isSigner":false,"isOptional":true,"docs":["We might need it in case reallocation is needed."]}],"args":[{"name":"args","type":{"defined":"MultisigAddMemberArgs"}}]},{"name":"multisigRemoveMember","docs":["Remove a member/key from the controlled multisig."],"accounts":[{"name":"multisig","isMut":true,"isSigner":false},{"name":"configAuthority","isMut":false,"isSigner":true,"docs":["Multisig `config_authority` that must authorize the configuration change."]},{"name":"rentPayer","isMut":true,"isSigner":true,"isOptional":true,"docs":["The account that will be charged or credited in case the multisig account needs to reallocate space,","for example when adding a new member or a spending limit.","This is usually the same as `config_authority`, but can be a different account if needed."]},{"name":"systemProgram","isMut":false,"isSigner":false,"isOptional":true,"docs":["We might need it in case reallocation is needed."]}],"args":[{"name":"args","type":{"defined":"MultisigRemoveMemberArgs"}}]},{"name":"multisigSetTimeLock","docs":["Set the `time_lock` config parameter for the controlled multisig."],"accounts":[{"name":"multisig","isMut":true,"isSigner":false},{"name":"configAuthority","isMut":false,"isSigner":true,"docs":["Multisig `config_authority` that must authorize the configuration change."]},{"name":"rentPayer","isMut":true,"isSigner":true,"isOptional":true,"docs":["The account that will be charged or credited in case the multisig account needs to reallocate space,","for example when adding a new member or a spending limit.","This is usually the same as `config_authority`, but can be a different account if needed."]},{"name":"systemProgram","isMut":false,"isSigner":false,"isOptional":true,"docs":["We might need it in case reallocation is needed."]}],"args":[{"name":"args","type":{"defined":"MultisigSetTimeLockArgs"}}]},{"name":"multisigChangeThreshold","docs":["Set the `threshold` config parameter for the controlled multisig."],"accounts":[{"name":"multisig","isMut":true,"isSigner":false},{"name":"configAuthority","isMut":false,"isSigner":true,"docs":["Multisig `config_authority` that must authorize the configuration change."]},{"name":"rentPayer","isMut":true,"isSigner":true,"isOptional":true,"docs":["The account that will be charged or credited in case the multisig account needs to reallocate space,","for example when adding a new member or a spending limit.","This is usually the same as `config_authority`, but can be a different account if needed."]},{"name":"systemProgram","isMut":false,"isSigner":false,"isOptional":true,"docs":["We might need it in case reallocation is needed."]}],"args":[{"name":"args","type":{"defined":"MultisigChangeThresholdArgs"}}]},{"name":"multisigSetConfigAuthority","docs":["Set the multisig `config_authority`."],"accounts":[{"name":"multisig","isMut":true,"isSigner":false},{"name":"configAuthority","isMut":false,"isSigner":true,"docs":["Multisig `config_authority` that must authorize the configuration change."]},{"name":"rentPayer","isMut":true,"isSigner":true,"isOptional":true,"docs":["The account that will be charged or credited in case the multisig account needs to reallocate space,","for example when adding a new member or a spending limit.","This is usually the same as `config_authority`, but can be a different account if needed."]},{"name":"systemProgram","isMut":false,"isSigner":false,"isOptional":true,"docs":["We might need it in case reallocation is needed."]}],"args":[{"name":"args","type":{"defined":"MultisigSetConfigAuthorityArgs"}}]},{"name":"multisigSetRentCollector","docs":["Set the multisig `rent_collector`."],"accounts":[{"name":"multisig","isMut":true,"isSigner":false},{"name":"configAuthority","isMut":false,"isSigner":true,"docs":["Multisig `config_authority` that must authorize the configuration change."]},{"name":"rentPayer","isMut":true,"isSigner":true,"isOptional":true,"docs":["The account that will be charged or credited in case the multisig account needs to reallocate space,","for example when adding a new member or a spending limit.","This is usually the same as `config_authority`, but can be a different account if needed."]},{"name":"systemProgram","isMut":false,"isSigner":false,"isOptional":true,"docs":["We might need it in case reallocation is needed."]}],"args":[{"name":"args","type":{"defined":"MultisigSetRentCollectorArgs"}}]},{"name":"multisigAddSpendingLimit","docs":["Create a new spending limit for the controlled multisig."],"accounts":[{"name":"multisig","isMut":false,"isSigner":false},{"name":"configAuthority","isMut":false,"isSigner":true,"docs":["Multisig `config_authority` that must authorize the configuration change."]},{"name":"spendingLimit","isMut":true,"isSigner":false},{"name":"rentPayer","isMut":true,"isSigner":true,"docs":["This is usually the same as `config_authority`, but can be a different account if needed."]},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[{"name":"args","type":{"defined":"MultisigAddSpendingLimitArgs"}}]},{"name":"multisigRemoveSpendingLimit","docs":["Remove the spending limit from the controlled multisig."],"accounts":[{"name":"multisig","isMut":false,"isSigner":false},{"name":"configAuthority","isMut":false,"isSigner":true,"docs":["Multisig `config_authority` that must authorize the configuration change."]},{"name":"spendingLimit","isMut":true,"isSigner":false},{"name":"rentCollector","isMut":true,"isSigner":false,"docs":["This is usually the same as `config_authority`, but can be a different account if needed."]}],"args":[{"name":"args","type":{"defined":"MultisigRemoveSpendingLimitArgs"}}]},{"name":"configTransactionCreate","docs":["Create a new config transaction."],"accounts":[{"name":"multisig","isMut":true,"isSigner":false},{"name":"transaction","isMut":true,"isSigner":false},{"name":"creator","isMut":false,"isSigner":true,"docs":["The member of the multisig that is creating the transaction."]},{"name":"rentPayer","isMut":true,"isSigner":true,"docs":["The payer for the transaction account rent."]},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[{"name":"args","type":{"defined":"ConfigTransactionCreateArgs"}}]},{"name":"configTransactionExecute","docs":["Execute a config transaction.","The transaction must be `Approved`."],"accounts":[{"name":"multisig","isMut":true,"isSigner":false,"docs":["The multisig account that owns the transaction."]},{"name":"member","isMut":false,"isSigner":true,"docs":["One of the multisig members with `Execute` permission."]},{"name":"proposal","isMut":true,"isSigner":false,"docs":["The proposal account associated with the transaction."]},{"name":"transaction","isMut":false,"isSigner":false,"docs":["The transaction to execute."]},{"name":"rentPayer","isMut":true,"isSigner":true,"isOptional":true,"docs":["The account that will be charged/credited in case the config transaction causes space reallocation,","for example when adding a new member, adding or removing a spending limit.","This is usually the same as `member`, but can be a different account if needed."]},{"name":"systemProgram","isMut":false,"isSigner":false,"isOptional":true,"docs":["We might need it in case reallocation is needed."]}],"args":[]},{"name":"vaultTransactionCreate","docs":["Create a new vault transaction."],"accounts":[{"name":"multisig","isMut":true,"isSigner":false},{"name":"transaction","isMut":true,"isSigner":false},{"name":"creator","isMut":false,"isSigner":true,"docs":["The member of the multisig that is creating the transaction."]},{"name":"rentPayer","isMut":true,"isSigner":true,"docs":["The payer for the transaction account rent."]},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[{"name":"args","type":{"defined":"VaultTransactionCreateArgs"}}]},{"name":"vaultTransactionExecute","docs":["Execute a vault transaction.","The transaction must be `Approved`."],"accounts":[{"name":"multisig","isMut":false,"isSigner":false},{"name":"proposal","isMut":true,"isSigner":false,"docs":["The proposal account associated with the transaction."]},{"name":"transaction","isMut":false,"isSigner":false,"docs":["The transaction to execute."]},{"name":"member","isMut":false,"isSigner":true}],"args":[]},{"name":"batchCreate","docs":["Create a new batch."],"accounts":[{"name":"multisig","isMut":true,"isSigner":false},{"name":"batch","isMut":true,"isSigner":false},{"name":"creator","isMut":false,"isSigner":true,"docs":["The member of the multisig that is creating the batch."]},{"name":"rentPayer","isMut":true,"isSigner":true,"docs":["The payer for the batch account rent."]},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[{"name":"args","type":{"defined":"BatchCreateArgs"}}]},{"name":"batchAddTransaction","docs":["Add a transaction to the batch."],"accounts":[{"name":"multisig","isMut":false,"isSigner":false,"docs":["Multisig account this batch belongs to."]},{"name":"proposal","isMut":false,"isSigner":false,"docs":["The proposal account associated with the batch."]},{"name":"batch","isMut":true,"isSigner":false},{"name":"transaction","isMut":true,"isSigner":false,"docs":["`VaultBatchTransaction` account to initialize and add to the `batch`."]},{"name":"member","isMut":false,"isSigner":true,"docs":["Member of the multisig."]},{"name":"rentPayer","isMut":true,"isSigner":true,"docs":["The payer for the batch transaction account rent."]},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[{"name":"args","type":{"defined":"BatchAddTransactionArgs"}}]},{"name":"batchExecuteTransaction","docs":["Execute a transaction from the batch."],"accounts":[{"name":"multisig","isMut":false,"isSigner":false,"docs":["Multisig account this batch belongs to."]},{"name":"member","isMut":false,"isSigner":true,"docs":["Member of the multisig."]},{"name":"proposal","isMut":true,"isSigner":false,"docs":["The proposal account associated with the batch.","If `transaction` is the last in the batch, the `proposal` status will be set to `Executed`."]},{"name":"batch","isMut":true,"isSigner":false},{"name":"transaction","isMut":false,"isSigner":false,"docs":["Batch transaction to execute."]}],"args":[]},{"name":"proposalCreate","docs":["Create a new multisig proposal."],"accounts":[{"name":"multisig","isMut":false,"isSigner":false},{"name":"proposal","isMut":true,"isSigner":false},{"name":"creator","isMut":false,"isSigner":true,"docs":["The member of the multisig that is creating the proposal."]},{"name":"rentPayer","isMut":true,"isSigner":true,"docs":["The payer for the proposal account rent."]},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[{"name":"args","type":{"defined":"ProposalCreateArgs"}}]},{"name":"proposalActivate","docs":["Update status of a multisig proposal from `Draft` to `Active`."],"accounts":[{"name":"multisig","isMut":false,"isSigner":false},{"name":"member","isMut":true,"isSigner":true},{"name":"proposal","isMut":true,"isSigner":false}],"args":[]},{"name":"proposalApprove","docs":["Approve a multisig proposal on behalf of the `member`.","The proposal must be `Active`."],"accounts":[{"name":"multisig","isMut":false,"isSigner":false},{"name":"member","isMut":true,"isSigner":true},{"name":"proposal","isMut":true,"isSigner":false}],"args":[{"name":"args","type":{"defined":"ProposalVoteArgs"}}]},{"name":"proposalReject","docs":["Reject a multisig proposal on behalf of the `member`.","The proposal must be `Active`."],"accounts":[{"name":"multisig","isMut":false,"isSigner":false},{"name":"member","isMut":true,"isSigner":true},{"name":"proposal","isMut":true,"isSigner":false}],"args":[{"name":"args","type":{"defined":"ProposalVoteArgs"}}]},{"name":"proposalCancel","docs":["Cancel a multisig proposal on behalf of the `member`.","The proposal must be `Approved`."],"accounts":[{"name":"multisig","isMut":false,"isSigner":false},{"name":"member","isMut":true,"isSigner":true},{"name":"proposal","isMut":true,"isSigner":false}],"args":[{"name":"args","type":{"defined":"ProposalVoteArgs"}}]},{"name":"spendingLimitUse","docs":["Use a spending limit to transfer tokens from a multisig vault to a destination account."],"accounts":[{"name":"multisig","isMut":false,"isSigner":false,"docs":["The multisig account the `spending_limit` is for."]},{"name":"member","isMut":false,"isSigner":true},{"name":"spendingLimit","isMut":true,"isSigner":false,"docs":["The SpendingLimit account to use."]},{"name":"vault","isMut":true,"isSigner":false,"docs":["Multisig vault account to transfer tokens from."]},{"name":"destination","isMut":true,"isSigner":false,"docs":["Destination account to transfer tokens to."]},{"name":"systemProgram","isMut":false,"isSigner":false,"isOptional":true,"docs":["In case `spending_limit.mint` is SOL."]},{"name":"mint","isMut":false,"isSigner":false,"isOptional":true,"docs":["The mint of the tokens to transfer in case `spending_limit.mint` is an SPL token."]},{"name":"vaultTokenAccount","isMut":true,"isSigner":false,"isOptional":true,"docs":["Multisig vault token account to transfer tokens from in case `spending_limit.mint` is an SPL token."]},{"name":"destinationTokenAccount","isMut":true,"isSigner":false,"isOptional":true,"docs":["Destination token account in case `spending_limit.mint` is an SPL token."]},{"name":"tokenProgram","isMut":false,"isSigner":false,"isOptional":true,"docs":["In case `spending_limit.mint` is an SPL token."]}],"args":[{"name":"args","type":{"defined":"SpendingLimitUseArgs"}}]},{"name":"configTransactionAccountsClose","docs":["Closes a `ConfigTransaction` and the corresponding `Proposal`.","`transaction` can be closed if either:","- the `proposal` is in a terminal state: `Executed`, `Rejected`, or `Cancelled`.","- the `proposal` is stale."],"accounts":[{"name":"multisig","isMut":false,"isSigner":false},{"name":"proposal","isMut":true,"isSigner":false},{"name":"transaction","isMut":true,"isSigner":false,"docs":["ConfigTransaction corresponding to the `proposal`."]},{"name":"rentCollector","isMut":true,"isSigner":false,"docs":["The rent collector."]},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[]},{"name":"vaultTransactionAccountsClose","docs":["Closes a `VaultTransaction` and the corresponding `Proposal`.","`transaction` can be closed if either:","- the `proposal` is in a terminal state: `Executed`, `Rejected`, or `Cancelled`.","- the `proposal` is stale and not `Approved`."],"accounts":[{"name":"multisig","isMut":false,"isSigner":false},{"name":"proposal","isMut":true,"isSigner":false},{"name":"transaction","isMut":true,"isSigner":false,"docs":["VaultTransaction corresponding to the `proposal`."]},{"name":"rentCollector","isMut":true,"isSigner":false,"docs":["The rent collector."]},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[]},{"name":"vaultBatchTransactionAccountClose","docs":["Closes a `VaultBatchTransaction` belonging to the `batch` and `proposal`.","`transaction` can be closed if either:","- it's marked as executed within the `batch`;","- the `proposal` is in a terminal state: `Executed`, `Rejected`, or `Cancelled`.","- the `proposal` is stale and not `Approved`."],"accounts":[{"name":"multisig","isMut":false,"isSigner":false},{"name":"proposal","isMut":false,"isSigner":false},{"name":"batch","isMut":true,"isSigner":false,"docs":["`Batch` corresponding to the `proposal`."]},{"name":"transaction","isMut":true,"isSigner":false,"docs":["`VaultBatchTransaction` account to close.","The transaction must be the current last one in the batch."]},{"name":"rentCollector","isMut":true,"isSigner":false,"docs":["The rent collector."]},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[]},{"name":"batchAccountsClose","docs":["Closes Batch and the corresponding Proposal accounts for proposals in terminal states:","`Executed`, `Rejected`, or `Cancelled` or stale proposals that aren't `Approved`.","","This instruction is only allowed to be executed when all `VaultBatchTransaction` accounts","in the `batch` are already closed: `batch.size == 0`."],"accounts":[{"name":"multisig","isMut":false,"isSigner":false},{"name":"proposal","isMut":true,"isSigner":false},{"name":"batch","isMut":true,"isSigner":false,"docs":["`Batch` corresponding to the `proposal`."]},{"name":"rentCollector","isMut":true,"isSigner":false,"docs":["The rent collector."]},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[]}],"accounts":[{"name":"Batch","docs":["Stores data required for serial execution of a batch of multisig vault transactions.","Vault transaction is a transaction that's executed on behalf of the multisig vault PDA","and wraps arbitrary Solana instructions, typically calling into other Solana programs.","The transactions themselves are stored in separate PDAs associated with the this account."],"type":{"kind":"struct","fields":[{"name":"multisig","docs":["The multisig this belongs to."],"type":"publicKey"},{"name":"creator","docs":["Member of the Multisig who submitted the batch."],"type":"publicKey"},{"name":"index","docs":["Index of this batch within the multisig transactions."],"type":"u64"},{"name":"bump","docs":["PDA bump."],"type":"u8"},{"name":"vaultIndex","docs":["Index of the vault this batch belongs to."],"type":"u8"},{"name":"vaultBump","docs":["Derivation bump of the vault PDA this batch belongs to."],"type":"u8"},{"name":"size","docs":["Number of transactions in the batch."],"type":"u32"},{"name":"executedTransactionIndex","docs":["Index of the last executed transaction within the batch.","0 means that no transactions have been executed yet."],"type":"u32"}]}},{"name":"VaultBatchTransaction","docs":["Stores data required for execution of one transaction from a batch."],"type":{"kind":"struct","fields":[{"name":"bump","docs":["PDA bump."],"type":"u8"},{"name":"ephemeralSignerBumps","docs":["Derivation bumps for additional signers.","Some transactions require multiple signers. Often these additional signers are \"ephemeral\" keypairs","that are generated on the client with a sole purpose of signing the transaction and be discarded immediately after.","When wrapping such transactions into multisig ones, we replace these \"ephemeral\" signing keypairs","with PDAs derived from the transaction's `transaction_index` and controlled by the Multisig Program;","during execution the program includes the seeds of these PDAs into the `invoke_signed` calls,","thus \"signing\" on behalf of these PDAs."],"type":"bytes"},{"name":"message","docs":["data required for executing the transaction."],"type":{"defined":"VaultTransactionMessage"}}]}},{"name":"ConfigTransaction","docs":["Stores data required for execution of a multisig configuration transaction.","Config transaction can perform a predefined set of actions on the Multisig PDA, such as adding/removing members,","changing the threshold, etc."],"type":{"kind":"struct","fields":[{"name":"multisig","docs":["The multisig this belongs to."],"type":"publicKey"},{"name":"creator","docs":["Member of the Multisig who submitted the transaction."],"type":"publicKey"},{"name":"index","docs":["Index of this transaction within the multisig."],"type":"u64"},{"name":"bump","docs":["bump for the transaction seeds."],"type":"u8"},{"name":"actions","docs":["Action to be performed on the multisig."],"type":{"vec":{"defined":"ConfigAction"}}}]}},{"name":"Multisig","type":{"kind":"struct","fields":[{"name":"createKey","docs":["Key that is used to seed the multisig PDA."],"type":"publicKey"},{"name":"configAuthority","docs":["The authority that can change the multisig config.","This is a very important parameter as this authority can change the members and threshold.","","The convention is to set this to `Pubkey::default()`.","In this case, the multisig becomes autonomous, so every config change goes through","the normal process of voting by the members.","","However, if this parameter is set to any other key, all the config changes for this multisig","will need to be signed by the `config_authority`. We call such a multisig a \"controlled multisig\"."],"type":"publicKey"},{"name":"threshold","docs":["Threshold for signatures."],"type":"u16"},{"name":"timeLock","docs":["How many seconds must pass between transaction voting settlement and execution."],"type":"u32"},{"name":"transactionIndex","docs":["Last transaction index. 0 means no transactions have been created."],"type":"u64"},{"name":"staleTransactionIndex","docs":["Last stale transaction index. All transactions up until this index are stale.","This index is updated when multisig config (members/threshold/time_lock) changes."],"type":"u64"},{"name":"rentCollector","docs":["The address where the rent for the accounts related to executed, rejected, or cancelled","transactions can be reclaimed. If set to `None`, the rent reclamation feature is turned off."],"type":{"option":"publicKey"}},{"name":"bump","docs":["Bump for the multisig PDA seed."],"type":"u8"},{"name":"members","docs":["Members of the multisig."],"type":{"vec":{"defined":"Member"}}}]}},{"name":"ProgramConfig","docs":["Global program configuration account."],"type":{"kind":"struct","fields":[{"name":"authority","docs":["The authority which can update the config."],"type":"publicKey"},{"name":"multisigCreationFee","docs":["The lamports amount charged for creating a new multisig account.","This fee is sent to the `treasury` account."],"type":"u64"},{"name":"treasury","docs":["The treasury account to send charged fees to."],"type":"publicKey"},{"name":"reserved","docs":["Reserved for future use."],"type":{"array":["u8",64]}}]}},{"name":"Proposal","docs":["Stores the data required for tracking the status of a multisig proposal.","Each `Proposal` has a 1:1 association with a transaction account, e.g. a `VaultTransaction` or a `ConfigTransaction`;","the latter can be executed only after the `Proposal` has been approved and its time lock is released."],"type":{"kind":"struct","fields":[{"name":"multisig","docs":["The multisig this belongs to."],"type":"publicKey"},{"name":"transactionIndex","docs":["Index of the multisig transaction this proposal is associated with."],"type":"u64"},{"name":"status","docs":["The status of the transaction."],"type":{"defined":"ProposalStatus"}},{"name":"bump","docs":["PDA bump."],"type":"u8"},{"name":"approved","docs":["Keys that have approved/signed."],"type":{"vec":"publicKey"}},{"name":"rejected","docs":["Keys that have rejected."],"type":{"vec":"publicKey"}},{"name":"cancelled","docs":["Keys that have cancelled (Approved only)."],"type":{"vec":"publicKey"}}]}},{"name":"SpendingLimit","type":{"kind":"struct","fields":[{"name":"multisig","docs":["The multisig this belongs to."],"type":"publicKey"},{"name":"createKey","docs":["Key that is used to seed the SpendingLimit PDA."],"type":"publicKey"},{"name":"vaultIndex","docs":["The index of the vault that the spending limit is for."],"type":"u8"},{"name":"mint","docs":["The token mint the spending limit is for.","Pubkey::default() means SOL.","use NATIVE_MINT for Wrapped SOL."],"type":"publicKey"},{"name":"amount","docs":["The amount of tokens that can be spent in a period.","This amount is in decimals of the mint,","so 1 SOL would be `1_000_000_000` and 1 USDC would be `1_000_000`."],"type":"u64"},{"name":"period","docs":["The reset period of the spending limit.","When it passes, the remaining amount is reset, unless it's `Period::OneTime`."],"type":{"defined":"Period"}},{"name":"remainingAmount","docs":["The remaining amount of tokens that can be spent in the current period.","When reaches 0, the spending limit cannot be used anymore until the period reset."],"type":"u64"},{"name":"lastReset","docs":["Unix timestamp marking the last time the spending limit was reset (or created)."],"type":"i64"},{"name":"bump","docs":["PDA bump."],"type":"u8"},{"name":"members","docs":["Members of the multisig that can use the spending limit.","In case a member is removed from the multisig, the spending limit will remain existent","(until explicitly deleted), but the removed member will not be able to use it anymore."],"type":{"vec":"publicKey"}},{"name":"destinations","docs":["The destination addresses the spending limit is allowed to sent funds to.","If empty, funds can be sent to any address."],"type":{"vec":"publicKey"}}]}},{"name":"VaultTransaction","docs":["Stores data required for tracking the voting and execution status of a vault transaction.","Vault transaction is a transaction that's executed on behalf of the multisig vault PDA","and wraps arbitrary Solana instructions, typically calling into other Solana programs."],"type":{"kind":"struct","fields":[{"name":"multisig","docs":["The multisig this belongs to."],"type":"publicKey"},{"name":"creator","docs":["Member of the Multisig who submitted the transaction."],"type":"publicKey"},{"name":"index","docs":["Index of this transaction within the multisig."],"type":"u64"},{"name":"bump","docs":["bump for the transaction seeds."],"type":"u8"},{"name":"vaultIndex","docs":["Index of the vault this transaction belongs to."],"type":"u8"},{"name":"vaultBump","docs":["Derivation bump of the vault PDA this transaction belongs to."],"type":"u8"},{"name":"ephemeralSignerBumps","docs":["Derivation bumps for additional signers.","Some transactions require multiple signers. Often these additional signers are \"ephemeral\" keypairs","that are generated on the client with a sole purpose of signing the transaction and be discarded immediately after.","When wrapping such transactions into multisig ones, we replace these \"ephemeral\" signing keypairs","with PDAs derived from the MultisigTransaction's `transaction_index` and controlled by the Multisig Program;","during execution the program includes the seeds of these PDAs into the `invoke_signed` calls,","thus \"signing\" on behalf of these PDAs."],"type":"bytes"},{"name":"message","docs":["data required for executing the transaction."],"type":{"defined":"VaultTransactionMessage"}}]}}],"types":[{"name":"BatchAddTransactionArgs","type":{"kind":"struct","fields":[{"name":"ephemeralSigners","docs":["Number of ephemeral signing PDAs required by the transaction."],"type":"u8"},{"name":"transactionMessage","type":"bytes"}]}},{"name":"BatchCreateArgs","type":{"kind":"struct","fields":[{"name":"vaultIndex","docs":["Index of the vault this transaction belongs to."],"type":"u8"},{"name":"memo","type":{"option":"string"}}]}},{"name":"ConfigTransactionCreateArgs","type":{"kind":"struct","fields":[{"name":"actions","type":{"vec":{"defined":"ConfigAction"}}},{"name":"memo","type":{"option":"string"}}]}},{"name":"MultisigAddSpendingLimitArgs","type":{"kind":"struct","fields":[{"name":"createKey","docs":["Key that is used to seed the SpendingLimit PDA."],"type":"publicKey"},{"name":"vaultIndex","docs":["The index of the vault that the spending limit is for."],"type":"u8"},{"name":"mint","docs":["The token mint the spending limit is for."],"type":"publicKey"},{"name":"amount","docs":["The amount of tokens that can be spent in a period.","This amount is in decimals of the mint,","so 1 SOL would be `1_000_000_000` and 1 USDC would be `1_000_000`."],"type":"u64"},{"name":"period","docs":["The reset period of the spending limit.","When it passes, the remaining amount is reset, unless it's `Period::OneTime`."],"type":{"defined":"Period"}},{"name":"members","docs":["Members of the multisig that can use the spending limit.","In case a member is removed from the multisig, the spending limit will remain existent","(until explicitly deleted), but the removed member will not be able to use it anymore."],"type":{"vec":"publicKey"}},{"name":"destinations","docs":["The destination addresses the spending limit is allowed to sent funds to.","If empty, funds can be sent to any address."],"type":{"vec":"publicKey"}},{"name":"memo","docs":["Memo is used for indexing only."],"type":{"option":"string"}}]}},{"name":"MultisigAddMemberArgs","type":{"kind":"struct","fields":[{"name":"newMember","type":{"defined":"Member"}},{"name":"memo","docs":["Memo is used for indexing only."],"type":{"option":"string"}}]}},{"name":"MultisigRemoveMemberArgs","type":{"kind":"struct","fields":[{"name":"oldMember","type":"publicKey"},{"name":"memo","docs":["Memo is used for indexing only."],"type":{"option":"string"}}]}},{"name":"MultisigChangeThresholdArgs","type":{"kind":"struct","fields":[{"name":"newThreshold","type":"u16"},{"name":"memo","docs":["Memo is used for indexing only."],"type":{"option":"string"}}]}},{"name":"MultisigSetTimeLockArgs","type":{"kind":"struct","fields":[{"name":"timeLock","type":"u32"},{"name":"memo","docs":["Memo is used for indexing only."],"type":{"option":"string"}}]}},{"name":"MultisigSetConfigAuthorityArgs","type":{"kind":"struct","fields":[{"name":"configAuthority","type":"publicKey"},{"name":"memo","docs":["Memo is used for indexing only."],"type":{"option":"string"}}]}},{"name":"MultisigSetRentCollectorArgs","type":{"kind":"struct","fields":[{"name":"rentCollector","type":{"option":"publicKey"}},{"name":"memo","docs":["Memo is used for indexing only."],"type":{"option":"string"}}]}},{"name":"MultisigCreateArgs","type":{"kind":"struct","fields":[{"name":"configAuthority","docs":["The authority that can configure the multisig: add/remove members, change the threshold, etc.","Should be set to `None` for autonomous multisigs."],"type":{"option":"publicKey"}},{"name":"threshold","docs":["The number of signatures required to execute a transaction."],"type":"u16"},{"name":"members","docs":["The members of the multisig."],"type":{"vec":{"defined":"Member"}}},{"name":"timeLock","docs":["How many seconds must pass between transaction voting, settlement, and execution."],"type":"u32"},{"name":"memo","docs":["Memo is used for indexing only."],"type":{"option":"string"}}]}},{"name":"MultisigCreateArgsV2","type":{"kind":"struct","fields":[{"name":"configAuthority","docs":["The authority that can configure the multisig: add/remove members, change the threshold, etc.","Should be set to `None` for autonomous multisigs."],"type":{"option":"publicKey"}},{"name":"threshold","docs":["The number of signatures required to execute a transaction."],"type":"u16"},{"name":"members","docs":["The members of the multisig."],"type":{"vec":{"defined":"Member"}}},{"name":"timeLock","docs":["How many seconds must pass between transaction voting, settlement, and execution."],"type":"u32"},{"name":"rentCollector","docs":["The address where the rent for the accounts related to executed, rejected, or cancelled","transactions can be reclaimed. If set to `None`, the rent reclamation feature is turned off."],"type":{"option":"publicKey"}},{"name":"memo","docs":["Memo is used for indexing only."],"type":{"option":"string"}}]}},{"name":"MultisigRemoveSpendingLimitArgs","type":{"kind":"struct","fields":[{"name":"memo","docs":["Memo is used for indexing only."],"type":{"option":"string"}}]}},{"name":"ProgramConfigInitArgs","type":{"kind":"struct","fields":[{"name":"authority","docs":["The authority that can configure the program config: change the treasury, etc."],"type":"publicKey"},{"name":"multisigCreationFee","docs":["The fee that is charged for creating a new multisig."],"type":"u64"},{"name":"treasury","docs":["The treasury where the creation fee is transferred to."],"type":"publicKey"}]}},{"name":"ProgramConfigSetAuthorityArgs","type":{"kind":"struct","fields":[{"name":"newAuthority","type":"publicKey"}]}},{"name":"ProgramConfigSetMultisigCreationFeeArgs","type":{"kind":"struct","fields":[{"name":"newMultisigCreationFee","type":"u64"}]}},{"name":"ProgramConfigSetTreasuryArgs","type":{"kind":"struct","fields":[{"name":"newTreasury","type":"publicKey"}]}},{"name":"ProposalCreateArgs","type":{"kind":"struct","fields":[{"name":"transactionIndex","docs":["Index of the multisig transaction this proposal is associated with."],"type":"u64"},{"name":"draft","docs":["Whether the proposal should be initialized with status `Draft`."],"type":"bool"}]}},{"name":"ProposalVoteArgs","type":{"kind":"struct","fields":[{"name":"memo","type":{"option":"string"}}]}},{"name":"SpendingLimitUseArgs","type":{"kind":"struct","fields":[{"name":"amount","docs":["Amount of tokens to transfer."],"type":"u64"},{"name":"decimals","docs":["Decimals of the token mint. Used for double-checking against incorrect order of magnitude of `amount`."],"type":"u8"},{"name":"memo","docs":["Memo used for indexing."],"type":{"option":"string"}}]}},{"name":"VaultTransactionCreateArgs","type":{"kind":"struct","fields":[{"name":"vaultIndex","docs":["Index of the vault this transaction belongs to."],"type":"u8"},{"name":"ephemeralSigners","docs":["Number of ephemeral signing PDAs required by the transaction."],"type":"u8"},{"name":"transactionMessage","type":"bytes"},{"name":"memo","type":{"option":"string"}}]}},{"name":"TransactionMessage","docs":["Unvalidated instruction data, must be treated as untrusted."],"type":{"kind":"struct","fields":[{"name":"numSigners","docs":["The number of signer pubkeys in the account_keys vec."],"type":"u8"},{"name":"numWritableSigners","docs":["The number of writable signer pubkeys in the account_keys vec."],"type":"u8"},{"name":"numWritableNonSigners","docs":["The number of writable non-signer pubkeys in the account_keys vec."],"type":"u8"},{"name":"accountKeys","docs":["The list of unique account public keys (including program IDs) that will be used in the provided instructions."],"type":{"defined":"SmallVec"}},{"name":"instructions","docs":["The list of instructions to execute."],"type":{"defined":"SmallVec"}},{"name":"addressTableLookups","docs":["List of address table lookups used to load additional accounts","for this transaction."],"type":{"defined":"SmallVec"}}]}},{"name":"CompiledInstruction","type":{"kind":"struct","fields":[{"name":"programIdIndex","type":"u8"},{"name":"accountIndexes","docs":["Indices into the tx's `account_keys` list indicating which accounts to pass to the instruction."],"type":{"defined":"SmallVec"}},{"name":"data","docs":["Instruction data."],"type":{"defined":"SmallVec"}}]}},{"name":"MessageAddressTableLookup","docs":["Address table lookups describe an on-chain address lookup table to use","for loading more readonly and writable accounts in a single tx."],"type":{"kind":"struct","fields":[{"name":"accountKey","docs":["Address lookup table account key"],"type":"publicKey"},{"name":"writableIndexes","docs":["List of indexes used to load writable account addresses"],"type":{"defined":"SmallVec"}},{"name":"readonlyIndexes","docs":["List of indexes used to load readonly account addresses"],"type":{"defined":"SmallVec"}}]}},{"name":"Member","type":{"kind":"struct","fields":[{"name":"key","type":"publicKey"},{"name":"permissions","type":{"defined":"Permissions"}}]}},{"name":"Permissions","docs":["Bitmask for permissions."],"type":{"kind":"struct","fields":[{"name":"mask","type":"u8"}]}},{"name":"VaultTransactionMessage","type":{"kind":"struct","fields":[{"name":"numSigners","docs":["The number of signer pubkeys in the account_keys vec."],"type":"u8"},{"name":"numWritableSigners","docs":["The number of writable signer pubkeys in the account_keys vec."],"type":"u8"},{"name":"numWritableNonSigners","docs":["The number of writable non-signer pubkeys in the account_keys vec."],"type":"u8"},{"name":"accountKeys","docs":["Unique account pubkeys (including program IDs) required for execution of the tx.","The signer pubkeys appear at the beginning of the vec, with writable pubkeys first, and read-only pubkeys following.","The non-signer pubkeys follow with writable pubkeys first and read-only ones following.","Program IDs are also stored at the end of the vec along with other non-signer non-writable pubkeys:","","```plaintext","[pubkey1, pubkey2, pubkey3, pubkey4, pubkey5, pubkey6, pubkey7, pubkey8]","|---writable---| |---readonly---| |---writable---| |---readonly---|","|------------signers-------------| |----------non-singers-----------|","```"],"type":{"vec":"publicKey"}},{"name":"instructions","docs":["List of instructions making up the tx."],"type":{"vec":{"defined":"MultisigCompiledInstruction"}}},{"name":"addressTableLookups","docs":["List of address table lookups used to load additional accounts","for this transaction."],"type":{"vec":{"defined":"MultisigMessageAddressTableLookup"}}}]}},{"name":"MultisigCompiledInstruction","docs":["Concise serialization schema for instructions that make up a transaction.","Closely mimics the Solana transaction wire format."],"type":{"kind":"struct","fields":[{"name":"programIdIndex","type":"u8"},{"name":"accountIndexes","docs":["Indices into the tx's `account_keys` list indicating which accounts to pass to the instruction."],"type":"bytes"},{"name":"data","docs":["Instruction data."],"type":"bytes"}]}},{"name":"MultisigMessageAddressTableLookup","docs":["Address table lookups describe an on-chain address lookup table to use","for loading more readonly and writable accounts into a transaction."],"type":{"kind":"struct","fields":[{"name":"accountKey","docs":["Address lookup table account key."],"type":"publicKey"},{"name":"writableIndexes","docs":["List of indexes used to load writable accounts."],"type":"bytes"},{"name":"readonlyIndexes","docs":["List of indexes used to load readonly accounts."],"type":"bytes"}]}},{"name":"Vote","type":{"kind":"enum","variants":[{"name":"Approve"},{"name":"Reject"},{"name":"Cancel"}]}},{"name":"ConfigAction","type":{"kind":"enum","variants":[{"name":"AddMember","fields":[{"name":"newMember","type":{"defined":"Member"}}]},{"name":"RemoveMember","fields":[{"name":"oldMember","type":"publicKey"}]},{"name":"ChangeThreshold","fields":[{"name":"newThreshold","type":"u16"}]},{"name":"SetTimeLock","fields":[{"name":"newTimeLock","type":"u32"}]},{"name":"AddSpendingLimit","fields":[{"name":"createKey","docs":["Key that is used to seed the SpendingLimit PDA."],"type":"publicKey"},{"name":"vaultIndex","docs":["The index of the vault that the spending limit is for."],"type":"u8"},{"name":"mint","docs":["The token mint the spending limit is for."],"type":"publicKey"},{"name":"amount","docs":["The amount of tokens that can be spent in a period.","This amount is in decimals of the mint,","so 1 SOL would be `1_000_000_000` and 1 USDC would be `1_000_000`."],"type":"u64"},{"name":"period","docs":["The reset period of the spending limit.","When it passes, the remaining amount is reset, unless it's `Period::OneTime`."],"type":{"defined":"Period"}},{"name":"members","docs":["Members of the multisig that can use the spending limit.","In case a member is removed from the multisig, the spending limit will remain existent","(until explicitly deleted), but the removed member will not be able to use it anymore."],"type":{"vec":"publicKey"}},{"name":"destinations","docs":["The destination addresses the spending limit is allowed to sent funds to.","If empty, funds can be sent to any address."],"type":{"vec":"publicKey"}}]},{"name":"RemoveSpendingLimit","fields":[{"name":"spendingLimit","type":"publicKey"}]},{"name":"SetRentCollector","fields":[{"name":"newRentCollector","type":{"option":"publicKey"}}]}]}},{"name":"Permission","type":{"kind":"enum","variants":[{"name":"Initiate"},{"name":"Vote"},{"name":"Execute"}]}},{"name":"ProposalStatus","docs":["The status of a proposal.","Each variant wraps a timestamp of when the status was set."],"type":{"kind":"enum","variants":[{"name":"Draft","fields":[{"name":"timestamp","type":"i64"}]},{"name":"Active","fields":[{"name":"timestamp","type":"i64"}]},{"name":"Rejected","fields":[{"name":"timestamp","type":"i64"}]},{"name":"Approved","fields":[{"name":"timestamp","type":"i64"}]},{"name":"Executing"},{"name":"Executed","fields":[{"name":"timestamp","type":"i64"}]},{"name":"Cancelled","fields":[{"name":"timestamp","type":"i64"}]}]}},{"name":"Period","docs":["The reset period of the spending limit."],"type":{"kind":"enum","variants":[{"name":"OneTime"},{"name":"Day"},{"name":"Week"},{"name":"Month"}]}}],"errors":[{"code":6000,"name":"DuplicateMember","msg":"Found multiple members with the same pubkey"},{"code":6001,"name":"EmptyMembers","msg":"Members array is empty"},{"code":6002,"name":"TooManyMembers","msg":"Too many members, can be up to 65535"},{"code":6003,"name":"InvalidThreshold","msg":"Invalid threshold, must be between 1 and number of members with Vote permission"},{"code":6004,"name":"Unauthorized","msg":"Attempted to perform an unauthorized action"},{"code":6005,"name":"NotAMember","msg":"Provided pubkey is not a member of multisig"},{"code":6006,"name":"InvalidTransactionMessage","msg":"TransactionMessage is malformed."},{"code":6007,"name":"StaleProposal","msg":"Proposal is stale"},{"code":6008,"name":"InvalidProposalStatus","msg":"Invalid proposal status"},{"code":6009,"name":"InvalidTransactionIndex","msg":"Invalid transaction index"},{"code":6010,"name":"AlreadyApproved","msg":"Member already approved the transaction"},{"code":6011,"name":"AlreadyRejected","msg":"Member already rejected the transaction"},{"code":6012,"name":"AlreadyCancelled","msg":"Member already cancelled the transaction"},{"code":6013,"name":"InvalidNumberOfAccounts","msg":"Wrong number of accounts provided"},{"code":6014,"name":"InvalidAccount","msg":"Invalid account provided"},{"code":6015,"name":"RemoveLastMember","msg":"Cannot remove last member"},{"code":6016,"name":"NoVoters","msg":"Members don't include any voters"},{"code":6017,"name":"NoProposers","msg":"Members don't include any proposers"},{"code":6018,"name":"NoExecutors","msg":"Members don't include any executors"},{"code":6019,"name":"InvalidStaleTransactionIndex","msg":"`stale_transaction_index` must be <= `transaction_index`"},{"code":6020,"name":"NotSupportedForControlled","msg":"Instruction not supported for controlled multisig"},{"code":6021,"name":"TimeLockNotReleased","msg":"Proposal time lock has not been released"},{"code":6022,"name":"NoActions","msg":"Config transaction must have at least one action"},{"code":6023,"name":"MissingAccount","msg":"Missing account"},{"code":6024,"name":"InvalidMint","msg":"Invalid mint"},{"code":6025,"name":"InvalidDestination","msg":"Invalid destination"},{"code":6026,"name":"SpendingLimitExceeded","msg":"Spending limit exceeded"},{"code":6027,"name":"DecimalsMismatch","msg":"Decimals don't match the mint"},{"code":6028,"name":"UnknownPermission","msg":"Member has unknown permission"},{"code":6029,"name":"ProtectedAccount","msg":"Account is protected, it cannot be passed into a CPI as writable"},{"code":6030,"name":"TimeLockExceedsMaxAllowed","msg":"Time lock exceeds the maximum allowed (90 days)"},{"code":6031,"name":"IllegalAccountOwner","msg":"Account is not owned by Multisig program"},{"code":6032,"name":"RentReclamationDisabled","msg":"Rent reclamation is disabled for this multisig"},{"code":6033,"name":"InvalidRentCollector","msg":"Invalid rent collector address"},{"code":6034,"name":"ProposalForAnotherMultisig","msg":"Proposal is for another multisig"},{"code":6035,"name":"TransactionForAnotherMultisig","msg":"Transaction is for another multisig"},{"code":6036,"name":"TransactionNotMatchingProposal","msg":"Transaction doesn't match proposal"},{"code":6037,"name":"TransactionNotLastInBatch","msg":"Transaction is not last in batch"},{"code":6038,"name":"BatchNotEmpty","msg":"Batch is not empty"},{"code":6039,"name":"SpendingLimitInvalidAmount","msg":"Invalid SpendingLimit amount"}]} \ No newline at end of file diff --git a/idls/stabble_weighted_swap.swapFpHZwjELNnjvThjajtiVmkz3yPQEHjLtka2fwHW.json b/idls/stabble_weighted_swap.swapFpHZwjELNnjvThjajtiVmkz3yPQEHjLtka2fwHW.json index e69de29..d735135 100644 --- a/idls/stabble_weighted_swap.swapFpHZwjELNnjvThjajtiVmkz3yPQEHjLtka2fwHW.json +++ b/idls/stabble_weighted_swap.swapFpHZwjELNnjvThjajtiVmkz3yPQEHjLtka2fwHW.json @@ -0,0 +1 @@ +{"address":"swapFpHZwjELNnjvThjajtiVmkz3yPQEHjLtka2fwHW","metadata":{"name":"weighted_swap","version":"1.5.0","spec":"0.1.0","description":"Created with Anchor"},"instructions":[{"name":"accept_owner","discriminator":[176,23,41,28,23,111,8,4],"accounts":[{"name":"pending_owner","signer":true},{"name":"pool","writable":true}],"args":[]},{"name":"change_max_supply","discriminator":[93,176,0,205,69,63,87,80],"accounts":[{"name":"owner","signer":true},{"name":"pool","writable":true}],"args":[{"name":"new_max_supply","type":"u64"}]},{"name":"change_swap_fee","discriminator":[231,15,132,51,132,165,64,170],"accounts":[{"name":"owner","signer":true},{"name":"pool","writable":true}],"args":[{"name":"new_swap_fee","type":"u64"}]},{"name":"deposit","docs":["add liquidity"],"discriminator":[242,35,198,137,82,225,242,182],"accounts":[{"name":"user","signer":true},{"name":"user_pool_token","writable":true},{"name":"mint","writable":true},{"name":"pool","writable":true},{"name":"pool_authority"},{"name":"vault"},{"name":"vault_authority"},{"name":"token_program"},{"name":"token_program_2022"}],"args":[{"name":"amounts","type":{"vec":"u64"}},{"name":"minimum_amount_out","type":"u64"}]},{"name":"initialize","docs":["initialize a pool"],"discriminator":[175,175,109,31,13,152,155,237],"accounts":[{"name":"owner","signer":true},{"name":"mint"},{"name":"pool","writable":true},{"name":"pool_authority"},{"name":"withdraw_authority"},{"name":"vault"}],"args":[{"name":"swap_fee","type":"u64"},{"name":"weights","type":{"vec":"u64"}},{"name":"max_caps","type":{"vec":"u64"}}]},{"name":"pause","discriminator":[211,22,221,251,74,121,193,47],"accounts":[{"name":"owner","signer":true},{"name":"pool","writable":true}],"args":[]},{"name":"reject_owner","discriminator":[238,206,198,215,51,178,133,228],"accounts":[{"name":"pending_owner","signer":true},{"name":"pool","writable":true}],"args":[]},{"name":"shutdown","docs":["shutdown the zero-liquidity pool"],"discriminator":[146,204,241,213,86,21,253,211],"accounts":[{"name":"owner","writable":true},{"name":"pool","writable":true}],"args":[]},{"name":"swap","docs":["swap"],"discriminator":[248,198,158,145,225,117,135,200],"accounts":[{"name":"user","signer":true},{"name":"user_token_in","writable":true},{"name":"user_token_out","writable":true},{"name":"vault_token_in","writable":true},{"name":"vault_token_out","writable":true},{"name":"beneficiary_token_out","writable":true},{"name":"pool","writable":true},{"name":"withdraw_authority"},{"name":"vault"},{"name":"vault_authority"},{"name":"vault_program"},{"name":"token_program"}],"args":[{"name":"amount_in","type":{"option":"u64"}},{"name":"minimum_amount_out","type":"u64"}]},{"name":"swap_v2","discriminator":[43,4,237,11,26,201,30,98],"accounts":[{"name":"user","signer":true},{"name":"mint_in"},{"name":"mint_out"},{"name":"user_token_in","writable":true},{"name":"user_token_out","writable":true},{"name":"vault_token_in","writable":true},{"name":"vault_token_out","writable":true},{"name":"beneficiary_token_out","writable":true},{"name":"pool","writable":true},{"name":"withdraw_authority"},{"name":"vault"},{"name":"vault_authority"},{"name":"vault_program"},{"name":"token_program"},{"name":"token_2022_program"}],"args":[{"name":"amount_in","type":{"option":"u64"}},{"name":"minimum_amount_out","type":"u64"}]},{"name":"transfer_owner","discriminator":[245,25,221,175,106,229,225,45],"accounts":[{"name":"owner","signer":true},{"name":"pool","writable":true}],"args":[{"name":"new_owner","type":"pubkey"}]},{"name":"unpause","discriminator":[169,144,4,38,10,141,188,255],"accounts":[{"name":"owner","signer":true},{"name":"pool","writable":true}],"args":[]},{"name":"withdraw","docs":["remove liquidity"],"discriminator":[183,18,70,156,148,109,161,34],"accounts":[{"name":"user","signer":true},{"name":"user_pool_token","writable":true},{"name":"mint","writable":true},{"name":"pool","writable":true},{"name":"withdraw_authority"},{"name":"vault"},{"name":"vault_authority"},{"name":"vault_program"},{"name":"token_program"},{"name":"token_program_2022"}],"args":[{"name":"amount","type":"u64"},{"name":"minimum_amounts_out","type":{"vec":"u64"}}]}],"accounts":[{"name":"Pool","discriminator":[241,154,109,4,17,177,109,188]},{"name":"Vault","discriminator":[211,8,232,43,2,152,117,119]}],"events":[{"name":"PoolBalanceUpdatedEvent","discriminator":[172,82,114,207,27,103,211,4]},{"name":"PoolUpdatedEvent","discriminator":[128,39,94,221,230,222,127,141]}],"types":[{"name":"Pool","type":{"kind":"struct","fields":[{"name":"owner","type":"pubkey"},{"name":"vault","type":"pubkey"},{"name":"mint","type":"pubkey"},{"name":"authority_bump","type":"u8"},{"name":"is_active","type":"bool"},{"name":"invariant","type":"u64"},{"name":"swap_fee","type":"u64"},{"name":"tokens","type":{"vec":{"defined":{"name":"PoolToken"}}}},{"name":"pending_owner","type":{"option":"pubkey"}},{"name":"max_supply","type":"u64"}]}},{"name":"PoolBalanceUpdatedData","type":{"kind":"struct","fields":[{"name":"balances","type":{"vec":"u64"}}]}},{"name":"PoolBalanceUpdatedEvent","type":{"kind":"struct","fields":[{"name":"pubkey","type":"pubkey"},{"name":"data","type":{"defined":{"name":"PoolBalanceUpdatedData"}}}]}},{"name":"PoolToken","type":{"kind":"struct","fields":[{"name":"mint","type":"pubkey"},{"name":"decimals","type":"u8"},{"name":"scaling_up","type":"bool"},{"name":"scaling_factor","type":"u64"},{"name":"balance","type":"u64"},{"name":"weight","type":"u64"}]}},{"name":"PoolUpdatedData","type":{"kind":"struct","fields":[{"name":"is_active","type":"bool"},{"name":"swap_fee","type":"u64"},{"name":"max_supply","type":"u64"}]}},{"name":"PoolUpdatedEvent","type":{"kind":"struct","fields":[{"name":"pubkey","type":"pubkey"},{"name":"data","type":{"defined":{"name":"PoolUpdatedData"}}}]}},{"name":"Vault","type":{"kind":"struct","fields":[{"name":"admin","type":"pubkey"},{"name":"withdraw_authority","docs":["PDA of pool programs seeded by vault address"],"type":"pubkey"},{"name":"withdraw_authority_bump","docs":["bump seed of withdraw_authority PDA"],"type":"u8"},{"name":"authority_bump","docs":["bump seed of vault_authority PDA"],"type":"u8"},{"name":"is_active","type":"bool"},{"name":"beneficiary","type":"pubkey"},{"name":"beneficiary_fee","type":"u64"},{"name":"pending_admin","type":{"option":"pubkey"}}]}}]} diff --git a/idls/tensor-cnft.TCMPhJdwDryooaGtiocG1u3xcYbRpiJzb283XfCZsDp.json b/idls/tensor-cnft.TCMPhJdwDryooaGtiocG1u3xcYbRpiJzb283XfCZsDp.json new file mode 100644 index 0000000..088d031 --- /dev/null +++ b/idls/tensor-cnft.TCMPhJdwDryooaGtiocG1u3xcYbRpiJzb283XfCZsDp.json @@ -0,0 +1 @@ +{"version":"2.0.1","name":"tcomp","constants":[{"name":"CURRENT_TCOMP_VERSION","type":"u8","value":"1"},{"name":"TCOMP_FEE_BPS","type":"u16","value":"150"},{"name":"MAX_EXPIRY_SEC","type":"i64","value":"31536000"},{"name":"MAKER_BROKER_PCT","type":"u16","value":"0"},{"name":"LIST_STATE_SIZE","type":{"defined":"usize"},"value":"8 + 1 + 1 + (32 * 2) + 8 + 33 + 8 + (33 * 2) + 128"},{"name":"BID_STATE_SIZE","type":{"defined":"usize"},"value":"8 + 1 + 1 + (32 * 2) + 1 + 32 + 2 + 33 + 4 * 2 + 8 + 33 + 8 + (33 * 3) + 128"}],"instructions":[{"name":"tcompNoop","accounts":[{"name":"tcompSigner","isMut":false,"isSigner":true}],"args":[{"name":"event","type":{"defined":"TcompEvent"}}]},{"name":"withdrawFees","accounts":[{"name":"tswap","isMut":true,"isSigner":false},{"name":"tcomp","isMut":true,"isSigner":false},{"name":"cosigner","isMut":false,"isSigner":true,"docs":["We ask also for a signature just to make sure this wallet can actually sign things"]},{"name":"owner","isMut":true,"isSigner":true},{"name":"destination","isMut":true,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[{"name":"amount","type":"u64"}]},{"name":"buy","accounts":[{"name":"tcomp","isMut":true,"isSigner":false},{"name":"treeAuthority","isMut":false,"isSigner":false},{"name":"merkleTree","isMut":true,"isSigner":false},{"name":"logWrapper","isMut":false,"isSigner":false},{"name":"compressionProgram","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"bubblegumProgram","isMut":false,"isSigner":false},{"name":"tcompProgram","isMut":false,"isSigner":false},{"name":"listState","isMut":true,"isSigner":false},{"name":"buyer","isMut":false,"isSigner":false},{"name":"payer","isMut":true,"isSigner":true},{"name":"owner","isMut":true,"isSigner":false},{"name":"takerBroker","isMut":true,"isSigner":false,"isOptional":true},{"name":"makerBroker","isMut":true,"isSigner":false,"isOptional":true},{"name":"rentDest","isMut":true,"isSigner":false}],"args":[{"name":"nonce","type":"u64"},{"name":"index","type":"u32"},{"name":"root","type":{"array":["u8",32]}},{"name":"metaHash","type":{"array":["u8",32]}},{"name":"creatorShares","type":"bytes"},{"name":"creatorVerified","type":{"vec":"bool"}},{"name":"sellerFeeBasisPoints","type":"u16"},{"name":"maxAmount","type":"u64"},{"name":"optionalRoyaltyPct","type":{"option":"u16"}}]},{"name":"buySpl","accounts":[{"name":"tcomp","isMut":true,"isSigner":false},{"name":"tcompAta","isMut":true,"isSigner":false},{"name":"treeAuthority","isMut":false,"isSigner":false},{"name":"merkleTree","isMut":true,"isSigner":false},{"name":"logWrapper","isMut":false,"isSigner":false},{"name":"compressionProgram","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"bubblegumProgram","isMut":false,"isSigner":false},{"name":"tcompProgram","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"associatedTokenProgram","isMut":false,"isSigner":false},{"name":"listState","isMut":true,"isSigner":false},{"name":"buyer","isMut":false,"isSigner":false},{"name":"payer","isMut":false,"isSigner":true},{"name":"payerSource","isMut":true,"isSigner":false},{"name":"owner","isMut":false,"isSigner":false},{"name":"ownerDest","isMut":true,"isSigner":false},{"name":"currency","isMut":false,"isSigner":false},{"name":"takerBroker","isMut":true,"isSigner":false,"isOptional":true},{"name":"takerBrokerAta","isMut":true,"isSigner":false,"isOptional":true},{"name":"makerBroker","isMut":true,"isSigner":false,"isOptional":true},{"name":"makerBrokerAta","isMut":true,"isSigner":false,"isOptional":true},{"name":"rentDest","isMut":true,"isSigner":false},{"name":"rentPayer","isMut":true,"isSigner":true}],"args":[{"name":"nonce","type":"u64"},{"name":"index","type":"u32"},{"name":"root","type":{"array":["u8",32]}},{"name":"metaHash","type":{"array":["u8",32]}},{"name":"creatorShares","type":"bytes"},{"name":"creatorVerified","type":{"vec":"bool"}},{"name":"sellerFeeBasisPoints","type":"u16"},{"name":"maxAmount","type":"u64"},{"name":"optionalRoyaltyPct","type":{"option":"u16"}}]},{"name":"buyCore","accounts":[{"name":"tcomp","isMut":true,"isSigner":false},{"name":"listState","isMut":true,"isSigner":false},{"name":"asset","isMut":true,"isSigner":false},{"name":"collection","isMut":false,"isSigner":false,"isOptional":true},{"name":"buyer","isMut":false,"isSigner":false},{"name":"payer","isMut":true,"isSigner":true},{"name":"owner","isMut":true,"isSigner":false},{"name":"takerBroker","isMut":true,"isSigner":false,"isOptional":true},{"name":"makerBroker","isMut":true,"isSigner":false,"isOptional":true},{"name":"rentDest","isMut":true,"isSigner":false},{"name":"mplCoreProgram","isMut":false,"isSigner":false},{"name":"tcompProgram","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[{"name":"maxAmount","type":"u64"}]},{"name":"list","accounts":[{"name":"treeAuthority","isMut":false,"isSigner":false},{"name":"owner","isMut":false,"isSigner":false},{"name":"delegate","isMut":false,"isSigner":false},{"name":"merkleTree","isMut":true,"isSigner":false},{"name":"logWrapper","isMut":false,"isSigner":false},{"name":"compressionProgram","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"bubblegumProgram","isMut":false,"isSigner":false},{"name":"tcompProgram","isMut":false,"isSigner":false},{"name":"listState","isMut":true,"isSigner":false},{"name":"rentPayer","isMut":true,"isSigner":true}],"args":[{"name":"nonce","type":"u64"},{"name":"index","type":"u32"},{"name":"root","type":{"array":["u8",32]}},{"name":"dataHash","type":{"array":["u8",32]}},{"name":"creatorHash","type":{"array":["u8",32]}},{"name":"amount","type":"u64"},{"name":"expireInSec","type":{"option":"u64"}},{"name":"currency","type":{"option":"publicKey"}},{"name":"privateTaker","type":{"option":"publicKey"}},{"name":"makerBroker","type":{"option":"publicKey"}}]},{"name":"delist","accounts":[{"name":"treeAuthority","isMut":false,"isSigner":false},{"name":"merkleTree","isMut":true,"isSigner":false},{"name":"logWrapper","isMut":false,"isSigner":false},{"name":"compressionProgram","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"bubblegumProgram","isMut":false,"isSigner":false},{"name":"listState","isMut":true,"isSigner":false},{"name":"owner","isMut":false,"isSigner":true},{"name":"tcompProgram","isMut":false,"isSigner":false},{"name":"rentDest","isMut":true,"isSigner":false}],"args":[{"name":"nonce","type":"u64"},{"name":"index","type":"u32"},{"name":"root","type":{"array":["u8",32]}},{"name":"dataHash","type":{"array":["u8",32]}},{"name":"creatorHash","type":{"array":["u8",32]}}]},{"name":"edit","accounts":[{"name":"listState","isMut":true,"isSigner":false},{"name":"owner","isMut":false,"isSigner":true},{"name":"tcompProgram","isMut":false,"isSigner":false}],"args":[{"name":"amount","type":"u64"},{"name":"expireInSec","type":{"option":"u64"}},{"name":"currency","type":{"option":"publicKey"}},{"name":"privateTaker","type":{"option":"publicKey"}},{"name":"makerBroker","type":{"option":"publicKey"}}]},{"name":"listCore","accounts":[{"name":"asset","isMut":true,"isSigner":false},{"name":"collection","isMut":false,"isSigner":false,"isOptional":true},{"name":"listState","isMut":true,"isSigner":false},{"name":"owner","isMut":false,"isSigner":true},{"name":"mplCoreProgram","isMut":false,"isSigner":false},{"name":"tcompProgram","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"payer","isMut":true,"isSigner":true}],"args":[{"name":"amount","type":"u64"},{"name":"expireInSec","type":{"option":"u64"}},{"name":"currency","type":{"option":"publicKey"}},{"name":"privateTaker","type":{"option":"publicKey"}},{"name":"makerBroker","type":{"option":"publicKey"}}]},{"name":"delistCore","accounts":[{"name":"asset","isMut":true,"isSigner":false},{"name":"collection","isMut":false,"isSigner":false,"isOptional":true},{"name":"owner","isMut":true,"isSigner":true},{"name":"listState","isMut":true,"isSigner":false},{"name":"mplCoreProgram","isMut":false,"isSigner":false},{"name":"tcompProgram","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"rentDest","isMut":true,"isSigner":true}],"args":[]},{"name":"bid","accounts":[{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"tcompProgram","isMut":false,"isSigner":false},{"name":"bidState","isMut":true,"isSigner":false},{"name":"owner","isMut":true,"isSigner":true},{"name":"marginAccount","isMut":true,"isSigner":false},{"name":"cosigner","isMut":false,"isSigner":true},{"name":"rentPayer","isMut":true,"isSigner":true}],"args":[{"name":"bidId","type":"publicKey"},{"name":"target","type":{"defined":"Target"}},{"name":"targetId","type":"publicKey"},{"name":"field","type":{"option":{"defined":"Field"}}},{"name":"fieldId","type":{"option":"publicKey"}},{"name":"amount","type":"u64"},{"name":"quantity","type":"u32"},{"name":"expireInSec","type":{"option":"u64"}},{"name":"currency","type":{"option":"publicKey"}},{"name":"privateTaker","type":{"option":"publicKey"}},{"name":"makerBroker","type":{"option":"publicKey"}}]},{"name":"cancelBid","accounts":[{"name":"bidState","isMut":true,"isSigner":false},{"name":"owner","isMut":true,"isSigner":true},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"tcompProgram","isMut":false,"isSigner":false},{"name":"rentDest","isMut":true,"isSigner":false}],"args":[]},{"name":"closeExpiredBid","accounts":[{"name":"bidState","isMut":true,"isSigner":false},{"name":"owner","isMut":true,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"tcompProgram","isMut":false,"isSigner":false},{"name":"rentDest","isMut":true,"isSigner":false}],"args":[]},{"name":"closeExpiredListing","accounts":[{"name":"listState","isMut":true,"isSigner":false},{"name":"owner","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"tcompProgram","isMut":false,"isSigner":false},{"name":"treeAuthority","isMut":false,"isSigner":false},{"name":"merkleTree","isMut":true,"isSigner":false},{"name":"logWrapper","isMut":false,"isSigner":false},{"name":"compressionProgram","isMut":false,"isSigner":false},{"name":"bubblegumProgram","isMut":false,"isSigner":false},{"name":"rentDest","isMut":true,"isSigner":false}],"args":[{"name":"nonce","type":"u64"},{"name":"index","type":"u32"},{"name":"root","type":{"array":["u8",32]}},{"name":"dataHash","type":{"array":["u8",32]}},{"name":"creatorHash","type":{"array":["u8",32]}}]},{"name":"closeExpiredListingCore","accounts":[{"name":"listState","isMut":true,"isSigner":false},{"name":"asset","isMut":true,"isSigner":false},{"name":"collection","isMut":false,"isSigner":false,"isOptional":true},{"name":"owner","isMut":false,"isSigner":false},{"name":"mplCoreProgram","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"tcompProgram","isMut":false,"isSigner":false},{"name":"rentDest","isMut":true,"isSigner":false}],"args":[]},{"name":"takeBidMetaHash","accounts":[{"name":"tcomp","isMut":true,"isSigner":false},{"name":"treeAuthority","isMut":false,"isSigner":false},{"name":"seller","isMut":true,"isSigner":false},{"name":"delegate","isMut":false,"isSigner":false},{"name":"merkleTree","isMut":true,"isSigner":false},{"name":"logWrapper","isMut":false,"isSigner":false},{"name":"compressionProgram","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"bubblegumProgram","isMut":false,"isSigner":false},{"name":"tcompProgram","isMut":false,"isSigner":false},{"name":"tensorswapProgram","isMut":false,"isSigner":false},{"name":"bidState","isMut":true,"isSigner":false},{"name":"owner","isMut":true,"isSigner":false},{"name":"takerBroker","isMut":true,"isSigner":false,"isOptional":true},{"name":"makerBroker","isMut":true,"isSigner":false,"isOptional":true},{"name":"marginAccount","isMut":true,"isSigner":false},{"name":"whitelist","isMut":false,"isSigner":false},{"name":"cosigner","isMut":false,"isSigner":true},{"name":"rentDest","isMut":true,"isSigner":false}],"args":[{"name":"nonce","type":"u64"},{"name":"index","type":"u32"},{"name":"root","type":{"array":["u8",32]}},{"name":"metaHash","type":{"array":["u8",32]}},{"name":"creatorShares","type":"bytes"},{"name":"creatorVerified","type":{"vec":"bool"}},{"name":"sellerFeeBasisPoints","type":"u16"},{"name":"minAmount","type":"u64"},{"name":"optionalRoyaltyPct","type":{"option":"u16"}}]},{"name":"takeBidFullMeta","accounts":[{"name":"tcomp","isMut":true,"isSigner":false},{"name":"treeAuthority","isMut":false,"isSigner":false},{"name":"seller","isMut":true,"isSigner":false},{"name":"delegate","isMut":false,"isSigner":false},{"name":"merkleTree","isMut":true,"isSigner":false},{"name":"logWrapper","isMut":false,"isSigner":false},{"name":"compressionProgram","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"bubblegumProgram","isMut":false,"isSigner":false},{"name":"tcompProgram","isMut":false,"isSigner":false},{"name":"tensorswapProgram","isMut":false,"isSigner":false},{"name":"bidState","isMut":true,"isSigner":false},{"name":"owner","isMut":true,"isSigner":false},{"name":"takerBroker","isMut":true,"isSigner":false,"isOptional":true},{"name":"makerBroker","isMut":true,"isSigner":false,"isOptional":true},{"name":"marginAccount","isMut":true,"isSigner":false},{"name":"whitelist","isMut":false,"isSigner":false},{"name":"cosigner","isMut":false,"isSigner":true},{"name":"rentDest","isMut":true,"isSigner":false}],"args":[{"name":"nonce","type":"u64"},{"name":"index","type":"u32"},{"name":"root","type":{"array":["u8",32]}},{"name":"metaArgs","type":{"defined":"TMetadataArgs"}},{"name":"minAmount","type":"u64"},{"name":"optionalRoyaltyPct","type":{"option":"u16"}}]},{"name":"takeBidLegacy","accounts":[{"name":"tcomp","isMut":true,"isSigner":false},{"name":"seller","isMut":true,"isSigner":true},{"name":"bidState","isMut":true,"isSigner":false},{"name":"owner","isMut":true,"isSigner":false},{"name":"takerBroker","isMut":true,"isSigner":false,"isOptional":true},{"name":"makerBroker","isMut":true,"isSigner":false,"isOptional":true},{"name":"marginAccount","isMut":true,"isSigner":false},{"name":"whitelist","isMut":false,"isSigner":false},{"name":"nftSellerAcc","isMut":true,"isSigner":false},{"name":"nftMint","isMut":false,"isSigner":false},{"name":"nftMetadata","isMut":true,"isSigner":false},{"name":"ownerAtaAcc","isMut":true,"isSigner":false},{"name":"nftEdition","isMut":false,"isSigner":false},{"name":"ownerTokenRecord","isMut":true,"isSigner":false},{"name":"destTokenRecord","isMut":true,"isSigner":false},{"name":"pnftShared","accounts":[{"name":"tokenMetadataProgram","isMut":false,"isSigner":false},{"name":"instructions","isMut":false,"isSigner":false},{"name":"authorizationRulesProgram","isMut":false,"isSigner":false}]},{"name":"nftEscrow","isMut":true,"isSigner":false,"docs":["Implicitly checked via transfer. Will fail if wrong account"]},{"name":"tempEscrowTokenRecord","isMut":true,"isSigner":false},{"name":"authRules","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"associatedTokenProgram","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"tcompProgram","isMut":false,"isSigner":false},{"name":"tensorswapProgram","isMut":false,"isSigner":false},{"name":"cosigner","isMut":false,"isSigner":true},{"name":"mintProof","isMut":false,"isSigner":false,"docs":["intentionally not deserializing, it would be dummy in the case of VOC/FVC based verification"]},{"name":"rentDest","isMut":true,"isSigner":false}],"args":[{"name":"minAmount","type":"u64"},{"name":"optionalRoyaltyPct","type":{"option":"u16"}},{"name":"rulesAccPresent","type":"bool"},{"name":"authorizationData","type":{"option":{"defined":"AuthorizationDataLocal"}}}]},{"name":"takeBidT22","accounts":[{"name":"tcomp","isMut":true,"isSigner":false},{"name":"seller","isMut":true,"isSigner":true},{"name":"bidState","isMut":true,"isSigner":false},{"name":"owner","isMut":true,"isSigner":false},{"name":"takerBroker","isMut":true,"isSigner":false,"isOptional":true},{"name":"makerBroker","isMut":true,"isSigner":false,"isOptional":true},{"name":"marginAccount","isMut":true,"isSigner":false},{"name":"whitelist","isMut":false,"isSigner":false},{"name":"nftSellerAcc","isMut":true,"isSigner":false},{"name":"nftMint","isMut":false,"isSigner":false},{"name":"ownerAtaAcc","isMut":true,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"associatedTokenProgram","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"tcompProgram","isMut":false,"isSigner":false},{"name":"tensorswapProgram","isMut":false,"isSigner":false},{"name":"cosigner","isMut":false,"isSigner":true},{"name":"mintProof","isMut":false,"isSigner":false,"docs":["intentionally not deserializing, it would be dummy in the case of VOC/FVC based verification"]},{"name":"rentDest","isMut":true,"isSigner":false}],"args":[{"name":"minAmount","type":"u64"}]},{"name":"takeBidWns","accounts":[{"name":"tcomp","isMut":true,"isSigner":false},{"name":"seller","isMut":true,"isSigner":true},{"name":"bidState","isMut":true,"isSigner":false},{"name":"owner","isMut":true,"isSigner":false},{"name":"takerBroker","isMut":true,"isSigner":false,"isOptional":true},{"name":"makerBroker","isMut":true,"isSigner":false,"isOptional":true},{"name":"marginAccount","isMut":true,"isSigner":false},{"name":"whitelist","isMut":false,"isSigner":false},{"name":"nftSellerAcc","isMut":true,"isSigner":false},{"name":"nftMint","isMut":false,"isSigner":false},{"name":"ownerAtaAcc","isMut":true,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"associatedTokenProgram","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"tcompProgram","isMut":false,"isSigner":false},{"name":"tensorswapProgram","isMut":false,"isSigner":false},{"name":"cosigner","isMut":false,"isSigner":true},{"name":"mintProof","isMut":false,"isSigner":false,"docs":["intentionally not deserializing, it would be dummy in the case of VOC/FVC based verification"]},{"name":"rentDest","isMut":true,"isSigner":false},{"name":"approveAccount","isMut":true,"isSigner":false},{"name":"distribution","isMut":true,"isSigner":false},{"name":"wnsProgram","isMut":false,"isSigner":false},{"name":"distributionProgram","isMut":false,"isSigner":false},{"name":"extraMetas","isMut":false,"isSigner":false}],"args":[{"name":"minAmount","type":"u64"}]},{"name":"takeBidCore","accounts":[{"name":"tcomp","isMut":true,"isSigner":false},{"name":"seller","isMut":true,"isSigner":true},{"name":"bidState","isMut":true,"isSigner":false},{"name":"owner","isMut":true,"isSigner":false},{"name":"takerBroker","isMut":true,"isSigner":false,"isOptional":true},{"name":"makerBroker","isMut":true,"isSigner":false,"isOptional":true},{"name":"marginAccount","isMut":true,"isSigner":false},{"name":"whitelist","isMut":false,"isSigner":false},{"name":"asset","isMut":true,"isSigner":false},{"name":"collection","isMut":false,"isSigner":false,"isOptional":true},{"name":"mplCoreProgram","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"tcompProgram","isMut":false,"isSigner":false},{"name":"tensorswapProgram","isMut":false,"isSigner":false},{"name":"cosigner","isMut":false,"isSigner":true},{"name":"mintProof","isMut":false,"isSigner":false,"docs":["intentionally not deserializing, it would be dummy in the case of VOC/FVC based verification"]},{"name":"rentDest","isMut":true,"isSigner":false}],"args":[{"name":"minAmount","type":"u64"}]}],"accounts":[{"name":"ListState","type":{"kind":"struct","fields":[{"name":"version","type":"u8"},{"name":"bump","type":{"array":["u8",1]}},{"name":"owner","type":"publicKey"},{"name":"assetId","type":"publicKey"},{"name":"amount","type":"u64"},{"name":"currency","type":{"option":"publicKey"}},{"name":"expiry","type":"i64"},{"name":"privateTaker","type":{"option":"publicKey"}},{"name":"makerBroker","type":{"option":"publicKey"}},{"name":"rentPayer","docs":["owner is the rent payer when this is PublicKey::default"],"type":"publicKey"},{"name":"reserved","type":{"array":["u8",32]}},{"name":"reserved1","type":{"array":["u8",64]}}]}},{"name":"BidState","type":{"kind":"struct","fields":[{"name":"version","type":"u8"},{"name":"bump","type":{"array":["u8",1]}},{"name":"owner","type":"publicKey"},{"name":"bidId","docs":["Randomly picked pubkey used in bid seeds. To avoid dangling bids can use assetId here."],"type":"publicKey"},{"name":"target","type":{"defined":"Target"}},{"name":"targetId","type":"publicKey"},{"name":"field","type":{"option":{"defined":"Field"}}},{"name":"fieldId","type":{"option":"publicKey"}},{"name":"quantity","type":"u32"},{"name":"filledQuantity","type":"u32"},{"name":"amount","type":"u64"},{"name":"currency","type":{"option":"publicKey"}},{"name":"expiry","type":"i64"},{"name":"privateTaker","type":{"option":"publicKey"}},{"name":"makerBroker","type":{"option":"publicKey"}},{"name":"margin","type":{"option":"publicKey"}},{"name":"updatedAt","type":"i64"},{"name":"cosigner","type":"publicKey"},{"name":"rentPayer","docs":["owner is the rent payer when this is PublicKey::default"],"type":"publicKey"},{"name":"reserved","type":{"array":["u8",8]}},{"name":"reserved1","type":{"array":["u8",16]}},{"name":"reserved2","type":{"array":["u8",32]}}]}}],"types":[{"name":"TUses","type":{"kind":"struct","fields":[{"name":"useMethod","type":{"defined":"TUseMethod"}},{"name":"remaining","type":"u64"},{"name":"total","type":"u64"}]}},{"name":"TCollection","type":{"kind":"struct","fields":[{"name":"verified","type":"bool"},{"name":"key","type":"publicKey"}]}},{"name":"TMetadataArgs","type":{"kind":"struct","fields":[{"name":"name","docs":["The name of the asset"],"type":"string"},{"name":"symbol","docs":["The symbol for the asset"],"type":"string"},{"name":"uri","docs":["URI pointing to JSON representing the asset"],"type":"string"},{"name":"sellerFeeBasisPoints","docs":["Royalty basis points that goes to creators in secondary sales (0-10000)"],"type":"u16"},{"name":"primarySaleHappened","type":"bool"},{"name":"isMutable","type":"bool"},{"name":"editionNonce","docs":["nonce for easy calculation of editions, if present"],"type":{"option":"u8"}},{"name":"tokenStandard","docs":["Since we cannot easily change Metadata, we add the new DataV2 fields here at the end."],"type":{"option":{"defined":"TTokenStandard"}}},{"name":"collection","docs":["Collection"],"type":{"option":{"defined":"TCollection"}}},{"name":"uses","docs":["Uses"],"type":{"option":{"defined":"TUses"}}},{"name":"tokenProgramVersion","type":{"defined":"TTokenProgramVersion"}},{"name":"creatorShares","type":"bytes"},{"name":"creatorVerified","type":{"vec":"bool"}}]}},{"name":"MakeEvent","type":{"kind":"struct","fields":[{"name":"maker","type":"publicKey"},{"name":"bidId","type":{"option":"publicKey"}},{"name":"target","type":{"defined":"Target"}},{"name":"targetId","type":"publicKey"},{"name":"field","type":{"option":{"defined":"Field"}}},{"name":"fieldId","type":{"option":"publicKey"}},{"name":"amount","type":"u64"},{"name":"quantity","type":"u32"},{"name":"currency","type":{"option":"publicKey"}},{"name":"expiry","type":"i64"},{"name":"privateTaker","type":{"option":"publicKey"}},{"name":"assetId","type":{"option":"publicKey"}}]}},{"name":"TakeEvent","type":{"kind":"struct","fields":[{"name":"taker","type":"publicKey"},{"name":"bidId","type":{"option":"publicKey"}},{"name":"target","type":{"defined":"Target"}},{"name":"targetId","type":"publicKey"},{"name":"field","type":{"option":{"defined":"Field"}}},{"name":"fieldId","type":{"option":"publicKey"}},{"name":"amount","type":"u64"},{"name":"quantity","type":"u32"},{"name":"tcompFee","type":"u64"},{"name":"takerBrokerFee","type":"u64"},{"name":"makerBrokerFee","type":"u64"},{"name":"creatorFee","type":"u64"},{"name":"currency","type":{"option":"publicKey"}},{"name":"assetId","type":{"option":"publicKey"}}]}},{"name":"AuthorizationDataLocal","type":{"kind":"struct","fields":[{"name":"payload","type":{"vec":{"defined":"TaggedPayload"}}}]}},{"name":"TaggedPayload","type":{"kind":"struct","fields":[{"name":"name","type":"string"},{"name":"payload","type":{"defined":"PayloadTypeLocal"}}]}},{"name":"SeedsVecLocal","type":{"kind":"struct","fields":[{"name":"seeds","docs":["The vector of derivation seeds."],"type":{"vec":"bytes"}}]}},{"name":"ProofInfoLocal","type":{"kind":"struct","fields":[{"name":"proof","docs":["The merkle proof."],"type":{"vec":{"array":["u8",32]}}}]}},{"name":"TTokenProgramVersion","type":{"kind":"enum","variants":[{"name":"Original"},{"name":"Token2022"}]}},{"name":"TTokenStandard","type":{"kind":"enum","variants":[{"name":"NonFungible"},{"name":"FungibleAsset"},{"name":"Fungible"},{"name":"NonFungibleEdition"}]}},{"name":"TUseMethod","type":{"kind":"enum","variants":[{"name":"Burn"},{"name":"Multiple"},{"name":"Single"}]}},{"name":"TcompEvent","type":{"kind":"enum","variants":[{"name":"Maker","fields":[{"defined":"MakeEvent"}]},{"name":"Taker","fields":[{"defined":"TakeEvent"}]}]}},{"name":"PayloadTypeLocal","type":{"kind":"enum","variants":[{"name":"Pubkey","fields":["publicKey"]},{"name":"Seeds","fields":[{"defined":"SeedsVecLocal"}]},{"name":"MerkleProof","fields":[{"defined":"ProofInfoLocal"}]},{"name":"Number","fields":["u64"]}]}},{"name":"Target","type":{"kind":"enum","variants":[{"name":"AssetId"},{"name":"Whitelist"}]}},{"name":"Field","type":{"kind":"enum","variants":[{"name":"Name"}]}}],"errors":[{"code":6100,"name":"ArithmeticError","msg":"arithmetic error"},{"code":6101,"name":"ExpiryTooLarge","msg":"expiry too large"},{"code":6102,"name":"BadOwner","msg":"bad owner"},{"code":6103,"name":"BadListState","msg":"bad list state"},{"code":6104,"name":"BadRoyaltiesPct","msg":"royalties pct must be between 0 and 100"},{"code":6105,"name":"PriceMismatch","msg":"price mismatch"},{"code":6106,"name":"CreatorMismatch","msg":"creator mismatch"},{"code":6107,"name":"InsufficientBalance","msg":"insufficient balance"},{"code":6108,"name":"BidExpired","msg":"bid has expired"},{"code":6109,"name":"TakerNotAllowed","msg":"taker not allowed"},{"code":6110,"name":"BadBidField","msg":"cannot pass bid field"},{"code":6111,"name":"BidNotYetExpired","msg":"bid not yet expired"},{"code":6112,"name":"BadMargin","msg":"bad margin"},{"code":6113,"name":"WrongIxForBidTarget","msg":"wrong ix for bid target called"},{"code":6114,"name":"WrongTargetId","msg":"wrong target id"},{"code":6115,"name":"MissingFvc","msg":"creator array missing first verified creator"},{"code":6116,"name":"MissingCollection","msg":"metadata missing collection"},{"code":6117,"name":"CannotModifyTarget","msg":"cannot modify bid target, create a new bid"},{"code":6118,"name":"TargetIdMustEqualBidId","msg":"target id and bid id must be the same for single bids"},{"code":6119,"name":"CurrencyNotYetEnabled","msg":"currency not yet enabled"},{"code":6120,"name":"MakerBrokerNotYetEnabled","msg":"maker broker not yet enabled"},{"code":6121,"name":"OptionalRoyaltiesNotYetEnabled","msg":"optional royalties not yet enabled"},{"code":6122,"name":"WrongStateVersion","msg":"wrong state version"},{"code":6123,"name":"WrongBidFieldId","msg":"wrong field id"},{"code":6124,"name":"BrokerMismatch","msg":"broker mismatch"},{"code":6125,"name":"AssetIdMismatch","msg":"asset id mismatch"},{"code":6126,"name":"ListingExpired","msg":"listing has expired"},{"code":6127,"name":"ListingNotYetExpired","msg":"listing not yet expired"},{"code":6128,"name":"BadQuantity","msg":"bad quantity passed in"},{"code":6129,"name":"BidFullyFilled","msg":"bid fully filled"},{"code":6130,"name":"BadWhitelist","msg":"bad whitelist"},{"code":6131,"name":"ForbiddenCollection","msg":"forbidden collection"},{"code":6132,"name":"BadCosigner","msg":"bad cosigner"},{"code":6133,"name":"BadMintProof","msg":"bad mint proof"},{"code":6134,"name":"CurrencyMismatch","msg":"Currency mismatch"},{"code":6135,"name":"BidBalanceNotEmptied","msg":"The bid balance was not emptied"},{"code":6136,"name":"BadRentDest","msg":"Bad rent dest."},{"code":6137,"name":"CurrencyNotYetWhitelisted","msg":"currency not yet whitelisted"},{"code":6138,"name":"MakerBrokerNotYetWhitelisted","msg":"maker broker not yet whitelisted"},{"code":6139,"name":"WrongTokenRecordDerivation","msg":"token record derivation is wrong"}]} diff --git a/kb_demo_app/package.json b/kb_demo_app/package.json index 7e89a9e..65952ae 100644 --- a/kb_demo_app/package.json +++ b/kb_demo_app/package.json @@ -1,7 +1,7 @@ { "name": "kb-demo-app", "private": true, - "version": "0.7.54", + "version": "0.7.55", "type": "module", "scripts": { "dev": "vite", diff --git a/kb_demo_app/tauri.conf.json b/kb_demo_app/tauri.conf.json index a409885..6ed9c5d 100644 --- a/kb_demo_app/tauri.conf.json +++ b/kb_demo_app/tauri.conf.json @@ -1,7 +1,7 @@ { "$schema": "https://schema.tauri.app/config/2", "productName": "kb-demo-app", - "version": "0.7.54", + "version": "0.7.55", "identifier": "com.sasedev.kb-demo-app", "build": { "beforeDevCommand": "npm run dev", diff --git a/kb_lib/src/db/queries/dex_decoded_event.rs b/kb_lib/src/db/queries/dex_decoded_event.rs index cfbea4c..65c0981 100644 --- a/kb_lib/src/db/queries/dex_decoded_event.rs +++ b/kb_lib/src/db/queries/dex_decoded_event.rs @@ -112,7 +112,7 @@ WHERE decoded_event_id IN ( WHERE transaction_id = ? AND protocol_name IN ( 'raydium_amm_v4', 'raydium_cpmm', 'raydium_clmm', 'raydium_launchpad', - 'pump_fun', 'pump_swap', 'meteora_dbc', 'meteora_dlmm', 'meteora_damm_v1', + 'pump_fun', 'pump_fees', 'pump_swap', 'meteora_dbc', 'meteora_dlmm', 'meteora_damm_v1', 'meteora_damm_v2', 'orca_whirlpools', 'fluxbeam', 'dexlab', 'openbook_v2', 'phoenix_v1' ) @@ -160,7 +160,7 @@ DELETE FROM k_sol_dex_decoded_events WHERE transaction_id = ? AND protocol_name IN ( 'raydium_amm_v4', 'raydium_cpmm', 'raydium_clmm', 'raydium_launchpad', - 'pump_fun', 'pump_swap', 'meteora_dbc', 'meteora_dlmm', 'meteora_damm_v1', + 'pump_fun', 'pump_fees', 'pump_swap', 'meteora_dbc', 'meteora_dlmm', 'meteora_damm_v1', 'meteora_damm_v2', 'orca_whirlpools', 'fluxbeam', 'dexlab', 'openbook_v2', 'phoenix_v1' ) diff --git a/kb_lib/src/dex.rs b/kb_lib/src/dex.rs index ee4479e..3f2c5da 100644 --- a/kb_lib/src/dex.rs +++ b/kb_lib/src/dex.rs @@ -11,6 +11,7 @@ mod meteora_dlmm; mod openbook_v2; mod orca_whirlpools; mod phoenix_v1; +mod pump_fees; mod pump_fun; mod pump_swap; mod raydium_amm_v4; @@ -61,14 +62,17 @@ pub use orca_whirlpools::OrcaWhirlpoolsSwapDecoded; pub use phoenix_v1::PhoenixV1AuditDecoded; pub use phoenix_v1::PhoenixV1DecodedEvent; pub use phoenix_v1::PhoenixV1Decoder; +pub use pump_fees::PumpFeesDecodedEvent; +pub use pump_fees::PumpFeesDecoder; +pub use pump_fees::PumpFeesInstructionDecoded; pub use pump_fun::PumpFunCreateV2TokenDecoded; -pub use pump_fun::PumpFunInstructionAuditDecoded; pub use pump_fun::PumpFunDecodedEvent; pub use pump_fun::PumpFunDecoder; +pub use pump_fun::PumpFunInstructionAuditDecoded; pub use pump_fun::PumpFunTradeDecoded; pub use pump_swap::PumpSwapDecodedEvent; -pub use pump_swap::PumpSwapInstructionDecoded; pub use pump_swap::PumpSwapDecoder; +pub use pump_swap::PumpSwapInstructionDecoded; pub use pump_swap::PumpSwapTradeDecoded; pub use raydium_amm_v4::RaydiumAmmV4DecodedEvent; pub use raydium_amm_v4::RaydiumAmmV4Decoder; diff --git a/kb_lib/src/dex/pump_fees.rs b/kb_lib/src/dex/pump_fees.rs new file mode 100644 index 0000000..b000da5 --- /dev/null +++ b/kb_lib/src/dex/pump_fees.rs @@ -0,0 +1,2447 @@ +// file: kb_lib/src/dex/pump_fees.rs + +//! Pump Fees fee/config/accounting transaction decoder. + +const PUMP_FEES_ANCHOR_SELF_CPI_LOG_DISCRIMINATOR: [u8; 8] = [228, 69, 165, 46, 81, 203, 154, 29]; +const PUMP_FEES_CLAIM_SOCIAL_FEE_PDA_DISCRIMINATOR: [u8; 8] = + [225, 21, 251, 133, 161, 30, 199, 226]; +const PUMP_FEES_CLAIM_SOCIAL_FEE_PDA_V2_DISCRIMINATOR: [u8; 8] = + [17, 77, 240, 134, 58, 188, 53, 149]; +const PUMP_FEES_CRANK_DONATION_FEE_PDA_DISCRIMINATOR: [u8; 8] = + [220, 10, 189, 167, 169, 17, 25, 69]; +const PUMP_FEES_CREATE_DONATION_FEE_PDA_DISCRIMINATOR: [u8; 8] = + [244, 139, 16, 88, 14, 255, 122, 26]; +const PUMP_FEES_CREATE_FEE_SHARING_CONFIG_DISCRIMINATOR: [u8; 8] = + [195, 78, 86, 76, 111, 52, 251, 213]; +const PUMP_FEES_CREATE_SOCIAL_FEE_PDA_DISCRIMINATOR: [u8; 8] = + [144, 224, 59, 211, 78, 248, 202, 220]; +const PUMP_FEES_EXTEND_FEE_CONFIG_DISCRIMINATOR: [u8; 8] = [68, 179, 244, 90, 173, 56, 17, 217]; +const PUMP_FEES_GET_FEES_DISCRIMINATOR: [u8; 8] = [231, 37, 126, 85, 207, 91, 63, 52]; +const PUMP_FEES_INITIALIZE_BUYBACK_DISCRIMINATOR: [u8; 8] = [250, 129, 236, 160, 227, 36, 103, 134]; +const PUMP_FEES_INITIALIZE_FEE_CONFIG_DISCRIMINATOR: [u8; 8] = [62, 162, 20, 133, 121, 65, 145, 27]; +const PUMP_FEES_INITIALIZE_FEE_PROGRAM_GLOBAL_DISCRIMINATOR: [u8; 8] = + [35, 215, 130, 84, 233, 56, 124, 167]; +const PUMP_FEES_RESET_FEE_SHARING_CONFIG_DISCRIMINATOR: [u8; 8] = + [10, 2, 182, 95, 16, 127, 129, 186]; +const PUMP_FEES_RESET_FEE_SHARING_CONFIG_V2_DISCRIMINATOR: [u8; 8] = + [169, 245, 17, 209, 94, 91, 248, 128]; +const PUMP_FEES_REVOKE_FEE_SHARING_AUTHORITY_DISCRIMINATOR: [u8; 8] = + [18, 233, 158, 39, 185, 207, 58, 104]; +const PUMP_FEES_SET_AUTHORITY_DISCRIMINATOR: [u8; 8] = [133, 250, 37, 21, 110, 163, 26, 121]; +const PUMP_FEES_SET_CLAIM_RATE_LIMIT_DISCRIMINATOR: [u8; 8] = [185, 211, 159, 174, 212, 49, 88, 4]; +const PUMP_FEES_SET_DISABLE_FLAGS_DISCRIMINATOR: [u8; 8] = [194, 217, 112, 35, 114, 222, 51, 190]; +const PUMP_FEES_SET_SOCIAL_CLAIM_AUTHORITY_DISCRIMINATOR: [u8; 8] = + [147, 54, 184, 154, 136, 237, 185, 153]; +const PUMP_FEES_SWEEP_BUYBACK_DISCRIMINATOR: [u8; 8] = [138, 33, 204, 38, 207, 161, 159, 226]; +const PUMP_FEES_TRANSFER_FEE_SHARING_AUTHORITY_DISCRIMINATOR: [u8; 8] = + [202, 10, 75, 200, 164, 34, 210, 96]; +const PUMP_FEES_UPDATE_ADMIN_DISCRIMINATOR: [u8; 8] = [161, 176, 40, 213, 60, 184, 179, 228]; +const PUMP_FEES_UPDATE_BUYBACK_AUTHORITY_DISCRIMINATOR: [u8; 8] = + [66, 98, 113, 202, 121, 37, 219, 107]; +const PUMP_FEES_UPDATE_BUYBACK_CLAIM_RATE_LIMIT_DISCRIMINATOR: [u8; 8] = + [186, 95, 135, 190, 255, 199, 137, 170]; +const PUMP_FEES_UPDATE_FEE_CONFIG_DISCRIMINATOR: [u8; 8] = [104, 184, 103, 242, 88, 151, 107, 20]; +const PUMP_FEES_UPDATE_FEE_SHARES_DISCRIMINATOR: [u8; 8] = [189, 13, 136, 99, 187, 164, 237, 35]; +const PUMP_FEES_UPDATE_FEE_SHARES_V2_DISCRIMINATOR: [u8; 8] = [111, 251, 49, 6, 78, 78, 106, 18]; +const PUMP_FEES_UPDATE_STABLE_FEE_CONFIG_DISCRIMINATOR: [u8; 8] = + [107, 169, 100, 179, 134, 155, 146, 221]; +const PUMP_FEES_UPSERT_FEE_TIERS_DISCRIMINATOR: [u8; 8] = [227, 23, 150, 12, 77, 86, 94, 4]; +const PUMP_FEES_UPSERT_STABLE_FEE_TIERS_DISCRIMINATOR: [u8; 8] = + [181, 160, 162, 252, 74, 76, 224, 221]; +const PUMP_FEES_CREATE_FEE_SHARING_CONFIG_EVENT_DISCRIMINATOR: [u8; 8] = + [133, 105, 170, 200, 184, 116, 251, 88]; +const PUMP_FEES_DONATION_FEE_PDA_CRANKED_DISCRIMINATOR: [u8; 8] = + [30, 208, 107, 93, 177, 0, 223, 78]; +const PUMP_FEES_DONATION_FEE_PDA_CREATED_DISCRIMINATOR: [u8; 8] = + [94, 20, 137, 239, 35, 77, 225, 235]; +const PUMP_FEES_EXTEND_FEE_CONFIG_EVENT_DISCRIMINATOR: [u8; 8] = + [226, 203, 224, 35, 153, 10, 88, 51]; +const PUMP_FEES_INITIALIZE_FEE_CONFIG_EVENT_DISCRIMINATOR: [u8; 8] = + [89, 138, 244, 230, 10, 56, 226, 126]; +const PUMP_FEES_INITIALIZE_FEE_PROGRAM_GLOBAL_EVENT_DISCRIMINATOR: [u8; 8] = + [40, 233, 156, 78, 95, 0, 8, 199]; +const PUMP_FEES_RESET_FEE_SHARING_CONFIG_EVENT_DISCRIMINATOR: [u8; 8] = + [203, 204, 151, 226, 120, 55, 214, 243]; +const PUMP_FEES_SET_AUTHORITY_EVENT_DISCRIMINATOR: [u8; 8] = [18, 175, 132, 66, 208, 201, 87, 242]; +const PUMP_FEES_SET_CLAIM_RATE_LIMIT_EVENT_DISCRIMINATOR: [u8; 8] = + [13, 143, 143, 235, 181, 19, 51, 40]; +const PUMP_FEES_SET_DISABLE_FLAGS_EVENT_DISCRIMINATOR: [u8; 8] = [5, 8, 179, 65, 49, 55, 145, 126]; +const PUMP_FEES_SET_SOCIAL_CLAIM_AUTHORITY_EVENT_DISCRIMINATOR: [u8; 8] = + [60, 118, 127, 132, 239, 52, 254, 14]; +const PUMP_FEES_SOCIAL_FEE_PDA_CLAIMED_DISCRIMINATOR: [u8; 8] = + [50, 18, 193, 65, 237, 210, 234, 236]; +const PUMP_FEES_SOCIAL_FEE_PDA_CREATED_DISCRIMINATOR: [u8; 8] = + [183, 183, 218, 147, 24, 124, 137, 169]; +const PUMP_FEES_SWEEP_BUYBACK_EVENT_DISCRIMINATOR: [u8; 8] = [43, 56, 42, 214, 153, 57, 166, 137]; +const PUMP_FEES_UPDATE_ADMIN_EVENT_DISCRIMINATOR: [u8; 8] = [225, 152, 171, 87, 246, 63, 66, 234]; +const PUMP_FEES_UPDATE_FEE_CONFIG_EVENT_DISCRIMINATOR: [u8; 8] = + [90, 23, 65, 35, 62, 244, 188, 208]; +const PUMP_FEES_UPDATE_FEE_SHARES_EVENT_DISCRIMINATOR: [u8; 8] = + [21, 186, 196, 184, 91, 228, 225, 203]; +const PUMP_FEES_UPDATE_STABLE_FEE_CONFIG_EVENT_DISCRIMINATOR: [u8; 8] = + [94, 5, 43, 237, 103, 147, 232, 245]; +const PUMP_FEES_UPSERT_FEE_TIERS_EVENT_DISCRIMINATOR: [u8; 8] = + [171, 89, 169, 187, 122, 186, 33, 204]; +const PUMP_FEES_UPSERT_STABLE_FEE_TIERS_EVENT_DISCRIMINATOR: [u8; 8] = + [232, 237, 237, 52, 98, 146, 73, 243]; + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +enum PumpFeesBorshFieldKind { + U8, + U64, + I64, + U128, + Bool, + Pubkey, + String, + OptionBool, + OptionPubkey, + Fees, + ConfigStatus, + ShareholderVec, + FeeTierVec, +} + +#[derive(Debug, Clone, Copy)] +struct PumpFeesBorshFieldDescriptor { + name: &'static str, + kind: PumpFeesBorshFieldKind, +} + +#[derive(Debug, Clone, Copy)] +struct PumpFeesInstructionSpec { + name: &'static str, + fields: &'static [PumpFeesBorshFieldDescriptor], +} + +#[derive(Debug, Clone, Copy)] +struct PumpFeesAnchorEventSpec { + name: &'static str, + event_kind: &'static str, + fields: &'static [PumpFeesBorshFieldDescriptor], +} + +const PUMP_FEES_INSTRUCTION_CLAIM_SOCIAL_FEE_PDA_FIELDS: &[PumpFeesBorshFieldDescriptor] = &[ + PumpFeesBorshFieldDescriptor { + name: "user_id", + kind: PumpFeesBorshFieldKind::String, + }, + PumpFeesBorshFieldDescriptor { + name: "platform", + kind: PumpFeesBorshFieldKind::U8, + }, +]; + +const PUMP_FEES_INSTRUCTION_CLAIM_SOCIAL_FEE_PDA_V2_FIELDS: &[PumpFeesBorshFieldDescriptor] = &[ + PumpFeesBorshFieldDescriptor { + name: "user_id", + kind: PumpFeesBorshFieldKind::String, + }, + PumpFeesBorshFieldDescriptor { + name: "platform", + kind: PumpFeesBorshFieldKind::U8, + }, +]; + +const PUMP_FEES_INSTRUCTION_CRANK_DONATION_FEE_PDA_FIELDS: &[PumpFeesBorshFieldDescriptor] = &[]; + +const PUMP_FEES_INSTRUCTION_CREATE_DONATION_FEE_PDA_FIELDS: &[PumpFeesBorshFieldDescriptor] = &[]; + +const PUMP_FEES_INSTRUCTION_CREATE_FEE_SHARING_CONFIG_FIELDS: &[PumpFeesBorshFieldDescriptor] = &[]; + +const PUMP_FEES_INSTRUCTION_CREATE_SOCIAL_FEE_PDA_FIELDS: &[PumpFeesBorshFieldDescriptor] = &[ + PumpFeesBorshFieldDescriptor { + name: "user_id", + kind: PumpFeesBorshFieldKind::String, + }, + PumpFeesBorshFieldDescriptor { + name: "platform", + kind: PumpFeesBorshFieldKind::U8, + }, +]; + +const PUMP_FEES_INSTRUCTION_EXTEND_FEE_CONFIG_FIELDS: &[PumpFeesBorshFieldDescriptor] = &[]; + +const PUMP_FEES_INSTRUCTION_GET_FEES_FIELDS: &[PumpFeesBorshFieldDescriptor] = &[ + PumpFeesBorshFieldDescriptor { + name: "is_pump_pool", + kind: PumpFeesBorshFieldKind::Bool, + }, + PumpFeesBorshFieldDescriptor { + name: "market_cap_lamports", + kind: PumpFeesBorshFieldKind::U128, + }, + PumpFeesBorshFieldDescriptor { + name: "trade_size_lamports", + kind: PumpFeesBorshFieldKind::U64, + }, + PumpFeesBorshFieldDescriptor { + name: "is_new_quote_mint", + kind: PumpFeesBorshFieldKind::OptionBool, + }, +]; + +const PUMP_FEES_INSTRUCTION_INITIALIZE_BUYBACK_FIELDS: &[PumpFeesBorshFieldDescriptor] = + &[PumpFeesBorshFieldDescriptor { + name: "index", + kind: PumpFeesBorshFieldKind::U8, + }]; + +const PUMP_FEES_INSTRUCTION_INITIALIZE_FEE_CONFIG_FIELDS: &[PumpFeesBorshFieldDescriptor] = &[]; + +const PUMP_FEES_INSTRUCTION_INITIALIZE_FEE_PROGRAM_GLOBAL_FIELDS: + &[PumpFeesBorshFieldDescriptor] = &[ + PumpFeesBorshFieldDescriptor { + name: "social_claim_authority", + kind: PumpFeesBorshFieldKind::Pubkey, + }, + PumpFeesBorshFieldDescriptor { + name: "disable_flags", + kind: PumpFeesBorshFieldKind::U8, + }, + PumpFeesBorshFieldDescriptor { + name: "claim_rate_limit", + kind: PumpFeesBorshFieldKind::U64, + }, +]; + +const PUMP_FEES_INSTRUCTION_RESET_FEE_SHARING_CONFIG_FIELDS: &[PumpFeesBorshFieldDescriptor] = &[]; + +const PUMP_FEES_INSTRUCTION_RESET_FEE_SHARING_CONFIG_V2_FIELDS: &[PumpFeesBorshFieldDescriptor] = + &[]; + +const PUMP_FEES_INSTRUCTION_REVOKE_FEE_SHARING_AUTHORITY_FIELDS: &[PumpFeesBorshFieldDescriptor] = + &[]; + +const PUMP_FEES_INSTRUCTION_SET_AUTHORITY_FIELDS: &[PumpFeesBorshFieldDescriptor] = + &[PumpFeesBorshFieldDescriptor { + name: "new_authority", + kind: PumpFeesBorshFieldKind::Pubkey, + }]; + +const PUMP_FEES_INSTRUCTION_SET_CLAIM_RATE_LIMIT_FIELDS: &[PumpFeesBorshFieldDescriptor] = + &[PumpFeesBorshFieldDescriptor { + name: "claim_rate_limit", + kind: PumpFeesBorshFieldKind::U64, + }]; + +const PUMP_FEES_INSTRUCTION_SET_DISABLE_FLAGS_FIELDS: &[PumpFeesBorshFieldDescriptor] = + &[PumpFeesBorshFieldDescriptor { + name: "disable_flags", + kind: PumpFeesBorshFieldKind::U8, + }]; + +const PUMP_FEES_INSTRUCTION_SET_SOCIAL_CLAIM_AUTHORITY_FIELDS: &[PumpFeesBorshFieldDescriptor] = + &[PumpFeesBorshFieldDescriptor { + name: "social_claim_authority", + kind: PumpFeesBorshFieldKind::Pubkey, + }]; + +const PUMP_FEES_INSTRUCTION_SWEEP_BUYBACK_FIELDS: &[PumpFeesBorshFieldDescriptor] = + &[PumpFeesBorshFieldDescriptor { + name: "index", + kind: PumpFeesBorshFieldKind::U8, + }]; + +const PUMP_FEES_INSTRUCTION_TRANSFER_FEE_SHARING_AUTHORITY_FIELDS: + &[PumpFeesBorshFieldDescriptor] = &[]; + +const PUMP_FEES_INSTRUCTION_UPDATE_ADMIN_FIELDS: &[PumpFeesBorshFieldDescriptor] = &[]; + +const PUMP_FEES_INSTRUCTION_UPDATE_BUYBACK_AUTHORITY_FIELDS: &[PumpFeesBorshFieldDescriptor] = &[ + PumpFeesBorshFieldDescriptor { + name: "index", + kind: PumpFeesBorshFieldKind::U8, + }, + PumpFeesBorshFieldDescriptor { + name: "new_authority", + kind: PumpFeesBorshFieldKind::Pubkey, + }, +]; + +const PUMP_FEES_INSTRUCTION_UPDATE_BUYBACK_CLAIM_RATE_LIMIT_FIELDS: + &[PumpFeesBorshFieldDescriptor] = &[ + PumpFeesBorshFieldDescriptor { + name: "index", + kind: PumpFeesBorshFieldKind::U8, + }, + PumpFeesBorshFieldDescriptor { + name: "claim_rate_limit", + kind: PumpFeesBorshFieldKind::I64, + }, +]; + +const PUMP_FEES_INSTRUCTION_UPDATE_FEE_CONFIG_FIELDS: &[PumpFeesBorshFieldDescriptor] = &[ + PumpFeesBorshFieldDescriptor { + name: "fee_tiers", + kind: PumpFeesBorshFieldKind::FeeTierVec, + }, + PumpFeesBorshFieldDescriptor { + name: "flat_fees", + kind: PumpFeesBorshFieldKind::Fees, + }, +]; + +const PUMP_FEES_INSTRUCTION_UPDATE_FEE_SHARES_FIELDS: &[PumpFeesBorshFieldDescriptor] = + &[PumpFeesBorshFieldDescriptor { + name: "shareholders", + kind: PumpFeesBorshFieldKind::ShareholderVec, + }]; + +const PUMP_FEES_INSTRUCTION_UPDATE_FEE_SHARES_V2_FIELDS: &[PumpFeesBorshFieldDescriptor] = + &[PumpFeesBorshFieldDescriptor { + name: "shareholders", + kind: PumpFeesBorshFieldKind::ShareholderVec, + }]; + +const PUMP_FEES_INSTRUCTION_UPDATE_STABLE_FEE_CONFIG_FIELDS: &[PumpFeesBorshFieldDescriptor] = + &[PumpFeesBorshFieldDescriptor { + name: "fee_tiers", + kind: PumpFeesBorshFieldKind::FeeTierVec, + }]; + +const PUMP_FEES_INSTRUCTION_UPSERT_FEE_TIERS_FIELDS: &[PumpFeesBorshFieldDescriptor] = &[ + PumpFeesBorshFieldDescriptor { + name: "fee_tiers", + kind: PumpFeesBorshFieldKind::FeeTierVec, + }, + PumpFeesBorshFieldDescriptor { + name: "offset", + kind: PumpFeesBorshFieldKind::U8, + }, +]; + +const PUMP_FEES_INSTRUCTION_UPSERT_STABLE_FEE_TIERS_FIELDS: &[PumpFeesBorshFieldDescriptor] = &[ + PumpFeesBorshFieldDescriptor { + name: "fee_tiers", + kind: PumpFeesBorshFieldKind::FeeTierVec, + }, + PumpFeesBorshFieldDescriptor { + name: "offset", + kind: PumpFeesBorshFieldKind::U8, + }, +]; + +const PUMP_FEES_EVENT_CREATE_FEE_SHARING_CONFIG_EVENT_FIELDS: &[PumpFeesBorshFieldDescriptor] = &[ + PumpFeesBorshFieldDescriptor { + name: "timestamp", + kind: PumpFeesBorshFieldKind::I64, + }, + PumpFeesBorshFieldDescriptor { + name: "mint", + kind: PumpFeesBorshFieldKind::Pubkey, + }, + PumpFeesBorshFieldDescriptor { + name: "bonding_curve", + kind: PumpFeesBorshFieldKind::Pubkey, + }, + PumpFeesBorshFieldDescriptor { + name: "pool", + kind: PumpFeesBorshFieldKind::OptionPubkey, + }, + PumpFeesBorshFieldDescriptor { + name: "sharing_config", + kind: PumpFeesBorshFieldKind::Pubkey, + }, + PumpFeesBorshFieldDescriptor { + name: "admin", + kind: PumpFeesBorshFieldKind::Pubkey, + }, + PumpFeesBorshFieldDescriptor { + name: "initial_shareholders", + kind: PumpFeesBorshFieldKind::ShareholderVec, + }, + PumpFeesBorshFieldDescriptor { + name: "status", + kind: PumpFeesBorshFieldKind::ConfigStatus, + }, +]; + +const PUMP_FEES_EVENT_DONATION_FEE_PDA_CRANKED_FIELDS: &[PumpFeesBorshFieldDescriptor] = &[ + PumpFeesBorshFieldDescriptor { + name: "timestamp", + kind: PumpFeesBorshFieldKind::I64, + }, + PumpFeesBorshFieldDescriptor { + name: "signer", + kind: PumpFeesBorshFieldKind::Pubkey, + }, + PumpFeesBorshFieldDescriptor { + name: "donation_fee_pda", + kind: PumpFeesBorshFieldKind::Pubkey, + }, + PumpFeesBorshFieldDescriptor { + name: "config_id", + kind: PumpFeesBorshFieldKind::Pubkey, + }, + PumpFeesBorshFieldDescriptor { + name: "base_mint", + kind: PumpFeesBorshFieldKind::Pubkey, + }, + PumpFeesBorshFieldDescriptor { + name: "quote_mint", + kind: PumpFeesBorshFieldKind::Pubkey, + }, + PumpFeesBorshFieldDescriptor { + name: "creator", + kind: PumpFeesBorshFieldKind::Pubkey, + }, + PumpFeesBorshFieldDescriptor { + name: "amount", + kind: PumpFeesBorshFieldKind::U64, + }, +]; + +const PUMP_FEES_EVENT_DONATION_FEE_PDA_CREATED_FIELDS: &[PumpFeesBorshFieldDescriptor] = &[ + PumpFeesBorshFieldDescriptor { + name: "timestamp", + kind: PumpFeesBorshFieldKind::I64, + }, + PumpFeesBorshFieldDescriptor { + name: "created_by", + kind: PumpFeesBorshFieldKind::Pubkey, + }, + PumpFeesBorshFieldDescriptor { + name: "donation_fee_pda", + kind: PumpFeesBorshFieldKind::Pubkey, + }, + PumpFeesBorshFieldDescriptor { + name: "config_id", + kind: PumpFeesBorshFieldKind::Pubkey, + }, + PumpFeesBorshFieldDescriptor { + name: "base_mint", + kind: PumpFeesBorshFieldKind::Pubkey, + }, + PumpFeesBorshFieldDescriptor { + name: "quote_mint", + kind: PumpFeesBorshFieldKind::Pubkey, + }, + PumpFeesBorshFieldDescriptor { + name: "creator", + kind: PumpFeesBorshFieldKind::Pubkey, + }, +]; + +const PUMP_FEES_EVENT_EXTEND_FEE_CONFIG_EVENT_FIELDS: &[PumpFeesBorshFieldDescriptor] = &[ + PumpFeesBorshFieldDescriptor { + name: "fee_config", + kind: PumpFeesBorshFieldKind::Pubkey, + }, + PumpFeesBorshFieldDescriptor { + name: "user", + kind: PumpFeesBorshFieldKind::Pubkey, + }, + PumpFeesBorshFieldDescriptor { + name: "current_size", + kind: PumpFeesBorshFieldKind::U64, + }, + PumpFeesBorshFieldDescriptor { + name: "new_size", + kind: PumpFeesBorshFieldKind::U64, + }, + PumpFeesBorshFieldDescriptor { + name: "timestamp", + kind: PumpFeesBorshFieldKind::I64, + }, +]; + +const PUMP_FEES_EVENT_INITIALIZE_FEE_CONFIG_EVENT_FIELDS: &[PumpFeesBorshFieldDescriptor] = &[ + PumpFeesBorshFieldDescriptor { + name: "timestamp", + kind: PumpFeesBorshFieldKind::I64, + }, + PumpFeesBorshFieldDescriptor { + name: "admin", + kind: PumpFeesBorshFieldKind::Pubkey, + }, + PumpFeesBorshFieldDescriptor { + name: "fee_config", + kind: PumpFeesBorshFieldKind::Pubkey, + }, +]; + +const PUMP_FEES_EVENT_INITIALIZE_FEE_PROGRAM_GLOBAL_EVENT_FIELDS: + &[PumpFeesBorshFieldDescriptor] = &[ + PumpFeesBorshFieldDescriptor { + name: "timestamp", + kind: PumpFeesBorshFieldKind::I64, + }, + PumpFeesBorshFieldDescriptor { + name: "authority", + kind: PumpFeesBorshFieldKind::Pubkey, + }, + PumpFeesBorshFieldDescriptor { + name: "social_claim_authority", + kind: PumpFeesBorshFieldKind::Pubkey, + }, + PumpFeesBorshFieldDescriptor { + name: "disable_flags", + kind: PumpFeesBorshFieldKind::U8, + }, + PumpFeesBorshFieldDescriptor { + name: "claim_rate_limit", + kind: PumpFeesBorshFieldKind::U64, + }, +]; + +const PUMP_FEES_EVENT_RESET_FEE_SHARING_CONFIG_EVENT_FIELDS: &[PumpFeesBorshFieldDescriptor] = &[ + PumpFeesBorshFieldDescriptor { + name: "timestamp", + kind: PumpFeesBorshFieldKind::I64, + }, + PumpFeesBorshFieldDescriptor { + name: "mint", + kind: PumpFeesBorshFieldKind::Pubkey, + }, + PumpFeesBorshFieldDescriptor { + name: "sharing_config", + kind: PumpFeesBorshFieldKind::Pubkey, + }, + PumpFeesBorshFieldDescriptor { + name: "old_admin", + kind: PumpFeesBorshFieldKind::Pubkey, + }, + PumpFeesBorshFieldDescriptor { + name: "old_shareholders", + kind: PumpFeesBorshFieldKind::ShareholderVec, + }, + PumpFeesBorshFieldDescriptor { + name: "new_admin", + kind: PumpFeesBorshFieldKind::Pubkey, + }, + PumpFeesBorshFieldDescriptor { + name: "new_shareholders", + kind: PumpFeesBorshFieldKind::ShareholderVec, + }, + PumpFeesBorshFieldDescriptor { + name: "old_version", + kind: PumpFeesBorshFieldKind::U8, + }, + PumpFeesBorshFieldDescriptor { + name: "new_version", + kind: PumpFeesBorshFieldKind::U8, + }, +]; + +const PUMP_FEES_EVENT_SET_AUTHORITY_EVENT_FIELDS: &[PumpFeesBorshFieldDescriptor] = &[ + PumpFeesBorshFieldDescriptor { + name: "timestamp", + kind: PumpFeesBorshFieldKind::I64, + }, + PumpFeesBorshFieldDescriptor { + name: "old_authority", + kind: PumpFeesBorshFieldKind::Pubkey, + }, + PumpFeesBorshFieldDescriptor { + name: "new_authority", + kind: PumpFeesBorshFieldKind::Pubkey, + }, +]; + +const PUMP_FEES_EVENT_SET_CLAIM_RATE_LIMIT_EVENT_FIELDS: &[PumpFeesBorshFieldDescriptor] = &[ + PumpFeesBorshFieldDescriptor { + name: "timestamp", + kind: PumpFeesBorshFieldKind::I64, + }, + PumpFeesBorshFieldDescriptor { + name: "claim_rate_limit", + kind: PumpFeesBorshFieldKind::U64, + }, +]; + +const PUMP_FEES_EVENT_SET_DISABLE_FLAGS_EVENT_FIELDS: &[PumpFeesBorshFieldDescriptor] = &[ + PumpFeesBorshFieldDescriptor { + name: "timestamp", + kind: PumpFeesBorshFieldKind::I64, + }, + PumpFeesBorshFieldDescriptor { + name: "disable_flags", + kind: PumpFeesBorshFieldKind::U8, + }, +]; + +const PUMP_FEES_EVENT_SET_SOCIAL_CLAIM_AUTHORITY_EVENT_FIELDS: &[PumpFeesBorshFieldDescriptor] = &[ + PumpFeesBorshFieldDescriptor { + name: "timestamp", + kind: PumpFeesBorshFieldKind::I64, + }, + PumpFeesBorshFieldDescriptor { + name: "social_claim_authority", + kind: PumpFeesBorshFieldKind::Pubkey, + }, +]; + +const PUMP_FEES_EVENT_SOCIAL_FEE_PDA_CLAIMED_FIELDS: &[PumpFeesBorshFieldDescriptor] = &[ + PumpFeesBorshFieldDescriptor { + name: "timestamp", + kind: PumpFeesBorshFieldKind::I64, + }, + PumpFeesBorshFieldDescriptor { + name: "user_id", + kind: PumpFeesBorshFieldKind::String, + }, + PumpFeesBorshFieldDescriptor { + name: "platform", + kind: PumpFeesBorshFieldKind::U8, + }, + PumpFeesBorshFieldDescriptor { + name: "social_fee_pda", + kind: PumpFeesBorshFieldKind::Pubkey, + }, + PumpFeesBorshFieldDescriptor { + name: "recipient", + kind: PumpFeesBorshFieldKind::Pubkey, + }, + PumpFeesBorshFieldDescriptor { + name: "social_claim_authority", + kind: PumpFeesBorshFieldKind::Pubkey, + }, + PumpFeesBorshFieldDescriptor { + name: "amount_claimed", + kind: PumpFeesBorshFieldKind::U64, + }, + PumpFeesBorshFieldDescriptor { + name: "claimable_before", + kind: PumpFeesBorshFieldKind::U64, + }, + PumpFeesBorshFieldDescriptor { + name: "lifetime_claimed", + kind: PumpFeesBorshFieldKind::U64, + }, + PumpFeesBorshFieldDescriptor { + name: "recipient_balance_before", + kind: PumpFeesBorshFieldKind::U64, + }, + PumpFeesBorshFieldDescriptor { + name: "recipient_balance_after", + kind: PumpFeesBorshFieldKind::U64, + }, + PumpFeesBorshFieldDescriptor { + name: "quote_mint", + kind: PumpFeesBorshFieldKind::Pubkey, + }, +]; + +const PUMP_FEES_EVENT_SOCIAL_FEE_PDA_CREATED_FIELDS: &[PumpFeesBorshFieldDescriptor] = &[ + PumpFeesBorshFieldDescriptor { + name: "timestamp", + kind: PumpFeesBorshFieldKind::I64, + }, + PumpFeesBorshFieldDescriptor { + name: "user_id", + kind: PumpFeesBorshFieldKind::String, + }, + PumpFeesBorshFieldDescriptor { + name: "platform", + kind: PumpFeesBorshFieldKind::U8, + }, + PumpFeesBorshFieldDescriptor { + name: "social_fee_pda", + kind: PumpFeesBorshFieldKind::Pubkey, + }, + PumpFeesBorshFieldDescriptor { + name: "created_by", + kind: PumpFeesBorshFieldKind::Pubkey, + }, +]; + +const PUMP_FEES_EVENT_SWEEP_BUYBACK_EVENT_FIELDS: &[PumpFeesBorshFieldDescriptor] = &[ + PumpFeesBorshFieldDescriptor { + name: "index", + kind: PumpFeesBorshFieldKind::U8, + }, + PumpFeesBorshFieldDescriptor { + name: "sol_amount", + kind: PumpFeesBorshFieldKind::U64, + }, + PumpFeesBorshFieldDescriptor { + name: "token_amount", + kind: PumpFeesBorshFieldKind::U64, + }, + PumpFeesBorshFieldDescriptor { + name: "destination", + kind: PumpFeesBorshFieldKind::Pubkey, + }, + PumpFeesBorshFieldDescriptor { + name: "buyback_vault", + kind: PumpFeesBorshFieldKind::Pubkey, + }, + PumpFeesBorshFieldDescriptor { + name: "mint", + kind: PumpFeesBorshFieldKind::Pubkey, + }, +]; + +const PUMP_FEES_EVENT_UPDATE_ADMIN_EVENT_FIELDS: &[PumpFeesBorshFieldDescriptor] = &[ + PumpFeesBorshFieldDescriptor { + name: "timestamp", + kind: PumpFeesBorshFieldKind::I64, + }, + PumpFeesBorshFieldDescriptor { + name: "old_admin", + kind: PumpFeesBorshFieldKind::Pubkey, + }, + PumpFeesBorshFieldDescriptor { + name: "new_admin", + kind: PumpFeesBorshFieldKind::Pubkey, + }, +]; + +const PUMP_FEES_EVENT_UPDATE_FEE_CONFIG_EVENT_FIELDS: &[PumpFeesBorshFieldDescriptor] = &[ + PumpFeesBorshFieldDescriptor { + name: "timestamp", + kind: PumpFeesBorshFieldKind::I64, + }, + PumpFeesBorshFieldDescriptor { + name: "admin", + kind: PumpFeesBorshFieldKind::Pubkey, + }, + PumpFeesBorshFieldDescriptor { + name: "fee_config", + kind: PumpFeesBorshFieldKind::Pubkey, + }, + PumpFeesBorshFieldDescriptor { + name: "fee_tiers", + kind: PumpFeesBorshFieldKind::FeeTierVec, + }, + PumpFeesBorshFieldDescriptor { + name: "flat_fees", + kind: PumpFeesBorshFieldKind::Fees, + }, +]; + +const PUMP_FEES_EVENT_UPDATE_FEE_SHARES_EVENT_FIELDS: &[PumpFeesBorshFieldDescriptor] = &[ + PumpFeesBorshFieldDescriptor { + name: "timestamp", + kind: PumpFeesBorshFieldKind::I64, + }, + PumpFeesBorshFieldDescriptor { + name: "mint", + kind: PumpFeesBorshFieldKind::Pubkey, + }, + PumpFeesBorshFieldDescriptor { + name: "sharing_config", + kind: PumpFeesBorshFieldKind::Pubkey, + }, + PumpFeesBorshFieldDescriptor { + name: "admin", + kind: PumpFeesBorshFieldKind::Pubkey, + }, + PumpFeesBorshFieldDescriptor { + name: "new_shareholders", + kind: PumpFeesBorshFieldKind::ShareholderVec, + }, + PumpFeesBorshFieldDescriptor { + name: "version", + kind: PumpFeesBorshFieldKind::U8, + }, +]; + +const PUMP_FEES_EVENT_UPDATE_STABLE_FEE_CONFIG_EVENT_FIELDS: &[PumpFeesBorshFieldDescriptor] = &[ + PumpFeesBorshFieldDescriptor { + name: "timestamp", + kind: PumpFeesBorshFieldKind::I64, + }, + PumpFeesBorshFieldDescriptor { + name: "admin", + kind: PumpFeesBorshFieldKind::Pubkey, + }, + PumpFeesBorshFieldDescriptor { + name: "fee_config", + kind: PumpFeesBorshFieldKind::Pubkey, + }, + PumpFeesBorshFieldDescriptor { + name: "stable_fee_tiers", + kind: PumpFeesBorshFieldKind::FeeTierVec, + }, + PumpFeesBorshFieldDescriptor { + name: "flat_fees", + kind: PumpFeesBorshFieldKind::Fees, + }, +]; + +const PUMP_FEES_EVENT_UPSERT_FEE_TIERS_EVENT_FIELDS: &[PumpFeesBorshFieldDescriptor] = &[ + PumpFeesBorshFieldDescriptor { + name: "timestamp", + kind: PumpFeesBorshFieldKind::I64, + }, + PumpFeesBorshFieldDescriptor { + name: "admin", + kind: PumpFeesBorshFieldKind::Pubkey, + }, + PumpFeesBorshFieldDescriptor { + name: "fee_config", + kind: PumpFeesBorshFieldKind::Pubkey, + }, + PumpFeesBorshFieldDescriptor { + name: "fee_tiers", + kind: PumpFeesBorshFieldKind::FeeTierVec, + }, + PumpFeesBorshFieldDescriptor { + name: "offset", + kind: PumpFeesBorshFieldKind::U8, + }, +]; + +const PUMP_FEES_EVENT_UPSERT_STABLE_FEE_TIERS_EVENT_FIELDS: &[PumpFeesBorshFieldDescriptor] = &[ + PumpFeesBorshFieldDescriptor { + name: "timestamp", + kind: PumpFeesBorshFieldKind::I64, + }, + PumpFeesBorshFieldDescriptor { + name: "admin", + kind: PumpFeesBorshFieldKind::Pubkey, + }, + PumpFeesBorshFieldDescriptor { + name: "fee_config", + kind: PumpFeesBorshFieldKind::Pubkey, + }, + PumpFeesBorshFieldDescriptor { + name: "stable_fee_tiers", + kind: PumpFeesBorshFieldKind::FeeTierVec, + }, + PumpFeesBorshFieldDescriptor { + name: "offset", + kind: PumpFeesBorshFieldKind::U8, + }, +]; + +#[derive(Debug, Clone)] +struct PumpFeesInstructionData { + spec: PumpFeesInstructionSpec, + discriminator_hex: std::string::String, + args_json: serde_json::Value, +} + +#[derive(Debug, Clone)] +struct PumpFeesAnchorEventData { + spec: PumpFeesAnchorEventSpec, + discriminator_hex: std::string::String, + payload_size: usize, + fields_json: serde_json::Value, +} + +/// Decoded Pump Fees instruction or Anchor event retained for coverage/materialization. +#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] +pub struct PumpFeesInstructionDecoded { + /// Parent transaction internal id. + pub transaction_id: i64, + /// Parent instruction internal id. + pub instruction_id: i64, + /// Decoded program id. + pub program_id: std::string::String, + /// Normalized event kind. + pub event_kind: std::string::String, + /// Best-effort accounting/config account. + pub pool_account: std::option::Option, + /// Best-effort base/subject mint. + pub token_a_mint: std::option::Option, + /// Best-effort quote/fee mint. + pub token_b_mint: std::option::Option, + /// Best-effort secondary account used as lp/config surrogate. + pub lp_mint: std::option::Option, + /// Decoded payload with instruction/event fields and materialization aliases. + pub payload_json: serde_json::Value, +} + +/// Decoded Pump Fees event. +#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] +pub enum PumpFeesDecodedEvent { + /// Decoded on-chain instruction. + Instruction(PumpFeesInstructionDecoded), + /// Decoded Anchor `Program data` event. + AnchorEvent(PumpFeesInstructionDecoded), +} + +/// Pump Fees decoder. +#[derive(Debug, Clone, Default)] +pub struct PumpFeesDecoder; + +impl PumpFeesDecoder { + /// Creates a new decoder. + pub fn new() -> Self { + return Self; + } + + /// Decodes one projected transaction into Pump Fees instruction and Anchor events. + pub fn decode_transaction( + &self, + transaction: &crate::ChainTransactionDto, + instructions: &[crate::ChainInstructionDto], + ) -> Result, crate::Error> { + let transaction_id = match transaction.id { + Some(transaction_id) => transaction_id, + None => { + return Err(crate::Error::InvalidState(format!( + "chain transaction '{}' has no internal id", + transaction.signature + ))); + }, + }; + let transaction_json_result = + serde_json::from_str::(transaction.transaction_json.as_str()); + let transaction_json = match transaction_json_result { + Ok(transaction_json) => transaction_json, + Err(error) => { + return Err(crate::Error::Json(format!( + "cannot parse transaction_json for signature '{}': {}", + transaction.signature, error + ))); + }, + }; + let log_messages = extract_log_messages(&transaction_json); + let anchor_events_result = decode_pump_fees_anchor_events(log_messages.as_slice()); + let anchor_events = match anchor_events_result { + Ok(anchor_events) => anchor_events, + Err(error) => return Err(error), + }; + let mut decoded_events = std::vec::Vec::new(); + let mut pump_instruction_links = std::vec::Vec::new(); + for instruction in instructions { + let program_id = match &instruction.program_id { + Some(program_id) => program_id, + None => continue, + }; + if program_id.as_str() != crate::PUMP_FEES_PROGRAM_ID { + continue; + } + let instruction_id = match instruction.id { + Some(instruction_id) => instruction_id, + None => continue, + }; + let instruction_data_result = + parse_pump_fees_instruction_data(instruction.data_json.as_ref()); + let instruction_data = match instruction_data_result { + Ok(instruction_data) => instruction_data, + Err(error) => return Err(error), + }; + let instruction_data = match instruction_data { + Some(instruction_data) => instruction_data, + None => continue, + }; + let accounts_result = parse_accounts_json(instruction.accounts_json.as_str()); + let accounts = match accounts_result { + Ok(accounts) => accounts, + Err(error) => return Err(error), + }; + let parsed_json_result = parse_optional_parsed_json(instruction.parsed_json.as_ref()); + let parsed_json = match parsed_json_result { + Ok(parsed_json) => parsed_json, + Err(error) => return Err(error), + }; + let accounts_by_name = accounts_by_name_json(parsed_json.as_ref(), accounts.as_slice()); + let mut payload = serde_json::Map::new(); + payload + .insert("decoder".to_string(), serde_json::Value::String("pump_fees".to_string())); + payload.insert( + "signature".to_string(), + serde_json::Value::String(transaction.signature.clone()), + ); + payload.insert("instructionId".to_string(), serde_json::json!(instruction_id)); + payload.insert( + "instructionIndex".to_string(), + serde_json::json!(instruction.instruction_index), + ); + if let Some(inner_instruction_index) = instruction.inner_instruction_index { + payload.insert( + "innerInstructionIndex".to_string(), + serde_json::json!(inner_instruction_index), + ); + } + payload.insert("programId".to_string(), serde_json::Value::String(program_id.clone())); + payload.insert( + "instructionName".to_string(), + serde_json::Value::String(instruction_data.spec.name.to_string()), + ); + payload.insert( + "discriminatorHex".to_string(), + serde_json::Value::String(instruction_data.discriminator_hex.clone()), + ); + payload.insert("decodedArguments".to_string(), instruction_data.args_json.clone()); + payload.insert("accounts".to_string(), serde_json::json!(accounts)); + payload.insert("accountsByName".to_string(), accounts_by_name.clone()); + add_materialization_aliases( + &mut payload, + instruction_data.spec.name, + &instruction_data.args_json, + &accounts_by_name, + ); + let payload_json = serde_json::Value::Object(payload); + let pool_account = payload_string( + &payload_json, + &[ + "feeConfig", + "fee_config", + "feeProgramGlobal", + "fee_program_global", + "sharingConfig", + "sharing_config", + "socialFeePda", + "social_fee_pda", + "donationFeePda", + "donation_fee_pda", + "buybackVault", + "buyback_vault", + "global", + ], + ); + let token_a_mint = payload_string(&payload_json, &["mint", "baseMint", "base_mint"]); + let token_b_mint = match payload_string(&payload_json, &["quoteMint", "quote_mint"]) { + Some(quote_mint) => Some(quote_mint), + None => { + if event_kind_is_buyback_or_fee(instruction_data.spec.name) { + Some(crate::WSOL_MINT_ID.to_string()) + } else { + None + } + }, + }; + let lp_mint = payload_string( + &payload_json, + &[ + "sharingConfig", + "sharing_config", + "buybackVaultAta", + "buyback_vault_ata", + "socialFeePda", + "social_fee_pda", + "donationFeePda", + "donation_fee_pda", + ], + ); + decoded_events.push(crate::PumpFeesDecodedEvent::Instruction( + PumpFeesInstructionDecoded { + transaction_id, + instruction_id, + program_id: program_id.clone(), + event_kind: format!("pump_fees.{}", instruction_data.spec.name), + pool_account, + token_a_mint, + token_b_mint, + lp_mint, + payload_json, + }, + )); + pump_instruction_links.push(( + instruction.instruction_index, + instruction_id, + accounts_by_name, + )); + } + for (event_index, anchor_event) in anchor_events.iter().enumerate() { + let instruction_id = match instruction_id_for_anchor_event( + event_index, + pump_instruction_links.as_slice(), + ) { + Some(instruction_id) => instruction_id, + None => continue, + }; + let accounts_by_name = + accounts_by_name_for_anchor_event(event_index, pump_instruction_links.as_slice()); + let mut payload = serde_json::Map::new(); + payload + .insert("decoder".to_string(), serde_json::Value::String("pump_fees".to_string())); + payload.insert( + "signature".to_string(), + serde_json::Value::String(transaction.signature.clone()), + ); + payload.insert("instructionId".to_string(), serde_json::json!(instruction_id)); + payload.insert("anchorEventIndex".to_string(), serde_json::json!(event_index)); + payload.insert( + "programId".to_string(), + serde_json::Value::String(crate::PUMP_FEES_PROGRAM_ID.to_string()), + ); + payload.insert( + "eventName".to_string(), + serde_json::Value::String(anchor_event.spec.name.to_string()), + ); + payload.insert( + "discriminatorHex".to_string(), + serde_json::Value::String(anchor_event.discriminator_hex.clone()), + ); + payload.insert( + "anchorEventPayloadSize".to_string(), + serde_json::json!(anchor_event.payload_size), + ); + payload.insert("decodedEvent".to_string(), anchor_event.fields_json.clone()); + add_materialization_aliases( + &mut payload, + anchor_event.spec.name, + &anchor_event.fields_json, + &accounts_by_name, + ); + let payload_json = serde_json::Value::Object(payload); + let pool_account = payload_string( + &payload_json, + &[ + "feeConfig", + "fee_config", + "feeProgramGlobal", + "fee_program_global", + "sharingConfig", + "sharing_config", + "socialFeePda", + "social_fee_pda", + "donationFeePda", + "donation_fee_pda", + "buybackVault", + "buyback_vault", + "global", + ], + ); + let token_a_mint = payload_string(&payload_json, &["mint", "baseMint", "base_mint"]); + let token_b_mint = payload_string(&payload_json, &["quoteMint", "quote_mint"]); + let lp_mint = payload_string( + &payload_json, + &[ + "sharingConfig", + "sharing_config", + "socialFeePda", + "social_fee_pda", + "donationFeePda", + "donation_fee_pda", + "buybackVault", + "buyback_vault", + ], + ); + decoded_events.push(crate::PumpFeesDecodedEvent::AnchorEvent( + PumpFeesInstructionDecoded { + transaction_id, + instruction_id, + program_id: crate::PUMP_FEES_PROGRAM_ID.to_string(), + event_kind: anchor_event.spec.event_kind.to_string(), + pool_account, + token_a_mint, + token_b_mint, + lp_mint, + payload_json, + }, + )); + } + return Ok(decoded_events); + } +} + +fn pump_fees_instruction_spec( + discriminator: [u8; 8], +) -> std::option::Option { + match discriminator { + PUMP_FEES_CLAIM_SOCIAL_FEE_PDA_DISCRIMINATOR => { + return Some(PumpFeesInstructionSpec { + name: "claim_social_fee_pda", + fields: PUMP_FEES_INSTRUCTION_CLAIM_SOCIAL_FEE_PDA_FIELDS, + }); + }, + PUMP_FEES_CLAIM_SOCIAL_FEE_PDA_V2_DISCRIMINATOR => { + return Some(PumpFeesInstructionSpec { + name: "claim_social_fee_pda_v2", + fields: PUMP_FEES_INSTRUCTION_CLAIM_SOCIAL_FEE_PDA_V2_FIELDS, + }); + }, + PUMP_FEES_CRANK_DONATION_FEE_PDA_DISCRIMINATOR => { + return Some(PumpFeesInstructionSpec { + name: "crank_donation_fee_pda", + fields: PUMP_FEES_INSTRUCTION_CRANK_DONATION_FEE_PDA_FIELDS, + }); + }, + PUMP_FEES_CREATE_DONATION_FEE_PDA_DISCRIMINATOR => { + return Some(PumpFeesInstructionSpec { + name: "create_donation_fee_pda", + fields: PUMP_FEES_INSTRUCTION_CREATE_DONATION_FEE_PDA_FIELDS, + }); + }, + PUMP_FEES_CREATE_FEE_SHARING_CONFIG_DISCRIMINATOR => { + return Some(PumpFeesInstructionSpec { + name: "create_fee_sharing_config", + fields: PUMP_FEES_INSTRUCTION_CREATE_FEE_SHARING_CONFIG_FIELDS, + }); + }, + PUMP_FEES_CREATE_SOCIAL_FEE_PDA_DISCRIMINATOR => { + return Some(PumpFeesInstructionSpec { + name: "create_social_fee_pda", + fields: PUMP_FEES_INSTRUCTION_CREATE_SOCIAL_FEE_PDA_FIELDS, + }); + }, + PUMP_FEES_EXTEND_FEE_CONFIG_DISCRIMINATOR => { + return Some(PumpFeesInstructionSpec { + name: "extend_fee_config", + fields: PUMP_FEES_INSTRUCTION_EXTEND_FEE_CONFIG_FIELDS, + }); + }, + PUMP_FEES_GET_FEES_DISCRIMINATOR => { + return Some(PumpFeesInstructionSpec { + name: "get_fees", + fields: PUMP_FEES_INSTRUCTION_GET_FEES_FIELDS, + }); + }, + PUMP_FEES_INITIALIZE_BUYBACK_DISCRIMINATOR => { + return Some(PumpFeesInstructionSpec { + name: "initialize_buyback", + fields: PUMP_FEES_INSTRUCTION_INITIALIZE_BUYBACK_FIELDS, + }); + }, + PUMP_FEES_INITIALIZE_FEE_CONFIG_DISCRIMINATOR => { + return Some(PumpFeesInstructionSpec { + name: "initialize_fee_config", + fields: PUMP_FEES_INSTRUCTION_INITIALIZE_FEE_CONFIG_FIELDS, + }); + }, + PUMP_FEES_INITIALIZE_FEE_PROGRAM_GLOBAL_DISCRIMINATOR => { + return Some(PumpFeesInstructionSpec { + name: "initialize_fee_program_global", + fields: PUMP_FEES_INSTRUCTION_INITIALIZE_FEE_PROGRAM_GLOBAL_FIELDS, + }); + }, + PUMP_FEES_RESET_FEE_SHARING_CONFIG_DISCRIMINATOR => { + return Some(PumpFeesInstructionSpec { + name: "reset_fee_sharing_config", + fields: PUMP_FEES_INSTRUCTION_RESET_FEE_SHARING_CONFIG_FIELDS, + }); + }, + PUMP_FEES_RESET_FEE_SHARING_CONFIG_V2_DISCRIMINATOR => { + return Some(PumpFeesInstructionSpec { + name: "reset_fee_sharing_config_v2", + fields: PUMP_FEES_INSTRUCTION_RESET_FEE_SHARING_CONFIG_V2_FIELDS, + }); + }, + PUMP_FEES_REVOKE_FEE_SHARING_AUTHORITY_DISCRIMINATOR => { + return Some(PumpFeesInstructionSpec { + name: "revoke_fee_sharing_authority", + fields: PUMP_FEES_INSTRUCTION_REVOKE_FEE_SHARING_AUTHORITY_FIELDS, + }); + }, + PUMP_FEES_SET_AUTHORITY_DISCRIMINATOR => { + return Some(PumpFeesInstructionSpec { + name: "set_authority", + fields: PUMP_FEES_INSTRUCTION_SET_AUTHORITY_FIELDS, + }); + }, + PUMP_FEES_SET_CLAIM_RATE_LIMIT_DISCRIMINATOR => { + return Some(PumpFeesInstructionSpec { + name: "set_claim_rate_limit", + fields: PUMP_FEES_INSTRUCTION_SET_CLAIM_RATE_LIMIT_FIELDS, + }); + }, + PUMP_FEES_SET_DISABLE_FLAGS_DISCRIMINATOR => { + return Some(PumpFeesInstructionSpec { + name: "set_disable_flags", + fields: PUMP_FEES_INSTRUCTION_SET_DISABLE_FLAGS_FIELDS, + }); + }, + PUMP_FEES_SET_SOCIAL_CLAIM_AUTHORITY_DISCRIMINATOR => { + return Some(PumpFeesInstructionSpec { + name: "set_social_claim_authority", + fields: PUMP_FEES_INSTRUCTION_SET_SOCIAL_CLAIM_AUTHORITY_FIELDS, + }); + }, + PUMP_FEES_SWEEP_BUYBACK_DISCRIMINATOR => { + return Some(PumpFeesInstructionSpec { + name: "sweep_buyback", + fields: PUMP_FEES_INSTRUCTION_SWEEP_BUYBACK_FIELDS, + }); + }, + PUMP_FEES_TRANSFER_FEE_SHARING_AUTHORITY_DISCRIMINATOR => { + return Some(PumpFeesInstructionSpec { + name: "transfer_fee_sharing_authority", + fields: PUMP_FEES_INSTRUCTION_TRANSFER_FEE_SHARING_AUTHORITY_FIELDS, + }); + }, + PUMP_FEES_UPDATE_ADMIN_DISCRIMINATOR => { + return Some(PumpFeesInstructionSpec { + name: "update_admin", + fields: PUMP_FEES_INSTRUCTION_UPDATE_ADMIN_FIELDS, + }); + }, + PUMP_FEES_UPDATE_BUYBACK_AUTHORITY_DISCRIMINATOR => { + return Some(PumpFeesInstructionSpec { + name: "update_buyback_authority", + fields: PUMP_FEES_INSTRUCTION_UPDATE_BUYBACK_AUTHORITY_FIELDS, + }); + }, + PUMP_FEES_UPDATE_BUYBACK_CLAIM_RATE_LIMIT_DISCRIMINATOR => { + return Some(PumpFeesInstructionSpec { + name: "update_buyback_claim_rate_limit", + fields: PUMP_FEES_INSTRUCTION_UPDATE_BUYBACK_CLAIM_RATE_LIMIT_FIELDS, + }); + }, + PUMP_FEES_UPDATE_FEE_CONFIG_DISCRIMINATOR => { + return Some(PumpFeesInstructionSpec { + name: "update_fee_config", + fields: PUMP_FEES_INSTRUCTION_UPDATE_FEE_CONFIG_FIELDS, + }); + }, + PUMP_FEES_UPDATE_FEE_SHARES_DISCRIMINATOR => { + return Some(PumpFeesInstructionSpec { + name: "update_fee_shares", + fields: PUMP_FEES_INSTRUCTION_UPDATE_FEE_SHARES_FIELDS, + }); + }, + PUMP_FEES_UPDATE_FEE_SHARES_V2_DISCRIMINATOR => { + return Some(PumpFeesInstructionSpec { + name: "update_fee_shares_v2", + fields: PUMP_FEES_INSTRUCTION_UPDATE_FEE_SHARES_V2_FIELDS, + }); + }, + PUMP_FEES_UPDATE_STABLE_FEE_CONFIG_DISCRIMINATOR => { + return Some(PumpFeesInstructionSpec { + name: "update_stable_fee_config", + fields: PUMP_FEES_INSTRUCTION_UPDATE_STABLE_FEE_CONFIG_FIELDS, + }); + }, + PUMP_FEES_UPSERT_FEE_TIERS_DISCRIMINATOR => { + return Some(PumpFeesInstructionSpec { + name: "upsert_fee_tiers", + fields: PUMP_FEES_INSTRUCTION_UPSERT_FEE_TIERS_FIELDS, + }); + }, + PUMP_FEES_UPSERT_STABLE_FEE_TIERS_DISCRIMINATOR => { + return Some(PumpFeesInstructionSpec { + name: "upsert_stable_fee_tiers", + fields: PUMP_FEES_INSTRUCTION_UPSERT_STABLE_FEE_TIERS_FIELDS, + }); + }, + _ => return None, + } +} + +fn pump_fees_anchor_event_spec( + discriminator: [u8; 8], +) -> std::option::Option { + match discriminator { + PUMP_FEES_CREATE_FEE_SHARING_CONFIG_EVENT_DISCRIMINATOR => { + return Some(PumpFeesAnchorEventSpec { + name: "CreateFeeSharingConfigEvent", + event_kind: "pump_fees.create_fee_sharing_config_event", + fields: PUMP_FEES_EVENT_CREATE_FEE_SHARING_CONFIG_EVENT_FIELDS, + }); + }, + PUMP_FEES_DONATION_FEE_PDA_CRANKED_DISCRIMINATOR => { + return Some(PumpFeesAnchorEventSpec { + name: "DonationFeePdaCranked", + event_kind: "pump_fees.donation_fee_pda_cranked", + fields: PUMP_FEES_EVENT_DONATION_FEE_PDA_CRANKED_FIELDS, + }); + }, + PUMP_FEES_DONATION_FEE_PDA_CREATED_DISCRIMINATOR => { + return Some(PumpFeesAnchorEventSpec { + name: "DonationFeePdaCreated", + event_kind: "pump_fees.donation_fee_pda_created", + fields: PUMP_FEES_EVENT_DONATION_FEE_PDA_CREATED_FIELDS, + }); + }, + PUMP_FEES_EXTEND_FEE_CONFIG_EVENT_DISCRIMINATOR => { + return Some(PumpFeesAnchorEventSpec { + name: "ExtendFeeConfigEvent", + event_kind: "pump_fees.extend_fee_config_event", + fields: PUMP_FEES_EVENT_EXTEND_FEE_CONFIG_EVENT_FIELDS, + }); + }, + PUMP_FEES_INITIALIZE_FEE_CONFIG_EVENT_DISCRIMINATOR => { + return Some(PumpFeesAnchorEventSpec { + name: "InitializeFeeConfigEvent", + event_kind: "pump_fees.initialize_fee_config_event", + fields: PUMP_FEES_EVENT_INITIALIZE_FEE_CONFIG_EVENT_FIELDS, + }); + }, + PUMP_FEES_INITIALIZE_FEE_PROGRAM_GLOBAL_EVENT_DISCRIMINATOR => { + return Some(PumpFeesAnchorEventSpec { + name: "InitializeFeeProgramGlobalEvent", + event_kind: "pump_fees.initialize_fee_program_global_event", + fields: PUMP_FEES_EVENT_INITIALIZE_FEE_PROGRAM_GLOBAL_EVENT_FIELDS, + }); + }, + PUMP_FEES_RESET_FEE_SHARING_CONFIG_EVENT_DISCRIMINATOR => { + return Some(PumpFeesAnchorEventSpec { + name: "ResetFeeSharingConfigEvent", + event_kind: "pump_fees.reset_fee_sharing_config_event", + fields: PUMP_FEES_EVENT_RESET_FEE_SHARING_CONFIG_EVENT_FIELDS, + }); + }, + PUMP_FEES_SET_AUTHORITY_EVENT_DISCRIMINATOR => { + return Some(PumpFeesAnchorEventSpec { + name: "SetAuthorityEvent", + event_kind: "pump_fees.set_authority_event", + fields: PUMP_FEES_EVENT_SET_AUTHORITY_EVENT_FIELDS, + }); + }, + PUMP_FEES_SET_CLAIM_RATE_LIMIT_EVENT_DISCRIMINATOR => { + return Some(PumpFeesAnchorEventSpec { + name: "SetClaimRateLimitEvent", + event_kind: "pump_fees.set_claim_rate_limit_event", + fields: PUMP_FEES_EVENT_SET_CLAIM_RATE_LIMIT_EVENT_FIELDS, + }); + }, + PUMP_FEES_SET_DISABLE_FLAGS_EVENT_DISCRIMINATOR => { + return Some(PumpFeesAnchorEventSpec { + name: "SetDisableFlagsEvent", + event_kind: "pump_fees.set_disable_flags_event", + fields: PUMP_FEES_EVENT_SET_DISABLE_FLAGS_EVENT_FIELDS, + }); + }, + PUMP_FEES_SET_SOCIAL_CLAIM_AUTHORITY_EVENT_DISCRIMINATOR => { + return Some(PumpFeesAnchorEventSpec { + name: "SetSocialClaimAuthorityEvent", + event_kind: "pump_fees.set_social_claim_authority_event", + fields: PUMP_FEES_EVENT_SET_SOCIAL_CLAIM_AUTHORITY_EVENT_FIELDS, + }); + }, + PUMP_FEES_SOCIAL_FEE_PDA_CLAIMED_DISCRIMINATOR => { + return Some(PumpFeesAnchorEventSpec { + name: "SocialFeePdaClaimed", + event_kind: "pump_fees.social_fee_pda_claimed", + fields: PUMP_FEES_EVENT_SOCIAL_FEE_PDA_CLAIMED_FIELDS, + }); + }, + PUMP_FEES_SOCIAL_FEE_PDA_CREATED_DISCRIMINATOR => { + return Some(PumpFeesAnchorEventSpec { + name: "SocialFeePdaCreated", + event_kind: "pump_fees.social_fee_pda_created", + fields: PUMP_FEES_EVENT_SOCIAL_FEE_PDA_CREATED_FIELDS, + }); + }, + PUMP_FEES_SWEEP_BUYBACK_EVENT_DISCRIMINATOR => { + return Some(PumpFeesAnchorEventSpec { + name: "SweepBuybackEvent", + event_kind: "pump_fees.sweep_buyback_event", + fields: PUMP_FEES_EVENT_SWEEP_BUYBACK_EVENT_FIELDS, + }); + }, + PUMP_FEES_UPDATE_ADMIN_EVENT_DISCRIMINATOR => { + return Some(PumpFeesAnchorEventSpec { + name: "UpdateAdminEvent", + event_kind: "pump_fees.update_admin_event", + fields: PUMP_FEES_EVENT_UPDATE_ADMIN_EVENT_FIELDS, + }); + }, + PUMP_FEES_UPDATE_FEE_CONFIG_EVENT_DISCRIMINATOR => { + return Some(PumpFeesAnchorEventSpec { + name: "UpdateFeeConfigEvent", + event_kind: "pump_fees.update_fee_config_event", + fields: PUMP_FEES_EVENT_UPDATE_FEE_CONFIG_EVENT_FIELDS, + }); + }, + PUMP_FEES_UPDATE_FEE_SHARES_EVENT_DISCRIMINATOR => { + return Some(PumpFeesAnchorEventSpec { + name: "UpdateFeeSharesEvent", + event_kind: "pump_fees.update_fee_shares_event", + fields: PUMP_FEES_EVENT_UPDATE_FEE_SHARES_EVENT_FIELDS, + }); + }, + PUMP_FEES_UPDATE_STABLE_FEE_CONFIG_EVENT_DISCRIMINATOR => { + return Some(PumpFeesAnchorEventSpec { + name: "UpdateStableFeeConfigEvent", + event_kind: "pump_fees.update_stable_fee_config_event", + fields: PUMP_FEES_EVENT_UPDATE_STABLE_FEE_CONFIG_EVENT_FIELDS, + }); + }, + PUMP_FEES_UPSERT_FEE_TIERS_EVENT_DISCRIMINATOR => { + return Some(PumpFeesAnchorEventSpec { + name: "UpsertFeeTiersEvent", + event_kind: "pump_fees.upsert_fee_tiers_event", + fields: PUMP_FEES_EVENT_UPSERT_FEE_TIERS_EVENT_FIELDS, + }); + }, + PUMP_FEES_UPSERT_STABLE_FEE_TIERS_EVENT_DISCRIMINATOR => { + return Some(PumpFeesAnchorEventSpec { + name: "UpsertStableFeeTiersEvent", + event_kind: "pump_fees.upsert_stable_fee_tiers_event", + fields: PUMP_FEES_EVENT_UPSERT_STABLE_FEE_TIERS_EVENT_FIELDS, + }); + }, + _ => return None, + } +} + +fn parse_pump_fees_instruction_data( + data_json: std::option::Option<&std::string::String>, +) -> Result, crate::Error> { + let data_text = match decode_instruction_data_text(data_json) { + Some(data_text) => data_text, + None => return Ok(None), + }; + let decoded_result = bs58::decode(data_text.trim()).into_vec(); + let decoded = match decoded_result { + Ok(decoded) => decoded, + Err(error) => { + return Err(crate::Error::InvalidState(format!( + "cannot base58-decode pump_fees instruction data: {}", + error + ))); + }, + }; + if decoded.len() < 8 { + return Ok(None); + } + let discriminator = match read_8_byte_array(decoded.as_slice(), 0) { + Some(discriminator) => discriminator, + None => return Ok(None), + }; + let spec = match pump_fees_instruction_spec(discriminator) { + Some(spec) => spec, + None => return Ok(None), + }; + let decoded_fields_result = decode_borsh_fields(&decoded[8..], spec.fields); + let decoded_fields = match decoded_fields_result { + Ok(decoded_fields) => decoded_fields, + Err(error) => return Err(error), + }; + return Ok(Some(PumpFeesInstructionData { + spec, + discriminator_hex: bytes_to_hex(discriminator.as_slice()), + args_json: decoded_fields, + })); +} + +fn decode_pump_fees_anchor_events( + log_messages: &[std::string::String], +) -> Result, crate::Error> { + let mut events = std::vec::Vec::new(); + for message in log_messages { + let encoded = match extract_program_data_base64(message.as_str()) { + Some(encoded) => encoded, + None => continue, + }; + let event_result = decode_pump_fees_anchor_event_from_base64(encoded); + let event_option = match event_result { + Ok(event_option) => event_option, + Err(error) => return Err(error), + }; + if let Some(event) = event_option { + events.push(event); + } + } + return Ok(events); +} + +fn decode_pump_fees_anchor_event_from_base64( + encoded: &str, +) -> Result, crate::Error> { + let decoded = decode_base64_standard(encoded); + let decoded = match decoded { + Some(decoded) => decoded, + None => return Ok(None), + }; + let payload = if decoded.len() >= 16 + && read_8_byte_array(decoded.as_slice(), 0) + == Some(PUMP_FEES_ANCHOR_SELF_CPI_LOG_DISCRIMINATOR) + { + &decoded[8..] + } else { + decoded.as_slice() + }; + if payload.len() < 8 { + return Ok(None); + } + let discriminator = match read_8_byte_array(payload, 0) { + Some(discriminator) => discriminator, + None => return Ok(None), + }; + let spec = match pump_fees_anchor_event_spec(discriminator) { + Some(spec) => spec, + None => return Ok(None), + }; + let fields_result = decode_borsh_fields(&payload[8..], spec.fields); + let fields_json = match fields_result { + Ok(fields_json) => fields_json, + Err(error) => return Err(error), + }; + return Ok(Some(PumpFeesAnchorEventData { + spec, + discriminator_hex: bytes_to_hex(discriminator.as_slice()), + payload_size: payload.len().saturating_sub(8), + fields_json, + })); +} + +fn decode_borsh_fields( + data: &[u8], + fields: &[PumpFeesBorshFieldDescriptor], +) -> Result { + let mut offset = 0_usize; + let mut object = serde_json::Map::new(); + for field in fields { + let decoded_result = read_borsh_field_value(data, offset, field.kind); + let (value, next_offset) = match decoded_result { + Ok(value) => value, + Err(error) => return Err(error), + }; + offset = next_offset; + object.insert(field.name.to_string(), value); + } + object.insert("payloadBytesConsumed".to_string(), serde_json::json!(offset)); + object.insert("payloadBytesTotal".to_string(), serde_json::json!(data.len())); + return Ok(serde_json::Value::Object(object)); +} + +fn read_borsh_field_value( + data: &[u8], + offset: usize, + kind: PumpFeesBorshFieldKind, +) -> Result<(serde_json::Value, usize), crate::Error> { + match kind { + PumpFeesBorshFieldKind::U8 => return read_u8_value(data, offset), + PumpFeesBorshFieldKind::U64 => return read_u64_value(data, offset), + PumpFeesBorshFieldKind::I64 => return read_i64_value(data, offset), + PumpFeesBorshFieldKind::U128 => return read_u128_value(data, offset), + PumpFeesBorshFieldKind::Bool => return read_bool_value(data, offset), + PumpFeesBorshFieldKind::Pubkey => return read_pubkey_value(data, offset), + PumpFeesBorshFieldKind::String => return read_string_value(data, offset), + PumpFeesBorshFieldKind::OptionBool => return read_option_bool_value(data, offset), + PumpFeesBorshFieldKind::OptionPubkey => return read_option_pubkey_value(data, offset), + PumpFeesBorshFieldKind::Fees => return read_fees_value(data, offset), + PumpFeesBorshFieldKind::ConfigStatus => return read_config_status_value(data, offset), + PumpFeesBorshFieldKind::ShareholderVec => return read_shareholder_vec_value(data, offset), + PumpFeesBorshFieldKind::FeeTierVec => return read_fee_tier_vec_value(data, offset), + } +} + +fn read_u8_value(data: &[u8], offset: usize) -> Result<(serde_json::Value, usize), crate::Error> { + if offset.saturating_add(1) > data.len() { + return Err(crate::Error::InvalidState("pump_fees u8 field exceeds payload".to_string())); + } + return Ok((serde_json::json!(data[offset]), offset.saturating_add(1))); +} + +fn read_u16_value(data: &[u8], offset: usize) -> Result<(serde_json::Value, usize), crate::Error> { + if offset.saturating_add(2) > data.len() { + return Err(crate::Error::InvalidState("pump_fees u16 field exceeds payload".to_string())); + } + let mut bytes = [0_u8; 2]; + bytes.copy_from_slice(&data[offset..offset + 2]); + return Ok((serde_json::json!(u16::from_le_bytes(bytes)), offset.saturating_add(2))); +} + +fn read_u32_raw(data: &[u8], offset: usize) -> Result<(u32, usize), crate::Error> { + if offset.saturating_add(4) > data.len() { + return Err(crate::Error::InvalidState("pump_fees u32 field exceeds payload".to_string())); + } + let mut bytes = [0_u8; 4]; + bytes.copy_from_slice(&data[offset..offset + 4]); + return Ok((u32::from_le_bytes(bytes), offset.saturating_add(4))); +} + +fn read_u64_value(data: &[u8], offset: usize) -> Result<(serde_json::Value, usize), crate::Error> { + if offset.saturating_add(8) > data.len() { + return Err(crate::Error::InvalidState("pump_fees u64 field exceeds payload".to_string())); + } + let mut bytes = [0_u8; 8]; + bytes.copy_from_slice(&data[offset..offset + 8]); + let value = u64::from_le_bytes(bytes); + return Ok((serde_json::Value::String(value.to_string()), offset.saturating_add(8))); +} + +fn read_i64_value(data: &[u8], offset: usize) -> Result<(serde_json::Value, usize), crate::Error> { + if offset.saturating_add(8) > data.len() { + return Err(crate::Error::InvalidState("pump_fees i64 field exceeds payload".to_string())); + } + let mut bytes = [0_u8; 8]; + bytes.copy_from_slice(&data[offset..offset + 8]); + let value = i64::from_le_bytes(bytes); + return Ok((serde_json::Value::String(value.to_string()), offset.saturating_add(8))); +} + +fn read_u128_value(data: &[u8], offset: usize) -> Result<(serde_json::Value, usize), crate::Error> { + if offset.saturating_add(16) > data.len() { + return Err(crate::Error::InvalidState("pump_fees u128 field exceeds payload".to_string())); + } + let mut bytes = [0_u8; 16]; + bytes.copy_from_slice(&data[offset..offset + 16]); + let value = u128::from_le_bytes(bytes); + return Ok((serde_json::Value::String(value.to_string()), offset.saturating_add(16))); +} + +fn read_bool_value(data: &[u8], offset: usize) -> Result<(serde_json::Value, usize), crate::Error> { + let value_result = read_u8_value(data, offset); + let (value, next_offset) = match value_result { + Ok(value) => value, + Err(error) => return Err(error), + }; + let numeric = match value.as_u64() { + Some(numeric) => numeric, + None => 0, + }; + return Ok((serde_json::Value::Bool(numeric != 0), next_offset)); +} + +fn read_pubkey_value( + data: &[u8], + offset: usize, +) -> Result<(serde_json::Value, usize), crate::Error> { + if offset.saturating_add(32) > data.len() { + return Err(crate::Error::InvalidState( + "pump_fees pubkey field exceeds payload".to_string(), + )); + } + let encoded = bs58::encode(&data[offset..offset + 32]).into_string(); + return Ok((serde_json::Value::String(encoded), offset.saturating_add(32))); +} + +fn read_string_value( + data: &[u8], + offset: usize, +) -> Result<(serde_json::Value, usize), crate::Error> { + let length_result = read_u32_raw(data, offset); + let (length, mut current_offset) = match length_result { + Ok(value) => value, + Err(error) => return Err(error), + }; + let length_usize = length as usize; + if current_offset.saturating_add(length_usize) > data.len() { + return Err(crate::Error::InvalidState( + "pump_fees string field exceeds payload".to_string(), + )); + } + let text_result = std::str::from_utf8(&data[current_offset..current_offset + length_usize]); + let text = match text_result { + Ok(text) => text, + Err(error) => { + return Err(crate::Error::InvalidState(format!( + "pump_fees string field is not utf8: {}", + error + ))); + }, + }; + current_offset = current_offset.saturating_add(length_usize); + return Ok((serde_json::Value::String(text.to_string()), current_offset)); +} + +fn read_option_bool_value( + data: &[u8], + offset: usize, +) -> Result<(serde_json::Value, usize), crate::Error> { + return read_bool_value(data, offset); +} + +fn read_option_pubkey_value( + data: &[u8], + offset: usize, +) -> Result<(serde_json::Value, usize), crate::Error> { + let tag_result = read_u8_value(data, offset); + let (tag_value, mut current_offset) = match tag_result { + Ok(value) => value, + Err(error) => return Err(error), + }; + let tag = match tag_value.as_u64() { + Some(tag) => tag, + None => 0, + }; + if tag == 0 { + return Ok((serde_json::Value::Null, current_offset)); + } + let pubkey_result = read_pubkey_value(data, current_offset); + let (pubkey, next_offset) = match pubkey_result { + Ok(value) => value, + Err(error) => return Err(error), + }; + current_offset = next_offset; + return Ok((pubkey, current_offset)); +} + +fn read_fees_value(data: &[u8], offset: usize) -> Result<(serde_json::Value, usize), crate::Error> { + let lp_result = read_u64_value(data, offset); + let (lp_fee_bps, current_offset) = match lp_result { + Ok(value) => value, + Err(error) => return Err(error), + }; + let protocol_result = read_u64_value(data, current_offset); + let (protocol_fee_bps, current_offset) = match protocol_result { + Ok(value) => value, + Err(error) => return Err(error), + }; + let creator_result = read_u64_value(data, current_offset); + let (creator_fee_bps, current_offset) = match creator_result { + Ok(value) => value, + Err(error) => return Err(error), + }; + let mut object = serde_json::Map::new(); + object.insert("lp_fee_bps".to_string(), lp_fee_bps); + object.insert("protocol_fee_bps".to_string(), protocol_fee_bps); + object.insert("creator_fee_bps".to_string(), creator_fee_bps); + return Ok((serde_json::Value::Object(object), current_offset)); +} + +fn read_config_status_value( + data: &[u8], + offset: usize, +) -> Result<(serde_json::Value, usize), crate::Error> { + let status_result = read_u8_value(data, offset); + let (status_value, next_offset) = match status_result { + Ok(value) => value, + Err(error) => return Err(error), + }; + let status_code = match status_value.as_u64() { + Some(status_code) => status_code, + None => 0, + }; + let status_name = match status_code { + 0 => "paused", + 1 => "active", + _ => "unknown", + }; + let mut object = serde_json::Map::new(); + object.insert("code".to_string(), serde_json::json!(status_code)); + object.insert("name".to_string(), serde_json::Value::String(status_name.to_string())); + return Ok((serde_json::Value::Object(object), next_offset)); +} + +fn read_shareholder_vec_value( + data: &[u8], + offset: usize, +) -> Result<(serde_json::Value, usize), crate::Error> { + let length_result = read_u32_raw(data, offset); + let (length, mut current_offset) = match length_result { + Ok(value) => value, + Err(error) => return Err(error), + }; + let mut values = std::vec::Vec::new(); + for _ in 0..length { + let address_result = read_pubkey_value(data, current_offset); + let (address, next_offset) = match address_result { + Ok(value) => value, + Err(error) => return Err(error), + }; + current_offset = next_offset; + let share_result = read_u16_value(data, current_offset); + let (share_bps, next_offset) = match share_result { + Ok(value) => value, + Err(error) => return Err(error), + }; + current_offset = next_offset; + let mut object = serde_json::Map::new(); + object.insert("address".to_string(), address); + object.insert("share_bps".to_string(), share_bps); + values.push(serde_json::Value::Object(object)); + } + return Ok((serde_json::Value::Array(values), current_offset)); +} + +fn read_fee_tier_vec_value( + data: &[u8], + offset: usize, +) -> Result<(serde_json::Value, usize), crate::Error> { + let length_result = read_u32_raw(data, offset); + let (length, mut current_offset) = match length_result { + Ok(value) => value, + Err(error) => return Err(error), + }; + let mut values = std::vec::Vec::new(); + for _ in 0..length { + let threshold_result = read_u128_value(data, current_offset); + let (threshold, next_offset) = match threshold_result { + Ok(value) => value, + Err(error) => return Err(error), + }; + current_offset = next_offset; + let fees_result = read_fees_value(data, current_offset); + let (fees, next_offset) = match fees_result { + Ok(value) => value, + Err(error) => return Err(error), + }; + current_offset = next_offset; + let mut object = serde_json::Map::new(); + object.insert("market_cap_lamports_threshold".to_string(), threshold); + object.insert("fees".to_string(), fees); + values.push(serde_json::Value::Object(object)); + } + return Ok((serde_json::Value::Array(values), current_offset)); +} + +fn add_materialization_aliases( + payload: &mut serde_json::Map, + name: &str, + decoded: &serde_json::Value, + accounts_by_name: &serde_json::Value, +) { + copy_payload_alias(payload, decoded, accounts_by_name, "mint", "mint"); + copy_payload_alias(payload, decoded, accounts_by_name, "base_mint", "baseMint"); + copy_payload_alias(payload, decoded, accounts_by_name, "quote_mint", "quoteMint"); + copy_payload_alias(payload, decoded, accounts_by_name, "fee_config", "feeConfig"); + copy_payload_alias( + payload, + decoded, + accounts_by_name, + "fee_program_global", + "feeProgramGlobal", + ); + copy_payload_alias(payload, decoded, accounts_by_name, "sharing_config", "sharingConfig"); + copy_payload_alias(payload, decoded, accounts_by_name, "social_fee_pda", "socialFeePda"); + copy_payload_alias(payload, decoded, accounts_by_name, "donation_fee_pda", "donationFeePda"); + copy_payload_alias(payload, decoded, accounts_by_name, "buyback_vault", "buybackVault"); + copy_payload_alias(payload, decoded, accounts_by_name, "admin", "admin"); + copy_payload_alias(payload, decoded, accounts_by_name, "authority", "authority"); + copy_payload_alias(payload, decoded, accounts_by_name, "recipient", "recipient"); + copy_payload_alias(payload, decoded, accounts_by_name, "destination", "destination"); + copy_payload_alias(payload, decoded, accounts_by_name, "signer", "signer"); + copy_payload_alias(payload, decoded, accounts_by_name, "creator", "creator"); + copy_payload_alias(payload, decoded, accounts_by_name, "new_authority", "newAuthority"); + copy_payload_alias(payload, decoded, accounts_by_name, "new_admin", "newAdmin"); + copy_payload_alias(payload, decoded, accounts_by_name, "amount", "amount"); + copy_payload_alias(payload, decoded, accounts_by_name, "amount_claimed", "amountClaimed"); + copy_payload_alias(payload, decoded, accounts_by_name, "sol_amount", "solAmount"); + copy_payload_alias(payload, decoded, accounts_by_name, "token_amount", "tokenAmount"); + let actor = first_string_value( + payload, + &[ + "recipient", + "destination", + "signer", + "authority", + "admin", + "creator", + "newAuthority", + "newAdmin", + ], + ); + if let Some(actor) = actor { + payload.insert("actorWallet".to_string(), serde_json::Value::String(actor)); + } + if name.contains("SocialFeePdaClaimed") + || name == "claim_social_fee_pda" + || name == "claim_social_fee_pda_v2" + { + if let Some(amount) = first_json_value(payload, &["amountClaimed", "amount"]) { + payload.insert("rewardAmountRaw".to_string(), amount); + } + if let Some(mint) = first_string_value(payload, &["quoteMint", "mint"]) { + payload.insert("rewardTokenMint".to_string(), serde_json::Value::String(mint)); + } + } + if name.contains("DonationFee") || name.contains("Buyback") || name == "sweep_buyback" { + if let Some(amount) = first_json_value(payload, &["amount", "solAmount", "tokenAmount"]) { + payload.insert("feeAmountRaw".to_string(), amount); + } + if let Some(mint) = first_string_value(payload, &["quoteMint", "mint"]) { + payload.insert("feeTokenMint".to_string(), serde_json::Value::String(mint)); + } + } + payload.insert( + "pumpFeesMaterializationPolicy".to_string(), + serde_json::Value::String(materialization_policy_for_name(name).to_string()), + ); +} + +fn materialization_policy_for_name(name: &str) -> &'static str { + if name == "get_fees" { + return "decoded_only_fee_calculation"; + } + if name.contains("Claimed") || name.contains("Cranked") || name.contains("Sweep") { + return "materialize_if_successful_fee_or_reward"; + } + if name.contains("Config") + || name.contains("Authority") + || name.contains("Admin") + || name.contains("Tier") + || name.contains("Disable") + || name.contains("Buyback") + || name.contains("Sharing") + || name.contains("Created") + || name.starts_with("create_") + || name.starts_with("initialize_") + || name.starts_with("update_") + || name.starts_with("set_") + || name.starts_with("reset_") + || name.starts_with("extend_") + || name.starts_with("upsert_") + { + return "materialize_if_successful_admin_or_lifecycle"; + } + return "decoded_only_audit"; +} + +fn event_kind_is_buyback_or_fee(name: &str) -> bool { + return name.contains("fee") || name.contains("buyback") || name.contains("donation"); +} + +fn copy_payload_alias( + payload: &mut serde_json::Map, + decoded: &serde_json::Value, + accounts_by_name: &serde_json::Value, + source_key: &str, + target_key: &str, +) { + if payload.get(target_key).is_some() { + return; + } + if let Some(value) = json_value_by_key(decoded, source_key) { + payload.insert(target_key.to_string(), value.clone()); + return; + } + if let Some(value) = json_value_by_key(accounts_by_name, source_key) { + payload.insert(target_key.to_string(), value.clone()); + } +} + +fn json_value_by_key<'a>( + value: &'a serde_json::Value, + key: &str, +) -> std::option::Option<&'a serde_json::Value> { + if let Some(object) = value.as_object() { + if let Some(found) = object.get(key) { + return Some(found); + } + let camel = snake_to_camel(key); + if let Some(found) = object.get(camel.as_str()) { + return Some(found); + } + } + return None; +} + +fn first_json_value( + payload: &serde_json::Map, + keys: &[&str], +) -> std::option::Option { + for key in keys { + if let Some(value) = payload.get(*key) { + return Some(value.clone()); + } + } + return None; +} + +fn first_string_value( + payload: &serde_json::Map, + keys: &[&str], +) -> std::option::Option { + for key in keys { + if let Some(value) = payload.get(*key) { + if let Some(text) = value.as_str() { + if !text.trim().is_empty() { + return Some(text.to_string()); + } + } + } + } + return None; +} + +fn payload_string( + payload: &serde_json::Value, + keys: &[&str], +) -> std::option::Option { + if let Some(object) = payload.as_object() { + for key in keys { + if let Some(value) = object.get(*key) { + if let Some(text) = value.as_str() { + if !text.trim().is_empty() { + return Some(text.to_string()); + } + } + } + let camel = snake_to_camel(key); + if let Some(value) = object.get(camel.as_str()) { + if let Some(text) = value.as_str() { + if !text.trim().is_empty() { + return Some(text.to_string()); + } + } + } + } + for nested_key in &["decodedArguments", "decodedEvent", "accountsByName"] { + if let Some(nested) = object.get(*nested_key) { + let nested_value = payload_string(nested, keys); + if nested_value.is_some() { + return nested_value; + } + } + } + } + return None; +} + +fn snake_to_camel(key: &str) -> std::string::String { + let mut result = std::string::String::new(); + let mut uppercase_next = false; + for character in key.chars() { + if character == '_' { + uppercase_next = true; + continue; + } + if uppercase_next { + for upper in character.to_uppercase() { + result.push(upper); + } + uppercase_next = false; + } else { + result.push(character); + } + } + return result; +} + +fn instruction_id_for_anchor_event( + event_index: usize, + links: &[(u32, i64, serde_json::Value)], +) -> std::option::Option { + if links.is_empty() { + return None; + } + if event_index < links.len() { + return Some(links[event_index].1); + } + return Some(links[links.len().saturating_sub(1)].1); +} + +fn accounts_by_name_for_anchor_event( + event_index: usize, + links: &[(u32, i64, serde_json::Value)], +) -> serde_json::Value { + if links.is_empty() { + return serde_json::json!({}); + } + if event_index < links.len() { + return links[event_index].2.clone(); + } + return links[links.len().saturating_sub(1)].2.clone(); +} + +fn accounts_by_name_json( + parsed_json: std::option::Option<&serde_json::Value>, + accounts: &[std::string::String], +) -> serde_json::Value { + let mut object = serde_json::Map::new(); + if let Some(parsed_json) = parsed_json { + if let Some(accounts_value) = parsed_json.get("accounts") { + if let Some(accounts_array) = accounts_value.as_array() { + for account in accounts_array { + let name = account.get("name").and_then(serde_json::Value::as_str); + let pubkey = account.get("pubkey").and_then(serde_json::Value::as_str); + if let (Some(name), Some(pubkey)) = (name, pubkey) { + object.insert( + name.to_string(), + serde_json::Value::String(pubkey.to_string()), + ); + } + } + } + } + } + let fallback_names = [ + "account0", + "account1", + "account2", + "account3", + "account4", + "account5", + "account6", + "account7", + "account8", + "account9", + "account10", + "account11", + "account12", + "account13", + "account14", + "account15", + "account16", + "account17", + "account18", + "account19", + "account20", + ]; + for (index, account) in accounts.iter().enumerate() { + if index >= fallback_names.len() { + continue; + } + object + .insert(fallback_names[index].to_string(), serde_json::Value::String(account.clone())); + } + return serde_json::Value::Object(object); +} + +fn extract_program_data_base64(message: &str) -> std::option::Option<&str> { + let trimmed = message.trim(); + if let Some(rest) = trimmed.strip_prefix("Program data: ") { + return Some(rest.trim()); + } + return None; +} + +fn decode_base64_standard(encoded: &str) -> std::option::Option> { + use base64::Engine as _; + let decoded = base64::engine::general_purpose::STANDARD.decode(encoded.as_bytes()); + match decoded { + Ok(decoded) => return Some(decoded), + Err(_) => return None, + } +} + +fn extract_log_messages( + transaction_json: &serde_json::Value, +) -> std::vec::Vec { + let mut messages = std::vec::Vec::new(); + let meta = match transaction_json.get("meta") { + Some(meta) => meta, + None => return messages, + }; + let logs = match meta.get("logMessages") { + Some(logs) => logs, + None => return messages, + }; + let logs_array = match logs.as_array() { + Some(logs_array) => logs_array, + None => return messages, + }; + for value in logs_array { + if let Some(text) = value.as_str() { + messages.push(text.to_string()); + } + } + return messages; +} + +fn parse_accounts_json( + accounts_json: &str, +) -> Result, crate::Error> { + let values_result = serde_json::from_str::>(accounts_json); + let values = match values_result { + Ok(values) => values, + Err(error) => { + return Err(crate::Error::Json(format!( + "cannot parse pump_fees accounts_json: {}", + error + ))); + }, + }; + let mut accounts = std::vec::Vec::new(); + for value in values { + if let Some(text) = value.as_str() { + accounts.push(text.to_string()); + } else if let Some(object) = value.as_object() { + if let Some(pubkey) = object.get("pubkey").and_then(serde_json::Value::as_str) { + accounts.push(pubkey.to_string()); + } + } + } + return Ok(accounts); +} + +fn parse_optional_parsed_json( + parsed_json: std::option::Option<&std::string::String>, +) -> Result, crate::Error> { + let text = match parsed_json { + Some(text) => text, + None => return Ok(None), + }; + let parsed = serde_json::from_str::(text.as_str()); + match parsed { + Ok(parsed) => return Ok(Some(parsed)), + Err(error) => { + return Err(crate::Error::Json(format!( + "cannot parse pump_fees parsed_json: {}", + error + ))); + }, + } +} + +fn decode_instruction_data_text( + data_json: std::option::Option<&std::string::String>, +) -> std::option::Option { + let data_json = match data_json { + Some(data_json) => data_json, + None => return None, + }; + let value_result = serde_json::from_str::(data_json.as_str()); + if let Ok(value) = value_result { + if let Some(text) = value.as_str() { + return Some(text.to_string()); + } + if let Some(object) = value.as_object() { + if let Some(text) = object.get("data").and_then(serde_json::Value::as_str) { + return Some(text.to_string()); + } + } + } + let trimmed = data_json.trim(); + if trimmed.is_empty() { + return None; + } + return Some(trimmed.to_string()); +} + +fn read_8_byte_array(data: &[u8], offset: usize) -> std::option::Option<[u8; 8]> { + if offset.saturating_add(8) > data.len() { + return None; + } + let mut bytes = [0_u8; 8]; + bytes.copy_from_slice(&data[offset..offset + 8]); + return Some(bytes); +} + +fn bytes_to_hex(data: &[u8]) -> std::string::String { + let mut text = std::string::String::new(); + for byte in data { + text.push_str(format!("{:02x}", byte).as_str()); + } + return text; +} + +#[cfg(test)] +mod tests { + #[test] + fn decodes_get_fees_instruction_args() { + let mut data = std::vec::Vec::new(); + data.extend_from_slice(&super::PUMP_FEES_GET_FEES_DISCRIMINATOR); + data.push(1_u8); + data.extend_from_slice(&123456789_u128.to_le_bytes()); + data.extend_from_slice(&42_u64.to_le_bytes()); + data.push(0_u8); + let encoded = bs58::encode(data).into_string(); + let data_json = serde_json::json!(encoded).to_string(); + let decoded_result = super::parse_pump_fees_instruction_data(Some(&data_json)); + let decoded = match decoded_result { + Ok(Some(decoded)) => decoded, + Ok(None) => panic!("expected decoded get_fees"), + Err(error) => panic!("unexpected decode error: {}", error), + }; + assert_eq!(decoded.spec.name, "get_fees"); + assert_eq!(decoded.discriminator_hex, "e7257e55cf5b3f34"); + assert_eq!(decoded.args_json["is_pump_pool"], serde_json::json!(true)); + assert_eq!(decoded.args_json["market_cap_lamports"], serde_json::json!("123456789")); + assert_eq!(decoded.args_json["trade_size_lamports"], serde_json::json!("42")); + assert_eq!(decoded.args_json["is_new_quote_mint"], serde_json::json!(false)); + } + + #[test] + fn decodes_update_fee_shares_shareholders() { + let mut data = std::vec::Vec::new(); + data.extend_from_slice(&super::PUMP_FEES_UPDATE_FEE_SHARES_DISCRIMINATOR); + data.extend_from_slice(&1_u32.to_le_bytes()); + data.extend_from_slice(&[7_u8; 32]); + data.extend_from_slice(&2500_u16.to_le_bytes()); + let encoded = bs58::encode(data).into_string(); + let data_json = serde_json::json!(encoded).to_string(); + let decoded_result = super::parse_pump_fees_instruction_data(Some(&data_json)); + let decoded = match decoded_result { + Ok(Some(decoded)) => decoded, + Ok(None) => panic!("expected decoded update_fee_shares"), + Err(error) => panic!("unexpected decode error: {}", error), + }; + assert_eq!(decoded.spec.name, "update_fee_shares"); + assert_eq!(decoded.discriminator_hex, "bd0d8863bba4ed23"); + assert_eq!(decoded.args_json["shareholders"][0]["share_bps"], serde_json::json!(2500)); + } + + #[test] + fn decodes_social_fee_claimed_anchor_event() { + let mut data = pump_fees_anchor_event_prefix( + &super::PUMP_FEES_SOCIAL_FEE_PDA_CLAIMED_DISCRIMINATOR, + ); + data.extend_from_slice(&123_i64.to_le_bytes()); + let user = "user-1".as_bytes(); + data.extend_from_slice(&(user.len() as u32).to_le_bytes()); + data.extend_from_slice(user); + data.push(2_u8); + data.extend_from_slice(&[1_u8; 32]); + data.extend_from_slice(&[2_u8; 32]); + data.extend_from_slice(&[3_u8; 32]); + data.extend_from_slice(&99_u64.to_le_bytes()); + data.extend_from_slice(&100_u64.to_le_bytes()); + data.extend_from_slice(&101_u64.to_le_bytes()); + data.extend_from_slice(&102_u64.to_le_bytes()); + data.extend_from_slice(&201_u64.to_le_bytes()); + data.extend_from_slice(&[4_u8; 32]); + let decoded = decode_synthetic_anchor_event(data, "social fee event"); + assert_eq!(decoded.spec.name, "SocialFeePdaClaimed"); + assert_eq!(decoded.discriminator_hex, "3212c141edd2eaec"); + assert_eq!(decoded.fields_json["user_id"], serde_json::json!("user-1")); + assert_eq!(decoded.fields_json["amount_claimed"], serde_json::json!("99")); + } + + #[test] + fn decodes_unobserved_idl_admin_anchor_events() { + let mut authority_data = pump_fees_anchor_event_prefix( + &super::PUMP_FEES_SET_AUTHORITY_EVENT_DISCRIMINATOR, + ); + authority_data.extend_from_slice(&11_i64.to_le_bytes()); + authority_data.extend_from_slice(&[1_u8; 32]); + authority_data.extend_from_slice(&[2_u8; 32]); + let authority_event = decode_synthetic_anchor_event(authority_data, "set authority event"); + assert_eq!(authority_event.spec.name, "SetAuthorityEvent"); + assert_eq!(authority_event.discriminator_hex, "12af8442d0c957f2"); + assert_eq!(authority_event.fields_json["timestamp"], serde_json::json!("11")); + assert_eq!( + authority_event.fields_json["old_authority"], + serde_json::json!(bs58::encode([1_u8; 32]).into_string()) + ); + assert_eq!( + authority_event.fields_json["new_authority"], + serde_json::json!(bs58::encode([2_u8; 32]).into_string()) + ); + + let mut claim_rate_limit_data = pump_fees_anchor_event_prefix( + &super::PUMP_FEES_SET_CLAIM_RATE_LIMIT_EVENT_DISCRIMINATOR, + ); + claim_rate_limit_data.extend_from_slice(&12_i64.to_le_bytes()); + claim_rate_limit_data.extend_from_slice(&999_u64.to_le_bytes()); + let claim_rate_limit_event = + decode_synthetic_anchor_event(claim_rate_limit_data, "set claim rate limit event"); + assert_eq!(claim_rate_limit_event.spec.name, "SetClaimRateLimitEvent"); + assert_eq!(claim_rate_limit_event.discriminator_hex, "0d8f8febb5133328"); + assert_eq!( + claim_rate_limit_event.fields_json["claim_rate_limit"], + serde_json::json!("999") + ); + + let mut disable_flags_data = pump_fees_anchor_event_prefix( + &super::PUMP_FEES_SET_DISABLE_FLAGS_EVENT_DISCRIMINATOR, + ); + disable_flags_data.extend_from_slice(&13_i64.to_le_bytes()); + disable_flags_data.push(7_u8); + let disable_flags_event = + decode_synthetic_anchor_event(disable_flags_data, "set disable flags event"); + assert_eq!(disable_flags_event.spec.name, "SetDisableFlagsEvent"); + assert_eq!(disable_flags_event.discriminator_hex, "0508b3413137917e"); + assert_eq!( + disable_flags_event.fields_json["disable_flags"], + serde_json::json!(7) + ); + + let mut social_claim_authority_data = pump_fees_anchor_event_prefix( + &super::PUMP_FEES_SET_SOCIAL_CLAIM_AUTHORITY_EVENT_DISCRIMINATOR, + ); + social_claim_authority_data.extend_from_slice(&14_i64.to_le_bytes()); + social_claim_authority_data.extend_from_slice(&[3_u8; 32]); + let social_claim_authority_event = decode_synthetic_anchor_event( + social_claim_authority_data, + "set social claim authority event", + ); + assert_eq!( + social_claim_authority_event.spec.name, + "SetSocialClaimAuthorityEvent" + ); + assert_eq!( + social_claim_authority_event.discriminator_hex, + "3c767f84ef34fe0e" + ); + assert_eq!( + social_claim_authority_event.fields_json["social_claim_authority"], + serde_json::json!(bs58::encode([3_u8; 32]).into_string()) + ); + } + + fn pump_fees_anchor_event_prefix(event_discriminator: &[u8; 8]) -> std::vec::Vec { + let mut data = std::vec::Vec::new(); + data.extend_from_slice(&super::PUMP_FEES_ANCHOR_SELF_CPI_LOG_DISCRIMINATOR); + data.extend_from_slice(event_discriminator); + return data; + } + + fn decode_synthetic_anchor_event( + data: std::vec::Vec, + context: &str, + ) -> super::PumpFeesAnchorEventData { + let encoded = { + use base64::Engine as _; + base64::engine::general_purpose::STANDARD.encode(data) + }; + let decoded_result = super::decode_pump_fees_anchor_event_from_base64(encoded.as_str()); + match decoded_result { + Ok(Some(decoded)) => return decoded, + Ok(None) => panic!("expected decoded {}", context), + Err(error) => panic!("unexpected decode error for {}: {}", context, error), + } + } +} diff --git a/kb_lib/src/dex_decode.rs b/kb_lib/src/dex_decode.rs index d0ddc5e..2e0bbab 100644 --- a/kb_lib/src/dex_decode.rs +++ b/kb_lib/src/dex_decode.rs @@ -13,6 +13,7 @@ pub struct DexDecodeService { raydium_clmm_decoder: crate::RaydiumClmmDecoder, raydium_stable_swap_decoder: crate::RaydiumStableSwapDecoder, pump_fun_decoder: crate::PumpFunDecoder, + pump_fees_decoder: crate::PumpFeesDecoder, pump_swap_decoder: crate::PumpSwapDecoder, orca_whirlpools_decoder: crate::OrcaWhirlpoolsDecoder, meteora_dbc_decoder: crate::MeteoraDbcDecoder, @@ -36,6 +37,7 @@ impl DexDecodeService { raydium_clmm_decoder: crate::RaydiumClmmDecoder::new(), raydium_stable_swap_decoder: crate::RaydiumStableSwapDecoder::new(), pump_fun_decoder: crate::PumpFunDecoder::new(), + pump_fees_decoder: crate::PumpFeesDecoder::new(), pump_swap_decoder: crate::PumpSwapDecoder::new(), orca_whirlpools_decoder: crate::OrcaWhirlpoolsDecoder::new(), meteora_dbc_decoder: crate::MeteoraDbcDecoder::new(), @@ -133,6 +135,13 @@ impl DexDecodeService { if let Err(error) = append_result { return Err(error); } + let append_result = append_persisted_events_result( + &mut persisted, + self.decode_and_persist_pump_fees_events(&transaction, &instructions).await, + ); + if let Err(error) = append_result { + return Err(error); + } let append_result = append_persisted_events_result( &mut persisted, self.decode_and_persist_meteora_dbc_events(&transaction, &instructions).await, @@ -1679,32 +1688,31 @@ impl DexDecodeService { } } - -fn pump_fun_payload_string( - payload: &serde_json::Value, - keys: &[&str], -) -> std::option::Option { - if let Some(object) = payload.as_object() { - for key in keys { - let value = object.get(*key); - if let Some(value) = value { - if let Some(text) = value.as_str() { - if !text.trim().is_empty() { - return Some(text.to_string()); + fn pump_fun_payload_string( + payload: &serde_json::Value, + keys: &[&str], + ) -> std::option::Option { + if let Some(object) = payload.as_object() { + for key in keys { + let value = object.get(*key); + if let Some(value) = value { + if let Some(text) = value.as_str() { + if !text.trim().is_empty() { + return Some(text.to_string()); + } } } } - } - let decoded_arguments = object.get("decodedArguments"); - if let Some(decoded_arguments) = decoded_arguments { - let nested = Self::pump_fun_payload_string(decoded_arguments, keys); - if nested.is_some() { - return nested; + let decoded_arguments = object.get("decodedArguments"); + if let Some(decoded_arguments) = decoded_arguments { + let nested = Self::pump_fun_payload_string(decoded_arguments, keys); + if nested.is_some() { + return nested; + } } } + return None; } - return None; -} async fn persist_pump_fun_trade_event( &self, @@ -1739,6 +1747,44 @@ fn pump_fun_payload_string( .await; } + async fn persist_pump_fees_event( + &self, + transaction: &crate::ChainTransactionDto, + decoded_event: &crate::PumpFeesDecodedEvent, + ) -> Result { + match decoded_event { + crate::PumpFeesDecodedEvent::Instruction(event) => { + return self.persist_pump_fees_instruction_event(transaction, event).await; + }, + crate::PumpFeesDecodedEvent::AnchorEvent(event) => { + return self.persist_pump_fees_instruction_event(transaction, event).await; + }, + } + } + + async fn persist_pump_fees_instruction_event( + &self, + transaction: &crate::ChainTransactionDto, + event: &crate::PumpFeesInstructionDecoded, + ) -> Result { + return self + .materialize_named_dex_event( + transaction, + event.transaction_id, + event.instruction_id, + "pump_fees", + event.program_id.clone(), + event.event_kind.as_str(), + event.pool_account.clone(), + None, + event.token_a_mint.clone(), + event.token_b_mint.clone(), + event.lp_mint.clone(), + event.payload_json.clone(), + ) + .await; + } + async fn persist_pump_swap_event( &self, transaction: &crate::ChainTransactionDto, @@ -2392,6 +2438,28 @@ fn pump_fun_payload_string( return Ok(persisted); } + async fn decode_and_persist_pump_fees_events( + &self, + transaction: &crate::ChainTransactionDto, + instructions: &[crate::ChainInstructionDto], + ) -> Result, crate::Error> { + let decoded_result = self.pump_fees_decoder.decode_transaction(transaction, instructions); + let decoded_events = match decoded_result { + Ok(decoded_events) => decoded_events, + Err(error) => return Err(error), + }; + let mut persisted = std::vec::Vec::new(); + for decoded_event in &decoded_events { + let persist_result = self.persist_pump_fees_event(transaction, decoded_event).await; + let persisted_event = match persist_result { + Ok(persisted_event) => persisted_event, + Err(error) => return Err(error), + }; + persisted.push(persisted_event); + } + return Ok(persisted); + } + async fn decode_and_persist_meteora_dbc_events( &self, transaction: &crate::ChainTransactionDto, @@ -4404,7 +4472,6 @@ fn dex_decode_extract_first_amount_string( return dex_decode_extract_first_number_as_string(value, candidate_keys); } - fn pump_fun_enrich_trade_events_with_instruction_context( decoded_events: std::vec::Vec, ) -> std::vec::Vec { @@ -4439,18 +4506,12 @@ fn pump_fun_merge_matching_instruction_context_into_trade_event( trade_event: &mut crate::PumpFunInstructionAuditDecoded, ) { let trade_payload = trade_event.payload_json.clone(); - let trade_instruction_id = dex_decode_extract_first_i64( - &trade_payload, - &["instructionId", "instruction_id"], - ); - let trade_mint = dex_decode_extract_first_string( - &trade_payload, - &["mint", "tokenMint", "tokenAMint"], - ); - let trade_actor = dex_decode_extract_first_string( - &trade_payload, - &["user", "actorWallet", "userWallet"], - ); + let trade_instruction_id = + dex_decode_extract_first_i64(&trade_payload, &["instructionId", "instruction_id"]); + let trade_mint = + dex_decode_extract_first_string(&trade_payload, &["mint", "tokenMint", "tokenAMint"]); + let trade_actor = + dex_decode_extract_first_string(&trade_payload, &["user", "actorWallet", "userWallet"]); for sibling in decoded_events { let sibling_event = match sibling { crate::PumpFunDecodedEvent::InstructionAudit(sibling_event) => sibling_event, @@ -4506,18 +4567,12 @@ fn pump_fun_mark_trade_event_duplicate_when_direct_instruction_exists( trade_event: &mut crate::PumpFunInstructionAuditDecoded, ) { let trade_payload = trade_event.payload_json.clone(); - let trade_instruction_id = dex_decode_extract_first_i64( - &trade_payload, - &["instructionId", "instruction_id"], - ); - let trade_mint = dex_decode_extract_first_string( - &trade_payload, - &["mint", "tokenMint", "tokenAMint"], - ); - let trade_actor = dex_decode_extract_first_string( - &trade_payload, - &["user", "actorWallet", "userWallet"], - ); + let trade_instruction_id = + dex_decode_extract_first_i64(&trade_payload, &["instructionId", "instruction_id"]); + let trade_mint = + dex_decode_extract_first_string(&trade_payload, &["mint", "tokenMint", "tokenAMint"]); + let trade_actor = + dex_decode_extract_first_string(&trade_payload, &["user", "actorWallet", "userWallet"]); for sibling in decoded_events { let direct_match = match sibling { crate::PumpFunDecodedEvent::BuyTrade(event) => { @@ -4613,7 +4668,6 @@ fn pump_fun_direct_trade_matches_anchor_trade_event( return true; } - fn pump_fun_merge_instruction_context_payload( instruction_payload: &serde_json::Value, trade_payload: &mut serde_json::Value, diff --git a/kb_lib/src/dex_event_classification.rs b/kb_lib/src/dex_event_classification.rs index 145bea9..004f5d6 100644 --- a/kb_lib/src/dex_event_classification.rs +++ b/kb_lib/src/dex_event_classification.rs @@ -294,6 +294,9 @@ pub fn is_dex_informational_event_kind(event_kind: &str) -> bool { if event_kind == crate::UPSTREAM_REGISTRY_INSTRUCTION_MATCH_EVENT_KIND { return true; } + if event_kind == "pump_fees.get_fees" { + return true; + } if event_kind.contains(".instruction_audit") { return true; } @@ -487,6 +490,13 @@ pub fn is_dex_position_close_event_kind(event_kind: &str) -> bool { /// Returns true for fee collection events. pub fn is_dex_fee_event_kind(event_kind: &str) -> bool { + if event_kind.starts_with("pump_fees.") + && (event_kind.contains("donation_fee_pda_cranked") + || event_kind.contains("sweep_buyback") + || event_kind.contains("crank_donation_fee_pda")) + { + return true; + } if event_kind.contains("claim_creator_fee") { return true; } @@ -543,6 +553,12 @@ pub fn is_dex_fee_event_kind(event_kind: &str) -> bool { /// Returns true for reward or incentive events. pub fn is_dex_reward_event_kind(event_kind: &str) -> bool { + if event_kind.starts_with("pump_fees.") + && (event_kind.contains("social_fee_pda_claimed") + || event_kind.contains("claim_social_fee_pda")) + { + return true; + } if event_kind.contains("reward") { return true; } @@ -607,6 +623,19 @@ pub fn is_dex_orderbook_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.starts_with("pump_fees.") + && (event_kind.contains("create_fee_sharing_config") + || event_kind.contains("create_social_fee_pda") + || event_kind.contains("create_donation_fee_pda") + || event_kind.contains("social_fee_pda_created") + || event_kind.contains("donation_fee_pda_created") + || event_kind.contains("initialize_fee_config") + || event_kind.contains("initialize_fee_program_global") + || event_kind.contains("initialize_buyback") + || event_kind.contains("extend_fee_config")) + { + return true; + } if event_kind == "raydium_amm_v4.pre_initialize" { return true; } @@ -795,6 +824,19 @@ pub fn is_dex_token_account_close_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.starts_with("pump_fees.") + && (event_kind.contains("authority") + || event_kind.contains("admin") + || event_kind.contains("config") + || event_kind.contains("tier") + || event_kind.contains("disable") + || event_kind.contains("reset_fee_sharing") + || event_kind.contains("update_fee_shares") + || event_kind.contains("set_claim_rate_limit") + || event_kind.contains("buyback_claim_rate_limit")) + { + return true; + } if event_kind.contains(".admin_cancel_orders") { return false; } @@ -1233,9 +1275,7 @@ mod tests { super::classify_dex_event_category_code("pump_swap.migrate_pool_coin_creator"), "admin" ); - assert!(!super::is_dex_pool_lifecycle_event_kind( - "pump_swap.migrate_pool_coin_creator" - )); + assert!(!super::is_dex_pool_lifecycle_event_kind("pump_swap.migrate_pool_coin_creator")); assert_eq!( super::classify_dex_event_category_code("raydium_clmm.increase_liquidity_v2"), "liquidity" diff --git a/kb_lib/src/dex_event_coverage.rs b/kb_lib/src/dex_event_coverage.rs index 6853f07..64f2517 100644 --- a/kb_lib/src/dex_event_coverage.rs +++ b/kb_lib/src/dex_event_coverage.rs @@ -321,6 +321,9 @@ fn infer_expected_db_target_for_entry( if decoder_code == "pump_fun" { return infer_pump_fun_expected_db_target(entry_name, entry_kind); } + if decoder_code == "pump_fees" { + return infer_pump_fees_expected_db_target(entry_name, entry_kind); + } if decoder_code == "raydium_cpmm" && (entry_name == "swap_event" || entry_name == "anchor_idl_instruction") { @@ -527,6 +530,51 @@ fn infer_expected_db_target( return Some(target.to_string()); } +fn infer_pump_fees_expected_db_target( + entry_name: &str, + entry_kind: &str, +) -> std::option::Option { + if entry_kind == crate::ENTRY_KIND_PROGRAM || entry_kind == crate::ENTRY_KIND_ACCOUNT { + return None; + } + if entry_name == "get_fees" { + return Some(crate::DexEventCoverageEntryDto::DB_TARGET_DECODED_EVENTS_ONLY.to_string()); + } + if entry_name == "claim_social_fee_pda" + || entry_name == "claim_social_fee_pda_v2" + || entry_name == "social_fee_pda_claimed" + { + return Some(crate::DexEventCoverageEntryDto::DB_TARGET_REWARD_EVENTS.to_string()); + } + if entry_name == "crank_donation_fee_pda" + || entry_name == "donation_fee_pda_cranked" + || entry_name == "sweep_buyback" + || entry_name == "sweep_buyback_event" + { + return Some(crate::DexEventCoverageEntryDto::DB_TARGET_FEE_EVENTS.to_string()); + } + if entry_name.contains("create_") + || entry_name.contains("created") + || entry_name.contains("initialize_") + || entry_name == "extend_fee_config" + || entry_name == "extend_fee_config_event" + { + return Some(crate::DexEventCoverageEntryDto::DB_TARGET_POOL_LIFECYCLE_EVENTS.to_string()); + } + if entry_name.contains("config") + || entry_name.contains("authority") + || entry_name.contains("admin") + || entry_name.contains("tier") + || entry_name.contains("disable") + || entry_name.contains("reset_fee_sharing") + || entry_name.contains("update_fee_shares") + || entry_name.contains("buyback_claim_rate_limit") + { + return Some(crate::DexEventCoverageEntryDto::DB_TARGET_POOL_ADMIN_EVENTS.to_string()); + } + return Some(crate::DexEventCoverageEntryDto::DB_TARGET_DECODED_EVENTS_ONLY.to_string()); +} + fn infer_pump_fun_expected_db_target( entry_name: &str, entry_kind: &str, @@ -555,9 +603,7 @@ fn infer_pump_fun_expected_db_target( return Some(crate::DexEventCoverageEntryDto::DB_TARGET_LAUNCH_EVENTS.to_string()); } if entry_name == "initialize" { - return Some( - crate::DexEventCoverageEntryDto::DB_TARGET_POOL_LIFECYCLE_EVENTS.to_string(), - ); + return Some(crate::DexEventCoverageEntryDto::DB_TARGET_POOL_LIFECYCLE_EVENTS.to_string()); } if entry_name == "migrate" || entry_name == "migrate_v2" @@ -755,6 +801,58 @@ fn infer_pump_swap_event_family( return infer_event_family(entry_name, entry_kind); } +fn infer_pump_fees_event_family( + entry_name: &str, + entry_kind: &str, +) -> std::option::Option { + if entry_kind == crate::ENTRY_KIND_PROGRAM || entry_kind == crate::ENTRY_KIND_ACCOUNT { + return None; + } + if entry_name == "get_fees" { + return Some("audit".to_string()); + } + if entry_name == "claim_social_fee_pda" + || entry_name == "claim_social_fee_pda_v2" + || entry_name == "social_fee_pda_claimed" + { + return Some("reward".to_string()); + } + if entry_name == "crank_donation_fee_pda" + || entry_name == "donation_fee_pda_cranked" + || entry_name == "sweep_buyback" + || entry_name == "sweep_buyback_event" + { + return Some("fee".to_string()); + } + if entry_name.contains("create_") + || entry_name.contains("created") + || entry_name.contains("initialize_") + || entry_name == "extend_fee_config" + || entry_name == "extend_fee_config_event" + { + return Some("pool_create".to_string()); + } + if entry_name.contains("config") + || entry_name.contains("authority") + || entry_name.contains("admin") + || entry_name.contains("tier") + || entry_name.contains("disable") + || entry_name.contains("reset_fee_sharing") + || entry_name.contains("update_fee_shares") + || entry_name.contains("buyback_claim_rate_limit") + { + return Some("admin_config".to_string()); + } + return Some("audit".to_string()); +} + +fn pump_fees_local_event_kind(entry_name: &str) -> std::option::Option { + if entry_name == "program" || entry_name == "program_alias" { + return None; + } + return Some(format!("pump_fees.{}", entry_name)); +} + fn infer_pump_fun_event_family( entry_name: &str, entry_kind: &str, @@ -975,6 +1073,9 @@ fn infer_event_family_for_entry( if decoder_code == "pump_fun" { return infer_pump_fun_event_family(entry_name, entry_kind); } + if decoder_code == "pump_fees" { + return infer_pump_fees_event_family(entry_name, entry_kind); + } if decoder_code == "pump_swap" { return infer_pump_swap_event_family(entry_name, entry_kind); } @@ -1268,7 +1369,7 @@ fn infer_event_family( if normalized.contains("stake") { return Some("stake".to_string()); } - return Some("unknown".to_string()); + return Some("audit".to_string()); } fn contains_any(value: &str, needles: &[&str]) -> bool { @@ -1372,6 +1473,9 @@ pub(crate) fn known_local_event_kind( if decoder_code == "pump_fun" { return pump_fun_local_event_kind(entry_name); } + if decoder_code == "pump_fees" { + return pump_fees_local_event_kind(entry_name); + } if decoder_code == "pump_swap" { return pump_swap_local_event_kind(entry_name); } @@ -1590,6 +1694,26 @@ mod tests { return std::sync::Arc::new(database); } + #[test] + fn generic_event_family_fallback_is_audit_not_unknown() { + assert_eq!( + super::infer_event_family("non_matching_entry", crate::ENTRY_KIND_INSTRUCTION), + Some("audit".to_string()) + ); + } + + #[test] + fn pump_fees_event_family_fallback_is_audit() { + assert_eq!( + super::infer_event_family_for_entry( + "pump_fees", + "non_matching_entry", + crate::ENTRY_KIND_INSTRUCTION + ), + Some("audit".to_string()) + ); + } + #[test] fn event_family_inference_covers_raydium_cpmm_core_entries() { assert_eq!( @@ -1777,7 +1901,11 @@ mod tests { Some("pump_fun.claim_cashback_event".to_string()) ); assert_eq!( - super::infer_event_family_for_entry("pump_fun", "create_event", crate::ENTRY_KIND_EVENT), + super::infer_event_family_for_entry( + "pump_fun", + "create_event", + crate::ENTRY_KIND_EVENT + ), Some("launch".to_string()) ); assert_eq!( @@ -1797,7 +1925,11 @@ mod tests { Some("reward".to_string()) ); assert_eq!( - super::infer_event_family_for_entry("pump_fun", "buy_v2", crate::ENTRY_KIND_INSTRUCTION), + super::infer_event_family_for_entry( + "pump_fun", + "buy_v2", + crate::ENTRY_KIND_INSTRUCTION + ), Some("swap".to_string()) ); assert_eq!( diff --git a/kb_lib/src/instruction_observation_index.rs b/kb_lib/src/instruction_observation_index.rs index affeb92..81603e1 100644 --- a/kb_lib/src/instruction_observation_index.rs +++ b/kb_lib/src/instruction_observation_index.rs @@ -319,6 +319,42 @@ fn resolve_instruction_name( }; return Some(format!("raydium_launchpad.{}", layout.instruction_name)); } + if program_id == crate::PUMP_FEES_PROGRAM_ID || decoder_code == Some("pump_fees") { + let name = match discriminator_hex { + "e445a52e51cb9a1d" => "anchor_self_cpi_log", + "e115fb85a11ec7e2" => "claim_social_fee_pda", + "114df0863abc3595" => "claim_social_fee_pda_v2", + "dc0abda7a9111945" => "crank_donation_fee_pda", + "f48b10580eff7a1a" => "create_donation_fee_pda", + "c34e564c6f34fbd5" => "create_fee_sharing_config", + "90e03bd34ef8cadc" => "create_social_fee_pda", + "44b3f45aad3811d9" => "extend_fee_config", + "e7257e55cf5b3f34" => "get_fees", + "fa81eca0e3246786" => "initialize_buyback", + "3ea214857941911b" => "initialize_fee_config", + "23d78254e9387ca7" => "initialize_fee_program_global", + "0a02b65f107f81ba" => "reset_fee_sharing_config", + "a9f511d15e5bf880" => "reset_fee_sharing_config_v2", + "12e99e27b9cf3a68" => "revoke_fee_sharing_authority", + "85fa25156ea31a79" => "set_authority", + "b9d39faed4315804" => "set_claim_rate_limit", + "c2d9702372de33be" => "set_disable_flags", + "9336b89a88edb999" => "set_social_claim_authority", + "8a21cc26cfa19fe2" => "sweep_buyback", + "ca0a4bc8a422d260" => "transfer_fee_sharing_authority", + "a1b028d53cb8b3e4" => "update_admin", + "426271ca7925db6b" => "update_buyback_authority", + "ba5f87beffc789aa" => "update_buyback_claim_rate_limit", + "68b867f258976b14" => "update_fee_config", + "bd0d8863bba4ed23" => "update_fee_shares", + "6ffb31064e4e6a12" => "update_fee_shares_v2", + "6ba964b3869b92dd" => "update_stable_fee_config", + "e317960c4d565e04" => "upsert_fee_tiers", + "b5a0a2fc4a4ce0dd" => "upsert_stable_fee_tiers", + _ => return None, + }; + return Some(format!("pump_fees.{}", name)); + } if program_id == crate::PUMP_FUN_PROGRAM_ID || decoder_code == Some("pump_fun") { let name = match discriminator_hex { "e445a52e51cb9a1d" => "anchor_self_cpi_log", diff --git a/kb_lib/src/lib.rs b/kb_lib/src/lib.rs index f53493c..64d7f32 100644 --- a/kb_lib/src/lib.rs +++ b/kb_lib/src/lib.rs @@ -1171,6 +1171,12 @@ pub use dex::PhoenixV1AuditDecoded; pub use dex::PhoenixV1DecodedEvent; /// Phoenix v1 audit-only decoder. pub use dex::PhoenixV1Decoder; +/// Decoded Pump Fees event. +pub use dex::PumpFeesDecodedEvent; +/// Pump Fees decoder. +pub use dex::PumpFeesDecoder; +/// Decoded Pump Fees instruction or Anchor event. +pub use dex::PumpFeesInstructionDecoded; /// Decoded Pump.fun `create_v2` token event. pub use dex::PumpFunCreateV2TokenDecoded; /// Decoded Pump.fun event. diff --git a/kb_lib/src/upstream_registry_generated.rs b/kb_lib/src/upstream_registry_generated.rs index d641089..6c15e9f 100644 --- a/kb_lib/src/upstream_registry_generated.rs +++ b/kb_lib/src/upstream_registry_generated.rs @@ -16,6 +16,35 @@ const RAYDIUM_IDL_SOURCE_REPO: &str = "raydium-io/raydium-idl"; const MANUAL_SOLSCAN_SOURCE_REPO: &str = "manual-solscan"; const RAYDIUM_IDL_DISCRIMINATOR_NOTES: &str = "entry name and discriminator extracted from Raydium official IDL snapshot; not corpus-verified; no trade/candle/materialization proof"; const MANUAL_SOLSCAN_DISCRIMINATOR_NOTES: &str = "entry name and discriminator derived from manual Solscan program IDL or Solscan transaction-log inspection; no trade/candle/materialization proof"; +const LOCAL_IDL_SOURCE_REPO: &str = "local-idl"; +const LOCAL_IDL_DISCRIMINATOR_NOTES: &str = "entry name and discriminator extracted from local IDL snapshot; not corpus-verified; no trade/candle/materialization proof"; + +const fn local_idl_discriminator_entry( + decoder_code: &'static str, + program_id: std::option::Option<&'static str>, + program_family: &'static str, + surface_kind: &'static str, + entry_kind: &'static str, + entry_name: &'static str, + discriminator_hex: &'static str, + discriminator_len: u16, + source_path: &'static str, +) -> crate::UpstreamRegistryEntry { + return crate::UpstreamRegistryEntry { + source_repo: Some(LOCAL_IDL_SOURCE_REPO), + source_path: Some(source_path), + decoder_code, + program_id, + program_family, + surface_kind, + entry_kind, + entry_name, + discriminator_hex: Some(discriminator_hex), + discriminator_len: Some(discriminator_len), + proof_status: crate::PROOF_STATUS_UPSTREAM_GIT_UNVERIFIED, + notes: LOCAL_IDL_DISCRIMINATOR_NOTES, + }; +} const fn manual_solscan_discriminator_entry( decoder_code: &'static str, @@ -10459,6 +10488,204 @@ pub(crate) const UPSTREAM_REGISTRY_ENTRIES: &[crate::UpstreamRegistryEntry] = &[ 8, "decoders/pump-fees-decoder/src/events/upsert_fee_tiers_event.rs", ), + local_idl_discriminator_entry( + "pump_fees", + Some(crate::PUMP_FEES_PROGRAM_ID), + "pump", + "fee_program", + crate::ENTRY_KIND_INSTRUCTION, + "claim_social_fee_pda_v2", + "114df0863abc3595", + 8, + "idls/pump_fees.pfeeUxB6jkeY1Hxd7CsFCAjcbHA9rWtchMGdZ6VojVZ.json", + ), + local_idl_discriminator_entry( + "pump_fees", + Some(crate::PUMP_FEES_PROGRAM_ID), + "pump", + "fee_program", + crate::ENTRY_KIND_INSTRUCTION, + "crank_donation_fee_pda", + "dc0abda7a9111945", + 8, + "idls/pump_fees.pfeeUxB6jkeY1Hxd7CsFCAjcbHA9rWtchMGdZ6VojVZ.json", + ), + local_idl_discriminator_entry( + "pump_fees", + Some(crate::PUMP_FEES_PROGRAM_ID), + "pump", + "fee_program", + crate::ENTRY_KIND_INSTRUCTION, + "create_donation_fee_pda", + "f48b10580eff7a1a", + 8, + "idls/pump_fees.pfeeUxB6jkeY1Hxd7CsFCAjcbHA9rWtchMGdZ6VojVZ.json", + ), + local_idl_discriminator_entry( + "pump_fees", + Some(crate::PUMP_FEES_PROGRAM_ID), + "pump", + "fee_program", + crate::ENTRY_KIND_INSTRUCTION, + "extend_fee_config", + "44b3f45aad3811d9", + 8, + "idls/pump_fees.pfeeUxB6jkeY1Hxd7CsFCAjcbHA9rWtchMGdZ6VojVZ.json", + ), + local_idl_discriminator_entry( + "pump_fees", + Some(crate::PUMP_FEES_PROGRAM_ID), + "pump", + "fee_program", + crate::ENTRY_KIND_INSTRUCTION, + "initialize_buyback", + "fa81eca0e3246786", + 8, + "idls/pump_fees.pfeeUxB6jkeY1Hxd7CsFCAjcbHA9rWtchMGdZ6VojVZ.json", + ), + local_idl_discriminator_entry( + "pump_fees", + Some(crate::PUMP_FEES_PROGRAM_ID), + "pump", + "fee_program", + crate::ENTRY_KIND_INSTRUCTION, + "reset_fee_sharing_config_v2", + "a9f511d15e5bf880", + 8, + "idls/pump_fees.pfeeUxB6jkeY1Hxd7CsFCAjcbHA9rWtchMGdZ6VojVZ.json", + ), + local_idl_discriminator_entry( + "pump_fees", + Some(crate::PUMP_FEES_PROGRAM_ID), + "pump", + "fee_program", + crate::ENTRY_KIND_INSTRUCTION, + "sweep_buyback", + "8a21cc26cfa19fe2", + 8, + "idls/pump_fees.pfeeUxB6jkeY1Hxd7CsFCAjcbHA9rWtchMGdZ6VojVZ.json", + ), + local_idl_discriminator_entry( + "pump_fees", + Some(crate::PUMP_FEES_PROGRAM_ID), + "pump", + "fee_program", + crate::ENTRY_KIND_INSTRUCTION, + "update_buyback_authority", + "426271ca7925db6b", + 8, + "idls/pump_fees.pfeeUxB6jkeY1Hxd7CsFCAjcbHA9rWtchMGdZ6VojVZ.json", + ), + local_idl_discriminator_entry( + "pump_fees", + Some(crate::PUMP_FEES_PROGRAM_ID), + "pump", + "fee_program", + crate::ENTRY_KIND_INSTRUCTION, + "update_buyback_claim_rate_limit", + "ba5f87beffc789aa", + 8, + "idls/pump_fees.pfeeUxB6jkeY1Hxd7CsFCAjcbHA9rWtchMGdZ6VojVZ.json", + ), + local_idl_discriminator_entry( + "pump_fees", + Some(crate::PUMP_FEES_PROGRAM_ID), + "pump", + "fee_program", + crate::ENTRY_KIND_INSTRUCTION, + "update_fee_shares_v2", + "6ffb31064e4e6a12", + 8, + "idls/pump_fees.pfeeUxB6jkeY1Hxd7CsFCAjcbHA9rWtchMGdZ6VojVZ.json", + ), + local_idl_discriminator_entry( + "pump_fees", + Some(crate::PUMP_FEES_PROGRAM_ID), + "pump", + "fee_program", + crate::ENTRY_KIND_INSTRUCTION, + "update_stable_fee_config", + "6ba964b3869b92dd", + 8, + "idls/pump_fees.pfeeUxB6jkeY1Hxd7CsFCAjcbHA9rWtchMGdZ6VojVZ.json", + ), + local_idl_discriminator_entry( + "pump_fees", + Some(crate::PUMP_FEES_PROGRAM_ID), + "pump", + "fee_program", + crate::ENTRY_KIND_INSTRUCTION, + "upsert_stable_fee_tiers", + "b5a0a2fc4a4ce0dd", + 8, + "idls/pump_fees.pfeeUxB6jkeY1Hxd7CsFCAjcbHA9rWtchMGdZ6VojVZ.json", + ), + local_idl_discriminator_entry( + "pump_fees", + Some(crate::PUMP_FEES_PROGRAM_ID), + "pump", + "fee_program", + crate::ENTRY_KIND_EVENT, + "donation_fee_pda_cranked", + "1ed06b5db100df4e", + 8, + "idls/pump_fees.pfeeUxB6jkeY1Hxd7CsFCAjcbHA9rWtchMGdZ6VojVZ.json", + ), + local_idl_discriminator_entry( + "pump_fees", + Some(crate::PUMP_FEES_PROGRAM_ID), + "pump", + "fee_program", + crate::ENTRY_KIND_EVENT, + "donation_fee_pda_created", + "5e1489ef234de1eb", + 8, + "idls/pump_fees.pfeeUxB6jkeY1Hxd7CsFCAjcbHA9rWtchMGdZ6VojVZ.json", + ), + local_idl_discriminator_entry( + "pump_fees", + Some(crate::PUMP_FEES_PROGRAM_ID), + "pump", + "fee_program", + crate::ENTRY_KIND_EVENT, + "extend_fee_config_event", + "e2cbe023990a5833", + 8, + "idls/pump_fees.pfeeUxB6jkeY1Hxd7CsFCAjcbHA9rWtchMGdZ6VojVZ.json", + ), + local_idl_discriminator_entry( + "pump_fees", + Some(crate::PUMP_FEES_PROGRAM_ID), + "pump", + "fee_program", + crate::ENTRY_KIND_EVENT, + "sweep_buyback_event", + "2b382ad69939a689", + 8, + "idls/pump_fees.pfeeUxB6jkeY1Hxd7CsFCAjcbHA9rWtchMGdZ6VojVZ.json", + ), + local_idl_discriminator_entry( + "pump_fees", + Some(crate::PUMP_FEES_PROGRAM_ID), + "pump", + "fee_program", + crate::ENTRY_KIND_EVENT, + "update_stable_fee_config_event", + "5e052bed6793e8f5", + 8, + "idls/pump_fees.pfeeUxB6jkeY1Hxd7CsFCAjcbHA9rWtchMGdZ6VojVZ.json", + ), + local_idl_discriminator_entry( + "pump_fees", + Some(crate::PUMP_FEES_PROGRAM_ID), + "pump", + "fee_program", + crate::ENTRY_KIND_EVENT, + "upsert_stable_fee_tiers_event", + "e8eded34629249f3", + 8, + "idls/pump_fees.pfeeUxB6jkeY1Hxd7CsFCAjcbHA9rWtchMGdZ6VojVZ.json", + ), upstream_git_discriminator_entry( "pump_swap", Some(crate::PUMP_SWAP_PROGRAM_ID), diff --git a/validation_sql/SQL_VALIDATION_PUMP_FEES_0_7_55.sql b/validation_sql/SQL_VALIDATION_PUMP_FEES_0_7_55.sql new file mode 100644 index 0000000..f60c4e6 --- /dev/null +++ b/validation_sql/SQL_VALIDATION_PUMP_FEES_0_7_55.sql @@ -0,0 +1,615 @@ +-- file: validation_sql/SQL_VALIDATION_PUMP_FEES_0_7_55.sql + +-- 0.7.55 pump_fees validation and corpus-seed checklist. +-- Run on a dedicated fresh SQLite database for the Pump Fees tranche. +-- Recommended replay settings after each backfill group: +-- skipDexDecode=no, forceDexDecode=yes, deferInstructionObservations=yes. +-- This file is intentionally read-only: it never mutates the database. + +-- 00. Corpus seed: upstream fallback samples to backfill first. +SELECT + json_extract(de.payload_json, '$.upstreamEntryName') AS upstream_entry_name, + json_extract(de.payload_json, '$.upstreamDiscriminatorHex') AS upstream_discriminator_hex, + COUNT(*) AS fallback_count, + COUNT(DISTINCT de.transaction_id) AS tx_count, + MIN(tx.signature) AS sample_signature +FROM k_sol_dex_decoded_events de +LEFT JOIN k_sol_chain_transactions tx + ON tx.id = de.transaction_id +WHERE de.protocol_name = 'upstream_git' + AND de.event_kind = 'upstream_git.instruction_match' + AND json_extract(de.payload_json, '$.upstreamDecoderCode') = 'pump_fees' +GROUP BY upstream_entry_name, upstream_discriminator_hex +ORDER BY fallback_count DESC, upstream_entry_name, upstream_discriminator_hex; + +-- 01. Corpus seed: local instruction observations. +SELECT + instruction_name, + discriminator_hex, + COUNT(*) AS observed_count, + COUNT(DISTINCT signature) AS tx_count, + MIN(signature) AS sample_signature +FROM k_sol_instruction_observations +WHERE decoder_code = 'pump_fees' +GROUP BY instruction_name, discriminator_hex +ORDER BY observed_count DESC, instruction_name, discriminator_hex; + + +-- 01b. Local instruction observations with missing Pump Fees instruction name. +-- Target after closure: empty, including the Anchor self-CPI log discriminator. +SELECT + decoder_code, + discriminator_hex, + COUNT(*) AS observed_count, + COUNT(DISTINCT signature) AS tx_count, + MIN(signature) AS sample_signature +FROM k_sol_instruction_observations +WHERE decoder_code = 'pump_fees' + AND ( + instruction_name IS NULL + OR TRIM(instruction_name) = '' + ) +GROUP BY decoder_code, discriminator_hex +ORDER BY observed_count DESC, discriminator_hex; + +-- 02. Coverage pump_fees. +SELECT + entry_name, + entry_kind, + event_family, + expected_db_target, + proof_status, + local_event_kind, + discriminator_hex, + observed_count, + materialized_count, + trade_count +FROM k_sol_dex_event_coverage_entries +WHERE decoder_code = 'pump_fees' +ORDER BY entry_kind, entry_name, discriminator_hex; + + +-- 02b. Coverage rows with unknown or blank event_family. +-- Target after closure: empty for pump_fees. Program/account rows may be NULL, but must not be unknown. +SELECT + entry_name, + entry_kind, + event_family, + expected_db_target, + proof_status, + local_event_kind, + discriminator_hex +FROM k_sol_dex_event_coverage_entries +WHERE decoder_code = 'pump_fees' + AND ( + LOWER(COALESCE(TRIM(event_family), '')) = 'unknown' + OR (entry_kind NOT IN ('program', 'account') AND COALESCE(TRIM(event_family), '') = '') + ) +ORDER BY entry_kind, entry_name, discriminator_hex; + +-- 03. Decoded events summary. +SELECT + de.event_kind, + COUNT(*) AS decoded_count, + COUNT(DISTINCT de.transaction_id) AS tx_count, + MIN(tx.signature) AS sample_signature +FROM k_sol_dex_decoded_events de +LEFT JOIN k_sol_chain_transactions tx + ON tx.id = de.transaction_id +WHERE de.protocol_name = 'pump_fees' +GROUP BY de.event_kind +ORDER BY decoded_count DESC, de.event_kind; + +-- 04. Decoded pump_fees events without coverage. +-- Target after closure: empty for all locally decoded pump_fees rows. +SELECT + de.event_kind, + COUNT(*) AS decoded_count, + COUNT(DISTINCT de.transaction_id) AS tx_count, + MIN(tx.signature) AS sample_signature +FROM k_sol_dex_decoded_events de +LEFT JOIN k_sol_chain_transactions tx + ON tx.id = de.transaction_id +LEFT JOIN k_sol_dex_event_coverage_entries ce + ON ce.decoder_code = 'pump_fees' + AND ce.local_event_kind = de.event_kind +WHERE de.protocol_name = 'pump_fees' + AND ce.id IS NULL +GROUP BY de.event_kind +ORDER BY decoded_count DESC, de.event_kind; + +-- 05. Residual upstream fallback for covered local entries. +-- Target after local promotion: empty for every entry that has a local_event_kind. +SELECT + json_extract(ug.payload_json, '$.upstreamEntryName') AS upstream_entry_name, + json_extract(ug.payload_json, '$.upstreamDiscriminatorHex') AS upstream_discriminator_hex, + json_extract(ug.payload_json, '$.upstreamSourceRepo') AS source_repo, + ce.local_event_kind, + ce.expected_db_target, + ce.proof_status, + COUNT(*) AS fallback_count, + COUNT(DISTINCT ug.transaction_id) AS tx_count, + MIN(tx.signature) AS sample_signature +FROM k_sol_dex_decoded_events ug +LEFT JOIN k_sol_chain_transactions tx + ON tx.id = ug.transaction_id +JOIN k_sol_dex_event_coverage_entries ce + ON ce.decoder_code = json_extract(ug.payload_json, '$.upstreamDecoderCode') + AND ce.entry_name = json_extract(ug.payload_json, '$.upstreamEntryName') + AND ce.discriminator_hex = json_extract(ug.payload_json, '$.upstreamDiscriminatorHex') + AND ce.local_event_kind IS NOT NULL + AND ce.local_event_kind <> '' +WHERE ug.protocol_name = 'upstream_git' + AND ug.event_kind = 'upstream_git.instruction_match' + AND json_extract(ug.payload_json, '$.upstreamDecoderCode') = 'pump_fees' +GROUP BY upstream_entry_name, upstream_discriminator_hex, source_repo, ce.local_event_kind, ce.expected_db_target, ce.proof_status +ORDER BY fallback_count DESC, upstream_entry_name; + +-- 06. Successful non-materialized events without explicit skip reason. +-- Target after closure: empty, except decoded-only/audit-only rows with informational policy. +SELECT + de.event_kind, + json_extract(de.payload_json, '$.pumpFeesMaterializationPolicy') AS pump_fees_policy, + COUNT(*) AS unexplained_count, + MIN(tx.signature) AS sample_signature +FROM k_sol_dex_decoded_events de +JOIN k_sol_chain_transactions tx + ON tx.id = de.transaction_id +LEFT JOIN k_sol_trade_events te + ON te.decoded_event_id = de.id +LEFT JOIN k_sol_launch_events lae + ON lae.decoded_event_id = de.id +LEFT JOIN k_sol_liquidity_events lie + ON lie.decoded_event_id = de.id +LEFT JOIN k_sol_pool_lifecycle_events ple + ON ple.decoded_event_id = de.id +LEFT JOIN k_sol_fee_events fee + ON fee.decoded_event_id = de.id +LEFT JOIN k_sol_reward_events rew + ON rew.decoded_event_id = de.id +LEFT JOIN k_sol_pool_admin_events adm + ON adm.decoded_event_id = de.id +LEFT JOIN k_sol_orderbook_events obe + ON obe.decoded_event_id = de.id +LEFT JOIN k_sol_token_account_events tae + ON tae.decoded_event_id = de.id +WHERE de.protocol_name = 'pump_fees' + AND ( + tx.err_json IS NULL + OR tx.err_json = '' + OR tx.err_json = 'null' + ) + AND COALESCE(json_extract(de.payload_json, '$.eventActionability'), '') <> 'informational' + AND COALESCE(json_extract(de.payload_json, '$.pumpFeesMaterializationPolicy'), '') NOT IN ( + 'decoded_only_fee_calculation', + 'decoded_only_audit' + ) + AND te.id IS NULL + AND lae.id IS NULL + AND lie.id IS NULL + AND ple.id IS NULL + AND fee.id IS NULL + AND rew.id IS NULL + AND adm.id IS NULL + AND obe.id IS NULL + AND tae.id IS NULL + AND COALESCE(TRIM(json_extract(de.payload_json, '$.skipTradeReason')), '') = '' + AND COALESCE(TRIM(json_extract(de.payload_json, '$.skipCandleReason')), '') = '' + AND COALESCE(TRIM(json_extract(de.payload_json, '$.skipLiquidityReason')), '') = '' + AND COALESCE(TRIM(json_extract(de.payload_json, '$.skipLifecycleReason')), '') = '' + AND COALESCE(TRIM(json_extract(de.payload_json, '$.skipCatalogReason')), '') = '' +GROUP BY de.event_kind, pump_fees_policy +ORDER BY unexplained_count DESC, de.event_kind; + +-- 07. Failed transaction materialization safety. +-- Target after closure: empty. Failed transactions may be decoded for audit, but must not be business-materialized. +SELECT + de.event_kind, + COUNT(DISTINCT de.id) AS decoded_failed_count, + COUNT(DISTINCT te.id) AS trade_count, + COUNT(DISTINCT lae.id) AS launch_count, + COUNT(DISTINCT lie.id) AS liquidity_count, + COUNT(DISTINCT ple.id) AS lifecycle_count, + COUNT(DISTINCT fee.id) AS fee_count, + COUNT(DISTINCT rew.id) AS reward_count, + COUNT(DISTINCT adm.id) AS admin_count, + COUNT(DISTINCT obe.id) AS orderbook_count, + COUNT(DISTINCT tae.id) AS token_account_count, + MIN(tx.signature) AS sample_signature +FROM k_sol_dex_decoded_events de +JOIN k_sol_chain_transactions tx + ON tx.id = de.transaction_id +LEFT JOIN k_sol_trade_events te + ON te.decoded_event_id = de.id +LEFT JOIN k_sol_launch_events lae + ON lae.decoded_event_id = de.id +LEFT JOIN k_sol_liquidity_events lie + ON lie.decoded_event_id = de.id +LEFT JOIN k_sol_pool_lifecycle_events ple + ON ple.decoded_event_id = de.id +LEFT JOIN k_sol_fee_events fee + ON fee.decoded_event_id = de.id +LEFT JOIN k_sol_reward_events rew + ON rew.decoded_event_id = de.id +LEFT JOIN k_sol_pool_admin_events adm + ON adm.decoded_event_id = de.id +LEFT JOIN k_sol_orderbook_events obe + ON obe.decoded_event_id = de.id +LEFT JOIN k_sol_token_account_events tae + ON tae.decoded_event_id = de.id +WHERE de.protocol_name = 'pump_fees' + AND tx.err_json IS NOT NULL + AND tx.err_json <> '' + AND tx.err_json <> 'null' +GROUP BY de.event_kind +HAVING trade_count > 0 + OR launch_count > 0 + OR liquidity_count > 0 + OR lifecycle_count > 0 + OR fee_count > 0 + OR reward_count > 0 + OR admin_count > 0 + OR orderbook_count > 0 + OR token_account_count > 0 +ORDER BY de.event_kind; + +-- 08. Multi-target materialization safety. +-- Target after closure: empty. One decoded event must not feed multiple business targets. +SELECT + de.event_kind, + COUNT(DISTINCT de.id) AS decoded_count, + COUNT(DISTINCT te.id) AS trade_count, + COUNT(DISTINCT lae.id) AS launch_count, + COUNT(DISTINCT lie.id) AS liquidity_count, + COUNT(DISTINCT ple.id) AS lifecycle_count, + COUNT(DISTINCT fee.id) AS fee_count, + COUNT(DISTINCT rew.id) AS reward_count, + COUNT(DISTINCT adm.id) AS admin_count, + COUNT(DISTINCT obe.id) AS orderbook_count, + COUNT(DISTINCT tae.id) AS token_account_count, + ( + CASE WHEN COUNT(DISTINCT te.id) > 0 THEN 1 ELSE 0 END + + CASE WHEN COUNT(DISTINCT lae.id) > 0 THEN 1 ELSE 0 END + + CASE WHEN COUNT(DISTINCT lie.id) > 0 THEN 1 ELSE 0 END + + CASE WHEN COUNT(DISTINCT ple.id) > 0 THEN 1 ELSE 0 END + + CASE WHEN COUNT(DISTINCT fee.id) > 0 THEN 1 ELSE 0 END + + CASE WHEN COUNT(DISTINCT rew.id) > 0 THEN 1 ELSE 0 END + + CASE WHEN COUNT(DISTINCT adm.id) > 0 THEN 1 ELSE 0 END + + CASE WHEN COUNT(DISTINCT obe.id) > 0 THEN 1 ELSE 0 END + + CASE WHEN COUNT(DISTINCT tae.id) > 0 THEN 1 ELSE 0 END + ) AS materialized_target_count +FROM k_sol_dex_decoded_events de +LEFT JOIN k_sol_trade_events te + ON te.decoded_event_id = de.id +LEFT JOIN k_sol_launch_events lae + ON lae.decoded_event_id = de.id +LEFT JOIN k_sol_liquidity_events lie + ON lie.decoded_event_id = de.id +LEFT JOIN k_sol_pool_lifecycle_events ple + ON ple.decoded_event_id = de.id +LEFT JOIN k_sol_fee_events fee + ON fee.decoded_event_id = de.id +LEFT JOIN k_sol_reward_events rew + ON rew.decoded_event_id = de.id +LEFT JOIN k_sol_pool_admin_events adm + ON adm.decoded_event_id = de.id +LEFT JOIN k_sol_orderbook_events obe + ON obe.decoded_event_id = de.id +LEFT JOIN k_sol_token_account_events tae + ON tae.decoded_event_id = de.id +WHERE de.protocol_name = 'pump_fees' +GROUP BY de.event_kind +HAVING materialized_target_count > 1 +ORDER BY materialized_target_count DESC, de.event_kind; + +-- 09. Materialization summary by table. +-- Failed transaction columns explain observed/materialized gaps without reopening failed-tx rows. +SELECT + de.event_kind, + COUNT(DISTINCT de.id) AS decoded_count, + COUNT(DISTINCT CASE + WHEN tx.err_json IS NULL OR tx.err_json = '' OR tx.err_json = 'null' + THEN de.transaction_id + END) AS successful_tx_count, + COUNT(DISTINCT CASE + WHEN tx.err_json IS NOT NULL AND tx.err_json <> '' AND tx.err_json <> 'null' + THEN de.transaction_id + END) AS failed_tx_count, + COUNT(DISTINCT te.id) AS trade_count, + COUNT(DISTINCT lae.id) AS launch_count, + COUNT(DISTINCT lie.id) AS liquidity_count, + COUNT(DISTINCT ple.id) AS lifecycle_count, + COUNT(DISTINCT fee.id) AS fee_count, + COUNT(DISTINCT rew.id) AS reward_count, + COUNT(DISTINCT adm.id) AS admin_count, + COUNT(DISTINCT obe.id) AS orderbook_count, + COUNT(DISTINCT tae.id) AS token_account_count, + COUNT(DISTINCT CASE + WHEN te.id IS NOT NULL + OR lae.id IS NOT NULL + OR lie.id IS NOT NULL + OR ple.id IS NOT NULL + OR fee.id IS NOT NULL + OR rew.id IS NOT NULL + OR adm.id IS NOT NULL + OR obe.id IS NOT NULL + OR tae.id IS NOT NULL + THEN de.id + END) AS business_materialized_count, + COUNT(DISTINCT CASE + WHEN (tx.err_json IS NULL OR tx.err_json = '' OR tx.err_json = 'null') + AND te.id IS NULL + AND lae.id IS NULL + AND lie.id IS NULL + AND ple.id IS NULL + AND fee.id IS NULL + AND rew.id IS NULL + AND adm.id IS NULL + AND obe.id IS NULL + AND tae.id IS NULL + THEN de.id + END) AS successful_non_materialized_count, + COUNT(DISTINCT CASE + WHEN tx.err_json IS NOT NULL AND tx.err_json <> '' AND tx.err_json <> 'null' + AND te.id IS NULL + AND lae.id IS NULL + AND lie.id IS NULL + AND ple.id IS NULL + AND fee.id IS NULL + AND rew.id IS NULL + AND adm.id IS NULL + AND obe.id IS NULL + AND tae.id IS NULL + THEN de.id + END) AS failed_non_materialized_count +FROM k_sol_dex_decoded_events de +JOIN k_sol_chain_transactions tx + ON tx.id = de.transaction_id +LEFT JOIN k_sol_trade_events te + ON te.decoded_event_id = de.id +LEFT JOIN k_sol_launch_events lae + ON lae.decoded_event_id = de.id +LEFT JOIN k_sol_liquidity_events lie + ON lie.decoded_event_id = de.id +LEFT JOIN k_sol_pool_lifecycle_events ple + ON ple.decoded_event_id = de.id +LEFT JOIN k_sol_fee_events fee + ON fee.decoded_event_id = de.id +LEFT JOIN k_sol_reward_events rew + ON rew.decoded_event_id = de.id +LEFT JOIN k_sol_pool_admin_events adm + ON adm.decoded_event_id = de.id +LEFT JOIN k_sol_orderbook_events obe + ON obe.decoded_event_id = de.id +LEFT JOIN k_sol_token_account_events tae + ON tae.decoded_event_id = de.id +WHERE de.protocol_name = 'pump_fees' +GROUP BY de.event_kind +ORDER BY de.event_kind; + +-- 09b. Coverage rows with observed/materialized gaps explained by failed transactions. +-- Target after closure: any remaining successful_non_materialized_count is policy/skip-explained by query 06. +SELECT + ce.entry_name, + ce.entry_kind, + ce.local_event_kind, + ce.expected_db_target, + ce.proof_status, + ce.observed_count AS coverage_observed_count, + ce.materialized_count AS coverage_materialized_count, + COUNT(DISTINCT de.id) AS decoded_count, + COUNT(DISTINCT CASE + WHEN tx.err_json IS NULL OR tx.err_json = '' OR tx.err_json = 'null' + THEN de.id + END) AS successful_decoded_count, + COUNT(DISTINCT CASE + WHEN tx.err_json IS NOT NULL AND tx.err_json <> '' AND tx.err_json <> 'null' + THEN de.id + END) AS failed_decoded_count, + COUNT(DISTINCT CASE + WHEN te.id IS NOT NULL + OR lae.id IS NOT NULL + OR lie.id IS NOT NULL + OR ple.id IS NOT NULL + OR fee.id IS NOT NULL + OR rew.id IS NOT NULL + OR adm.id IS NOT NULL + OR obe.id IS NOT NULL + OR tae.id IS NOT NULL + THEN de.id + END) AS business_materialized_count, + COUNT(DISTINCT CASE + WHEN (tx.err_json IS NULL OR tx.err_json = '' OR tx.err_json = 'null') + AND te.id IS NULL + AND lae.id IS NULL + AND lie.id IS NULL + AND ple.id IS NULL + AND fee.id IS NULL + AND rew.id IS NULL + AND adm.id IS NULL + AND obe.id IS NULL + AND tae.id IS NULL + THEN de.id + END) AS successful_non_materialized_count, + MIN(tx.signature) AS sample_signature +FROM k_sol_dex_event_coverage_entries ce +LEFT JOIN k_sol_dex_decoded_events de + ON de.protocol_name = 'pump_fees' + AND de.event_kind = ce.local_event_kind +LEFT JOIN k_sol_chain_transactions tx + ON tx.id = de.transaction_id +LEFT JOIN k_sol_trade_events te + ON te.decoded_event_id = de.id +LEFT JOIN k_sol_launch_events lae + ON lae.decoded_event_id = de.id +LEFT JOIN k_sol_liquidity_events lie + ON lie.decoded_event_id = de.id +LEFT JOIN k_sol_pool_lifecycle_events ple + ON ple.decoded_event_id = de.id +LEFT JOIN k_sol_fee_events fee + ON fee.decoded_event_id = de.id +LEFT JOIN k_sol_reward_events rew + ON rew.decoded_event_id = de.id +LEFT JOIN k_sol_pool_admin_events adm + ON adm.decoded_event_id = de.id +LEFT JOIN k_sol_orderbook_events obe + ON obe.decoded_event_id = de.id +LEFT JOIN k_sol_token_account_events tae + ON tae.decoded_event_id = de.id +WHERE ce.decoder_code = 'pump_fees' + AND ce.entry_kind IN ('instruction', 'event') + AND ce.local_event_kind IS NOT NULL + AND ce.local_event_kind <> '' +GROUP BY + ce.entry_name, + ce.entry_kind, + ce.local_event_kind, + ce.expected_db_target, + ce.proof_status, + ce.observed_count, + ce.materialized_count +HAVING coverage_observed_count <> coverage_materialized_count + OR successful_non_materialized_count > 0 + OR failed_decoded_count > 0 +ORDER BY successful_non_materialized_count DESC, failed_decoded_count DESC, ce.entry_kind, ce.entry_name; + +-- 10. Instruction observation versus coverage. +-- Target after closure: every observed non-transport discriminator is covered or documented. +WITH normalized_io AS ( + SELECT + io.decoder_code, + io.instruction_name, + CASE + WHEN io.instruction_name LIKE 'pump_fees.%' + THEN SUBSTR(io.instruction_name, LENGTH('pump_fees') + 2) + ELSE io.instruction_name + END AS normalized_entry_name, + io.discriminator_hex, + io.signature + FROM k_sol_instruction_observations io + WHERE io.decoder_code = 'pump_fees' + AND io.discriminator_hex IS NOT NULL + AND io.discriminator_hex <> '' + AND io.discriminator_hex <> 'e445a52e51cb9a1d' +) +SELECT + nio.instruction_name, + nio.normalized_entry_name, + nio.discriminator_hex, + COUNT(*) AS observed_count, + COUNT(DISTINCT nio.signature) AS tx_count, + MIN(nio.signature) AS sample_signature, + CASE + WHEN ce.id IS NULL THEN 'coverage_gap' + ELSE 'covered' + END AS observation_coverage_status, + ce.local_event_kind, + ce.expected_db_target, + ce.proof_status +FROM normalized_io nio +LEFT JOIN k_sol_dex_event_coverage_entries ce + ON ce.decoder_code = 'pump_fees' + AND COALESCE(ce.discriminator_hex, '') = COALESCE(nio.discriminator_hex, '') + AND ( + COALESCE(TRIM(nio.instruction_name), '') = '' + OR ce.entry_name = nio.instruction_name + OR ce.entry_name = nio.normalized_entry_name + OR ce.local_event_kind = nio.instruction_name + OR ce.local_event_kind = ('pump_fees.' || nio.normalized_entry_name) + ) +GROUP BY + nio.instruction_name, + nio.normalized_entry_name, + nio.discriminator_hex, + observation_coverage_status, + ce.local_event_kind, + ce.expected_db_target, + ce.proof_status +ORDER BY observed_count DESC, nio.instruction_name, nio.discriminator_hex; + +-- 11. Anti-trade/candle direct pump_fees control. +-- Target after closure: empty. pump_fees must not create trade/candle rows without strict proof. +SELECT + de.event_kind, + COUNT(DISTINCT te.id) AS trade_count, + MIN(tx.signature) AS sample_signature +FROM k_sol_dex_decoded_events de +LEFT JOIN k_sol_chain_transactions tx + ON tx.id = de.transaction_id +LEFT JOIN k_sol_trade_events te + ON te.decoded_event_id = de.id +WHERE de.protocol_name = 'pump_fees' +GROUP BY de.event_kind +HAVING trade_count > 0 +ORDER BY trade_count DESC, de.event_kind; + +-- 12. Global watchlist after pump_fees replay. +-- Expected after local promotion: pump_fees rows should no longer dominate this list unless explicitly deferred. +SELECT + json_extract(de.payload_json, '$.upstreamDecoderCode') AS upstream_decoder_code, + json_extract(de.payload_json, '$.upstreamEntryName') AS upstream_entry_name, + json_extract(de.payload_json, '$.upstreamDiscriminatorHex') AS upstream_discriminator_hex, + COUNT(*) AS decoded_count, + COUNT(DISTINCT de.transaction_id) AS tx_count, + MIN(tx.signature) AS sample_signature +FROM k_sol_dex_decoded_events de +LEFT JOIN k_sol_chain_transactions tx + ON tx.id = de.transaction_id +WHERE de.protocol_name = 'upstream_git' + AND de.event_kind = 'upstream_git.instruction_match' +GROUP BY upstream_decoder_code, upstream_entry_name, upstream_discriminator_hex +ORDER BY decoded_count DESC, upstream_decoder_code, upstream_entry_name; + +-- 13. Local-IDL-only pump_fees entries. +-- Target after coverage sync: these local IDL rows are present and mapped to local_event_kind. +SELECT + ce.entry_name, + ce.entry_kind, + ce.discriminator_hex, + ce.source_repo, + ce.source_path, + ce.local_event_kind, + ce.expected_db_target, + ce.proof_status, + ce.observed_count, + ce.materialized_count, + ce.trade_count +FROM k_sol_dex_event_coverage_entries ce +WHERE ce.decoder_code = 'pump_fees' + AND ce.source_repo = 'local-idl' +ORDER BY ce.entry_kind, ce.entry_name; + +-- 14. Anchor event coverage local kind check. +-- Target after decoder delta: every Pump Fees event registry row has a local_event_kind. +SELECT + ce.entry_name, + ce.discriminator_hex, + ce.local_event_kind, + ce.expected_db_target, + ce.proof_status, + ce.observed_count, + ce.materialized_count +FROM k_sol_dex_event_coverage_entries ce +WHERE ce.decoder_code = 'pump_fees' + AND ce.entry_kind = 'event' + AND ( + ce.local_event_kind IS NULL + OR TRIM(ce.local_event_kind) = '' + ) +ORDER BY ce.entry_name; + +-- 15. Decoded Anchor events summary. +SELECT + de.event_kind, + json_extract(de.payload_json, '$.eventName') AS anchor_event_name, + json_extract(de.payload_json, '$.discriminatorHex') AS anchor_event_discriminator_hex, + COUNT(*) AS decoded_count, + COUNT(DISTINCT de.transaction_id) AS tx_count, + MIN(tx.signature) AS sample_signature +FROM k_sol_dex_decoded_events de +LEFT JOIN k_sol_chain_transactions tx + ON tx.id = de.transaction_id +WHERE de.protocol_name = 'pump_fees' + AND COALESCE(TRIM(json_extract(de.payload_json, '$.eventName')), '') <> '' +GROUP BY de.event_kind, anchor_event_name, anchor_event_discriminator_hex +ORDER BY decoded_count DESC, de.event_kind;