diff --git a/CHANGELOG.md b/CHANGELOG.md
index ed2fc6e..08978b9 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -65,3 +65,4 @@
0.7.32 - Clarification des sémantiques de validation locale : distinction entre gaps littéraux, gaps bloquants et paires actionnables, afin d’éviter de bloquer sur des paires détectées mais non matérialisées par trade.
0.7.33 - Ajout du profil `0.7.33_pair_trading_readiness`, avec classification des paires directes WSOL, directes stable, inverses stable/WSOL et cross-quotes nécessitant un router.
0.7.34 - Ajout du profil `0.7.34_non_trade_liquidity_lifecycle`, matérialisation des tables non-trade liquidité/lifecycle, warning non bloquant pour DEX attendus absents du corpus local, première tranche DLMM : `add_liquidity`, `remove_liquidity`, `initialize_position`, `initialize_bin_array`, intégration de la matérialisation non-trade dans les backfills token/pool ciblés, et distinction `PositionOpen`/`PositionClose` dans `LiquidityEventKind`.
+0.7.35 - Ajout du profil `0.7.35_non_trade_fee_reward_admin`, événements non-trade p2 : fees, rewards et administration.
diff --git a/Cargo.toml b/Cargo.toml
index 8b1828d..8c33813 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -8,7 +8,7 @@ members = [
]
[workspace.package]
-version = "0.7.34"
+version = "0.7.35"
edition = "2024"
license = "MIT"
repository = "https://git.sasedev.com/Sasedev/khadhroony-bobobot"
diff --git a/ROADMAP.md b/ROADMAP.md
index 8ef818c..00a69a3 100644
--- a/ROADMAP.md
+++ b/ROADMAP.md
@@ -881,9 +881,7 @@ Réalisé :
- conserver `meteora_damm_v1` manquant comme warning non bloquant lorsque le corpus de backfill local ne contient pas ce DEX.
### 6.067. Version `0.7.35` — Événements non-trade v2 : fees, rewards et administration
-Objectif : conserver les événements utiles au risque, au scoring, à l’économie du pool et à la traçabilité opérationnelle.
-
-À faire :
+Réalisé :
- ajouter `k_sol_fee_events`,
- ajouter `k_sol_reward_events`,
diff --git a/kb_demo_app/frontend/demo_pipeline2.html b/kb_demo_app/frontend/demo_pipeline2.html
index 86a3e9c..8fe0f5a 100644
--- a/kb_demo_app/frontend/demo_pipeline2.html
+++ b/kb_demo_app/frontend/demo_pipeline2.html
@@ -166,7 +166,8 @@
Validation profile
- 0.7.34 — non-trade liquidity/lifecycle
+ 0.7.35 — non-trade fee/reward admin
+ 0.7.34 — non-trade liquidity/lifecycle
0.7.33 — pair trading readiness
0.7.32 — validation report semantics
0.7.31 — trade event actionability policy
diff --git a/kb_demo_app/package.json b/kb_demo_app/package.json
index 9b2aa78..b39175b 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.34",
+ "version": "0.7.35",
"type": "module",
"scripts": {
"dev": "vite",
diff --git a/kb_demo_app/src/demo_pipeline2.rs b/kb_demo_app/src/demo_pipeline2.rs
index d78d128..7d16828 100644
--- a/kb_demo_app/src/demo_pipeline2.rs
+++ b/kb_demo_app/src/demo_pipeline2.rs
@@ -1087,7 +1087,7 @@ pub(crate) async fn demo_pipeline2_validate_local_pipeline(
let service = kb_lib::LocalPipelineValidationService::new(database.clone());
let profile_code = match request {
Some(request) => request.profile_code,
- None => "0.7.34_non_trade_liquidity_lifecycle".to_string(),
+ None => "0.7.35_non_trade_fee_reward_admin".to_string(),
};
let run_result = match profile_code.as_str() {
"0.7.27" | "0.7.27_dexes_non_regression" => {
@@ -1114,6 +1114,9 @@ pub(crate) async fn demo_pipeline2_validate_local_pipeline(
"0.7.34" | "0.7.34_non_trade_liquidity_lifecycle" => {
service.validate_v0_7_34_current_database().await
},
+ "0.7.35" | "0.7.35_non_trade_fee_reward_admin" => {
+ service.validate_v0_7_35_current_database().await
+ },
other => Err(kb_lib::Error::InvalidState(format!(
"unsupported local pipeline validation profile: {other}"
))),
diff --git a/kb_demo_app/tauri.conf.json b/kb_demo_app/tauri.conf.json
index a5be4a8..1df9682 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.34",
+ "version": "0.7.35",
"identifier": "com.sasedev.kb-demo-app",
"build": {
"beforeDevCommand": "npm run dev",
diff --git a/kb_lib/src/db.rs b/kb_lib/src/db.rs
index 70ae02f..3ce9cba 100644
--- a/kb_lib/src/db.rs
+++ b/kb_lib/src/db.rs
@@ -23,6 +23,7 @@ pub use dtos::DbMetadataDto;
pub use dtos::DbRuntimeEventDto;
pub use dtos::DexDecodedEventDto;
pub use dtos::DexDto;
+pub use dtos::FeeEventDto;
pub use dtos::KnownHttpEndpointDto;
pub use dtos::KnownWsEndpointDto;
pub use dtos::LaunchAttributionDto;
@@ -49,6 +50,7 @@ pub use dtos::PairAnalyticSignalDto;
pub use dtos::PairCandleDto;
pub use dtos::PairDto;
pub use dtos::PairMetricDto;
+pub use dtos::PoolAdminEventDto;
pub use dtos::PoolDto;
pub use dtos::PoolLifecycleEventDto;
pub use dtos::PoolListingDto;
@@ -58,6 +60,7 @@ pub use dtos::ProgramInstructionDiagnosticDto;
pub use dtos::ProgramInstructionDiscriminatorSummaryDto;
pub use dtos::ProtocolCandidateDto;
pub use dtos::ProtocolCandidateSummaryDto;
+pub use dtos::RewardEventDto;
pub use dtos::SwapDto;
pub use dtos::TokenBurnEventDto;
pub use dtos::TokenDto;
@@ -75,6 +78,7 @@ pub use entities::DbMetadataEntity;
pub use entities::DbRuntimeEventEntity;
pub use entities::DexDecodedEventEntity;
pub use entities::DexEntity;
+pub use entities::FeeEventEntity;
pub use entities::KnownHttpEndpointEntity;
pub use entities::KnownWsEndpointEntity;
pub use entities::LaunchAttributionEntity;
@@ -87,6 +91,7 @@ pub use entities::PairAnalyticSignalEntity;
pub use entities::PairCandleEntity;
pub use entities::PairEntity;
pub use entities::PairMetricEntity;
+pub use entities::PoolAdminEventEntity;
pub use entities::PoolEntity;
pub use entities::PoolLifecycleEventEntity;
pub use entities::PoolListingEntity;
@@ -96,6 +101,7 @@ pub use entities::ProgramInstructionDiagnosticEntity;
pub use entities::ProgramInstructionDiscriminatorRowEntity;
pub use entities::ProtocolCandidateEntity;
pub use entities::ProtocolCandidateSummaryEntity;
+pub use entities::RewardEventEntity;
pub use entities::SwapEntity;
pub use entities::TokenBurnEventEntity;
pub use entities::TokenEntity;
@@ -130,6 +136,9 @@ pub use queries::query_dex_decoded_events_upsert;
pub use queries::query_dexs_get_by_code;
pub use queries::query_dexs_list;
pub use queries::query_dexs_upsert;
+pub use queries::query_fee_events_get_by_decoded_event_id;
+pub use queries::query_fee_events_list_recent;
+pub use queries::query_fee_events_upsert;
pub use queries::query_known_http_endpoints_get;
pub use queries::query_known_http_endpoints_list;
pub use queries::query_known_http_endpoints_upsert;
@@ -179,6 +188,9 @@ pub use queries::query_pairs_get_by_pool_id;
pub use queries::query_pairs_list;
pub use queries::query_pairs_update_symbol;
pub use queries::query_pairs_upsert;
+pub use queries::query_pool_admin_events_get_by_decoded_event_id;
+pub use queries::query_pool_admin_events_list_recent;
+pub use queries::query_pool_admin_events_upsert;
pub use queries::query_pool_lifecycle_events_get_by_decoded_event_id;
pub use queries::query_pool_lifecycle_events_list_recent;
pub use queries::query_pool_lifecycle_events_upsert;
@@ -201,6 +213,9 @@ pub use queries::query_protocol_candidates_insert;
pub use queries::query_protocol_candidates_list_by_program_id;
pub use queries::query_protocol_candidates_list_by_transaction_id;
pub use queries::query_protocol_candidates_list_recent;
+pub use queries::query_reward_events_get_by_decoded_event_id;
+pub use queries::query_reward_events_list_recent;
+pub use queries::query_reward_events_upsert;
pub use queries::query_swaps_list_recent;
pub use queries::query_swaps_upsert;
pub use queries::query_token_burn_events_list_recent;
diff --git a/kb_lib/src/db/dtos.rs b/kb_lib/src/db/dtos.rs
index 0707ec7..6f1d85b 100644
--- a/kb_lib/src/db/dtos.rs
+++ b/kb_lib/src/db/dtos.rs
@@ -10,6 +10,7 @@ mod db_metadata;
mod db_runtime_event;
mod dex;
mod dex_decoded_event;
+mod fee_event;
mod known_http_endpoint;
mod known_ws_endpoint;
mod launch_attribution;
@@ -24,13 +25,16 @@ mod pair_analytic_signal;
mod pair_candle;
mod pair_metric;
mod pool;
-mod pool_listing;
+mod pool_admin_event;
mod pool_lifecycle_event;
+mod pool_listing;
mod pool_origin;
mod pool_token;
mod program_instruction_diagnostic;
+mod program_instruction_discriminator_summary;
mod protocol_candidate;
mod protocol_candidate_summary;
+mod reward_event;
mod swap;
mod token;
mod token_burn_event;
@@ -39,24 +43,22 @@ mod trade_event;
mod transaction_classification;
mod wallet;
mod wallet_holding;
-mod program_instruction_discriminator_summary;
mod wallet_participation;
pub(crate) use local_pipeline_diagnostics::LocalDecodedEventDiagnosticSummaryRow;
-pub(crate) use local_pipeline_diagnostics::LocalEventClassificationDiagnosticSummaryRow;
pub(crate) use local_pipeline_diagnostics::LocalDexDiagnosticSummaryRow;
pub(crate) use local_pipeline_diagnostics::LocalDuplicateDecodedEventTradeDiagnosticSampleRow;
+pub(crate) use local_pipeline_diagnostics::LocalEventClassificationDiagnosticSummaryRow;
pub(crate) use local_pipeline_diagnostics::LocalMissingTradeEventDiagnosticSampleRow;
pub(crate) use local_pipeline_diagnostics::LocalMissingTradeEventReasonSummaryRow;
pub(crate) use local_pipeline_diagnostics::LocalMultiTradeSignaturePairDiagnosticSampleRow;
pub(crate) use local_pipeline_diagnostics::LocalNonActionablePairDiagnosticSummaryRow;
-pub(crate) use local_pipeline_diagnostics::LocalPairDiagnosticSummaryRow;
pub(crate) use local_pipeline_diagnostics::LocalPairActionabilityDiagnosticSummaryRow;
-pub(crate) use local_pipeline_diagnostics::LocalPairTradingReadinessDiagnosticSummaryRow;
+pub(crate) use local_pipeline_diagnostics::LocalPairDiagnosticSummaryRow;
pub(crate) use local_pipeline_diagnostics::LocalPairGapDiagnosticSampleRow;
+pub(crate) use local_pipeline_diagnostics::LocalPairTradingReadinessDiagnosticSummaryRow;
pub(crate) use local_pipeline_diagnostics::LocalPipelineDiagnosticCountersRow;
-pub use program_instruction_discriminator_summary::ProgramInstructionDiscriminatorSummaryDto;
pub use analysis_signal::AnalysisSignalDto;
pub use chain_instruction::ChainInstructionDto;
pub use chain_slot::ChainSlotDto;
@@ -65,6 +67,7 @@ pub use db_metadata::DbMetadataDto;
pub use db_runtime_event::DbRuntimeEventDto;
pub use dex::DexDto;
pub use dex_decoded_event::DexDecodedEventDto;
+pub use fee_event::FeeEventDto;
pub use known_http_endpoint::KnownHttpEndpointDto;
pub use known_ws_endpoint::KnownWsEndpointDto;
pub use launch_attribution::LaunchAttributionDto;
@@ -72,17 +75,17 @@ pub use launch_surface::LaunchSurfaceDto;
pub use launch_surface_key::LaunchSurfaceKeyDto;
pub use liquidity_event::LiquidityEventDto;
pub use local_pipeline_diagnostics::LocalDecodedEventDiagnosticSummaryDto;
-pub use local_pipeline_diagnostics::LocalEventClassificationDiagnosticSummaryDto;
pub use local_pipeline_diagnostics::LocalDexDiagnosticSummaryDto;
pub use local_pipeline_diagnostics::LocalDuplicateDecodedEventTradeDiagnosticSampleDto;
+pub use local_pipeline_diagnostics::LocalEventClassificationDiagnosticSummaryDto;
pub use local_pipeline_diagnostics::LocalMissingTradeEventDiagnosticSampleDto;
pub use local_pipeline_diagnostics::LocalMissingTradeEventReasonSummaryDto;
pub use local_pipeline_diagnostics::LocalMultiTradeSignaturePairDiagnosticSampleDto;
pub use local_pipeline_diagnostics::LocalNonActionablePairDiagnosticSummaryDto;
-pub use local_pipeline_diagnostics::LocalPairDiagnosticSummaryDto;
pub use local_pipeline_diagnostics::LocalPairActionabilityDiagnosticSummaryDto;
-pub use local_pipeline_diagnostics::LocalPairTradingReadinessDiagnosticSummaryDto;
+pub use local_pipeline_diagnostics::LocalPairDiagnosticSummaryDto;
pub use local_pipeline_diagnostics::LocalPairGapDiagnosticSampleDto;
+pub use local_pipeline_diagnostics::LocalPairTradingReadinessDiagnosticSummaryDto;
pub use local_pipeline_diagnostics::LocalPipelineDiagnosticCountersDto;
pub use local_pipeline_diagnostics::LocalPipelineDiagnosticSummaryDto;
pub use observed_token::ObservedTokenDto;
@@ -92,13 +95,16 @@ pub use pair_analytic_signal::PairAnalyticSignalDto;
pub use pair_candle::PairCandleDto;
pub use pair_metric::PairMetricDto;
pub use pool::PoolDto;
-pub use pool_listing::PoolListingDto;
+pub use pool_admin_event::PoolAdminEventDto;
pub use pool_lifecycle_event::PoolLifecycleEventDto;
+pub use pool_listing::PoolListingDto;
pub use pool_origin::PoolOriginDto;
pub use pool_token::PoolTokenDto;
pub use program_instruction_diagnostic::ProgramInstructionDiagnosticDto;
+pub use program_instruction_discriminator_summary::ProgramInstructionDiscriminatorSummaryDto;
pub use protocol_candidate::ProtocolCandidateDto;
pub use protocol_candidate_summary::ProtocolCandidateSummaryDto;
+pub use reward_event::RewardEventDto;
pub use swap::SwapDto;
pub use token::TokenDto;
pub use token_burn_event::TokenBurnEventDto;
diff --git a/kb_lib/src/db/dtos/fee_event.rs b/kb_lib/src/db/dtos/fee_event.rs
new file mode 100644
index 0000000..e4165e2
--- /dev/null
+++ b/kb_lib/src/db/dtos/fee_event.rs
@@ -0,0 +1,150 @@
+// file: kb_lib/src/db/dtos/fee_event.rs
+
+//! Fee event DTO.
+
+/// Application-facing normalized fee event DTO.
+#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
+pub struct FeeEventDto {
+ /// Optional numeric primary key.
+ pub id: std::option::Option,
+ /// Related transaction id.
+ pub transaction_id: i64,
+ /// Related decoded DEX event id, when available.
+ pub decoded_event_id: std::option::Option,
+ /// Related DEX id, when the DEX row is known.
+ pub dex_id: std::option::Option,
+ /// Related pool id, when the pool row is known.
+ pub pool_id: std::option::Option,
+ /// Related pair id, when the pair row is known.
+ pub pair_id: std::option::Option,
+ /// Transaction signature.
+ pub signature: std::string::String,
+ /// Optional slot number.
+ pub slot: std::option::Option,
+ /// Protocol name that emitted the decoded event.
+ pub protocol_name: std::string::String,
+ /// Program id that emitted the decoded event.
+ pub program_id: std::string::String,
+ /// Stable decoded event kind.
+ pub event_kind: std::string::String,
+ /// Pool account address, when decoded.
+ pub pool_account: std::option::Option,
+ /// Wallet or authority associated with the fee event, when decoded.
+ pub actor_wallet: std::option::Option,
+ /// Token mint used to pay or collect the fee, when decoded.
+ pub fee_token_mint: std::option::Option,
+ /// Raw fee amount as decimal text, when decoded.
+ pub fee_amount_raw: std::option::Option,
+ /// Source decoded payload JSON.
+ pub payload_json: std::string::String,
+ /// Execution timestamp.
+ pub executed_at: chrono::DateTime,
+ /// Creation timestamp.
+ pub created_at: chrono::DateTime,
+}
+
+impl FeeEventDto {
+ /// Creates a new fee event DTO.
+ #[allow(clippy::too_many_arguments)]
+ pub fn new(
+ transaction_id: i64,
+ decoded_event_id: std::option::Option,
+ dex_id: std::option::Option,
+ pool_id: std::option::Option,
+ pair_id: std::option::Option,
+ signature: std::string::String,
+ slot: std::option::Option,
+ protocol_name: std::string::String,
+ program_id: std::string::String,
+ event_kind: std::string::String,
+ pool_account: std::option::Option,
+ actor_wallet: std::option::Option,
+ fee_token_mint: std::option::Option,
+ fee_amount_raw: std::option::Option,
+ payload_json: std::string::String,
+ ) -> Self {
+ let now = chrono::Utc::now();
+ return Self {
+ id: None,
+ transaction_id,
+ decoded_event_id,
+ dex_id,
+ pool_id,
+ pair_id,
+ signature,
+ slot,
+ protocol_name,
+ program_id,
+ event_kind,
+ pool_account,
+ actor_wallet,
+ fee_token_mint,
+ fee_amount_raw,
+ payload_json,
+ executed_at: now,
+ created_at: now,
+ };
+ }
+}
+
+impl TryFrom for FeeEventDto {
+ type Error = crate::Error;
+
+ fn try_from(entity: crate::FeeEventEntity) -> Result {
+ let executed_at_result = chrono::DateTime::parse_from_rfc3339(&entity.executed_at);
+ let executed_at = match executed_at_result {
+ Ok(executed_at) => executed_at.with_timezone(&chrono::Utc),
+ Err(error) => {
+ return Err(crate::Error::Db(format!(
+ "cannot parse fee event executed_at '{}': {}",
+ entity.executed_at, error
+ )));
+ },
+ };
+ let created_at_result = chrono::DateTime::parse_from_rfc3339(&entity.created_at);
+ let created_at = match created_at_result {
+ Ok(created_at) => created_at.with_timezone(&chrono::Utc),
+ Err(error) => {
+ return Err(crate::Error::Db(format!(
+ "cannot parse fee event created_at '{}': {}",
+ entity.created_at, error
+ )));
+ },
+ };
+ let slot = match entity.slot {
+ Some(slot) => {
+ let slot_result = u64::try_from(slot);
+ match slot_result {
+ Ok(slot) => Some(slot),
+ Err(error) => {
+ return Err(crate::Error::Db(format!(
+ "cannot convert fee event slot '{}' to u64: {}",
+ slot, error
+ )));
+ },
+ }
+ },
+ None => None,
+ };
+ return Ok(Self {
+ id: Some(entity.id),
+ transaction_id: entity.transaction_id,
+ decoded_event_id: entity.decoded_event_id,
+ dex_id: entity.dex_id,
+ pool_id: entity.pool_id,
+ pair_id: entity.pair_id,
+ signature: entity.signature,
+ slot,
+ protocol_name: entity.protocol_name,
+ program_id: entity.program_id,
+ event_kind: entity.event_kind,
+ pool_account: entity.pool_account,
+ actor_wallet: entity.actor_wallet,
+ fee_token_mint: entity.fee_token_mint,
+ fee_amount_raw: entity.fee_amount_raw,
+ payload_json: entity.payload_json,
+ executed_at,
+ created_at,
+ });
+ }
+}
diff --git a/kb_lib/src/db/dtos/local_pipeline_diagnostics.rs b/kb_lib/src/db/dtos/local_pipeline_diagnostics.rs
index 034a6b7..0403174 100644
--- a/kb_lib/src/db/dtos/local_pipeline_diagnostics.rs
+++ b/kb_lib/src/db/dtos/local_pipeline_diagnostics.rs
@@ -27,6 +27,12 @@ pub struct LocalPipelineDiagnosticSummaryDto {
pub liquidity_event_count: i64,
/// Total persisted pool lifecycle events.
pub pool_lifecycle_event_count: i64,
+ /// Total persisted fee events.
+ pub fee_event_count: i64,
+ /// Total persisted reward events.
+ pub reward_event_count: i64,
+ /// Total persisted pool administration events.
+ pub pool_admin_event_count: i64,
/// Whether the local persisted pipeline has no blocking diagnostic issue.
pub diagnostics_clean: bool,
/// Number of blocking diagnostic issues.
@@ -371,6 +377,12 @@ pub struct LocalPipelineDiagnosticCountersDto {
pub liquidity_event_count: i64,
/// Total persisted pool lifecycle events.
pub pool_lifecycle_event_count: i64,
+ /// Total persisted fee events.
+ pub fee_event_count: i64,
+ /// Total persisted reward events.
+ pub reward_event_count: i64,
+ /// Total persisted pool administration events.
+ pub pool_admin_event_count: i64,
/// Total decoded trade candidates without trade event, including ignored failed transactions.
pub missing_trade_event_count: i64,
/// Explicit alias for decoded trade candidates without linked trade event.
@@ -443,6 +455,9 @@ pub(crate) struct LocalPipelineDiagnosticCountersRow {
pub(crate) decoded_unknown_event_count: i64,
pub(crate) liquidity_event_count: i64,
pub(crate) pool_lifecycle_event_count: i64,
+ pub(crate) fee_event_count: i64,
+ pub(crate) reward_event_count: i64,
+ pub(crate) pool_admin_event_count: i64,
pub(crate) missing_trade_event_count: i64,
pub(crate) decoded_trade_candidate_without_trade_event_count: i64,
pub(crate) decoded_trade_candidate_without_trade_event_on_ok_transaction_count: i64,
diff --git a/kb_lib/src/db/dtos/pool_admin_event.rs b/kb_lib/src/db/dtos/pool_admin_event.rs
new file mode 100644
index 0000000..dc09f96
--- /dev/null
+++ b/kb_lib/src/db/dtos/pool_admin_event.rs
@@ -0,0 +1,145 @@
+// file: kb_lib/src/db/dtos/pool_admin_event.rs
+
+//! Pool administration event DTO.
+
+/// Application-facing normalized pool administration event DTO.
+#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
+pub struct PoolAdminEventDto {
+ /// Optional numeric primary key.
+ pub id: std::option::Option,
+ /// Related transaction id.
+ pub transaction_id: i64,
+ /// Related decoded DEX event id, when available.
+ pub decoded_event_id: std::option::Option,
+ /// Related DEX id, when the DEX row is known.
+ pub dex_id: std::option::Option,
+ /// Related pool id, when the pool row is known.
+ pub pool_id: std::option::Option,
+ /// Related pair id, when the pair row is known.
+ pub pair_id: std::option::Option,
+ /// Transaction signature.
+ pub signature: std::string::String,
+ /// Optional slot number.
+ pub slot: std::option::Option,
+ /// Protocol name that emitted the decoded event.
+ pub protocol_name: std::string::String,
+ /// Program id that emitted the decoded event.
+ pub program_id: std::string::String,
+ /// Stable decoded event kind.
+ pub event_kind: std::string::String,
+ /// Pool account address, when decoded.
+ pub pool_account: std::option::Option,
+ /// Wallet or authority associated with the admin event, when decoded.
+ pub actor_wallet: std::option::Option,
+ /// Normalized admin action label, when available.
+ pub admin_action: std::option::Option,
+ /// Source decoded payload JSON.
+ pub payload_json: std::string::String,
+ /// Execution timestamp.
+ pub executed_at: chrono::DateTime,
+ /// Creation timestamp.
+ pub created_at: chrono::DateTime,
+}
+
+impl PoolAdminEventDto {
+ /// Creates a new pool administration event DTO.
+ #[allow(clippy::too_many_arguments)]
+ pub fn new(
+ transaction_id: i64,
+ decoded_event_id: std::option::Option,
+ dex_id: std::option::Option,
+ pool_id: std::option::Option,
+ pair_id: std::option::Option,
+ signature: std::string::String,
+ slot: std::option::Option,
+ protocol_name: std::string::String,
+ program_id: std::string::String,
+ event_kind: std::string::String,
+ pool_account: std::option::Option,
+ actor_wallet: std::option::Option,
+ admin_action: std::option::Option,
+ payload_json: std::string::String,
+ ) -> Self {
+ let now = chrono::Utc::now();
+ return Self {
+ id: None,
+ transaction_id,
+ decoded_event_id,
+ dex_id,
+ pool_id,
+ pair_id,
+ signature,
+ slot,
+ protocol_name,
+ program_id,
+ event_kind,
+ pool_account,
+ actor_wallet,
+ admin_action,
+ payload_json,
+ executed_at: now,
+ created_at: now,
+ };
+ }
+}
+
+impl TryFrom for PoolAdminEventDto {
+ type Error = crate::Error;
+
+ fn try_from(entity: crate::PoolAdminEventEntity) -> Result {
+ let executed_at_result = chrono::DateTime::parse_from_rfc3339(&entity.executed_at);
+ let executed_at = match executed_at_result {
+ Ok(executed_at) => executed_at.with_timezone(&chrono::Utc),
+ Err(error) => {
+ return Err(crate::Error::Db(format!(
+ "cannot parse pool admin event executed_at '{}': {}",
+ entity.executed_at, error
+ )));
+ },
+ };
+ let created_at_result = chrono::DateTime::parse_from_rfc3339(&entity.created_at);
+ let created_at = match created_at_result {
+ Ok(created_at) => created_at.with_timezone(&chrono::Utc),
+ Err(error) => {
+ return Err(crate::Error::Db(format!(
+ "cannot parse pool admin event created_at '{}': {}",
+ entity.created_at, error
+ )));
+ },
+ };
+ let slot = match entity.slot {
+ Some(slot) => {
+ let slot_result = u64::try_from(slot);
+ match slot_result {
+ Ok(slot) => Some(slot),
+ Err(error) => {
+ return Err(crate::Error::Db(format!(
+ "cannot convert pool admin event slot '{}' to u64: {}",
+ slot, error
+ )));
+ },
+ }
+ },
+ None => None,
+ };
+ return Ok(Self {
+ id: Some(entity.id),
+ transaction_id: entity.transaction_id,
+ decoded_event_id: entity.decoded_event_id,
+ dex_id: entity.dex_id,
+ pool_id: entity.pool_id,
+ pair_id: entity.pair_id,
+ signature: entity.signature,
+ slot,
+ protocol_name: entity.protocol_name,
+ program_id: entity.program_id,
+ event_kind: entity.event_kind,
+ pool_account: entity.pool_account,
+ actor_wallet: entity.actor_wallet,
+ admin_action: entity.admin_action,
+ payload_json: entity.payload_json,
+ executed_at,
+ created_at,
+ });
+ }
+}
diff --git a/kb_lib/src/db/dtos/reward_event.rs b/kb_lib/src/db/dtos/reward_event.rs
new file mode 100644
index 0000000..bf796a0
--- /dev/null
+++ b/kb_lib/src/db/dtos/reward_event.rs
@@ -0,0 +1,150 @@
+// file: kb_lib/src/db/dtos/reward_event.rs
+
+//! Reward event DTO.
+
+/// Application-facing normalized reward event DTO.
+#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
+pub struct RewardEventDto {
+ /// Optional numeric primary key.
+ pub id: std::option::Option,
+ /// Related transaction id.
+ pub transaction_id: i64,
+ /// Related decoded DEX event id, when available.
+ pub decoded_event_id: std::option::Option,
+ /// Related DEX id, when the DEX row is known.
+ pub dex_id: std::option::Option,
+ /// Related pool id, when the pool row is known.
+ pub pool_id: std::option::Option,
+ /// Related pair id, when the pair row is known.
+ pub pair_id: std::option::Option,
+ /// Transaction signature.
+ pub signature: std::string::String,
+ /// Optional slot number.
+ pub slot: std::option::Option,
+ /// Protocol name that emitted the decoded event.
+ pub protocol_name: std::string::String,
+ /// Program id that emitted the decoded event.
+ pub program_id: std::string::String,
+ /// Stable decoded event kind.
+ pub event_kind: std::string::String,
+ /// Pool account address, when decoded.
+ pub pool_account: std::option::Option,
+ /// Wallet or authority associated with the reward event, when decoded.
+ pub actor_wallet: std::option::Option,
+ /// Token mint used by the reward event, when decoded.
+ pub reward_token_mint: std::option::Option,
+ /// Raw reward amount as decimal text, when decoded.
+ pub reward_amount_raw: std::option::Option,
+ /// Source decoded payload JSON.
+ pub payload_json: std::string::String,
+ /// Execution timestamp.
+ pub executed_at: chrono::DateTime,
+ /// Creation timestamp.
+ pub created_at: chrono::DateTime,
+}
+
+impl RewardEventDto {
+ /// Creates a new reward event DTO.
+ #[allow(clippy::too_many_arguments)]
+ pub fn new(
+ transaction_id: i64,
+ decoded_event_id: std::option::Option,
+ dex_id: std::option::Option,
+ pool_id: std::option::Option,
+ pair_id: std::option::Option,
+ signature: std::string::String,
+ slot: std::option::Option,
+ protocol_name: std::string::String,
+ program_id: std::string::String,
+ event_kind: std::string::String,
+ pool_account: std::option::Option,
+ actor_wallet: std::option::Option,
+ reward_token_mint: std::option::Option,
+ reward_amount_raw: std::option::Option,
+ payload_json: std::string::String,
+ ) -> Self {
+ let now = chrono::Utc::now();
+ return Self {
+ id: None,
+ transaction_id,
+ decoded_event_id,
+ dex_id,
+ pool_id,
+ pair_id,
+ signature,
+ slot,
+ protocol_name,
+ program_id,
+ event_kind,
+ pool_account,
+ actor_wallet,
+ reward_token_mint,
+ reward_amount_raw,
+ payload_json,
+ executed_at: now,
+ created_at: now,
+ };
+ }
+}
+
+impl TryFrom for RewardEventDto {
+ type Error = crate::Error;
+
+ fn try_from(entity: crate::RewardEventEntity) -> Result {
+ let executed_at_result = chrono::DateTime::parse_from_rfc3339(&entity.executed_at);
+ let executed_at = match executed_at_result {
+ Ok(executed_at) => executed_at.with_timezone(&chrono::Utc),
+ Err(error) => {
+ return Err(crate::Error::Db(format!(
+ "cannot parse reward event executed_at '{}': {}",
+ entity.executed_at, error
+ )));
+ },
+ };
+ let created_at_result = chrono::DateTime::parse_from_rfc3339(&entity.created_at);
+ let created_at = match created_at_result {
+ Ok(created_at) => created_at.with_timezone(&chrono::Utc),
+ Err(error) => {
+ return Err(crate::Error::Db(format!(
+ "cannot parse reward event created_at '{}': {}",
+ entity.created_at, error
+ )));
+ },
+ };
+ let slot = match entity.slot {
+ Some(slot) => {
+ let slot_result = u64::try_from(slot);
+ match slot_result {
+ Ok(slot) => Some(slot),
+ Err(error) => {
+ return Err(crate::Error::Db(format!(
+ "cannot convert reward event slot '{}' to u64: {}",
+ slot, error
+ )));
+ },
+ }
+ },
+ None => None,
+ };
+ return Ok(Self {
+ id: Some(entity.id),
+ transaction_id: entity.transaction_id,
+ decoded_event_id: entity.decoded_event_id,
+ dex_id: entity.dex_id,
+ pool_id: entity.pool_id,
+ pair_id: entity.pair_id,
+ signature: entity.signature,
+ slot,
+ protocol_name: entity.protocol_name,
+ program_id: entity.program_id,
+ event_kind: entity.event_kind,
+ pool_account: entity.pool_account,
+ actor_wallet: entity.actor_wallet,
+ reward_token_mint: entity.reward_token_mint,
+ reward_amount_raw: entity.reward_amount_raw,
+ payload_json: entity.payload_json,
+ executed_at,
+ created_at,
+ });
+ }
+}
diff --git a/kb_lib/src/db/entities.rs b/kb_lib/src/db/entities.rs
index 0c60fe5..8ece597 100644
--- a/kb_lib/src/db/entities.rs
+++ b/kb_lib/src/db/entities.rs
@@ -12,6 +12,7 @@ mod db_metadata;
mod db_runtime_event;
mod dex;
mod dex_decoded_event;
+mod fee_event;
mod known_http_endpoint;
mod known_ws_endpoint;
mod launch_attribution;
@@ -25,14 +26,16 @@ mod pair_analytic_signal;
mod pair_candle;
mod pair_metric;
mod pool;
-mod pool_listing;
+mod pool_admin_event;
mod pool_lifecycle_event;
+mod pool_listing;
mod pool_origin;
mod pool_token;
mod program_instruction_diagnostic;
mod program_instruction_discriminator_row;
mod protocol_candidate;
mod protocol_candidate_summary;
+mod reward_event;
mod swap;
mod token;
mod token_burn_event;
@@ -51,6 +54,7 @@ pub use db_metadata::DbMetadataEntity;
pub use db_runtime_event::DbRuntimeEventEntity;
pub use dex::DexEntity;
pub use dex_decoded_event::DexDecodedEventEntity;
+pub use fee_event::FeeEventEntity;
pub use known_http_endpoint::KnownHttpEndpointEntity;
pub use known_ws_endpoint::KnownWsEndpointEntity;
pub use launch_attribution::LaunchAttributionEntity;
@@ -64,14 +68,16 @@ pub use pair_analytic_signal::PairAnalyticSignalEntity;
pub use pair_candle::PairCandleEntity;
pub use pair_metric::PairMetricEntity;
pub use pool::PoolEntity;
-pub use pool_listing::PoolListingEntity;
+pub use pool_admin_event::PoolAdminEventEntity;
pub use pool_lifecycle_event::PoolLifecycleEventEntity;
+pub use pool_listing::PoolListingEntity;
pub use pool_origin::PoolOriginEntity;
pub use pool_token::PoolTokenEntity;
pub use program_instruction_diagnostic::ProgramInstructionDiagnosticEntity;
pub use program_instruction_discriminator_row::ProgramInstructionDiscriminatorRowEntity;
pub use protocol_candidate::ProtocolCandidateEntity;
pub use protocol_candidate_summary::ProtocolCandidateSummaryEntity;
+pub use reward_event::RewardEventEntity;
pub use swap::SwapEntity;
pub use token::TokenEntity;
pub use token_burn_event::TokenBurnEventEntity;
diff --git a/kb_lib/src/db/entities/fee_event.rs b/kb_lib/src/db/entities/fee_event.rs
new file mode 100644
index 0000000..0635acf
--- /dev/null
+++ b/kb_lib/src/db/entities/fee_event.rs
@@ -0,0 +1,44 @@
+// file: kb_lib/src/db/entities/fee_event.rs
+
+//! Fee event entity.
+
+/// Persisted normalized fee event row.
+#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, sqlx::FromRow)]
+pub struct FeeEventEntity {
+ /// Numeric primary key.
+ pub id: i64,
+ /// Related transaction id.
+ pub transaction_id: i64,
+ /// Related decoded DEX event id, when available.
+ pub decoded_event_id: std::option::Option,
+ /// Related DEX id, when the DEX row is known.
+ pub dex_id: std::option::Option,
+ /// Related pool id, when the pool row is known.
+ pub pool_id: std::option::Option,
+ /// Related pair id, when the pair row is known.
+ pub pair_id: std::option::Option,
+ /// Transaction signature.
+ pub signature: std::string::String,
+ /// Optional slot number.
+ pub slot: std::option::Option,
+ /// Protocol name that emitted the decoded event.
+ pub protocol_name: std::string::String,
+ /// Program id that emitted the decoded event.
+ pub program_id: std::string::String,
+ /// Stable decoded event kind.
+ pub event_kind: std::string::String,
+ /// Pool account address, when decoded.
+ pub pool_account: std::option::Option,
+ /// Wallet or authority associated with the fee event, when decoded.
+ pub actor_wallet: std::option::Option,
+ /// Token mint used to pay or collect the fee, when decoded.
+ pub fee_token_mint: std::option::Option,
+ /// Raw fee amount as decimal text, when decoded.
+ pub fee_amount_raw: std::option::Option,
+ /// Source decoded payload JSON.
+ pub payload_json: std::string::String,
+ /// Execution timestamp encoded as RFC3339 UTC text.
+ pub executed_at: std::string::String,
+ /// Creation timestamp encoded as RFC3339 UTC text.
+ pub created_at: std::string::String,
+}
diff --git a/kb_lib/src/db/entities/pool_admin_event.rs b/kb_lib/src/db/entities/pool_admin_event.rs
new file mode 100644
index 0000000..0a19375
--- /dev/null
+++ b/kb_lib/src/db/entities/pool_admin_event.rs
@@ -0,0 +1,42 @@
+// file: kb_lib/src/db/entities/pool_admin_event.rs
+
+//! Pool administration event entity.
+
+/// Persisted normalized pool administration event row.
+#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, sqlx::FromRow)]
+pub struct PoolAdminEventEntity {
+ /// Numeric primary key.
+ pub id: i64,
+ /// Related transaction id.
+ pub transaction_id: i64,
+ /// Related decoded DEX event id, when available.
+ pub decoded_event_id: std::option::Option,
+ /// Related DEX id, when the DEX row is known.
+ pub dex_id: std::option::Option,
+ /// Related pool id, when the pool row is known.
+ pub pool_id: std::option::Option,
+ /// Related pair id, when the pair row is known.
+ pub pair_id: std::option::Option,
+ /// Transaction signature.
+ pub signature: std::string::String,
+ /// Optional slot number.
+ pub slot: std::option::Option,
+ /// Protocol name that emitted the decoded event.
+ pub protocol_name: std::string::String,
+ /// Program id that emitted the decoded event.
+ pub program_id: std::string::String,
+ /// Stable decoded event kind.
+ pub event_kind: std::string::String,
+ /// Pool account address, when decoded.
+ pub pool_account: std::option::Option,
+ /// Wallet or authority associated with the admin event, when decoded.
+ pub actor_wallet: std::option::Option,
+ /// Normalized admin action label, when available.
+ pub admin_action: std::option::Option,
+ /// Source decoded payload JSON.
+ pub payload_json: std::string::String,
+ /// Execution timestamp encoded as RFC3339 UTC text.
+ pub executed_at: std::string::String,
+ /// Creation timestamp encoded as RFC3339 UTC text.
+ pub created_at: std::string::String,
+}
diff --git a/kb_lib/src/db/entities/reward_event.rs b/kb_lib/src/db/entities/reward_event.rs
new file mode 100644
index 0000000..5e84abd
--- /dev/null
+++ b/kb_lib/src/db/entities/reward_event.rs
@@ -0,0 +1,44 @@
+// file: kb_lib/src/db/entities/reward_event.rs
+
+//! Reward event entity.
+
+/// Persisted normalized reward event row.
+#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, sqlx::FromRow)]
+pub struct RewardEventEntity {
+ /// Numeric primary key.
+ pub id: i64,
+ /// Related transaction id.
+ pub transaction_id: i64,
+ /// Related decoded DEX event id, when available.
+ pub decoded_event_id: std::option::Option,
+ /// Related DEX id, when the DEX row is known.
+ pub dex_id: std::option::Option,
+ /// Related pool id, when the pool row is known.
+ pub pool_id: std::option::Option,
+ /// Related pair id, when the pair row is known.
+ pub pair_id: std::option::Option,
+ /// Transaction signature.
+ pub signature: std::string::String,
+ /// Optional slot number.
+ pub slot: std::option::Option,
+ /// Protocol name that emitted the decoded event.
+ pub protocol_name: std::string::String,
+ /// Program id that emitted the decoded event.
+ pub program_id: std::string::String,
+ /// Stable decoded event kind.
+ pub event_kind: std::string::String,
+ /// Pool account address, when decoded.
+ pub pool_account: std::option::Option,
+ /// Wallet or authority associated with the reward event, when decoded.
+ pub actor_wallet: std::option::Option,
+ /// Token mint used by the reward event, when decoded.
+ pub reward_token_mint: std::option::Option,
+ /// Raw reward amount as decimal text, when decoded.
+ pub reward_amount_raw: std::option::Option,
+ /// Source decoded payload JSON.
+ pub payload_json: std::string::String,
+ /// Execution timestamp encoded as RFC3339 UTC text.
+ pub executed_at: std::string::String,
+ /// Creation timestamp encoded as RFC3339 UTC text.
+ pub created_at: std::string::String,
+}
diff --git a/kb_lib/src/db/queries.rs b/kb_lib/src/db/queries.rs
index 88401b4..d97d45a 100644
--- a/kb_lib/src/db/queries.rs
+++ b/kb_lib/src/db/queries.rs
@@ -10,6 +10,7 @@ mod db_metadata;
mod db_runtime_event;
mod dex;
mod dex_decoded_event;
+mod fee_event;
mod known_http_endpoint;
mod known_ws_endpoint;
mod launch_attribution;
@@ -24,12 +25,14 @@ mod pair_analytic_signal;
mod pair_candle;
mod pair_metric;
mod pool;
+mod pool_admin_event;
mod pool_lifecycle_event;
mod pool_listing;
mod pool_origin;
mod pool_token;
mod program_instruction_diagnostic;
mod protocol_candidate;
+mod reward_event;
mod swap;
mod token;
mod token_burn_event;
@@ -65,6 +68,9 @@ pub use dex_decoded_event::query_dex_decoded_events_get_by_key;
pub use dex_decoded_event::query_dex_decoded_events_get_latest_pump_fun_create_payload_by_mint;
pub use dex_decoded_event::query_dex_decoded_events_list_by_transaction_id;
pub use dex_decoded_event::query_dex_decoded_events_upsert;
+pub use fee_event::query_fee_events_get_by_decoded_event_id;
+pub use fee_event::query_fee_events_list_recent;
+pub use fee_event::query_fee_events_upsert;
pub use known_http_endpoint::query_known_http_endpoints_get;
pub use known_http_endpoint::query_known_http_endpoints_list;
pub use known_http_endpoint::query_known_http_endpoints_upsert;
@@ -117,6 +123,9 @@ pub use pair_metric::query_pair_metrics_upsert;
pub use pool::query_pools_get_by_address;
pub use pool::query_pools_list;
pub use pool::query_pools_upsert;
+pub use pool_admin_event::query_pool_admin_events_get_by_decoded_event_id;
+pub use pool_admin_event::query_pool_admin_events_list_recent;
+pub use pool_admin_event::query_pool_admin_events_upsert;
pub use pool_lifecycle_event::query_pool_lifecycle_events_get_by_decoded_event_id;
pub use pool_lifecycle_event::query_pool_lifecycle_events_list_recent;
pub use pool_lifecycle_event::query_pool_lifecycle_events_upsert;
@@ -136,6 +145,9 @@ pub use protocol_candidate::query_protocol_candidates_insert;
pub use protocol_candidate::query_protocol_candidates_list_by_program_id;
pub use protocol_candidate::query_protocol_candidates_list_by_transaction_id;
pub use protocol_candidate::query_protocol_candidates_list_recent;
+pub use reward_event::query_reward_events_get_by_decoded_event_id;
+pub use reward_event::query_reward_events_list_recent;
+pub use reward_event::query_reward_events_upsert;
pub use swap::query_swaps_list_recent;
pub use swap::query_swaps_upsert;
pub use token::query_tokens_get_by_id;
diff --git a/kb_lib/src/db/queries/fee_event.rs b/kb_lib/src/db/queries/fee_event.rs
new file mode 100644
index 0000000..b6038c7
--- /dev/null
+++ b/kb_lib/src/db/queries/fee_event.rs
@@ -0,0 +1,302 @@
+// file: kb_lib/src/db/queries/fee_event.rs
+
+//! Queries for `k_sol_fee_events`.
+
+/// Returns one fee event by decoded event id.
+pub async fn query_fee_events_get_by_decoded_event_id(
+ database: &crate::Database,
+ decoded_event_id: i64,
+) -> Result, crate::Error> {
+ match database.connection() {
+ crate::DatabaseConnection::Sqlite(pool) => {
+ let query_result = sqlx::query_as::(
+ r#"
+SELECT
+ id,
+ transaction_id,
+ decoded_event_id,
+ dex_id,
+ pool_id,
+ pair_id,
+ signature,
+ slot,
+ protocol_name,
+ program_id,
+ event_kind,
+ pool_account,
+ actor_wallet,
+ fee_token_mint,
+ fee_amount_raw,
+ payload_json,
+ executed_at,
+ created_at
+FROM k_sol_fee_events
+WHERE decoded_event_id = ?
+LIMIT 1
+ "#,
+ )
+ .bind(decoded_event_id)
+ .fetch_optional(pool)
+ .await;
+ let entity_option = match query_result {
+ Ok(entity_option) => entity_option,
+ Err(error) => {
+ return Err(crate::Error::Db(format!(
+ "cannot fetch k_sol_fee_events by decoded_event_id '{}' on sqlite: {}",
+ decoded_event_id, error
+ )));
+ },
+ };
+ match entity_option {
+ Some(entity) => {
+ let dto_result = crate::FeeEventDto::try_from(entity);
+ match dto_result {
+ Ok(dto) => return Ok(Some(dto)),
+ Err(error) => return Err(error),
+ }
+ },
+ None => return Ok(None),
+ }
+ },
+ }
+}
+
+/// Inserts or updates one normalized fee event row.
+pub async fn query_fee_events_upsert(
+ database: &crate::Database,
+ dto: &crate::FeeEventDto,
+) -> Result {
+ let slot_i64 = match dto.slot {
+ Some(slot) => {
+ let slot_result = i64::try_from(slot);
+ match slot_result {
+ Ok(slot) => Some(slot),
+ Err(error) => {
+ return Err(crate::Error::Db(format!(
+ "cannot convert fee event slot '{}' to i64: {}",
+ slot, error
+ )));
+ },
+ }
+ },
+ None => None,
+ };
+ match database.connection() {
+ crate::DatabaseConnection::Sqlite(pool) => {
+ let existing_id = match dto.decoded_event_id {
+ Some(decoded_event_id) => {
+ let existing_result = sqlx::query_scalar::(
+ r#"
+SELECT id
+FROM k_sol_fee_events
+WHERE decoded_event_id = ?
+LIMIT 1
+ "#,
+ )
+ .bind(decoded_event_id)
+ .fetch_optional(pool)
+ .await;
+ match existing_result {
+ Ok(existing_id) => existing_id,
+ Err(error) => {
+ return Err(crate::Error::Db(format!(
+ "cannot fetch k_sol_fee_events id for decoded_event_id '{}' on sqlite: {}",
+ decoded_event_id, error
+ )));
+ },
+ }
+ },
+ None => None,
+ };
+ if let Some(id) = existing_id {
+ let update_result = sqlx::query(
+ r#"
+UPDATE k_sol_fee_events
+SET
+ transaction_id = ?,
+ dex_id = ?,
+ pool_id = ?,
+ pair_id = ?,
+ signature = ?,
+ slot = ?,
+ protocol_name = ?,
+ program_id = ?,
+ event_kind = ?,
+ pool_account = ?,
+ actor_wallet = ?,
+ fee_token_mint = ?,
+ fee_amount_raw = ?,
+
+ payload_json = ?,
+ executed_at = ?
+WHERE id = ?
+ "#,
+ )
+ .bind(dto.transaction_id)
+ .bind(dto.dex_id)
+ .bind(dto.pool_id)
+ .bind(dto.pair_id)
+ .bind(dto.signature.clone())
+ .bind(slot_i64)
+ .bind(dto.protocol_name.clone())
+ .bind(dto.program_id.clone())
+ .bind(dto.event_kind.clone())
+ .bind(dto.pool_account.clone())
+ .bind(dto.actor_wallet.clone())
+ .bind(dto.fee_token_mint.clone())
+ .bind(dto.fee_amount_raw.clone())
+ .bind(dto.payload_json.clone())
+ .bind(dto.executed_at.to_rfc3339())
+ .bind(id)
+ .execute(pool)
+ .await;
+ if let Err(error) = update_result {
+ return Err(crate::Error::Db(format!(
+ "cannot update k_sol_fee_events id '{}' on sqlite: {}",
+ id, error
+ )));
+ }
+ return Ok(id);
+ }
+ let insert_result = sqlx::query(
+ r#"
+INSERT INTO k_sol_fee_events (
+ transaction_id,
+ decoded_event_id,
+ dex_id,
+ pool_id,
+ pair_id,
+ signature,
+ slot,
+ protocol_name,
+ program_id,
+ event_kind,
+ pool_account,
+ actor_wallet,
+ fee_token_mint,
+ fee_amount_raw,
+
+ payload_json,
+ executed_at,
+ created_at
+)
+VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
+ "#,
+ )
+ .bind(dto.transaction_id)
+ .bind(dto.decoded_event_id)
+ .bind(dto.dex_id)
+ .bind(dto.pool_id)
+ .bind(dto.pair_id)
+ .bind(dto.signature.clone())
+ .bind(slot_i64)
+ .bind(dto.protocol_name.clone())
+ .bind(dto.program_id.clone())
+ .bind(dto.event_kind.clone())
+ .bind(dto.pool_account.clone())
+ .bind(dto.actor_wallet.clone())
+ .bind(dto.fee_token_mint.clone())
+ .bind(dto.fee_amount_raw.clone())
+ .bind(dto.payload_json.clone())
+ .bind(dto.executed_at.to_rfc3339())
+ .bind(dto.created_at.to_rfc3339())
+ .execute(pool)
+ .await;
+ if let Err(error) = insert_result {
+ return Err(crate::Error::Db(format!(
+ "cannot insert k_sol_fee_events on sqlite: {}",
+ error
+ )));
+ }
+ let id_result = sqlx::query_scalar::(
+ r#"
+SELECT id
+FROM k_sol_fee_events
+WHERE transaction_id = ?
+ AND protocol_name = ?
+ AND event_kind = ?
+ AND signature = ?
+ORDER BY id DESC
+LIMIT 1
+ "#,
+ )
+ .bind(dto.transaction_id)
+ .bind(dto.protocol_name.clone())
+ .bind(dto.event_kind.clone())
+ .bind(dto.signature.clone())
+ .fetch_one(pool)
+ .await;
+ match id_result {
+ Ok(id) => return Ok(id),
+ Err(error) => {
+ return Err(crate::Error::Db(format!(
+ "cannot fetch inserted k_sol_fee_events id for signature '{}' on sqlite: {}",
+ dto.signature, error
+ )));
+ },
+ }
+ },
+ }
+}
+
+/// Lists recent fee events ordered from newest to oldest.
+pub async fn query_fee_events_list_recent(
+ database: &crate::Database,
+ limit: u32,
+) -> Result, crate::Error> {
+ if limit == 0 {
+ return Ok(std::vec::Vec::new());
+ }
+ match database.connection() {
+ crate::DatabaseConnection::Sqlite(pool) => {
+ let query_result = sqlx::query_as::(
+ r#"
+SELECT
+ id,
+ transaction_id,
+ decoded_event_id,
+ dex_id,
+ pool_id,
+ pair_id,
+ signature,
+ slot,
+ protocol_name,
+ program_id,
+ event_kind,
+ pool_account,
+ actor_wallet,
+ fee_token_mint,
+ fee_amount_raw,
+
+ payload_json,
+ executed_at,
+ created_at
+FROM k_sol_fee_events
+ORDER BY id DESC
+LIMIT ?
+ "#,
+ )
+ .bind(i64::from(limit))
+ .fetch_all(pool)
+ .await;
+ let entities = match query_result {
+ Ok(entities) => entities,
+ Err(error) => {
+ return Err(crate::Error::Db(format!(
+ "cannot list k_sol_fee_events on sqlite: {}",
+ error
+ )));
+ },
+ };
+ let mut dtos = std::vec::Vec::with_capacity(entities.len());
+ for entity in entities {
+ let dto_result = crate::FeeEventDto::try_from(entity);
+ match dto_result {
+ Ok(dto) => dtos.push(dto),
+ Err(error) => return Err(error),
+ }
+ }
+ return Ok(dtos);
+ },
+ }
+}
diff --git a/kb_lib/src/db/queries/local_pipeline_diagnostics.rs b/kb_lib/src/db/queries/local_pipeline_diagnostics.rs
index e9d843e..2816b78 100644
--- a/kb_lib/src/db/queries/local_pipeline_diagnostics.rs
+++ b/kb_lib/src/db/queries/local_pipeline_diagnostics.rs
@@ -50,6 +50,9 @@ SELECT
) AS decoded_unknown_event_count,
(SELECT COUNT(*) FROM k_sol_liquidity_events) AS liquidity_event_count,
(SELECT COUNT(*) FROM k_sol_pool_lifecycle_events) AS pool_lifecycle_event_count,
+ (SELECT COUNT(*) FROM k_sol_fee_events) AS fee_event_count,
+ (SELECT COUNT(*) FROM k_sol_reward_events) AS reward_event_count,
+ (SELECT COUNT(*) FROM k_sol_pool_admin_events) AS pool_admin_event_count,
(
SELECT COUNT(*)
FROM k_sol_dex_decoded_events dde
@@ -361,6 +364,9 @@ SELECT
decoded_unknown_event_count: row.decoded_unknown_event_count,
liquidity_event_count: row.liquidity_event_count,
pool_lifecycle_event_count: row.pool_lifecycle_event_count,
+ fee_event_count: row.fee_event_count,
+ reward_event_count: row.reward_event_count,
+ pool_admin_event_count: row.pool_admin_event_count,
missing_trade_event_count: row.missing_trade_event_count,
decoded_trade_candidate_without_trade_event_count: row
.decoded_trade_candidate_without_trade_event_count,
@@ -772,7 +778,7 @@ ORDER BY
return Ok(summaries);
},
}
- }
+}
/// Lists local pair trading-readiness summaries.
pub async fn query_local_pair_trading_readiness_diagnostic_list_summaries(
diff --git a/kb_lib/src/db/queries/pool_admin_event.rs b/kb_lib/src/db/queries/pool_admin_event.rs
new file mode 100644
index 0000000..504ede4
--- /dev/null
+++ b/kb_lib/src/db/queries/pool_admin_event.rs
@@ -0,0 +1,293 @@
+// file: kb_lib/src/db/queries/pool_admin_event.rs
+
+//! Queries for `k_sol_pool_admin_events`.
+
+/// Returns one pool admin event by decoded event id.
+pub async fn query_pool_admin_events_get_by_decoded_event_id(
+ database: &crate::Database,
+ decoded_event_id: i64,
+) -> Result, crate::Error> {
+ match database.connection() {
+ crate::DatabaseConnection::Sqlite(pool) => {
+ let query_result = sqlx::query_as::(
+ r#"
+SELECT
+ id,
+ transaction_id,
+ decoded_event_id,
+ dex_id,
+ pool_id,
+ pair_id,
+ signature,
+ slot,
+ protocol_name,
+ program_id,
+ event_kind,
+ pool_account,
+ actor_wallet,
+ admin_action,
+ payload_json,
+ executed_at,
+ created_at
+FROM k_sol_pool_admin_events
+WHERE decoded_event_id = ?
+LIMIT 1
+ "#,
+ )
+ .bind(decoded_event_id)
+ .fetch_optional(pool)
+ .await;
+ let entity_option = match query_result {
+ Ok(entity_option) => entity_option,
+ Err(error) => {
+ return Err(crate::Error::Db(format!(
+ "cannot fetch k_sol_pool_admin_events by decoded_event_id '{}' on sqlite: {}",
+ decoded_event_id, error
+ )));
+ },
+ };
+ match entity_option {
+ Some(entity) => {
+ let dto_result = crate::PoolAdminEventDto::try_from(entity);
+ match dto_result {
+ Ok(dto) => return Ok(Some(dto)),
+ Err(error) => return Err(error),
+ }
+ },
+ None => return Ok(None),
+ }
+ },
+ }
+}
+
+/// Inserts or updates one normalized pool admin event row.
+pub async fn query_pool_admin_events_upsert(
+ database: &crate::Database,
+ dto: &crate::PoolAdminEventDto,
+) -> Result {
+ let slot_i64 = match dto.slot {
+ Some(slot) => {
+ let slot_result = i64::try_from(slot);
+ match slot_result {
+ Ok(slot) => Some(slot),
+ Err(error) => {
+ return Err(crate::Error::Db(format!(
+ "cannot convert pool admin event slot '{}' to i64: {}",
+ slot, error
+ )));
+ },
+ }
+ },
+ None => None,
+ };
+ match database.connection() {
+ crate::DatabaseConnection::Sqlite(pool) => {
+ let existing_id = match dto.decoded_event_id {
+ Some(decoded_event_id) => {
+ let existing_result = sqlx::query_scalar::(
+ r#"
+SELECT id
+FROM k_sol_pool_admin_events
+WHERE decoded_event_id = ?
+LIMIT 1
+ "#,
+ )
+ .bind(decoded_event_id)
+ .fetch_optional(pool)
+ .await;
+ match existing_result {
+ Ok(existing_id) => existing_id,
+ Err(error) => {
+ return Err(crate::Error::Db(format!(
+ "cannot fetch k_sol_pool_admin_events id for decoded_event_id '{}' on sqlite: {}",
+ decoded_event_id, error
+ )));
+ },
+ }
+ },
+ None => None,
+ };
+ if let Some(id) = existing_id {
+ let update_result = sqlx::query(
+ r#"
+UPDATE k_sol_pool_admin_events
+SET
+ transaction_id = ?,
+ dex_id = ?,
+ pool_id = ?,
+ pair_id = ?,
+ signature = ?,
+ slot = ?,
+ protocol_name = ?,
+ program_id = ?,
+ event_kind = ?,
+ pool_account = ?,
+ actor_wallet = ?,
+ admin_action = ?,
+ payload_json = ?,
+ executed_at = ?
+WHERE id = ?
+ "#,
+ )
+ .bind(dto.transaction_id)
+ .bind(dto.dex_id)
+ .bind(dto.pool_id)
+ .bind(dto.pair_id)
+ .bind(dto.signature.clone())
+ .bind(slot_i64)
+ .bind(dto.protocol_name.clone())
+ .bind(dto.program_id.clone())
+ .bind(dto.event_kind.clone())
+ .bind(dto.pool_account.clone())
+ .bind(dto.actor_wallet.clone())
+ .bind(dto.admin_action.clone())
+ .bind(dto.payload_json.clone())
+ .bind(dto.executed_at.to_rfc3339())
+ .bind(id)
+ .execute(pool)
+ .await;
+ if let Err(error) = update_result {
+ return Err(crate::Error::Db(format!(
+ "cannot update k_sol_pool_admin_events id '{}' on sqlite: {}",
+ id, error
+ )));
+ }
+ return Ok(id);
+ }
+ let insert_result = sqlx::query(
+ r#"
+INSERT INTO k_sol_pool_admin_events (
+ transaction_id,
+ decoded_event_id,
+ dex_id,
+ pool_id,
+ pair_id,
+ signature,
+ slot,
+ protocol_name,
+ program_id,
+ event_kind,
+ pool_account,
+ actor_wallet,
+ admin_action,
+ payload_json,
+ executed_at,
+ created_at
+)
+VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
+ "#,
+ )
+ .bind(dto.transaction_id)
+ .bind(dto.decoded_event_id)
+ .bind(dto.dex_id)
+ .bind(dto.pool_id)
+ .bind(dto.pair_id)
+ .bind(dto.signature.clone())
+ .bind(slot_i64)
+ .bind(dto.protocol_name.clone())
+ .bind(dto.program_id.clone())
+ .bind(dto.event_kind.clone())
+ .bind(dto.pool_account.clone())
+ .bind(dto.actor_wallet.clone())
+ .bind(dto.admin_action.clone())
+ .bind(dto.payload_json.clone())
+ .bind(dto.executed_at.to_rfc3339())
+ .bind(dto.created_at.to_rfc3339())
+ .execute(pool)
+ .await;
+ if let Err(error) = insert_result {
+ return Err(crate::Error::Db(format!(
+ "cannot insert k_sol_pool_admin_events on sqlite: {}",
+ error
+ )));
+ }
+ let id_result = sqlx::query_scalar::(
+ r#"
+SELECT id
+FROM k_sol_pool_admin_events
+WHERE transaction_id = ?
+ AND protocol_name = ?
+ AND event_kind = ?
+ AND signature = ?
+ORDER BY id DESC
+LIMIT 1
+ "#,
+ )
+ .bind(dto.transaction_id)
+ .bind(dto.protocol_name.clone())
+ .bind(dto.event_kind.clone())
+ .bind(dto.signature.clone())
+ .fetch_one(pool)
+ .await;
+ match id_result {
+ Ok(id) => return Ok(id),
+ Err(error) => {
+ return Err(crate::Error::Db(format!(
+ "cannot fetch inserted k_sol_pool_admin_events id for signature '{}' on sqlite: {}",
+ dto.signature, error
+ )));
+ },
+ }
+ },
+ }
+}
+
+/// Lists recent pool admin events ordered from newest to oldest.
+pub async fn query_pool_admin_events_list_recent(
+ database: &crate::Database,
+ limit: u32,
+) -> Result, crate::Error> {
+ if limit == 0 {
+ return Ok(std::vec::Vec::new());
+ }
+ match database.connection() {
+ crate::DatabaseConnection::Sqlite(pool) => {
+ let query_result = sqlx::query_as::(
+ r#"
+SELECT
+ id,
+ transaction_id,
+ decoded_event_id,
+ dex_id,
+ pool_id,
+ pair_id,
+ signature,
+ slot,
+ protocol_name,
+ program_id,
+ event_kind,
+ pool_account,
+ actor_wallet,
+ admin_action,
+ payload_json,
+ executed_at,
+ created_at
+FROM k_sol_pool_admin_events
+ORDER BY id DESC
+LIMIT ?
+ "#,
+ )
+ .bind(i64::from(limit))
+ .fetch_all(pool)
+ .await;
+ let entities = match query_result {
+ Ok(entities) => entities,
+ Err(error) => {
+ return Err(crate::Error::Db(format!(
+ "cannot list k_sol_pool_admin_events on sqlite: {}",
+ error
+ )));
+ },
+ };
+ let mut dtos = std::vec::Vec::with_capacity(entities.len());
+ for entity in entities {
+ let dto_result = crate::PoolAdminEventDto::try_from(entity);
+ match dto_result {
+ Ok(dto) => dtos.push(dto),
+ Err(error) => return Err(error),
+ }
+ }
+ return Ok(dtos);
+ },
+ }
+}
diff --git a/kb_lib/src/db/queries/reward_event.rs b/kb_lib/src/db/queries/reward_event.rs
new file mode 100644
index 0000000..344d5ec
--- /dev/null
+++ b/kb_lib/src/db/queries/reward_event.rs
@@ -0,0 +1,299 @@
+// file: kb_lib/src/db/queries/reward_event.rs
+
+//! Queries for `k_sol_reward_events`.
+
+/// Returns one reward event by decoded event id.
+pub async fn query_reward_events_get_by_decoded_event_id(
+ database: &crate::Database,
+ decoded_event_id: i64,
+) -> Result, crate::Error> {
+ match database.connection() {
+ crate::DatabaseConnection::Sqlite(pool) => {
+ let query_result = sqlx::query_as::(
+ r#"
+SELECT
+ id,
+ transaction_id,
+ decoded_event_id,
+ dex_id,
+ pool_id,
+ pair_id,
+ signature,
+ slot,
+ protocol_name,
+ program_id,
+ event_kind,
+ pool_account,
+ actor_wallet,
+ reward_token_mint,
+ reward_amount_raw,
+ payload_json,
+ executed_at,
+ created_at
+FROM k_sol_reward_events
+WHERE decoded_event_id = ?
+LIMIT 1
+ "#,
+ )
+ .bind(decoded_event_id)
+ .fetch_optional(pool)
+ .await;
+ let entity_option = match query_result {
+ Ok(entity_option) => entity_option,
+ Err(error) => {
+ return Err(crate::Error::Db(format!(
+ "cannot fetch k_sol_reward_events by decoded_event_id '{}' on sqlite: {}",
+ decoded_event_id, error
+ )));
+ },
+ };
+ match entity_option {
+ Some(entity) => {
+ let dto_result = crate::RewardEventDto::try_from(entity);
+ match dto_result {
+ Ok(dto) => return Ok(Some(dto)),
+ Err(error) => return Err(error),
+ }
+ },
+ None => return Ok(None),
+ }
+ },
+ }
+}
+
+/// Inserts or updates one normalized reward event row.
+pub async fn query_reward_events_upsert(
+ database: &crate::Database,
+ dto: &crate::RewardEventDto,
+) -> Result {
+ let slot_i64 = match dto.slot {
+ Some(slot) => {
+ let slot_result = i64::try_from(slot);
+ match slot_result {
+ Ok(slot) => Some(slot),
+ Err(error) => {
+ return Err(crate::Error::Db(format!(
+ "cannot convert reward event slot '{}' to i64: {}",
+ slot, error
+ )));
+ },
+ }
+ },
+ None => None,
+ };
+ match database.connection() {
+ crate::DatabaseConnection::Sqlite(pool) => {
+ let existing_id = match dto.decoded_event_id {
+ Some(decoded_event_id) => {
+ let existing_result = sqlx::query_scalar::(
+ r#"
+SELECT id
+FROM k_sol_reward_events
+WHERE decoded_event_id = ?
+LIMIT 1
+ "#,
+ )
+ .bind(decoded_event_id)
+ .fetch_optional(pool)
+ .await;
+ match existing_result {
+ Ok(existing_id) => existing_id,
+ Err(error) => {
+ return Err(crate::Error::Db(format!(
+ "cannot fetch k_sol_reward_events id for decoded_event_id '{}' on sqlite: {}",
+ decoded_event_id, error
+ )));
+ },
+ }
+ },
+ None => None,
+ };
+ if let Some(id) = existing_id {
+ let update_result = sqlx::query(
+ r#"
+UPDATE k_sol_reward_events
+SET
+ transaction_id = ?,
+ dex_id = ?,
+ pool_id = ?,
+ pair_id = ?,
+ signature = ?,
+ slot = ?,
+ protocol_name = ?,
+ program_id = ?,
+ event_kind = ?,
+ pool_account = ?,
+ actor_wallet = ?,
+ reward_token_mint = ?,
+ reward_amount_raw = ?,
+ payload_json = ?,
+ executed_at = ?
+WHERE id = ?
+ "#,
+ )
+ .bind(dto.transaction_id)
+ .bind(dto.dex_id)
+ .bind(dto.pool_id)
+ .bind(dto.pair_id)
+ .bind(dto.signature.clone())
+ .bind(slot_i64)
+ .bind(dto.protocol_name.clone())
+ .bind(dto.program_id.clone())
+ .bind(dto.event_kind.clone())
+ .bind(dto.pool_account.clone())
+ .bind(dto.actor_wallet.clone())
+ .bind(dto.reward_token_mint.clone())
+ .bind(dto.reward_amount_raw.clone())
+ .bind(dto.payload_json.clone())
+ .bind(dto.executed_at.to_rfc3339())
+ .bind(id)
+ .execute(pool)
+ .await;
+ if let Err(error) = update_result {
+ return Err(crate::Error::Db(format!(
+ "cannot update k_sol_reward_events id '{}' on sqlite: {}",
+ id, error
+ )));
+ }
+ return Ok(id);
+ }
+ let insert_result = sqlx::query(
+ r#"
+INSERT INTO k_sol_reward_events (
+ transaction_id,
+ decoded_event_id,
+ dex_id,
+ pool_id,
+ pair_id,
+ signature,
+ slot,
+ protocol_name,
+ program_id,
+ event_kind,
+ pool_account,
+ actor_wallet,
+ reward_token_mint,
+ reward_amount_raw,
+ payload_json,
+ executed_at,
+ created_at
+)
+VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
+ "#,
+ )
+ .bind(dto.transaction_id)
+ .bind(dto.decoded_event_id)
+ .bind(dto.dex_id)
+ .bind(dto.pool_id)
+ .bind(dto.pair_id)
+ .bind(dto.signature.clone())
+ .bind(slot_i64)
+ .bind(dto.protocol_name.clone())
+ .bind(dto.program_id.clone())
+ .bind(dto.event_kind.clone())
+ .bind(dto.pool_account.clone())
+ .bind(dto.actor_wallet.clone())
+ .bind(dto.reward_token_mint.clone())
+ .bind(dto.reward_amount_raw.clone())
+ .bind(dto.payload_json.clone())
+ .bind(dto.executed_at.to_rfc3339())
+ .bind(dto.created_at.to_rfc3339())
+ .execute(pool)
+ .await;
+ if let Err(error) = insert_result {
+ return Err(crate::Error::Db(format!(
+ "cannot insert k_sol_reward_events on sqlite: {}",
+ error
+ )));
+ }
+ let id_result = sqlx::query_scalar::(
+ r#"
+SELECT id
+FROM k_sol_reward_events
+WHERE transaction_id = ?
+ AND protocol_name = ?
+ AND event_kind = ?
+ AND signature = ?
+ORDER BY id DESC
+LIMIT 1
+ "#,
+ )
+ .bind(dto.transaction_id)
+ .bind(dto.protocol_name.clone())
+ .bind(dto.event_kind.clone())
+ .bind(dto.signature.clone())
+ .fetch_one(pool)
+ .await;
+ match id_result {
+ Ok(id) => return Ok(id),
+ Err(error) => {
+ return Err(crate::Error::Db(format!(
+ "cannot fetch inserted k_sol_reward_events id for signature '{}' on sqlite: {}",
+ dto.signature, error
+ )));
+ },
+ }
+ },
+ }
+}
+
+/// Lists recent reward events ordered from newest to oldest.
+pub async fn query_reward_events_list_recent(
+ database: &crate::Database,
+ limit: u32,
+) -> Result, crate::Error> {
+ if limit == 0 {
+ return Ok(std::vec::Vec::new());
+ }
+ match database.connection() {
+ crate::DatabaseConnection::Sqlite(pool) => {
+ let query_result = sqlx::query_as::(
+ r#"
+SELECT
+ id,
+ transaction_id,
+ decoded_event_id,
+ dex_id,
+ pool_id,
+ pair_id,
+ signature,
+ slot,
+ protocol_name,
+ program_id,
+ event_kind,
+ pool_account,
+ actor_wallet,
+ reward_token_mint,
+ reward_amount_raw,
+ payload_json,
+ executed_at,
+ created_at
+FROM k_sol_reward_events
+ORDER BY id DESC
+LIMIT ?
+ "#,
+ )
+ .bind(i64::from(limit))
+ .fetch_all(pool)
+ .await;
+ let entities = match query_result {
+ Ok(entities) => entities,
+ Err(error) => {
+ return Err(crate::Error::Db(format!(
+ "cannot list k_sol_reward_events on sqlite: {}",
+ error
+ )));
+ },
+ };
+ let mut dtos = std::vec::Vec::with_capacity(entities.len());
+ for entity in entities {
+ let dto_result = crate::RewardEventDto::try_from(entity);
+ match dto_result {
+ Ok(dto) => dtos.push(dto),
+ Err(error) => return Err(error),
+ }
+ }
+ return Ok(dtos);
+ },
+ }
+}
diff --git a/kb_lib/src/lib.rs b/kb_lib/src/lib.rs
index 7e6a08c..8c442d6 100644
--- a/kb_lib/src/lib.rs
+++ b/kb_lib/src/lib.rs
@@ -317,6 +317,10 @@ pub use db::DexDecodedEventEntity;
pub use db::DexDto;
/// Persisted normalized DEX row.
pub use db::DexEntity;
+/// Normalized fee event persisted from useful non-trade DEX events.
+pub use db::FeeEventDto;
+/// Persisted fee event row.
+pub use db::FeeEventEntity;
/// Application-facing known HTTP endpoint DTO.
pub use db::KnownHttpEndpointDto;
/// Application-facing known WebSocket endpoint DTO.
@@ -399,6 +403,10 @@ pub use db::PairEntity;
pub use db::PairMetricDto;
/// Persisted pair-metric row.
pub use db::PairMetricEntity;
+/// Normalized pool administration event persisted from useful non-trade DEX events.
+pub use db::PoolAdminEventDto;
+/// Persisted pool administration event row.
+pub use db::PoolAdminEventEntity;
/// Application-facing normalized pool DTO.
pub use db::PoolDto;
/// Persisted normalized pool row.
@@ -445,6 +453,10 @@ pub use db::ProtocolCandidateEntity;
pub use db::ProtocolCandidateSummaryDto;
/// Aggregated protocol candidate diagnostic row.
pub use db::ProtocolCandidateSummaryEntity;
+/// Normalized reward event persisted from useful non-trade DEX events.
+pub use db::RewardEventDto;
+/// Persisted reward event row.
+pub use db::RewardEventEntity;
/// Application-facing normalized swap DTO.
pub use db::SwapDto;
/// Persisted normalized swap row.
@@ -533,6 +545,12 @@ pub use db::query_dexs_get_by_code;
pub use db::query_dexs_list;
/// Inserts or updates one normalized DEX row by code.
pub use db::query_dexs_upsert;
+/// Returns one fee event by decoded-event id.
+pub use db::query_fee_events_get_by_decoded_event_id;
+/// Lists recent fee events ordered from newest to oldest.
+pub use db::query_fee_events_list_recent;
+/// Inserts or updates one normalized fee event row.
+pub use db::query_fee_events_upsert;
/// Reads one known HTTP endpoint by name.
pub use db::query_known_http_endpoints_get;
/// Lists all known HTTP endpoints.
@@ -631,6 +649,12 @@ pub use db::query_pairs_list;
pub use db::query_pairs_update_symbol;
/// Inserts or updates one normalized pair row by pool id.
pub use db::query_pairs_upsert;
+/// Returns one pool administration event by decoded-event id.
+pub use db::query_pool_admin_events_get_by_decoded_event_id;
+/// Lists recent pool administration events ordered from newest to oldest.
+pub use db::query_pool_admin_events_list_recent;
+/// Inserts or updates one normalized pool administration event row.
+pub use db::query_pool_admin_events_upsert;
/// Returns one pool lifecycle event by decoded event id.
pub use db::query_pool_lifecycle_events_get_by_decoded_event_id;
/// Lists recent pool lifecycle events ordered from newest to oldest.
@@ -677,6 +701,12 @@ pub use db::query_protocol_candidates_list_by_program_id;
pub use db::query_protocol_candidates_list_by_transaction_id;
/// Lists recent protocol candidates ordered from newest to oldest.
pub use db::query_protocol_candidates_list_recent;
+/// Returns one reward event by decoded-event id.
+pub use db::query_reward_events_get_by_decoded_event_id;
+/// Lists recent reward events ordered from newest to oldest.
+pub use db::query_reward_events_list_recent;
+/// Inserts or updates one normalized reward event row.
+pub use db::query_reward_events_upsert;
/// Lists recent swaps ordered from newest to oldest.
pub use db::query_swaps_list_recent;
/// Inserts or updates one normalized swap row.
diff --git a/kb_lib/src/local_pipeline_diagnostics.rs b/kb_lib/src/local_pipeline_diagnostics.rs
index dbb429e..b42ecbe 100644
--- a/kb_lib/src/local_pipeline_diagnostics.rs
+++ b/kb_lib/src/local_pipeline_diagnostics.rs
@@ -154,6 +154,9 @@ impl LocalPipelineDiagnosticsService {
decoded_unknown_event_count: counters.decoded_unknown_event_count,
liquidity_event_count: counters.liquidity_event_count,
pool_lifecycle_event_count: counters.pool_lifecycle_event_count,
+ fee_event_count: counters.fee_event_count,
+ reward_event_count: counters.reward_event_count,
+ pool_admin_event_count: counters.pool_admin_event_count,
diagnostics_clean,
blocking_issue_count,
missing_trade_event_count: counters.missing_trade_event_count,
diff --git a/kb_lib/src/local_pipeline_replay.rs b/kb_lib/src/local_pipeline_replay.rs
index 1179798..32ecd68 100644
--- a/kb_lib/src/local_pipeline_replay.rs
+++ b/kb_lib/src/local_pipeline_replay.rs
@@ -61,6 +61,12 @@ pub struct LocalPipelineReplayResult {
pub liquidity_event_count: usize,
/// Total pool lifecycle event materialization results returned by replayed non-trade calls.
pub pool_lifecycle_event_count: usize,
+ /// Total fee event materialization results returned by replayed non-trade calls.
+ pub fee_event_count: usize,
+ /// Total reward event materialization results returned by replayed non-trade calls.
+ pub reward_event_count: usize,
+ /// Total pool administration event materialization results returned by replayed non-trade calls.
+ pub pool_admin_event_count: usize,
/// Total candle upsert results returned by replayed candle calls.
///
/// This is a replay write/result counter, not the number of distinct rows
@@ -203,6 +209,9 @@ impl LocalPipelineReplayService {
result.liquidity_event_count += non_trade_result.liquidity_event_count;
result.pool_lifecycle_event_count +=
non_trade_result.pool_lifecycle_event_count;
+ result.fee_event_count += non_trade_result.fee_event_count;
+ result.reward_event_count += non_trade_result.reward_event_count;
+ result.pool_admin_event_count += non_trade_result.pool_admin_event_count;
},
Err(error) => {
result.non_trade_materialization_error_count += 1;
diff --git a/kb_lib/src/local_pipeline_validation.rs b/kb_lib/src/local_pipeline_validation.rs
index 7d98498..80b6860 100644
--- a/kb_lib/src/local_pipeline_validation.rs
+++ b/kb_lib/src/local_pipeline_validation.rs
@@ -224,6 +224,17 @@ impl LocalPipelineValidationConfig {
config.allow_unexpected_dexes = true;
return config;
}
+
+ /// Builds the `0.7.35` non-trade fee/reward/admin validation config.
+ ///
+ /// This profile keeps the `0.7.34` checks and exposes fee, reward and
+ /// administration materialization counters without changing trade/candle
+ /// validation semantics.
+ pub fn v0_7_35_non_trade_fee_reward_admin() -> Self {
+ let mut config = Self::v0_7_34_non_trade_liquidity_lifecycle();
+ config.profile_code = "0.7.35_non_trade_fee_reward_admin".to_string();
+ return config;
+ }
}
/// A single local pipeline validation issue.
@@ -266,6 +277,12 @@ pub struct LocalPipelineValidationReportDto {
pub liquidity_event_count: i64,
/// Total persisted pool lifecycle events.
pub pool_lifecycle_event_count: i64,
+ /// Total persisted fee events.
+ pub fee_event_count: i64,
+ /// Total persisted reward events.
+ pub reward_event_count: i64,
+ /// Total persisted pool administration events.
+ pub pool_admin_event_count: i64,
/// Number of entries currently exposed by the DEX support matrix.
pub dex_support_matrix_entry_count: i64,
/// DEX support matrix snapshot exposed with the validation report.
@@ -396,6 +413,14 @@ impl LocalPipelineValidationService {
let config = crate::LocalPipelineValidationConfig::v0_7_34_non_trade_liquidity_lifecycle();
return self.validate_current_database(&config).await;
}
+
+ /// Diagnoses the current database with the `0.7.35` non-trade fee/reward/admin profile.
+ pub async fn validate_v0_7_35_current_database(
+ &self,
+ ) -> Result {
+ let config = crate::LocalPipelineValidationConfig::v0_7_35_non_trade_fee_reward_admin();
+ return self.validate_current_database(&config).await;
+ }
}
/// Validates a diagnostics summary without performing database access.
@@ -513,8 +538,9 @@ pub fn validate_local_pipeline_diagnostics_summary(
if config.require_pair_trading_readiness_semantics {
validate_pair_trading_readiness_semantics(&mut issues, summary);
}
- let missing_expected_dex_is_warning =
- config.profile_code == "0.7.34_non_trade_liquidity_lifecycle";
+ let missing_expected_dex_is_warning = config.profile_code
+ == "0.7.34_non_trade_liquidity_lifecycle"
+ || config.profile_code == "0.7.35_non_trade_fee_reward_admin";
if config.require_all_expected_dexes || missing_expected_dex_is_warning {
for expected_dex_code in &expected_dex_codes {
if !observed_dex_codes.contains(expected_dex_code) {
@@ -586,6 +612,9 @@ pub fn validate_local_pipeline_diagnostics_summary(
decoded_unknown_event_count: summary.decoded_unknown_event_count,
liquidity_event_count: summary.liquidity_event_count,
pool_lifecycle_event_count: summary.pool_lifecycle_event_count,
+ fee_event_count: summary.fee_event_count,
+ reward_event_count: summary.reward_event_count,
+ pool_admin_event_count: summary.pool_admin_event_count,
dex_support_matrix_entry_count: crate::dex_support_matrix_entries().len() as i64,
dex_support_matrix: crate::dex_support_matrix_entry_dtos(),
issues,
@@ -796,6 +825,9 @@ mod tests {
decoded_unknown_event_count: 0,
liquidity_event_count: 0,
pool_lifecycle_event_count: 0,
+ fee_event_count: 0,
+ reward_event_count: 0,
+ pool_admin_event_count: 0,
diagnostics_clean: true,
blocking_issue_count: 0,
missing_trade_event_count: 6,
@@ -1125,6 +1157,24 @@ mod tests {
assert!(!report.issues[0].blocking);
}
+ #[test]
+ fn validation_accepts_0_7_35_non_trade_fee_reward_admin_summary() {
+ let mut summary = make_0_7_28_summary_with_meteora();
+ summary.decoded_non_trade_useful_event_count = 5;
+ summary.liquidity_event_count = 2;
+ summary.pool_lifecycle_event_count = 1;
+ summary.fee_event_count = 1;
+ summary.reward_event_count = 1;
+ summary.pool_admin_event_count = 1;
+ let config = crate::LocalPipelineValidationConfig::v0_7_35_non_trade_fee_reward_admin();
+ let report = crate::validate_local_pipeline_diagnostics_summary(&summary, &config);
+ assert!(report.validation_passed);
+ assert_eq!(report.validation_profile_code, "0.7.35_non_trade_fee_reward_admin");
+ assert_eq!(report.fee_event_count, 1);
+ assert_eq!(report.reward_event_count, 1);
+ assert_eq!(report.pool_admin_event_count, 1);
+ }
+
#[test]
fn validation_rejects_0_7_33_pair_trading_readiness_mismatch() {
let mut summary = make_0_7_28_summary_with_meteora();
diff --git a/kb_lib/src/non_trade_event_materialization.rs b/kb_lib/src/non_trade_event_materialization.rs
index e59021a..761d151 100644
--- a/kb_lib/src/non_trade_event_materialization.rs
+++ b/kb_lib/src/non_trade_event_materialization.rs
@@ -2,9 +2,9 @@
//! Materialization of useful non-trade DEX events.
//!
-//! This service persists liquidity and pool lifecycle events from already
-//! decoded DEX events. It deliberately does not feed trade, metric or candle
-//! materialization.
+//! This service persists liquidity, pool lifecycle, fee, reward and pool
+//! administration events from already decoded DEX events. It deliberately does
+//! not feed trade, metric or candle materialization.
/// Result of non-trade event materialization for one transaction.
#[derive(Debug, Clone, Default, serde::Serialize, serde::Deserialize)]
@@ -13,6 +13,12 @@ pub struct NonTradeEventMaterializationResult {
pub liquidity_event_count: usize,
/// Number of pool lifecycle events inserted or refreshed.
pub pool_lifecycle_event_count: usize,
+ /// Number of fee events inserted or refreshed.
+ pub fee_event_count: usize,
+ /// Number of reward events inserted or refreshed.
+ pub reward_event_count: usize,
+ /// Number of pool administration events inserted or refreshed.
+ pub pool_admin_event_count: usize,
}
/// Materializes useful non-trade decoded DEX events.
@@ -127,6 +133,50 @@ impl NonTradeEventMaterializationService {
Err(error) => return Err(error),
}
}
+ if crate::is_dex_fee_event_kind(decoded_event.event_kind.as_str()) {
+ let materialized = self
+ .materialize_fee_event(&transaction, transaction_id, decoded_event, &payload)
+ .await;
+ match materialized {
+ Ok(was_materialized) => {
+ if was_materialized {
+ result.fee_event_count += 1;
+ }
+ },
+ Err(error) => return Err(error),
+ }
+ }
+ if crate::is_dex_reward_event_kind(decoded_event.event_kind.as_str()) {
+ let materialized = self
+ .materialize_reward_event(&transaction, transaction_id, decoded_event, &payload)
+ .await;
+ match materialized {
+ Ok(was_materialized) => {
+ if was_materialized {
+ result.reward_event_count += 1;
+ }
+ },
+ Err(error) => return Err(error),
+ }
+ }
+ if crate::is_dex_admin_event_kind(decoded_event.event_kind.as_str()) {
+ let materialized = self
+ .materialize_pool_admin_event(
+ &transaction,
+ transaction_id,
+ decoded_event,
+ &payload,
+ )
+ .await;
+ match materialized {
+ Ok(was_materialized) => {
+ if was_materialized {
+ result.pool_admin_event_count += 1;
+ }
+ },
+ Err(error) => return Err(error),
+ }
+ }
}
return Ok(result);
}
@@ -170,6 +220,207 @@ impl NonTradeEventMaterializationService {
}
}
+ async fn materialize_fee_event(
+ &self,
+ transaction: &crate::ChainTransactionDto,
+ transaction_id: i64,
+ decoded_event: &crate::DexDecodedEventDto,
+ payload: &serde_json::Value,
+ ) -> Result {
+ let decoded_event_id = match decoded_event.id {
+ Some(decoded_event_id) => decoded_event_id,
+ None => return Ok(false),
+ };
+ let context = self.resolve_decoded_event_context(decoded_event).await;
+ let context = match context {
+ Ok(context) => context,
+ Err(error) => return Err(error),
+ };
+ let actor_wallet = extract_first_string(
+ payload,
+ &[
+ "actorWallet",
+ "actor_wallet",
+ "receiver",
+ "recipient",
+ "owner",
+ "payer",
+ "authority",
+ "user",
+ ],
+ );
+ let fee_token_mint = extract_first_string(
+ payload,
+ &[
+ "feeTokenMint",
+ "fee_token_mint",
+ "tokenMint",
+ "token_mint",
+ "mint",
+ "quoteMint",
+ "quote_mint",
+ ],
+ );
+ let fee_amount_raw = extract_first_amount_string(
+ payload,
+ &[
+ "feeAmountRaw",
+ "fee_amount_raw",
+ "feeAmount",
+ "fee_amount",
+ "protocolFeeAmount",
+ "protocol_fee_amount",
+ "fundFeeAmount",
+ "fund_fee_amount",
+ "creatorFeeAmount",
+ "creator_fee_amount",
+ "amount",
+ ],
+ );
+ let dto = crate::FeeEventDto::new(
+ transaction_id,
+ Some(decoded_event_id),
+ context.dex_id,
+ context.pool_id,
+ context.pair_id,
+ transaction.signature.clone(),
+ transaction.slot,
+ decoded_event.protocol_name.clone(),
+ decoded_event.program_id.clone(),
+ decoded_event.event_kind.clone(),
+ decoded_event.pool_account.clone(),
+ actor_wallet,
+ fee_token_mint,
+ fee_amount_raw,
+ decoded_event.payload_json.clone(),
+ );
+ let upsert_result = crate::query_fee_events_upsert(self.database.as_ref(), &dto).await;
+ match upsert_result {
+ Ok(_) => return Ok(true),
+ Err(error) => return Err(error),
+ }
+ }
+
+ async fn materialize_reward_event(
+ &self,
+ transaction: &crate::ChainTransactionDto,
+ transaction_id: i64,
+ decoded_event: &crate::DexDecodedEventDto,
+ payload: &serde_json::Value,
+ ) -> Result {
+ let decoded_event_id = match decoded_event.id {
+ Some(decoded_event_id) => decoded_event_id,
+ None => return Ok(false),
+ };
+ let context = self.resolve_decoded_event_context(decoded_event).await;
+ let context = match context {
+ Ok(context) => context,
+ Err(error) => return Err(error),
+ };
+ let actor_wallet = extract_first_string(
+ payload,
+ &[
+ "actorWallet",
+ "actor_wallet",
+ "receiver",
+ "recipient",
+ "owner",
+ "payer",
+ "authority",
+ "user",
+ ],
+ );
+ let reward_token_mint = extract_first_string(
+ payload,
+ &["rewardTokenMint", "reward_token_mint", "tokenMint", "token_mint", "mint"],
+ );
+ let reward_amount_raw = extract_first_amount_string(
+ payload,
+ &[
+ "rewardAmountRaw",
+ "reward_amount_raw",
+ "rewardAmount",
+ "reward_amount",
+ "emissionAmount",
+ "emission_amount",
+ "amount",
+ ],
+ );
+ let dto = crate::RewardEventDto::new(
+ transaction_id,
+ Some(decoded_event_id),
+ context.dex_id,
+ context.pool_id,
+ context.pair_id,
+ transaction.signature.clone(),
+ transaction.slot,
+ decoded_event.protocol_name.clone(),
+ decoded_event.program_id.clone(),
+ decoded_event.event_kind.clone(),
+ decoded_event.pool_account.clone(),
+ actor_wallet,
+ reward_token_mint,
+ reward_amount_raw,
+ decoded_event.payload_json.clone(),
+ );
+ let upsert_result = crate::query_reward_events_upsert(self.database.as_ref(), &dto).await;
+ match upsert_result {
+ Ok(_) => return Ok(true),
+ Err(error) => return Err(error),
+ }
+ }
+
+ async fn materialize_pool_admin_event(
+ &self,
+ transaction: &crate::ChainTransactionDto,
+ transaction_id: i64,
+ decoded_event: &crate::DexDecodedEventDto,
+ payload: &serde_json::Value,
+ ) -> Result {
+ let decoded_event_id = match decoded_event.id {
+ Some(decoded_event_id) => decoded_event_id,
+ None => return Ok(false),
+ };
+ let context = self.resolve_decoded_event_context(decoded_event).await;
+ let context = match context {
+ Ok(context) => context,
+ Err(error) => return Err(error),
+ };
+ let actor_wallet = extract_first_string(
+ payload,
+ &["actorWallet", "actor_wallet", "authority", "admin", "owner", "payer", "user"],
+ );
+ let admin_action = match extract_first_string(
+ payload,
+ &["adminAction", "admin_action", "action", "configAction", "config_action"],
+ ) {
+ Some(admin_action) => Some(admin_action),
+ None => Some(decoded_event.event_kind.clone()),
+ };
+ let dto = crate::PoolAdminEventDto::new(
+ transaction_id,
+ Some(decoded_event_id),
+ context.dex_id,
+ context.pool_id,
+ context.pair_id,
+ transaction.signature.clone(),
+ transaction.slot,
+ decoded_event.protocol_name.clone(),
+ decoded_event.program_id.clone(),
+ decoded_event.event_kind.clone(),
+ decoded_event.pool_account.clone(),
+ actor_wallet,
+ admin_action,
+ decoded_event.payload_json.clone(),
+ );
+ let upsert_result =
+ crate::query_pool_admin_events_upsert(self.database.as_ref(), &dto).await;
+ match upsert_result {
+ Ok(_) => return Ok(true),
+ Err(error) => return Err(error),
+ }
+ }
+
async fn materialize_liquidity_event(
&self,
transaction: &crate::ChainTransactionDto,
@@ -202,15 +453,16 @@ impl NonTradeEventMaterializationService {
Some(pair_id) => Some(pair_id),
None => None,
};
- let event_kind = if crate::is_dex_position_open_event_kind(decoded_event.event_kind.as_str()) {
- crate::LiquidityEventKind::PositionOpen
- } else if crate::is_dex_position_close_event_kind(decoded_event.event_kind.as_str()) {
- crate::LiquidityEventKind::PositionClose
- } else if crate::is_dex_liquidity_remove_event_kind(decoded_event.event_kind.as_str()) {
- crate::LiquidityEventKind::Remove
- } else {
- crate::LiquidityEventKind::Add
- };
+ let event_kind =
+ if crate::is_dex_position_open_event_kind(decoded_event.event_kind.as_str()) {
+ crate::LiquidityEventKind::PositionOpen
+ } else if crate::is_dex_position_close_event_kind(decoded_event.event_kind.as_str()) {
+ crate::LiquidityEventKind::PositionClose
+ } else if crate::is_dex_liquidity_remove_event_kind(decoded_event.event_kind.as_str()) {
+ crate::LiquidityEventKind::Remove
+ } else {
+ crate::LiquidityEventKind::Add
+ };
let actor_wallet = extract_first_string(
payload,
&[
diff --git a/kb_lib/src/token_backfill.rs b/kb_lib/src/token_backfill.rs
index 8c73f51..f7a771a 100644
--- a/kb_lib/src/token_backfill.rs
+++ b/kb_lib/src/token_backfill.rs
@@ -35,6 +35,12 @@ pub struct TokenBackfillResult {
pub liquidity_event_count: usize,
/// Total number of pool lifecycle event materialization results produced during this run.
pub pool_lifecycle_event_count: usize,
+ /// Total number of fee event materialization results produced during this run.
+ pub fee_event_count: usize,
+ /// Total number of reward event materialization results produced during this run.
+ pub reward_event_count: usize,
+ /// Total number of pool administration event materialization results produced during this run.
+ pub pool_admin_event_count: usize,
/// Total number of pair-candle aggregation results produced during this run.
pub pair_candle_count: usize,
}
@@ -68,6 +74,12 @@ pub struct PoolBackfillResult {
pub liquidity_event_count: usize,
/// Total number of pool lifecycle event materialization results produced during this run.
pub pool_lifecycle_event_count: usize,
+ /// Total number of fee event materialization results produced during this run.
+ pub fee_event_count: usize,
+ /// Total number of reward event materialization results produced during this run.
+ pub reward_event_count: usize,
+ /// Total number of pool administration event materialization results produced during this run.
+ pub pool_admin_event_count: usize,
/// Total number of pair-candle aggregation results produced during this run.
pub pair_candle_count: usize,
}
@@ -163,6 +175,9 @@ impl TokenBackfillService {
trade_event_count: 0,
liquidity_event_count: 0,
pool_lifecycle_event_count: 0,
+ fee_event_count: 0,
+ reward_event_count: 0,
+ pool_admin_event_count: 0,
pair_candle_count: 0,
};
let mut seen_signatures = std::collections::HashSet::::new();
@@ -237,6 +252,9 @@ impl TokenBackfillService {
"tradeEventCount": result.trade_event_count,
"liquidityEventCount": result.liquidity_event_count,
"poolLifecycleEventCount": result.pool_lifecycle_event_count,
+ "feeEventCount": result.fee_event_count,
+ "rewardEventCount": result.reward_event_count,
+ "poolAdminEventCount": result.pool_admin_event_count,
"pairCandleCount": result.pair_candle_count
});
let observation_result = self
@@ -377,6 +395,9 @@ impl TokenBackfillService {
trade_event_count: 0,
liquidity_event_count: 0,
pool_lifecycle_event_count: 0,
+ fee_event_count: 0,
+ reward_event_count: 0,
+ pool_admin_event_count: 0,
pair_candle_count: 0,
});
}
@@ -484,6 +505,9 @@ impl TokenBackfillService {
trade_event_count: trade_aggregations.len(),
liquidity_event_count: non_trade_materialization.liquidity_event_count,
pool_lifecycle_event_count: non_trade_materialization.pool_lifecycle_event_count,
+ fee_event_count: non_trade_materialization.fee_event_count,
+ reward_event_count: non_trade_materialization.reward_event_count,
+ pool_admin_event_count: non_trade_materialization.pool_admin_event_count,
pair_candle_count: pair_candle_aggregations.len(),
});
}
@@ -509,6 +533,9 @@ impl TokenBackfillService {
trade_event_count: 0,
liquidity_event_count: 0,
pool_lifecycle_event_count: 0,
+ fee_event_count: 0,
+ reward_event_count: 0,
+ pool_admin_event_count: 0,
pair_candle_count: 0,
};
let mut seen_addresses = std::collections::BTreeSet::::new();
@@ -594,6 +621,9 @@ impl TokenBackfillService {
result.trade_event_count += replay_result.trade_event_count;
result.liquidity_event_count += replay_result.liquidity_event_count;
result.pool_lifecycle_event_count += replay_result.pool_lifecycle_event_count;
+ result.fee_event_count += replay_result.fee_event_count;
+ result.reward_event_count += replay_result.reward_event_count;
+ result.pool_admin_event_count += replay_result.pool_admin_event_count;
result.pair_candle_count += replay_result.pair_candle_count;
}
}
@@ -612,6 +642,9 @@ impl TokenBackfillService {
"tradeEventCount": result.trade_event_count,
"liquidityEventCount": result.liquidity_event_count,
"poolLifecycleEventCount": result.pool_lifecycle_event_count,
+ "feeEventCount": result.fee_event_count,
+ "rewardEventCount": result.reward_event_count,
+ "poolAdminEventCount": result.pool_admin_event_count,
"pairCandleCount": result.pair_candle_count,
"scannedAddressCount": addresses_to_scan.len(),
"effectiveSignatureLimit": effective_limit
@@ -687,6 +720,9 @@ struct TokenBackfillSignatureResult {
trade_event_count: usize,
liquidity_event_count: usize,
pool_lifecycle_event_count: usize,
+ fee_event_count: usize,
+ reward_event_count: usize,
+ pool_admin_event_count: usize,
pair_candle_count: usize,
}
@@ -704,6 +740,9 @@ fn merge_token_backfill_signature_result(
aggregate.trade_event_count += value.trade_event_count;
aggregate.liquidity_event_count += value.liquidity_event_count;
aggregate.pool_lifecycle_event_count += value.pool_lifecycle_event_count;
+ aggregate.fee_event_count += value.fee_event_count;
+ aggregate.reward_event_count += value.reward_event_count;
+ aggregate.pool_admin_event_count += value.pool_admin_event_count;
aggregate.pair_candle_count += value.pair_candle_count;
}
diff --git a/kb_lib/src/tx_resolution.rs b/kb_lib/src/tx_resolution.rs
index 1b2c555..a20df9c 100644
--- a/kb_lib/src/tx_resolution.rs
+++ b/kb_lib/src/tx_resolution.rs
@@ -391,6 +391,9 @@ impl TransactionResolutionService {
};
let liquidity_event_count = non_trade_materialization.liquidity_event_count;
let pool_lifecycle_event_count = non_trade_materialization.pool_lifecycle_event_count;
+ let fee_event_count = non_trade_materialization.fee_event_count;
+ let reward_event_count = non_trade_materialization.reward_event_count;
+ let pool_admin_event_count = non_trade_materialization.pool_admin_event_count;
let trade_aggregations_result = self
.trade_aggregation_service
.record_transaction_by_signature(request.signature.as_str())
@@ -445,6 +448,9 @@ impl TransactionResolutionService {
"walletHoldingCount": wallet_holding_count,
"liquidityEventCount": liquidity_event_count,
"poolLifecycleEventCount": pool_lifecycle_event_count,
+ "feeEventCount": fee_event_count,
+ "rewardEventCount": reward_event_count,
+ "poolAdminEventCount": pool_admin_event_count,
"tradeEventCount": trade_event_count,
"pairCandleCount": pair_candle_count,
"pairAnalyticSignalCount": pair_analytic_signal_count,