0.7.35
This commit is contained in:
@@ -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.
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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`,
|
||||
|
||||
@@ -166,7 +166,8 @@
|
||||
<div class="mb-3">
|
||||
<label for="demoPipeline2ValidationProfileSelect" class="form-label">Validation profile</label>
|
||||
<select id="demoPipeline2ValidationProfileSelect" class="form-select">
|
||||
<option value="0.7.34_non_trade_liquidity_lifecycle" selected>0.7.34 — non-trade liquidity/lifecycle</option>
|
||||
<option value="0.7.35_non_trade_fee_reward_admin" selected>0.7.35 — non-trade fee/reward admin</option>
|
||||
<option value="0.7.34_non_trade_liquidity_lifecycle">0.7.34 — non-trade liquidity/lifecycle</option>
|
||||
<option value="0.7.33_pair_trading_readiness">0.7.33 — pair trading readiness</option>
|
||||
<option value="0.7.32_validation_report_semantics">0.7.32 — validation report semantics</option>
|
||||
<option value="0.7.31_trade_event_actionability_policy">0.7.31 — trade event actionability policy</option>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "kb-demo-app",
|
||||
"private": true,
|
||||
"version": "0.7.34",
|
||||
"version": "0.7.35",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
|
||||
@@ -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}"
|
||||
))),
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
150
kb_lib/src/db/dtos/fee_event.rs
Normal file
150
kb_lib/src/db/dtos/fee_event.rs
Normal file
@@ -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<i64>,
|
||||
/// Related transaction id.
|
||||
pub transaction_id: i64,
|
||||
/// Related decoded DEX event id, when available.
|
||||
pub decoded_event_id: std::option::Option<i64>,
|
||||
/// Related DEX id, when the DEX row is known.
|
||||
pub dex_id: std::option::Option<i64>,
|
||||
/// Related pool id, when the pool row is known.
|
||||
pub pool_id: std::option::Option<i64>,
|
||||
/// Related pair id, when the pair row is known.
|
||||
pub pair_id: std::option::Option<i64>,
|
||||
/// Transaction signature.
|
||||
pub signature: std::string::String,
|
||||
/// Optional slot number.
|
||||
pub slot: std::option::Option<u64>,
|
||||
/// 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<std::string::String>,
|
||||
/// Wallet or authority associated with the fee event, when decoded.
|
||||
pub actor_wallet: std::option::Option<std::string::String>,
|
||||
/// Token mint used to pay or collect the fee, when decoded.
|
||||
pub fee_token_mint: std::option::Option<std::string::String>,
|
||||
/// Raw fee amount as decimal text, when decoded.
|
||||
pub fee_amount_raw: std::option::Option<std::string::String>,
|
||||
/// Source decoded payload JSON.
|
||||
pub payload_json: std::string::String,
|
||||
/// Execution timestamp.
|
||||
pub executed_at: chrono::DateTime<chrono::Utc>,
|
||||
/// Creation timestamp.
|
||||
pub created_at: chrono::DateTime<chrono::Utc>,
|
||||
}
|
||||
|
||||
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<i64>,
|
||||
dex_id: std::option::Option<i64>,
|
||||
pool_id: std::option::Option<i64>,
|
||||
pair_id: std::option::Option<i64>,
|
||||
signature: std::string::String,
|
||||
slot: std::option::Option<u64>,
|
||||
protocol_name: std::string::String,
|
||||
program_id: std::string::String,
|
||||
event_kind: std::string::String,
|
||||
pool_account: std::option::Option<std::string::String>,
|
||||
actor_wallet: std::option::Option<std::string::String>,
|
||||
fee_token_mint: std::option::Option<std::string::String>,
|
||||
fee_amount_raw: std::option::Option<std::string::String>,
|
||||
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<crate::FeeEventEntity> for FeeEventDto {
|
||||
type Error = crate::Error;
|
||||
|
||||
fn try_from(entity: crate::FeeEventEntity) -> Result<Self, Self::Error> {
|
||||
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,
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -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,
|
||||
|
||||
145
kb_lib/src/db/dtos/pool_admin_event.rs
Normal file
145
kb_lib/src/db/dtos/pool_admin_event.rs
Normal file
@@ -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<i64>,
|
||||
/// Related transaction id.
|
||||
pub transaction_id: i64,
|
||||
/// Related decoded DEX event id, when available.
|
||||
pub decoded_event_id: std::option::Option<i64>,
|
||||
/// Related DEX id, when the DEX row is known.
|
||||
pub dex_id: std::option::Option<i64>,
|
||||
/// Related pool id, when the pool row is known.
|
||||
pub pool_id: std::option::Option<i64>,
|
||||
/// Related pair id, when the pair row is known.
|
||||
pub pair_id: std::option::Option<i64>,
|
||||
/// Transaction signature.
|
||||
pub signature: std::string::String,
|
||||
/// Optional slot number.
|
||||
pub slot: std::option::Option<u64>,
|
||||
/// 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<std::string::String>,
|
||||
/// Wallet or authority associated with the admin event, when decoded.
|
||||
pub actor_wallet: std::option::Option<std::string::String>,
|
||||
/// Normalized admin action label, when available.
|
||||
pub admin_action: std::option::Option<std::string::String>,
|
||||
/// Source decoded payload JSON.
|
||||
pub payload_json: std::string::String,
|
||||
/// Execution timestamp.
|
||||
pub executed_at: chrono::DateTime<chrono::Utc>,
|
||||
/// Creation timestamp.
|
||||
pub created_at: chrono::DateTime<chrono::Utc>,
|
||||
}
|
||||
|
||||
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<i64>,
|
||||
dex_id: std::option::Option<i64>,
|
||||
pool_id: std::option::Option<i64>,
|
||||
pair_id: std::option::Option<i64>,
|
||||
signature: std::string::String,
|
||||
slot: std::option::Option<u64>,
|
||||
protocol_name: std::string::String,
|
||||
program_id: std::string::String,
|
||||
event_kind: std::string::String,
|
||||
pool_account: std::option::Option<std::string::String>,
|
||||
actor_wallet: std::option::Option<std::string::String>,
|
||||
admin_action: std::option::Option<std::string::String>,
|
||||
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<crate::PoolAdminEventEntity> for PoolAdminEventDto {
|
||||
type Error = crate::Error;
|
||||
|
||||
fn try_from(entity: crate::PoolAdminEventEntity) -> Result<Self, Self::Error> {
|
||||
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,
|
||||
});
|
||||
}
|
||||
}
|
||||
150
kb_lib/src/db/dtos/reward_event.rs
Normal file
150
kb_lib/src/db/dtos/reward_event.rs
Normal file
@@ -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<i64>,
|
||||
/// Related transaction id.
|
||||
pub transaction_id: i64,
|
||||
/// Related decoded DEX event id, when available.
|
||||
pub decoded_event_id: std::option::Option<i64>,
|
||||
/// Related DEX id, when the DEX row is known.
|
||||
pub dex_id: std::option::Option<i64>,
|
||||
/// Related pool id, when the pool row is known.
|
||||
pub pool_id: std::option::Option<i64>,
|
||||
/// Related pair id, when the pair row is known.
|
||||
pub pair_id: std::option::Option<i64>,
|
||||
/// Transaction signature.
|
||||
pub signature: std::string::String,
|
||||
/// Optional slot number.
|
||||
pub slot: std::option::Option<u64>,
|
||||
/// 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<std::string::String>,
|
||||
/// Wallet or authority associated with the reward event, when decoded.
|
||||
pub actor_wallet: std::option::Option<std::string::String>,
|
||||
/// Token mint used by the reward event, when decoded.
|
||||
pub reward_token_mint: std::option::Option<std::string::String>,
|
||||
/// Raw reward amount as decimal text, when decoded.
|
||||
pub reward_amount_raw: std::option::Option<std::string::String>,
|
||||
/// Source decoded payload JSON.
|
||||
pub payload_json: std::string::String,
|
||||
/// Execution timestamp.
|
||||
pub executed_at: chrono::DateTime<chrono::Utc>,
|
||||
/// Creation timestamp.
|
||||
pub created_at: chrono::DateTime<chrono::Utc>,
|
||||
}
|
||||
|
||||
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<i64>,
|
||||
dex_id: std::option::Option<i64>,
|
||||
pool_id: std::option::Option<i64>,
|
||||
pair_id: std::option::Option<i64>,
|
||||
signature: std::string::String,
|
||||
slot: std::option::Option<u64>,
|
||||
protocol_name: std::string::String,
|
||||
program_id: std::string::String,
|
||||
event_kind: std::string::String,
|
||||
pool_account: std::option::Option<std::string::String>,
|
||||
actor_wallet: std::option::Option<std::string::String>,
|
||||
reward_token_mint: std::option::Option<std::string::String>,
|
||||
reward_amount_raw: std::option::Option<std::string::String>,
|
||||
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<crate::RewardEventEntity> for RewardEventDto {
|
||||
type Error = crate::Error;
|
||||
|
||||
fn try_from(entity: crate::RewardEventEntity) -> Result<Self, Self::Error> {
|
||||
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,
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
44
kb_lib/src/db/entities/fee_event.rs
Normal file
44
kb_lib/src/db/entities/fee_event.rs
Normal file
@@ -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<i64>,
|
||||
/// Related DEX id, when the DEX row is known.
|
||||
pub dex_id: std::option::Option<i64>,
|
||||
/// Related pool id, when the pool row is known.
|
||||
pub pool_id: std::option::Option<i64>,
|
||||
/// Related pair id, when the pair row is known.
|
||||
pub pair_id: std::option::Option<i64>,
|
||||
/// Transaction signature.
|
||||
pub signature: std::string::String,
|
||||
/// Optional slot number.
|
||||
pub slot: std::option::Option<i64>,
|
||||
/// 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<std::string::String>,
|
||||
/// Wallet or authority associated with the fee event, when decoded.
|
||||
pub actor_wallet: std::option::Option<std::string::String>,
|
||||
/// Token mint used to pay or collect the fee, when decoded.
|
||||
pub fee_token_mint: std::option::Option<std::string::String>,
|
||||
/// Raw fee amount as decimal text, when decoded.
|
||||
pub fee_amount_raw: std::option::Option<std::string::String>,
|
||||
/// 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,
|
||||
}
|
||||
42
kb_lib/src/db/entities/pool_admin_event.rs
Normal file
42
kb_lib/src/db/entities/pool_admin_event.rs
Normal file
@@ -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<i64>,
|
||||
/// Related DEX id, when the DEX row is known.
|
||||
pub dex_id: std::option::Option<i64>,
|
||||
/// Related pool id, when the pool row is known.
|
||||
pub pool_id: std::option::Option<i64>,
|
||||
/// Related pair id, when the pair row is known.
|
||||
pub pair_id: std::option::Option<i64>,
|
||||
/// Transaction signature.
|
||||
pub signature: std::string::String,
|
||||
/// Optional slot number.
|
||||
pub slot: std::option::Option<i64>,
|
||||
/// 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<std::string::String>,
|
||||
/// Wallet or authority associated with the admin event, when decoded.
|
||||
pub actor_wallet: std::option::Option<std::string::String>,
|
||||
/// Normalized admin action label, when available.
|
||||
pub admin_action: std::option::Option<std::string::String>,
|
||||
/// 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,
|
||||
}
|
||||
44
kb_lib/src/db/entities/reward_event.rs
Normal file
44
kb_lib/src/db/entities/reward_event.rs
Normal file
@@ -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<i64>,
|
||||
/// Related DEX id, when the DEX row is known.
|
||||
pub dex_id: std::option::Option<i64>,
|
||||
/// Related pool id, when the pool row is known.
|
||||
pub pool_id: std::option::Option<i64>,
|
||||
/// Related pair id, when the pair row is known.
|
||||
pub pair_id: std::option::Option<i64>,
|
||||
/// Transaction signature.
|
||||
pub signature: std::string::String,
|
||||
/// Optional slot number.
|
||||
pub slot: std::option::Option<i64>,
|
||||
/// 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<std::string::String>,
|
||||
/// Wallet or authority associated with the reward event, when decoded.
|
||||
pub actor_wallet: std::option::Option<std::string::String>,
|
||||
/// Token mint used by the reward event, when decoded.
|
||||
pub reward_token_mint: std::option::Option<std::string::String>,
|
||||
/// Raw reward amount as decimal text, when decoded.
|
||||
pub reward_amount_raw: std::option::Option<std::string::String>,
|
||||
/// 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,
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
302
kb_lib/src/db/queries/fee_event.rs
Normal file
302
kb_lib/src/db/queries/fee_event.rs
Normal file
@@ -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<std::option::Option<crate::FeeEventDto>, crate::Error> {
|
||||
match database.connection() {
|
||||
crate::DatabaseConnection::Sqlite(pool) => {
|
||||
let query_result = sqlx::query_as::<sqlx::Sqlite, crate::FeeEventEntity>(
|
||||
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<i64, crate::Error> {
|
||||
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::<sqlx::Sqlite, i64>(
|
||||
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::<sqlx::Sqlite, i64>(
|
||||
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<std::vec::Vec<crate::FeeEventDto>, crate::Error> {
|
||||
if limit == 0 {
|
||||
return Ok(std::vec::Vec::new());
|
||||
}
|
||||
match database.connection() {
|
||||
crate::DatabaseConnection::Sqlite(pool) => {
|
||||
let query_result = sqlx::query_as::<sqlx::Sqlite, crate::FeeEventEntity>(
|
||||
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);
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -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(
|
||||
|
||||
293
kb_lib/src/db/queries/pool_admin_event.rs
Normal file
293
kb_lib/src/db/queries/pool_admin_event.rs
Normal file
@@ -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<std::option::Option<crate::PoolAdminEventDto>, crate::Error> {
|
||||
match database.connection() {
|
||||
crate::DatabaseConnection::Sqlite(pool) => {
|
||||
let query_result = sqlx::query_as::<sqlx::Sqlite, crate::PoolAdminEventEntity>(
|
||||
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<i64, crate::Error> {
|
||||
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::<sqlx::Sqlite, i64>(
|
||||
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::<sqlx::Sqlite, i64>(
|
||||
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<std::vec::Vec<crate::PoolAdminEventDto>, crate::Error> {
|
||||
if limit == 0 {
|
||||
return Ok(std::vec::Vec::new());
|
||||
}
|
||||
match database.connection() {
|
||||
crate::DatabaseConnection::Sqlite(pool) => {
|
||||
let query_result = sqlx::query_as::<sqlx::Sqlite, crate::PoolAdminEventEntity>(
|
||||
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);
|
||||
},
|
||||
}
|
||||
}
|
||||
299
kb_lib/src/db/queries/reward_event.rs
Normal file
299
kb_lib/src/db/queries/reward_event.rs
Normal file
@@ -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<std::option::Option<crate::RewardEventDto>, crate::Error> {
|
||||
match database.connection() {
|
||||
crate::DatabaseConnection::Sqlite(pool) => {
|
||||
let query_result = sqlx::query_as::<sqlx::Sqlite, crate::RewardEventEntity>(
|
||||
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<i64, crate::Error> {
|
||||
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::<sqlx::Sqlite, i64>(
|
||||
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::<sqlx::Sqlite, i64>(
|
||||
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<std::vec::Vec<crate::RewardEventDto>, crate::Error> {
|
||||
if limit == 0 {
|
||||
return Ok(std::vec::Vec::new());
|
||||
}
|
||||
match database.connection() {
|
||||
crate::DatabaseConnection::Sqlite(pool) => {
|
||||
let query_result = sqlx::query_as::<sqlx::Sqlite, crate::RewardEventEntity>(
|
||||
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);
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -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.
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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<crate::LocalPipelineValidationRunDto, crate::Error> {
|
||||
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();
|
||||
|
||||
@@ -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<bool, crate::Error> {
|
||||
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<bool, crate::Error> {
|
||||
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<bool, crate::Error> {
|
||||
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,
|
||||
&[
|
||||
|
||||
@@ -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::<std::string::String>::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::<std::string::String>::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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user