0.7.43-E5C
This commit is contained in:
@@ -131,6 +131,14 @@ impl DexDecodeService {
|
||||
if let Err(error) = append_result {
|
||||
return Err(error);
|
||||
}
|
||||
let append_result = append_persisted_events_result(
|
||||
&mut persisted,
|
||||
self.preserve_unmatched_meteora_instruction_audits(&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)
|
||||
@@ -1069,15 +1077,15 @@ impl DexDecodeService {
|
||||
instruction.accounts_json.as_str(),
|
||||
);
|
||||
let token_a_mint = candidate_raydium_mapped_account(
|
||||
mapped_spec.and_then(|spec| spec.token_a_mint_index),
|
||||
mapped_spec.and_then(|spec| return spec.token_a_mint_index),
|
||||
accounts.as_slice(),
|
||||
);
|
||||
let token_b_mint = candidate_raydium_mapped_account(
|
||||
mapped_spec.and_then(|spec| spec.token_b_mint_index),
|
||||
mapped_spec.and_then(|spec| return spec.token_b_mint_index),
|
||||
accounts.as_slice(),
|
||||
);
|
||||
let lp_mint = candidate_raydium_mapped_account(
|
||||
mapped_spec.and_then(|spec| spec.lp_mint_index),
|
||||
mapped_spec.and_then(|spec| return spec.lp_mint_index),
|
||||
accounts.as_slice(),
|
||||
);
|
||||
let persist_result = self
|
||||
@@ -1105,6 +1113,95 @@ impl DexDecodeService {
|
||||
return Ok(persisted);
|
||||
}
|
||||
|
||||
async fn preserve_unmatched_meteora_instruction_audits(
|
||||
&self,
|
||||
transaction: &crate::ChainTransactionDto,
|
||||
instructions: &[crate::ChainInstructionDto],
|
||||
) -> Result<std::vec::Vec<crate::DexDecodedEventDto>, crate::Error> {
|
||||
let transaction_id = match transaction.id {
|
||||
Some(transaction_id) => transaction_id,
|
||||
None => {
|
||||
return Err(crate::Error::InvalidState(format!(
|
||||
"transaction '{}' has no internal id",
|
||||
transaction.signature
|
||||
)));
|
||||
},
|
||||
};
|
||||
let decoded_events_result = crate::query_dex_decoded_events_list_by_transaction_id(
|
||||
self.database.as_ref(),
|
||||
transaction_id,
|
||||
)
|
||||
.await;
|
||||
let decoded_events = match decoded_events_result {
|
||||
Ok(decoded_events) => decoded_events,
|
||||
Err(error) => return Err(error),
|
||||
};
|
||||
let mut decoded_instruction_ids = std::collections::HashSet::<i64>::new();
|
||||
for decoded_event in &decoded_events {
|
||||
if !decoded_event.protocol_name.starts_with("meteora_") {
|
||||
continue;
|
||||
}
|
||||
if decoded_event.event_kind.ends_with(".instruction_audit") {
|
||||
continue;
|
||||
}
|
||||
let instruction_id = match decoded_event.instruction_id {
|
||||
Some(instruction_id) => instruction_id,
|
||||
None => continue,
|
||||
};
|
||||
decoded_instruction_ids.insert(instruction_id);
|
||||
}
|
||||
let mut persisted = std::vec::Vec::new();
|
||||
for instruction in instructions {
|
||||
let program_id = match instruction.program_id.as_ref() {
|
||||
Some(program_id) => program_id,
|
||||
None => continue,
|
||||
};
|
||||
let audit_spec = match meteora_instruction_audit_spec(program_id.as_str()) {
|
||||
Some(audit_spec) => audit_spec,
|
||||
None => continue,
|
||||
};
|
||||
let instruction_id = match instruction.id {
|
||||
Some(instruction_id) => instruction_id,
|
||||
None => continue,
|
||||
};
|
||||
if decoded_instruction_ids.contains(&instruction_id) {
|
||||
continue;
|
||||
}
|
||||
let accounts = parse_instruction_accounts_vec(instruction.accounts_json.as_str());
|
||||
let payload = build_meteora_instruction_audit_payload(
|
||||
transaction,
|
||||
instruction,
|
||||
audit_spec.protocol_name,
|
||||
audit_spec.event_kind,
|
||||
program_id.as_str(),
|
||||
);
|
||||
let pool_account =
|
||||
candidate_meteora_audit_pool_account(audit_spec, accounts.as_slice());
|
||||
let persist_result = self
|
||||
.materialize_named_dex_event(
|
||||
transaction,
|
||||
transaction_id,
|
||||
instruction_id,
|
||||
audit_spec.protocol_name,
|
||||
program_id.clone(),
|
||||
audit_spec.event_kind,
|
||||
pool_account,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
payload,
|
||||
)
|
||||
.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_pump_fun_events(
|
||||
&self,
|
||||
transaction: &crate::ChainTransactionDto,
|
||||
@@ -1318,6 +1415,13 @@ struct RaydiumInstructionAuditSpec {
|
||||
candidate_pool_account_index: usize,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
struct MeteoraInstructionAuditSpec {
|
||||
protocol_name: &'static str,
|
||||
event_kind: &'static str,
|
||||
candidate_pool_account_index: std::option::Option<usize>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
struct RaydiumMappedNonTradeInstructionSpec {
|
||||
instruction_name: &'static str,
|
||||
@@ -1624,6 +1728,94 @@ fn read_u128_le_from_bytes(data: &[u8], offset: usize) -> std::option::Option<u1
|
||||
return Some(u128::from_le_bytes(bytes));
|
||||
}
|
||||
|
||||
fn meteora_instruction_audit_spec(
|
||||
program_id: &str,
|
||||
) -> std::option::Option<MeteoraInstructionAuditSpec> {
|
||||
if program_id == crate::METEORA_DBC_PROGRAM_ID {
|
||||
return Some(MeteoraInstructionAuditSpec {
|
||||
protocol_name: "meteora_dbc",
|
||||
event_kind: "meteora_dbc.instruction_audit",
|
||||
candidate_pool_account_index: Some(1),
|
||||
});
|
||||
}
|
||||
if program_id == crate::METEORA_DLMM_PROGRAM_ID {
|
||||
return Some(MeteoraInstructionAuditSpec {
|
||||
protocol_name: "meteora_dlmm",
|
||||
event_kind: "meteora_dlmm.instruction_audit",
|
||||
candidate_pool_account_index: Some(0),
|
||||
});
|
||||
}
|
||||
if program_id == crate::METEORA_DAMM_V1_PROGRAM_ID {
|
||||
return Some(MeteoraInstructionAuditSpec {
|
||||
protocol_name: "meteora_damm_v1",
|
||||
event_kind: "meteora_damm_v1.instruction_audit",
|
||||
candidate_pool_account_index: Some(0),
|
||||
});
|
||||
}
|
||||
if program_id == crate::METEORA_DAMM_V2_PROGRAM_ID {
|
||||
return Some(MeteoraInstructionAuditSpec {
|
||||
protocol_name: "meteora_damm_v2",
|
||||
event_kind: "meteora_damm_v2.instruction_audit",
|
||||
candidate_pool_account_index: Some(1),
|
||||
});
|
||||
}
|
||||
return None;
|
||||
}
|
||||
|
||||
fn candidate_meteora_audit_pool_account(
|
||||
audit_spec: MeteoraInstructionAuditSpec,
|
||||
accounts: &[std::string::String],
|
||||
) -> std::option::Option<std::string::String> {
|
||||
let index = match audit_spec.candidate_pool_account_index {
|
||||
Some(index) => index,
|
||||
None => return None,
|
||||
};
|
||||
return accounts.get(index).cloned();
|
||||
}
|
||||
|
||||
fn build_meteora_instruction_audit_payload(
|
||||
transaction: &crate::ChainTransactionDto,
|
||||
instruction: &crate::ChainInstructionDto,
|
||||
protocol_name: &str,
|
||||
event_kind: &str,
|
||||
program_id: &str,
|
||||
) -> serde_json::Value {
|
||||
let accounts = parse_instruction_accounts_value(instruction.accounts_json.as_str());
|
||||
let account_count = match accounts.as_array() {
|
||||
Some(items) => items.len(),
|
||||
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_prefix = data_base58
|
||||
.as_ref()
|
||||
.map(|value| return value.chars().take(16).collect::<std::string::String>());
|
||||
return serde_json::json!({
|
||||
"decoder": protocol_name,
|
||||
"eventKind": event_kind,
|
||||
"signature": transaction.signature,
|
||||
"instructionId": instruction.id,
|
||||
"instructionIndex": instruction.instruction_index,
|
||||
"innerInstructionIndex": instruction.inner_instruction_index,
|
||||
"innerInstruction": instruction.inner_instruction_index.is_some(),
|
||||
"parentInstructionId": instruction.parent_instruction_id,
|
||||
"programId": program_id,
|
||||
"programFamily": "meteora",
|
||||
"accounts": accounts,
|
||||
"accountCount": account_count,
|
||||
"data": data_base58,
|
||||
"dataPrefix": data_prefix,
|
||||
"discriminatorHex": discriminator_hex,
|
||||
"auditReason": "meteora_instruction_not_decoded_by_specific_decoder",
|
||||
"proofStatus": "unclassified_local_corpus_instruction",
|
||||
"tradeCandidate": false,
|
||||
"candleCandidate": false,
|
||||
"nonTradeUseful": false,
|
||||
"skipTradeReason": "instruction_audit_only",
|
||||
"skipCandleReason": "instruction_audit_only"
|
||||
});
|
||||
}
|
||||
|
||||
fn raydium_instruction_audit_event_kind_by_protocol(
|
||||
protocol_name: &str,
|
||||
) -> std::option::Option<&'static str> {
|
||||
@@ -2774,7 +2966,6 @@ mod tests {
|
||||
assert_eq!(decrease.pool_account_index, Some(3));
|
||||
assert_eq!(decrease.token_a_mint_index, Some(14));
|
||||
assert_eq!(decrease.token_b_mint_index, Some(15));
|
||||
|
||||
let increase = super::raydium_mapped_non_trade_instruction_spec(
|
||||
"raydium_clmm",
|
||||
Some("851d59df45eeb00a"),
|
||||
@@ -2800,7 +2991,6 @@ mod tests {
|
||||
None => panic!("collect_creator_fee discriminator must be mapped"),
|
||||
};
|
||||
assert_eq!(collect_creator_fee.event_kind, "raydium_cpmm.collect_creator_fee");
|
||||
|
||||
let withdraw = super::raydium_mapped_non_trade_instruction_spec(
|
||||
"raydium_cpmm",
|
||||
Some("b712469c946da122"),
|
||||
@@ -2811,7 +3001,6 @@ mod tests {
|
||||
None => panic!("withdraw discriminator must be mapped"),
|
||||
};
|
||||
assert_eq!(withdraw.event_kind, "raydium_cpmm.withdraw");
|
||||
|
||||
let initialize = super::raydium_mapped_non_trade_instruction_spec(
|
||||
"raydium_cpmm",
|
||||
Some("afaf6d1f0d989bed"),
|
||||
|
||||
Reference in New Issue
Block a user