0.7.28 - final
This commit is contained in:
@@ -12,6 +12,7 @@ pub struct DexDecodeService {
|
||||
pump_swap_decoder: crate::PumpSwapDecoder,
|
||||
orca_whirlpools_decoder: crate::OrcaWhirlpoolsDecoder,
|
||||
meteora_dbc_decoder: crate::MeteoraDbcDecoder,
|
||||
meteora_dlmm_decoder: crate::MeteoraDlmmDecoder,
|
||||
meteora_damm_v1_decoder: crate::MeteoraDammV1Decoder,
|
||||
meteora_damm_v2_decoder: crate::MeteoraDammV2Decoder,
|
||||
fluxbeam_decoder: crate::FluxbeamDecoder,
|
||||
@@ -30,6 +31,7 @@ impl DexDecodeService {
|
||||
pump_swap_decoder: crate::PumpSwapDecoder::new(),
|
||||
orca_whirlpools_decoder: crate::OrcaWhirlpoolsDecoder::new(),
|
||||
meteora_dbc_decoder: crate::MeteoraDbcDecoder::new(),
|
||||
meteora_dlmm_decoder: crate::MeteoraDlmmDecoder::new(),
|
||||
meteora_damm_v1_decoder: crate::MeteoraDammV1Decoder::new(),
|
||||
meteora_damm_v2_decoder: crate::MeteoraDammV2Decoder::new(),
|
||||
fluxbeam_decoder: crate::FluxbeamDecoder::new(),
|
||||
@@ -98,8 +100,7 @@ impl DexDecodeService {
|
||||
}
|
||||
let append_result = append_persisted_events_result(
|
||||
&mut persisted,
|
||||
self.decode_and_persist_meteora_damm_v2_events(&transaction, &instructions)
|
||||
.await,
|
||||
self.decode_and_persist_meteora_dlmm_events(&transaction, &instructions).await,
|
||||
);
|
||||
if let Err(error) = append_result {
|
||||
return Err(error);
|
||||
@@ -112,6 +113,14 @@ impl DexDecodeService {
|
||||
if let Err(error) = append_result {
|
||||
return Err(error);
|
||||
}
|
||||
let append_result = append_persisted_events_result(
|
||||
&mut persisted,
|
||||
self.decode_and_persist_meteora_damm_v2_events(&transaction, &instructions)
|
||||
.await,
|
||||
);
|
||||
if let Err(error) = append_result {
|
||||
return Err(error);
|
||||
}
|
||||
let append_result = append_persisted_events_result(
|
||||
&mut persisted,
|
||||
self.decode_and_persist_orca_whirlpools_events(&transaction, &instructions)
|
||||
@@ -311,6 +320,96 @@ impl DexDecodeService {
|
||||
}
|
||||
}
|
||||
|
||||
async fn persist_meteora_dbc_event(
|
||||
&self,
|
||||
transaction: &crate::ChainTransactionDto,
|
||||
decoded_event: &crate::MeteoraDbcDecodedEvent,
|
||||
) -> Result<crate::DexDecodedEventDto, crate::Error> {
|
||||
match decoded_event {
|
||||
crate::MeteoraDbcDecodedEvent::CreatePool(event) => {
|
||||
return self
|
||||
.materialize_named_dex_event(
|
||||
transaction,
|
||||
event.transaction_id,
|
||||
event.instruction_id,
|
||||
"meteora_dbc",
|
||||
event.program_id.clone(),
|
||||
"meteora_dbc.create_pool",
|
||||
event.pool_account.clone(),
|
||||
None,
|
||||
event.token_a_mint.clone(),
|
||||
event.token_b_mint.clone(),
|
||||
event.config_account.clone(),
|
||||
event.payload_json.clone(),
|
||||
)
|
||||
.await;
|
||||
},
|
||||
crate::MeteoraDbcDecodedEvent::Swap(event) => {
|
||||
return self
|
||||
.materialize_named_dex_event(
|
||||
transaction,
|
||||
event.transaction_id,
|
||||
event.instruction_id,
|
||||
"meteora_dbc",
|
||||
event.program_id.clone(),
|
||||
"meteora_dbc.swap",
|
||||
event.pool_account.clone(),
|
||||
None,
|
||||
event.token_a_mint.clone(),
|
||||
event.token_b_mint.clone(),
|
||||
None,
|
||||
event.payload_json.clone(),
|
||||
)
|
||||
.await;
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
async fn persist_meteora_dlmm_event(
|
||||
&self,
|
||||
transaction: &crate::ChainTransactionDto,
|
||||
decoded_event: &crate::MeteoraDlmmDecodedEvent,
|
||||
) -> Result<crate::DexDecodedEventDto, crate::Error> {
|
||||
match decoded_event {
|
||||
crate::MeteoraDlmmDecodedEvent::CreatePool(event) => {
|
||||
return self
|
||||
.materialize_named_dex_event(
|
||||
transaction,
|
||||
event.transaction_id,
|
||||
event.instruction_id,
|
||||
"meteora_dlmm",
|
||||
event.program_id.clone(),
|
||||
"meteora_dlmm.create_pool",
|
||||
event.pool_account.clone(),
|
||||
None,
|
||||
event.token_a_mint.clone(),
|
||||
event.token_b_mint.clone(),
|
||||
event.config_account.clone(),
|
||||
event.payload_json.clone(),
|
||||
)
|
||||
.await;
|
||||
},
|
||||
crate::MeteoraDlmmDecodedEvent::Swap(event) => {
|
||||
return self
|
||||
.materialize_named_dex_event(
|
||||
transaction,
|
||||
event.transaction_id,
|
||||
event.instruction_id,
|
||||
"meteora_dlmm",
|
||||
event.program_id.clone(),
|
||||
"meteora_dlmm.swap",
|
||||
event.pool_account.clone(),
|
||||
None,
|
||||
event.token_a_mint.clone(),
|
||||
event.token_b_mint.clone(),
|
||||
None,
|
||||
event.payload_json.clone(),
|
||||
)
|
||||
.await;
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
async fn persist_meteora_damm_v1_event(
|
||||
&self,
|
||||
transaction: &crate::ChainTransactionDto,
|
||||
@@ -336,6 +435,8 @@ impl DexDecodeService {
|
||||
.await;
|
||||
},
|
||||
crate::MeteoraDammV1DecodedEvent::Swap(event) => {
|
||||
let enrichment_payload_json =
|
||||
prepare_meteora_damm_v1_swap_payload_for_classification(event);
|
||||
return self
|
||||
.materialize_named_dex_event(
|
||||
transaction,
|
||||
@@ -349,7 +450,7 @@ impl DexDecodeService {
|
||||
event.token_a_mint.clone(),
|
||||
event.token_b_mint.clone(),
|
||||
None,
|
||||
event.payload_json.clone(),
|
||||
enrichment_payload_json,
|
||||
)
|
||||
.await;
|
||||
},
|
||||
@@ -401,51 +502,6 @@ impl DexDecodeService {
|
||||
}
|
||||
}
|
||||
|
||||
async fn persist_meteora_dbc_event(
|
||||
&self,
|
||||
transaction: &crate::ChainTransactionDto,
|
||||
decoded_event: &crate::MeteoraDbcDecodedEvent,
|
||||
) -> Result<crate::DexDecodedEventDto, crate::Error> {
|
||||
match decoded_event {
|
||||
crate::MeteoraDbcDecodedEvent::CreatePool(event) => {
|
||||
return self
|
||||
.materialize_named_dex_event(
|
||||
transaction,
|
||||
event.transaction_id,
|
||||
event.instruction_id,
|
||||
"meteora_dbc",
|
||||
event.program_id.clone(),
|
||||
"meteora_dbc.create_pool",
|
||||
event.pool_account.clone(),
|
||||
None,
|
||||
event.token_a_mint.clone(),
|
||||
event.token_b_mint.clone(),
|
||||
event.config_account.clone(),
|
||||
event.payload_json.clone(),
|
||||
)
|
||||
.await;
|
||||
},
|
||||
crate::MeteoraDbcDecodedEvent::Swap(event) => {
|
||||
return self
|
||||
.materialize_named_dex_event(
|
||||
transaction,
|
||||
event.transaction_id,
|
||||
event.instruction_id,
|
||||
"meteora_dbc",
|
||||
event.program_id.clone(),
|
||||
"meteora_dbc.swap",
|
||||
event.pool_account.clone(),
|
||||
None,
|
||||
event.token_a_mint.clone(),
|
||||
event.token_b_mint.clone(),
|
||||
None,
|
||||
event.payload_json.clone(),
|
||||
)
|
||||
.await;
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
async fn persist_raydium_amm_v4_event(
|
||||
&self,
|
||||
transaction: &crate::ChainTransactionDto,
|
||||
@@ -900,6 +956,29 @@ impl DexDecodeService {
|
||||
return Ok(persisted);
|
||||
}
|
||||
|
||||
async fn decode_and_persist_meteora_dlmm_events(
|
||||
&self,
|
||||
transaction: &crate::ChainTransactionDto,
|
||||
instructions: &[crate::ChainInstructionDto],
|
||||
) -> Result<std::vec::Vec<crate::DexDecodedEventDto>, crate::Error> {
|
||||
let decoded_result =
|
||||
self.meteora_dlmm_decoder.decode_transaction(transaction, instructions);
|
||||
let decoded_events = match decoded_result {
|
||||
Ok(decoded_events) => decoded_events,
|
||||
Err(error) => return Err(error),
|
||||
};
|
||||
let mut persisted = std::vec::Vec::new();
|
||||
for decoded_event in &decoded_events {
|
||||
let persist_result = self.persist_meteora_dlmm_event(transaction, decoded_event).await;
|
||||
let persisted_event = match persist_result {
|
||||
Ok(persisted_event) => persisted_event,
|
||||
Err(error) => return Err(error),
|
||||
};
|
||||
persisted.push(persisted_event);
|
||||
}
|
||||
return Ok(persisted);
|
||||
}
|
||||
|
||||
async fn decode_and_persist_meteora_damm_v1_events(
|
||||
&self,
|
||||
transaction: &crate::ChainTransactionDto,
|
||||
@@ -1056,6 +1135,38 @@ fn enriched_raydium_payload_value(
|
||||
return Ok(crate::enrich_dex_decoded_payload(protocol_name, event_kind, payload_value));
|
||||
}
|
||||
|
||||
// Marks Meteora DAMM v1 swaps without direct amounts as non-materializable candidates
|
||||
// before generic classification metadata is inserted.
|
||||
fn prepare_meteora_damm_v1_swap_payload_for_classification(
|
||||
event: &crate::MeteoraDammV1SwapDecoded,
|
||||
) -> serde_json::Value {
|
||||
let mut object = match event.payload_json.clone() {
|
||||
serde_json::Value::Object(object) => object,
|
||||
other => {
|
||||
let mut object = serde_json::Map::new();
|
||||
object.insert("rawPayload".to_string(), other);
|
||||
object
|
||||
},
|
||||
};
|
||||
let payload_json = serde_json::Value::Object(object.clone());
|
||||
if crate::dex_event_classification::decoded_payload_has_trade_amount_or_price_payload(
|
||||
&payload_json,
|
||||
) {
|
||||
return serde_json::Value::Object(object);
|
||||
}
|
||||
object.insert("tradeCandidate".to_string(), serde_json::Value::Bool(false));
|
||||
object.insert("candleCandidate".to_string(), serde_json::Value::Bool(false));
|
||||
object.insert(
|
||||
"skipTradeReason".to_string(),
|
||||
serde_json::Value::String("meteora_damm_v1_swap_without_amount_payload".to_string()),
|
||||
);
|
||||
object.insert(
|
||||
"skipCandleReason".to_string(),
|
||||
serde_json::Value::String("meteora_damm_v1_swap_without_amount_payload".to_string()),
|
||||
);
|
||||
return serde_json::Value::Object(object);
|
||||
}
|
||||
|
||||
// Marks incomplete PumpSwap decoded trades as non-materializable candidates before generic
|
||||
// classification metadata is inserted.
|
||||
fn prepare_pump_swap_trade_payload_for_classification(
|
||||
@@ -1313,6 +1424,73 @@ mod tests {
|
||||
assert_eq!(decoded[0].token_a_mint, Some("MintPF111".to_string()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn prepare_meteora_damm_v1_swap_without_amounts_marks_event_non_actionable() {
|
||||
let event = crate::MeteoraDammV1SwapDecoded {
|
||||
transaction_id: 1,
|
||||
instruction_id: 2,
|
||||
signature: "sig-damm-v1-no-amounts".to_string(),
|
||||
program_id: crate::METEORA_DAMM_V1_PROGRAM_ID.to_string(),
|
||||
trade_side: crate::SwapTradeSide::Unknown,
|
||||
pool_account: Some("PoolDammV1".to_string()),
|
||||
token_a_mint: Some("TokenA".to_string()),
|
||||
token_b_mint: Some("TokenB".to_string()),
|
||||
payload_json: serde_json::json!({
|
||||
"decoder": "meteora_damm_v1",
|
||||
"eventKind": "swap"
|
||||
}),
|
||||
};
|
||||
let prepared_payload =
|
||||
super::prepare_meteora_damm_v1_swap_payload_for_classification(&event);
|
||||
let object_option = prepared_payload.as_object();
|
||||
let object = match object_option {
|
||||
Some(object) => object,
|
||||
None => {
|
||||
panic!("expected prepared payload object");
|
||||
},
|
||||
};
|
||||
assert_eq!(object.get("tradeCandidate"), Some(&serde_json::Value::Bool(false)));
|
||||
assert_eq!(object.get("candleCandidate"), Some(&serde_json::Value::Bool(false)));
|
||||
assert_eq!(
|
||||
object.get("skipTradeReason"),
|
||||
Some(&serde_json::Value::String(
|
||||
"meteora_damm_v1_swap_without_amount_payload".to_string()
|
||||
))
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn prepare_meteora_damm_v1_swap_with_amounts_keeps_event_actionable() {
|
||||
let event = crate::MeteoraDammV1SwapDecoded {
|
||||
transaction_id: 1,
|
||||
instruction_id: 2,
|
||||
signature: "sig-damm-v1-with-amounts".to_string(),
|
||||
program_id: crate::METEORA_DAMM_V1_PROGRAM_ID.to_string(),
|
||||
trade_side: crate::SwapTradeSide::Unknown,
|
||||
pool_account: Some("PoolDammV1".to_string()),
|
||||
token_a_mint: Some("TokenA".to_string()),
|
||||
token_b_mint: Some("TokenB".to_string()),
|
||||
payload_json: serde_json::json!({
|
||||
"decoder": "meteora_damm_v1",
|
||||
"eventKind": "swap",
|
||||
"baseAmountRaw": "100",
|
||||
"quoteAmountRaw": "250"
|
||||
}),
|
||||
};
|
||||
let prepared_payload =
|
||||
super::prepare_meteora_damm_v1_swap_payload_for_classification(&event);
|
||||
let object_option = prepared_payload.as_object();
|
||||
let object = match object_option {
|
||||
Some(object) => object,
|
||||
None => {
|
||||
panic!("expected prepared payload object");
|
||||
},
|
||||
};
|
||||
assert_eq!(object.get("tradeCandidate"), None);
|
||||
assert_eq!(object.get("candleCandidate"), None);
|
||||
assert_eq!(object.get("skipTradeReason"), None);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn decode_transaction_by_signature_persists_decoded_pump_swap_event() {
|
||||
let database = make_database().await;
|
||||
|
||||
Reference in New Issue
Block a user