0.7.47-1FE5
This commit is contained in:
@@ -20,6 +20,8 @@ pub struct DexDecodeService {
|
||||
meteora_damm_v2_decoder: crate::MeteoraDammV2Decoder,
|
||||
fluxbeam_decoder: crate::FluxbeamDecoder,
|
||||
dexlab_decoder: crate::DexlabDecoder,
|
||||
openbook_v2_decoder: crate::OpenBookV2Decoder,
|
||||
phoenix_v1_decoder: crate::PhoenixV1Decoder,
|
||||
}
|
||||
|
||||
impl DexDecodeService {
|
||||
@@ -40,6 +42,8 @@ impl DexDecodeService {
|
||||
meteora_damm_v2_decoder: crate::MeteoraDammV2Decoder::new(),
|
||||
fluxbeam_decoder: crate::FluxbeamDecoder::new(),
|
||||
dexlab_decoder: crate::DexlabDecoder::new(),
|
||||
openbook_v2_decoder: crate::OpenBookV2Decoder::new(),
|
||||
phoenix_v1_decoder: crate::PhoenixV1Decoder::new(),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -163,6 +167,36 @@ impl DexDecodeService {
|
||||
if let Err(error) = append_result {
|
||||
return Err(error);
|
||||
}
|
||||
let append_result = append_persisted_events_result(
|
||||
&mut persisted,
|
||||
self.decode_and_persist_openbook_v2_audit_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_phoenix_v1_audit_events(&transaction, &instructions)
|
||||
.await,
|
||||
);
|
||||
if let Err(error) = append_result {
|
||||
return Err(error);
|
||||
}
|
||||
|
||||
let decoded_instruction_ids = decoded_instruction_ids_from_persisted_events(&persisted);
|
||||
let append_result = append_persisted_events_result(
|
||||
&mut persisted,
|
||||
self.decode_and_persist_upstream_registry_matches(
|
||||
&transaction,
|
||||
&instructions,
|
||||
&decoded_instruction_ids,
|
||||
)
|
||||
.await,
|
||||
);
|
||||
if let Err(error) = append_result {
|
||||
return Err(error);
|
||||
}
|
||||
return Ok(persisted);
|
||||
}
|
||||
|
||||
@@ -218,9 +252,46 @@ impl DexDecodeService {
|
||||
if let Err(error) = cleanup_result {
|
||||
return Err(error);
|
||||
}
|
||||
let cleanup_result = self
|
||||
.delete_replaced_upstream_registry_match(
|
||||
transaction_id,
|
||||
instruction_id,
|
||||
protocol_name,
|
||||
event_kind,
|
||||
)
|
||||
.await;
|
||||
if let Err(error) = cleanup_result {
|
||||
return Err(error);
|
||||
}
|
||||
return Ok(materialized);
|
||||
}
|
||||
|
||||
async fn delete_replaced_upstream_registry_match(
|
||||
&self,
|
||||
transaction_id: i64,
|
||||
instruction_id: i64,
|
||||
protocol_name: &str,
|
||||
event_kind: &str,
|
||||
) -> Result<(), crate::Error> {
|
||||
if protocol_name == crate::UPSTREAM_REGISTRY_PROTOCOL_NAME {
|
||||
return Ok(());
|
||||
}
|
||||
if event_kind == crate::UPSTREAM_REGISTRY_INSTRUCTION_MATCH_EVENT_KIND {
|
||||
return Ok(());
|
||||
}
|
||||
let delete_result = crate::query_dex_decoded_events_delete_by_key(
|
||||
self.database.as_ref(),
|
||||
transaction_id,
|
||||
Some(instruction_id),
|
||||
crate::UPSTREAM_REGISTRY_INSTRUCTION_MATCH_EVENT_KIND,
|
||||
)
|
||||
.await;
|
||||
match delete_result {
|
||||
Ok(_) => return Ok(()),
|
||||
Err(error) => return Err(error),
|
||||
}
|
||||
}
|
||||
|
||||
async fn delete_replaced_instruction_audit(
|
||||
&self,
|
||||
transaction_id: i64,
|
||||
@@ -248,6 +319,92 @@ impl DexDecodeService {
|
||||
}
|
||||
}
|
||||
|
||||
async fn decode_and_persist_upstream_registry_matches(
|
||||
&self,
|
||||
transaction: &crate::ChainTransactionDto,
|
||||
instructions: &[crate::ChainInstructionDto],
|
||||
already_decoded_instruction_ids: &std::collections::HashSet<i64>,
|
||||
) -> 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 = already_decoded_instruction_ids.clone();
|
||||
for decoded_event in &decoded_events {
|
||||
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 instruction_id = match instruction.id {
|
||||
Some(instruction_id) => instruction_id,
|
||||
None => continue,
|
||||
};
|
||||
if decoded_instruction_ids.contains(&instruction_id) {
|
||||
continue;
|
||||
}
|
||||
let program_id = match instruction.program_id.as_ref() {
|
||||
Some(program_id) => program_id,
|
||||
None => continue,
|
||||
};
|
||||
let data_base58 = parse_instruction_data_base58(instruction.data_json.as_deref());
|
||||
let registry_match =
|
||||
crate::upstream_registry_match::upstream_registry_match_instruction_data(
|
||||
program_id.as_str(),
|
||||
data_base58.as_deref(),
|
||||
);
|
||||
let registry_match = match registry_match {
|
||||
Some(registry_match) => registry_match,
|
||||
None => continue,
|
||||
};
|
||||
let payload = build_upstream_registry_instruction_match_payload(
|
||||
transaction,
|
||||
instruction,
|
||||
®istry_match,
|
||||
data_base58.as_deref(),
|
||||
);
|
||||
let persist_result = self
|
||||
.materialize_named_dex_event(
|
||||
transaction,
|
||||
transaction_id,
|
||||
instruction_id,
|
||||
crate::UPSTREAM_REGISTRY_PROTOCOL_NAME,
|
||||
program_id.clone(),
|
||||
crate::UPSTREAM_REGISTRY_INSTRUCTION_MATCH_EVENT_KIND,
|
||||
None,
|
||||
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 persist_dexlab_event(
|
||||
&self,
|
||||
transaction: &crate::ChainTransactionDto,
|
||||
@@ -1480,6 +1637,104 @@ impl DexDecodeService {
|
||||
return Ok(persisted);
|
||||
}
|
||||
|
||||
async fn persist_openbook_v2_event(
|
||||
&self,
|
||||
transaction: &crate::ChainTransactionDto,
|
||||
decoded_event: &crate::OpenBookV2DecodedEvent,
|
||||
) -> Result<crate::DexDecodedEventDto, crate::Error> {
|
||||
match decoded_event {
|
||||
crate::OpenBookV2DecodedEvent::Audit(event) => {
|
||||
return self
|
||||
.materialize_named_dex_event(
|
||||
transaction,
|
||||
event.transaction_id,
|
||||
event.instruction_id,
|
||||
"openbook_v2",
|
||||
event.program_id.clone(),
|
||||
event.event_kind.as_str(),
|
||||
None,
|
||||
event.market_account.clone(),
|
||||
event.token_a_mint.clone(),
|
||||
event.token_b_mint.clone(),
|
||||
None,
|
||||
event.payload_json.clone(),
|
||||
)
|
||||
.await;
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
async fn decode_and_persist_openbook_v2_audit_events(
|
||||
&self,
|
||||
transaction: &crate::ChainTransactionDto,
|
||||
instructions: &[crate::ChainInstructionDto],
|
||||
) -> Result<std::vec::Vec<crate::DexDecodedEventDto>, crate::Error> {
|
||||
let decoded_result = self.openbook_v2_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_openbook_v2_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 persist_phoenix_v1_event(
|
||||
&self,
|
||||
transaction: &crate::ChainTransactionDto,
|
||||
decoded_event: &crate::PhoenixV1DecodedEvent,
|
||||
) -> Result<crate::DexDecodedEventDto, crate::Error> {
|
||||
match decoded_event {
|
||||
crate::PhoenixV1DecodedEvent::Audit(event) => {
|
||||
return self
|
||||
.materialize_named_dex_event(
|
||||
transaction,
|
||||
event.transaction_id,
|
||||
event.instruction_id,
|
||||
"phoenix_v1",
|
||||
event.program_id.clone(),
|
||||
event.event_kind.as_str(),
|
||||
None,
|
||||
event.market_account.clone(),
|
||||
event.token_a_mint.clone(),
|
||||
event.token_b_mint.clone(),
|
||||
None,
|
||||
event.payload_json.clone(),
|
||||
)
|
||||
.await;
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
async fn decode_and_persist_phoenix_v1_audit_events(
|
||||
&self,
|
||||
transaction: &crate::ChainTransactionDto,
|
||||
instructions: &[crate::ChainInstructionDto],
|
||||
) -> Result<std::vec::Vec<crate::DexDecodedEventDto>, crate::Error> {
|
||||
let decoded_result = self.phoenix_v1_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_phoenix_v1_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_fluxbeam_events(
|
||||
&self,
|
||||
transaction: &crate::ChainTransactionDto,
|
||||
@@ -1964,7 +2219,7 @@ fn build_meteora_instruction_audit_payload(
|
||||
return serde_json::json!({
|
||||
"decoder": protocol_name,
|
||||
"eventKind": event_kind,
|
||||
"signature": transaction.signature,
|
||||
"signature": transaction.signature.clone(),
|
||||
"instructionId": instruction.id,
|
||||
"instructionIndex": instruction.instruction_index,
|
||||
"innerInstructionIndex": instruction.inner_instruction_index,
|
||||
@@ -2023,7 +2278,7 @@ fn build_raydium_instruction_audit_payload(
|
||||
return serde_json::json!({
|
||||
"decoder": protocol_name,
|
||||
"eventKind": event_kind,
|
||||
"signature": transaction.signature,
|
||||
"signature": transaction.signature.clone(),
|
||||
"instructionId": instruction.id,
|
||||
"instructionIndex": instruction.instruction_index,
|
||||
"innerInstructionIndex": instruction.inner_instruction_index,
|
||||
@@ -2073,6 +2328,58 @@ fn candidate_raydium_audit_pool_account(
|
||||
return accounts.get(spec.candidate_pool_account_index).cloned();
|
||||
}
|
||||
|
||||
fn build_upstream_registry_instruction_match_payload(
|
||||
transaction: &crate::ChainTransactionDto,
|
||||
instruction: &crate::ChainInstructionDto,
|
||||
registry_match: &crate::UpstreamRegistryEntryDto,
|
||||
data_base58: std::option::Option<&str>,
|
||||
) -> serde_json::Value {
|
||||
let data_bytes = instruction_data_bytes_from_base58(data_base58);
|
||||
let data_byte_len = match data_bytes.as_ref() {
|
||||
Some(data_bytes) => {
|
||||
let len_result = u64::try_from(data_bytes.len());
|
||||
match len_result {
|
||||
Ok(len) => serde_json::Value::Number(serde_json::Number::from(len)),
|
||||
Err(_) => serde_json::Value::Null,
|
||||
}
|
||||
},
|
||||
None => serde_json::Value::Null,
|
||||
};
|
||||
return serde_json::json!({
|
||||
"decoder": crate::UPSTREAM_REGISTRY_PROTOCOL_NAME,
|
||||
"matchKind": "instruction_discriminator",
|
||||
"signature": transaction.signature.clone(),
|
||||
"slot": transaction.slot,
|
||||
"instructionId": instruction.id,
|
||||
"instructionIndex": instruction.instruction_index,
|
||||
"innerInstructionIndex": instruction.inner_instruction_index,
|
||||
"parentInstructionId": instruction.parent_instruction_id,
|
||||
"stackHeight": instruction.stack_height,
|
||||
"programId": instruction.program_id.clone(),
|
||||
"programName": instruction.program_name.clone(),
|
||||
"accounts": parse_instruction_accounts_value(instruction.accounts_json.as_str()),
|
||||
"accountCount": parse_instruction_accounts_vec(instruction.accounts_json.as_str()).len(),
|
||||
"dataBase58": data_base58,
|
||||
"dataByteLen": data_byte_len,
|
||||
"upstreamSourceRepo": registry_match.source_repo.clone(),
|
||||
"upstreamSourcePath": registry_match.source_path.clone(),
|
||||
"upstreamDecoderCode": registry_match.decoder_code.clone(),
|
||||
"upstreamProgramFamily": registry_match.program_family.clone(),
|
||||
"upstreamSurfaceKind": registry_match.surface_kind.clone(),
|
||||
"upstreamEntryKind": registry_match.entry_kind.clone(),
|
||||
"upstreamEntryName": registry_match.entry_name.clone(),
|
||||
"upstreamDiscriminatorHex": registry_match.discriminator_hex.clone(),
|
||||
"upstreamDiscriminatorLen": registry_match.discriminator_len,
|
||||
"upstreamProofStatus": registry_match.proof_status.clone(),
|
||||
"upstreamNotes": registry_match.notes.clone(),
|
||||
"tradeCandidate": false,
|
||||
"candleCandidate": false,
|
||||
"nonTradeUseful": false,
|
||||
"skipTradeReason": "upstream_git_unverified_registry_match",
|
||||
"skipCandleReason": "upstream_git_unverified_registry_match"
|
||||
});
|
||||
}
|
||||
|
||||
fn parse_instruction_accounts_vec(accounts_json: &str) -> std::vec::Vec<std::string::String> {
|
||||
let accounts_result = serde_json::from_str::<std::vec::Vec<std::string::String>>(accounts_json);
|
||||
match accounts_result {
|
||||
@@ -2146,6 +2453,20 @@ fn append_persisted_events(
|
||||
}
|
||||
}
|
||||
|
||||
fn decoded_instruction_ids_from_persisted_events(
|
||||
persisted: &[crate::DexDecodedEventDto],
|
||||
) -> std::collections::HashSet<i64> {
|
||||
let mut decoded_instruction_ids = std::collections::HashSet::<i64>::new();
|
||||
for decoded_event in persisted {
|
||||
let instruction_id = match decoded_event.instruction_id {
|
||||
Some(instruction_id) => instruction_id,
|
||||
None => continue,
|
||||
};
|
||||
decoded_instruction_ids.insert(instruction_id);
|
||||
}
|
||||
return decoded_instruction_ids;
|
||||
}
|
||||
|
||||
fn append_persisted_events_result(
|
||||
target: &mut std::vec::Vec<crate::DexDecodedEventDto>,
|
||||
source_result: Result<std::vec::Vec<crate::DexDecodedEventDto>, crate::Error>,
|
||||
@@ -3223,4 +3544,60 @@ mod tests {
|
||||
);
|
||||
assert_eq!(super::instruction_audit_event_kind_by_protocol("unknown"), None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn upstream_registry_match_payload_is_never_trade_or_candle_candidate() {
|
||||
let transaction = crate::ChainTransactionDto::new(
|
||||
"upstream-registry-test-signature".to_string(),
|
||||
Some(123),
|
||||
Some(123456),
|
||||
Some("test".to_string()),
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
"{}".to_string(),
|
||||
);
|
||||
let instruction = crate::ChainInstructionDto::new(
|
||||
1,
|
||||
None,
|
||||
0,
|
||||
None,
|
||||
Some(crate::METEORA_DAMM_V2_PROGRAM_ID.to_string()),
|
||||
None,
|
||||
None,
|
||||
"[]".to_string(),
|
||||
Some("data".to_string()),
|
||||
None,
|
||||
None,
|
||||
);
|
||||
let registry_match = crate::UpstreamRegistryEntryDto {
|
||||
source_repo: Some("sevenlabs-hq/carbon".to_string()),
|
||||
source_path: Some("decoders/example.rs".to_string()),
|
||||
decoder_code: "meteora-damm-v2".to_string(),
|
||||
program_id: Some(crate::METEORA_DAMM_V2_PROGRAM_ID.to_string()),
|
||||
program_family: "meteora".to_string(),
|
||||
surface_kind: "amm".to_string(),
|
||||
entry_kind: crate::ENTRY_KIND_INSTRUCTION.to_string(),
|
||||
entry_name: "swap".to_string(),
|
||||
discriminator_hex: Some("f8c69e91e17587c8".to_string()),
|
||||
discriminator_len: Some(8),
|
||||
proof_status: crate::PROOF_STATUS_UPSTREAM_GIT_UNVERIFIED.to_string(),
|
||||
notes: "test".to_string(),
|
||||
};
|
||||
let payload = super::build_upstream_registry_instruction_match_payload(
|
||||
&transaction,
|
||||
&instruction,
|
||||
®istry_match,
|
||||
Some("data"),
|
||||
);
|
||||
assert_eq!(payload.get("tradeCandidate").and_then(serde_json::Value::as_bool), Some(false));
|
||||
assert_eq!(
|
||||
payload.get("candleCandidate").and_then(serde_json::Value::as_bool),
|
||||
Some(false)
|
||||
);
|
||||
assert_eq!(
|
||||
payload.get("upstreamProofStatus").and_then(serde_json::Value::as_str),
|
||||
Some(crate::PROOF_STATUS_UPSTREAM_GIT_UNVERIFIED)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user