This commit is contained in:
2026-06-01 19:05:46 +02:00
parent abb810d544
commit 27e25d5bf4
59 changed files with 5727 additions and 1706 deletions

View File

@@ -102,7 +102,17 @@ impl NonTradeEventMaterializationService {
continue;
},
};
if crate::is_dex_liquidity_event_kind(decoded_event.event_kind.as_str()) {
if crate::is_dex_pool_lifecycle_event_kind(decoded_event.event_kind.as_str()) {
let cleanup_result =
self.delete_stale_pool_admin_event_for_lifecycle(decoded_event).await;
match cleanup_result {
Ok(_) => {},
Err(error) => return Err(error),
}
}
if crate::is_dex_liquidity_event_kind(decoded_event.event_kind.as_str())
&& !decoded_event.event_kind.ends_with(".lp_change_event")
{
let materialized = self
.materialize_liquidity_event(
&transaction,
@@ -159,7 +169,9 @@ impl NonTradeEventMaterializationService {
Err(error) => return Err(error),
}
}
if crate::is_dex_admin_event_kind(decoded_event.event_kind.as_str()) {
if crate::is_dex_admin_event_kind(decoded_event.event_kind.as_str())
&& !crate::is_dex_pool_lifecycle_event_kind(decoded_event.event_kind.as_str())
{
let materialized = self
.materialize_pool_admin_event(
&transaction,
@@ -178,6 +190,36 @@ impl NonTradeEventMaterializationService {
}
}
}
for decoded_event in &decoded_events {
if !decoded_event.event_kind.ends_with(".lp_change_event") {
continue;
}
let payload_result =
serde_json::from_str::<serde_json::Value>(decoded_event.payload_json.as_str());
let payload = match payload_result {
Ok(payload) => payload,
Err(error) => {
tracing::warn!(
signature = %transaction.signature,
event_kind = %decoded_event.event_kind,
error = %error,
"skipping postponed lp_change_event materialization for invalid decoded payload"
);
continue;
},
};
let materialized = self
.materialize_liquidity_event(&transaction, transaction_id, decoded_event, &payload)
.await;
match materialized {
Ok(was_materialized) => {
if was_materialized {
result.liquidity_event_count += 1;
}
},
Err(error) => return Err(error),
}
}
return Ok(result);
}
@@ -274,6 +316,12 @@ impl NonTradeEventMaterializationService {
"fund_fee_amount",
"creatorFeeAmount",
"creator_fee_amount",
"amount0RequestedRaw",
"amount_0_requested_raw",
"amount1RequestedRaw",
"amount_1_requested_raw",
"tokenAAmount",
"tokenBAmount",
"amount",
],
);
@@ -370,6 +418,48 @@ impl NonTradeEventMaterializationService {
}
}
async fn delete_stale_pool_admin_event_for_lifecycle(
&self,
decoded_event: &crate::DexDecodedEventDto,
) -> Result<(), crate::Error> {
let decoded_event_id = match decoded_event.id {
Some(decoded_event_id) => decoded_event_id,
None => return Ok(()),
};
match self.database.connection() {
crate::DatabaseConnection::Sqlite(pool) => {
let delete_result = sqlx::query(
r#"
DELETE FROM k_sol_pool_admin_events
WHERE decoded_event_id = ?
"#,
)
.bind(decoded_event_id)
.execute(pool)
.await;
let delete_result = match delete_result {
Ok(delete_result) => delete_result,
Err(error) => {
return Err(crate::Error::Db(format!(
"cannot delete stale k_sol_pool_admin_events for lifecycle decoded_event_id '{}' on sqlite: {}",
decoded_event_id, error
)));
},
};
let deleted_count = delete_result.rows_affected();
if deleted_count > 0 {
tracing::debug!(
decoded_event_id = decoded_event_id,
event_kind = %decoded_event.event_kind,
deleted_count = deleted_count,
"removed stale pool admin materialization for lifecycle event"
);
}
return Ok(());
},
}
}
async fn materialize_pool_admin_event(
&self,
transaction: &crate::ChainTransactionDto,
@@ -437,6 +527,16 @@ impl NonTradeEventMaterializationService {
Ok(context) => context,
Err(error) => return Err(error),
};
let context = if context.pool_id.is_some() && context.pair.is_some() {
context
} else {
let ensured_context =
self.ensure_liquidity_context_from_decoded_event(decoded_event, context).await;
match ensured_context {
Ok(ensured_context) => ensured_context,
Err(error) => return Err(error),
}
};
let dex_id = match context.dex_id {
Some(dex_id) => dex_id,
None => return Ok(false),
@@ -458,6 +558,12 @@ impl NonTradeEventMaterializationService {
crate::LiquidityEventKind::PositionOpen
} else if crate::is_dex_position_close_event_kind(decoded_event.event_kind.as_str()) {
crate::LiquidityEventKind::PositionClose
} else if decoded_event.event_kind.ends_with(".lp_change_event") {
let change_type = extract_first_u64(payload, &["changeType", "change_type"]);
match change_type {
Some(1) => crate::LiquidityEventKind::Remove,
_ => crate::LiquidityEventKind::Add,
}
} else if crate::is_dex_liquidity_remove_event_kind(decoded_event.event_kind.as_str()) {
crate::LiquidityEventKind::Remove
} else {
@@ -487,6 +593,10 @@ impl NonTradeEventMaterializationService {
"amount_base",
"tokenAAmount",
"token_a_amount",
"token0AmountRaw",
"token_0_amount_raw",
"amount0RequestedRaw",
"amount_0_requested_raw",
"amountA",
"amount_a",
],
@@ -502,6 +612,10 @@ impl NonTradeEventMaterializationService {
"amount_quote",
"tokenBAmount",
"token_b_amount",
"token1AmountRaw",
"token_1_amount_raw",
"amount1RequestedRaw",
"amount_1_requested_raw",
"amountB",
"amount_b",
],
@@ -559,6 +673,54 @@ impl NonTradeEventMaterializationService {
}
}
async fn ensure_liquidity_context_from_decoded_event(
&self,
decoded_event: &crate::DexDecodedEventDto,
context: NonTradeDecodedEventContext,
) -> Result<NonTradeDecodedEventContext, crate::Error> {
let dex_id = match context.dex_id {
Some(dex_id) => dex_id,
None => return Ok(context),
};
if context.pool_id.is_some() && context.pair.is_some() {
return Ok(context);
}
if decoded_event.pool_account.is_none()
|| decoded_event.token_a_mint.is_none()
|| decoded_event.token_b_mint.is_none()
{
return Ok(context);
}
let materialization_input_result =
crate::dex_pool_materialization::DexPoolMaterializationInput::from_decoded_event(
decoded_event,
dex_id,
crate::PoolKind::Amm,
crate::PoolStatus::Active,
crate::dex_pool_materialization::DexPoolTokenOrder::AlreadyBaseQuote,
None,
None,
None,
);
let materialization_input = match materialization_input_result {
Ok(materialization_input) => materialization_input,
Err(_) => return Ok(context),
};
let materialization_result = crate::dex_pool_materialization::materialize_dex_pool(
self.database.as_ref(),
&materialization_input,
)
.await;
if let Err(error) = materialization_result {
return Err(error);
}
let refreshed_context = self.resolve_decoded_event_context(decoded_event).await;
match refreshed_context {
Ok(refreshed_context) => return Ok(refreshed_context),
Err(error) => return Err(error),
}
}
async fn resolve_decoded_event_context(
&self,
decoded_event: &crate::DexDecodedEventDto,
@@ -627,6 +789,29 @@ impl NonTradeEventMaterializationService {
}
}
fn extract_first_u64(
value: &serde_json::Value,
candidate_keys: &[&str],
) -> std::option::Option<u64> {
if let Some(object) = value.as_object() {
for candidate_key in candidate_keys {
let candidate_value = object.get(*candidate_key);
if let Some(candidate_value) = candidate_value {
if let Some(number) = candidate_value.as_u64() {
return Some(number);
}
if let Some(text) = candidate_value.as_str() {
let parsed = text.parse::<u64>();
if let Ok(parsed) = parsed {
return Some(parsed);
}
}
}
}
}
return None;
}
fn extract_first_amount_string(
value: &serde_json::Value,
candidate_keys: &[&str],