0.7.45
This commit is contained in:
@@ -2,6 +2,8 @@
|
||||
|
||||
//! Persistence-oriented DEX decoding service.
|
||||
|
||||
const METEORA_ANCHOR_SELF_CPI_LOG_SELECTOR_HEX: &str = "e445a52e51cb9a1d";
|
||||
|
||||
/// DEX decode service.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct DexDecodeService {
|
||||
@@ -206,7 +208,7 @@ impl DexDecodeService {
|
||||
Err(error) => return Err(error),
|
||||
};
|
||||
let cleanup_result = self
|
||||
.delete_replaced_raydium_instruction_audit(
|
||||
.delete_replaced_instruction_audit(
|
||||
transaction_id,
|
||||
instruction_id,
|
||||
protocol_name,
|
||||
@@ -219,7 +221,7 @@ impl DexDecodeService {
|
||||
return Ok(materialized);
|
||||
}
|
||||
|
||||
async fn delete_replaced_raydium_instruction_audit(
|
||||
async fn delete_replaced_instruction_audit(
|
||||
&self,
|
||||
transaction_id: i64,
|
||||
instruction_id: i64,
|
||||
@@ -229,15 +231,14 @@ impl DexDecodeService {
|
||||
if event_kind.ends_with(".instruction_audit") {
|
||||
return Ok(());
|
||||
}
|
||||
let audit_event_kind = match raydium_instruction_audit_event_kind_by_protocol(protocol_name)
|
||||
{
|
||||
let audit_event_kind = match instruction_audit_event_kind_by_protocol(protocol_name) {
|
||||
Some(audit_event_kind) => audit_event_kind,
|
||||
None => return Ok(()),
|
||||
};
|
||||
let delete_result = crate::query_dex_decoded_events_delete_by_key(
|
||||
let delete_result = crate::query_dex_decoded_events_delete_related_instruction_audit(
|
||||
self.database.as_ref(),
|
||||
transaction_id,
|
||||
Some(instruction_id),
|
||||
instruction_id,
|
||||
audit_event_kind,
|
||||
)
|
||||
.await;
|
||||
@@ -505,6 +506,42 @@ impl DexDecodeService {
|
||||
)
|
||||
.await;
|
||||
},
|
||||
crate::MeteoraDlmmDecodedEvent::Fee(event) => {
|
||||
return self
|
||||
.materialize_named_dex_event(
|
||||
transaction,
|
||||
event.transaction_id,
|
||||
event.instruction_id,
|
||||
"meteora_dlmm",
|
||||
event.program_id.clone(),
|
||||
event.event_kind.as_str(),
|
||||
event.pool_account.clone(),
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
event.payload_json.clone(),
|
||||
)
|
||||
.await;
|
||||
},
|
||||
crate::MeteoraDlmmDecodedEvent::Reward(event) => {
|
||||
return self
|
||||
.materialize_named_dex_event(
|
||||
transaction,
|
||||
event.transaction_id,
|
||||
event.instruction_id,
|
||||
"meteora_dlmm",
|
||||
event.program_id.clone(),
|
||||
event.event_kind.as_str(),
|
||||
event.pool_account.clone(),
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
event.payload_json.clone(),
|
||||
)
|
||||
.await;
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1167,6 +1204,13 @@ impl DexDecodeService {
|
||||
if decoded_instruction_ids.contains(&instruction_id) {
|
||||
continue;
|
||||
}
|
||||
if is_meteora_dlmm_anchor_swap_log_replaced_by_decoded_swap(
|
||||
audit_spec.protocol_name,
|
||||
instruction,
|
||||
decoded_events.as_slice(),
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
let accounts = parse_instruction_accounts_vec(instruction.accounts_json.as_str());
|
||||
let payload = build_meteora_instruction_audit_payload(
|
||||
transaction,
|
||||
@@ -1773,6 +1817,35 @@ fn candidate_meteora_audit_pool_account(
|
||||
return accounts.get(index).cloned();
|
||||
}
|
||||
|
||||
fn is_meteora_dlmm_anchor_swap_log_replaced_by_decoded_swap(
|
||||
protocol_name: &str,
|
||||
instruction: &crate::ChainInstructionDto,
|
||||
decoded_events: &[crate::DexDecodedEventDto],
|
||||
) -> bool {
|
||||
if protocol_name != "meteora_dlmm" {
|
||||
return false;
|
||||
}
|
||||
let data_base58 = parse_instruction_data_base58(instruction.data_json.as_deref());
|
||||
let data_bytes = instruction_data_bytes_from_base58(data_base58.as_deref());
|
||||
let selector_hex = discriminator_hex_from_bytes(data_bytes.as_deref(), 0);
|
||||
if selector_hex.as_deref() != Some(METEORA_ANCHOR_SELF_CPI_LOG_SELECTOR_HEX) {
|
||||
return false;
|
||||
}
|
||||
let event_discriminator_hex = discriminator_hex_from_bytes(data_bytes.as_deref(), 8);
|
||||
match event_discriminator_hex.as_deref() {
|
||||
Some("516ce3becdd00ac4") | Some("2e7452d7941b544d") => {},
|
||||
_ => return false,
|
||||
}
|
||||
for decoded_event in decoded_events {
|
||||
if decoded_event.protocol_name == "meteora_dlmm"
|
||||
&& decoded_event.event_kind == "meteora_dlmm.swap"
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
fn build_meteora_instruction_audit_payload(
|
||||
transaction: &crate::ChainTransactionDto,
|
||||
instruction: &crate::ChainInstructionDto,
|
||||
@@ -1786,10 +1859,36 @@ fn build_meteora_instruction_audit_payload(
|
||||
None => 0,
|
||||
};
|
||||
let data_base58 = parse_instruction_data_base58(instruction.data_json.as_deref());
|
||||
let discriminator_hex = discriminator_hex_from_base58(data_base58.as_deref());
|
||||
let data_bytes = instruction_data_bytes_from_base58(data_base58.as_deref());
|
||||
let discriminator_hex = discriminator_hex_from_bytes(data_bytes.as_deref(), 0);
|
||||
let anchor_self_cpi_log =
|
||||
discriminator_hex.as_deref() == Some(METEORA_ANCHOR_SELF_CPI_LOG_SELECTOR_HEX);
|
||||
let anchor_event_discriminator_hex = if anchor_self_cpi_log {
|
||||
discriminator_hex_from_bytes(data_bytes.as_deref(), 8)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
let anchor_event_payload_size = if anchor_self_cpi_log {
|
||||
match data_bytes.as_ref() {
|
||||
Some(data_bytes) => data_bytes.len().checked_sub(8),
|
||||
None => None,
|
||||
}
|
||||
} else {
|
||||
None
|
||||
};
|
||||
let data_prefix = data_base58
|
||||
.as_ref()
|
||||
.map(|value| return value.chars().take(16).collect::<std::string::String>());
|
||||
let audit_reason = if anchor_self_cpi_log {
|
||||
"meteora_anchor_self_cpi_log_not_decoded_by_specific_event_decoder"
|
||||
} else {
|
||||
"meteora_instruction_not_decoded_by_specific_decoder"
|
||||
};
|
||||
let proof_status = if anchor_self_cpi_log {
|
||||
"observed_local_corpus_anchor_self_cpi_log"
|
||||
} else {
|
||||
"unclassified_local_corpus_instruction"
|
||||
};
|
||||
return serde_json::json!({
|
||||
"decoder": protocol_name,
|
||||
"eventKind": event_kind,
|
||||
@@ -1806,8 +1905,12 @@ fn build_meteora_instruction_audit_payload(
|
||||
"data": data_base58,
|
||||
"dataPrefix": data_prefix,
|
||||
"discriminatorHex": discriminator_hex,
|
||||
"auditReason": "meteora_instruction_not_decoded_by_specific_decoder",
|
||||
"proofStatus": "unclassified_local_corpus_instruction",
|
||||
"anchorSelfCpiLog": anchor_self_cpi_log,
|
||||
"anchorSelfCpiLogSelectorHex": if anchor_self_cpi_log { Some(METEORA_ANCHOR_SELF_CPI_LOG_SELECTOR_HEX) } else { None },
|
||||
"anchorEventDiscriminatorHex": anchor_event_discriminator_hex,
|
||||
"anchorEventPayloadSize": anchor_event_payload_size,
|
||||
"auditReason": audit_reason,
|
||||
"proofStatus": proof_status,
|
||||
"tradeCandidate": false,
|
||||
"candleCandidate": false,
|
||||
"nonTradeUseful": false,
|
||||
@@ -1816,13 +1919,14 @@ fn build_meteora_instruction_audit_payload(
|
||||
});
|
||||
}
|
||||
|
||||
fn raydium_instruction_audit_event_kind_by_protocol(
|
||||
fn instruction_audit_event_kind_by_protocol(
|
||||
protocol_name: &str,
|
||||
) -> std::option::Option<&'static str> {
|
||||
match protocol_name {
|
||||
"raydium_amm_v4" => return Some("raydium_amm_v4.instruction_audit"),
|
||||
"raydium_clmm" => return Some("raydium_clmm.instruction_audit"),
|
||||
"raydium_cpmm" => return Some("raydium_cpmm.instruction_audit"),
|
||||
"meteora_dlmm" => return Some("meteora_dlmm.instruction_audit"),
|
||||
_ => return None,
|
||||
}
|
||||
}
|
||||
@@ -1932,21 +2036,28 @@ fn parse_instruction_data_base58(
|
||||
fn discriminator_hex_from_base58(
|
||||
data_base58: std::option::Option<&str>,
|
||||
) -> std::option::Option<std::string::String> {
|
||||
let data_base58 = match data_base58 {
|
||||
Some(data_base58) => data_base58,
|
||||
let bytes = instruction_data_bytes_from_base58(data_base58);
|
||||
return discriminator_hex_from_bytes(bytes.as_deref(), 0);
|
||||
}
|
||||
|
||||
fn discriminator_hex_from_bytes(
|
||||
bytes: std::option::Option<&[u8]>,
|
||||
offset: usize,
|
||||
) -> std::option::Option<std::string::String> {
|
||||
let bytes = match bytes {
|
||||
Some(bytes) => bytes,
|
||||
None => return None,
|
||||
};
|
||||
let bytes_result = bs58::decode(data_base58).into_vec();
|
||||
let bytes = match bytes_result {
|
||||
Ok(bytes) => bytes,
|
||||
Err(_) => return None,
|
||||
};
|
||||
if bytes.len() < 8 {
|
||||
if bytes.len() < offset + 8 {
|
||||
return None;
|
||||
}
|
||||
let mut text = std::string::String::new();
|
||||
for byte in bytes.iter().take(8) {
|
||||
let mut index = offset;
|
||||
let end = offset + 8;
|
||||
while index < end {
|
||||
let byte = bytes[index];
|
||||
text.push_str(format!("{byte:02x}").as_str());
|
||||
index += 1;
|
||||
}
|
||||
return Some(text);
|
||||
}
|
||||
@@ -3012,4 +3123,17 @@ mod tests {
|
||||
};
|
||||
assert_eq!(initialize.event_kind, "raydium_cpmm.initialize");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn maps_instruction_audit_event_kind_for_raydium_and_meteora_dlmm_protocols() {
|
||||
assert_eq!(
|
||||
super::instruction_audit_event_kind_by_protocol("raydium_clmm"),
|
||||
Some("raydium_clmm.instruction_audit")
|
||||
);
|
||||
assert_eq!(
|
||||
super::instruction_audit_event_kind_by_protocol("meteora_dlmm"),
|
||||
Some("meteora_dlmm.instruction_audit")
|
||||
);
|
||||
assert_eq!(super::instruction_audit_event_kind_by_protocol("unknown"), None);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user