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

@@ -442,7 +442,7 @@ mod tests {
0,
None,
Some(crate::RAYDIUM_AMM_V4_PROGRAM_ID.to_string()),
Some("raydium-amm-v4".to_string()),
Some("raydium_amm_v4".to_string()),
Some(1),
r#"["Account0","Pool111","Lp111","TokenA111","TokenB111"]"#.to_string(),
None,
@@ -529,7 +529,7 @@ mod tests {
0,
None,
Some(crate::METEORA_DLMM_PROGRAM_ID.to_string()),
Some("meteora-dlmm".to_string()),
Some("meteora_dlmm".to_string()),
Some(1),
r#"["ParentAccount","Pool111"]"#.to_string(),
None,
@@ -548,7 +548,7 @@ mod tests {
0,
Some(0),
Some(crate::METEORA_DLMM_PROGRAM_ID.to_string()),
Some("meteora-dlmm".to_string()),
Some("meteora_dlmm".to_string()),
Some(2),
r#"["ChildAccount","Pool111"]"#.to_string(),
None,

View File

@@ -735,7 +735,7 @@ mod tests {
let database = make_database().await;
let upstream_service = crate::UpstreamRegistryService::new();
let request = crate::UpstreamRegistrySearchRequestDto {
decoder_code: Some("raydium-cpmm".to_string()),
decoder_code: Some("raydium_cpmm".to_string()),
program_id: None,
program_family: None,
surface_kind: None,
@@ -759,7 +759,7 @@ mod tests {
.expect("coverage upsert must succeed");
assert!(id > 0);
let rows =
crate::query_dex_event_coverage_entries_list_by_decoder(&database, "raydium-cpmm")
crate::query_dex_event_coverage_entries_list_by_decoder(&database, "raydium_cpmm")
.await
.expect("coverage list must succeed");
assert_eq!(rows.len(), 1);
@@ -768,7 +768,7 @@ mod tests {
.await
.expect("coverage summary must succeed");
assert_eq!(summaries.len(), 1);
assert_eq!(summaries[0].decoder_code, "raydium-cpmm");
assert_eq!(summaries[0].decoder_code, "raydium_cpmm");
assert_eq!(summaries[0].listed_entry_count, 1);
assert_eq!(summaries[0].decoded_entry_count, 1);
assert_eq!(summaries[0].observed_entry_count, 1);

View File

@@ -0,0 +1,173 @@
// file: kb_lib/src/db/queries/instruction_observation.rs
//! Queries for `k_sol_instruction_observations`.
/// Upserts one instruction observation row.
pub async fn query_instruction_observations_upsert(
database: &crate::Database,
dto: &crate::InstructionObservationDto,
) -> Result<i64, crate::Error> {
match database.connection() {
crate::DatabaseConnection::Sqlite(pool) => {
let query_result = sqlx::query(
r#"
INSERT INTO k_sol_instruction_observations (
observation_key,
transaction_id,
signature,
slot,
block_time,
failed,
instruction_id,
parent_instruction_id,
instruction_index,
inner_instruction_index,
program_id,
decoder_code,
discriminator_hex,
instruction_name,
accounts_json,
data_json,
pool_account,
decoded_event_kind,
decoded_event_id,
observed_at,
updated_at
)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
ON CONFLICT(observation_key) DO UPDATE SET
transaction_id = excluded.transaction_id,
signature = excluded.signature,
slot = excluded.slot,
block_time = excluded.block_time,
failed = excluded.failed,
instruction_id = excluded.instruction_id,
parent_instruction_id = excluded.parent_instruction_id,
instruction_index = excluded.instruction_index,
inner_instruction_index = excluded.inner_instruction_index,
program_id = excluded.program_id,
decoder_code = excluded.decoder_code,
discriminator_hex = excluded.discriminator_hex,
instruction_name = excluded.instruction_name,
accounts_json = excluded.accounts_json,
data_json = excluded.data_json,
pool_account = excluded.pool_account,
decoded_event_kind = excluded.decoded_event_kind,
decoded_event_id = excluded.decoded_event_id,
updated_at = excluded.updated_at
"#,
)
.bind(dto.observation_key.clone())
.bind(dto.transaction_id)
.bind(dto.signature.clone())
.bind(dto.slot)
.bind(dto.block_time)
.bind(if dto.failed { 1_i64 } else { 0_i64 })
.bind(dto.instruction_id)
.bind(dto.parent_instruction_id)
.bind(dto.instruction_index)
.bind(dto.inner_instruction_index)
.bind(dto.program_id.clone())
.bind(dto.decoder_code.clone())
.bind(dto.discriminator_hex.clone())
.bind(dto.instruction_name.clone())
.bind(dto.accounts_json.clone())
.bind(dto.data_json.clone())
.bind(dto.pool_account.clone())
.bind(dto.decoded_event_kind.clone())
.bind(dto.decoded_event_id)
.bind(dto.observed_at.to_rfc3339())
.bind(dto.updated_at.to_rfc3339())
.execute(pool)
.await;
let query_result = match query_result {
Ok(query_result) => query_result,
Err(error) => {
return Err(crate::Error::Db(format!(
"cannot upsert k_sol_instruction_observations on sqlite: {}",
error
)));
},
};
return Ok(query_result.last_insert_rowid());
},
}
}
/// Lists instruction observations by optional decoder/discriminator/instruction filters.
pub async fn query_instruction_observations_list_by_filter(
database: &crate::Database,
decoder_code: std::option::Option<&str>,
discriminator_hex: std::option::Option<&str>,
instruction_name: std::option::Option<&str>,
limit: u32,
) -> Result<std::vec::Vec<crate::InstructionObservationDto>, crate::Error> {
if limit == 0 {
return Ok(std::vec::Vec::new());
}
match database.connection() {
crate::DatabaseConnection::Sqlite(pool) => {
let query_result = sqlx::query_as::<sqlx::Sqlite, crate::InstructionObservationEntity>(
r#"
SELECT
id,
observation_key,
transaction_id,
signature,
slot,
block_time,
failed,
instruction_id,
parent_instruction_id,
instruction_index,
inner_instruction_index,
program_id,
decoder_code,
discriminator_hex,
instruction_name,
accounts_json,
data_json,
pool_account,
decoded_event_kind,
decoded_event_id,
observed_at,
updated_at
FROM k_sol_instruction_observations
WHERE (? IS NULL OR decoder_code = ?)
AND (? IS NULL OR discriminator_hex = ?)
AND (? IS NULL OR instruction_name = ?)
ORDER BY slot DESC, transaction_id DESC, instruction_id ASC
LIMIT ?
"#,
)
.bind(decoder_code.map(str::to_string))
.bind(decoder_code.map(str::to_string))
.bind(discriminator_hex.map(str::to_string))
.bind(discriminator_hex.map(str::to_string))
.bind(instruction_name.map(str::to_string))
.bind(instruction_name.map(str::to_string))
.bind(i64::from(limit))
.fetch_all(pool)
.await;
let entities = match query_result {
Ok(entities) => entities,
Err(error) => {
return Err(crate::Error::Db(format!(
"cannot list k_sol_instruction_observations on sqlite: {}",
error
)));
},
};
let mut dtos = std::vec::Vec::new();
for entity in entities {
let dto_result = crate::InstructionObservationDto::try_from(entity);
let dto = match dto_result {
Ok(dto) => dto,
Err(error) => return Err(error),
};
dtos.push(dto);
}
return Ok(dtos);
},
}
}